1.为什么要拆分数据库?

单体项目在构建之初,数据库的负载和数据量都不大,所以不需要对数据库做拆分,小型财务系统、文书系统、ERP系统、OA系统,用一个MySQL数据库实例基本就够用了。
就像《淘宝技术这十年》里面说到的,电商业务的数据量增长飞快,所以最开始的PHP+MySQL的架构已经不能满足实际要求了,于是淘宝想到的第一个办法就是把MySQL替换成Oracle。但是没过了多久,在08年前后,单节点的Oracle数据库也不好用了,于是淘宝终于告别了单节点数据库,开始拆分数据库。从一个节点,变成多个节点。
拆分数据库是有讲究的,比如说拆分方法有两种:垂直切分和水平切分。那你是先水平切分还是垂直切分呢?顺序无所谓?不,顺序有所谓,次序绝对不能错:先水平切分,然后垂直切分。

2.什么是垂直切分?

垂直切分是根据业务来拆分数据库,同一类业务的数据表拆分到一个独立的数据库,另一类的数据表拆分到其他数据库。
比如说一个新零售的电商数据库,我们可以把跟商品相关的数据表拆分成一个数据库,然后在这些数据表的基础之上,构建出商品系统。比如用JAVA或者PHP语言,创建出一个商城系统。然后把跟进销存相关的数据表拆分到另外一个数据库上,再用程序构建出仓库系统。

image-20230425163203533

#垂直切分解决了什么问题:
垂直切分可以降低单节点数据库的负载。原来所有数据表都放在一个数据库节点上,无疑所有的读写请求也都发到这个MySQL上面,所以数据库的负载太高。如果把一个节点的数据库拆分成多个MySQL数据库,这样就可以有效的降低每个MySQL数据库的负载。
垂直切分不能解决什么问题
垂直切分不能解决的是缩表,比如说商品表无论划分给哪个数据库节点,商品表的记录还是那么多,不管你把数据库垂直拆分的有多细致,每个数据表里面的数据量是没有变化的。
MySQL单表记录超过2000万,读写性能会下降的很快,因此说垂直切分并不能起到缩表的效果。

3.什么是水平切分?

水平切分是按照某个字段的某种规则,把数据切分到多张数据表。一张数据表化整为零,拆分成多张数据表,这样就可以起到缩表的效果了。

image-20230425163306095

很多人,都会水平切分存在误解,以为水平切分出来的数据表必须保存在不同的MySQL节点上。其实水平切分出来的数据表也可以保存在一个MySQL节点上面。不是水平切分一定需要多个MySQL节点。为什么这么说呢?

许多人不知道MySQL自带一种数据分区的技术,可以把一张表的数据,按照特殊规则,切分存储在不同的目录下。如果我们给Linux主机挂载了多块硬盘,我们完全可以利用MySQL分区技术,把一张表的数据切分存储在多个硬盘上。这样就由原来一块硬盘有限的IO能力,升级成了多个磁盘增强型的IO。

水平切分的用途:

水平切分可以把数据切分到多张数据表,可以起到缩表的作用。
但是也不是所有的数据表都要做水平切分。数据量较大的数据表才需要做数据切分,比如说电商系统中的,用户表、商品表、产品表、地址表、订单表等等。有些数据表就不需要切分,因为数据量不多,比如说品牌表、供货商表、仓库表,这些都是不需要切分的。

水平切分的缺点:

不同数据表的切分规则并不一致,要根据实际业务来确定。所以我们在选择数据库中间件产品的时候,就要选择切分规则丰富的产品。常见的数据库中间件有:MyCat、Atlas、ProxySQL等等。有些人觉得MyCat是Java语言开发的,就怀疑MyCat运行效率。其实数据库中间件的作用相当于SQL语句的路由器。你家路由器硬件配置不怎么高,但是不影响你享用百兆宽带。MyCat也是一个道理,它仅仅是起到SQL语句转发的作用,并不会实际执行SQL语句。我推荐使用MyCat最主要的原因是它自带了非常多的数据切分规则,我们可以按照主键求模切分数据,可以按照主键范围切分数据,还可以按照日期切分数据等等。因此说,为了满足业务的需要,MyCat目前来说算是非常不错的中间件产品。

水平切分的另一个缺点就是扩容比较麻烦,日积月累,分片迟早有不够用的时候。这时候不是首先选择增加新的集群分片。因为一个MySQL分片,需要4~8个MySQL节点(最小规模),增加一个分片的投入成本是很高的。所以正确的做法是做冷热数据分离,定期对分片中的数据归档。把过期的业务数据,从分片中转移到归档库。目前来说数据压缩比最高的MySQL引擎是TokuDB,而且带着事物的写入速度是InnoDB引擎的6-14倍。用TokuDB作为归档数据库最适合不过。
image-20230425163527433

4.为什么先做水平切分,后作垂直切分?

随着数据量的增加,最先应该做的是数据分片,利用多块硬盘来增大数据IO能力和存储空间,这么做的成本是最低的。几块硬盘的钱就能收获不错的IO性能。

进入到下一个阶段,数据量继续增大,这时候我们应该把数据切分到多个MySQL节点上,用MyCat管理数据切分。当然还要做数据的读写分离等等,这里不展开讨论。在后台做水平切分的同时,业务系统也可以引入负载均衡、分布式架构等等。理论上,使用了冷热数据分离之后,水平切分这种方式可以继续维持很长一段时间,数据量再大也不怕,定期归档就好了。

数据库到了水平切分的阶段,数据量的增加已经不是更改架构设计的主要原因了。反而这个阶段业务系统承受不住了,如果再不对系统做模块拆分,业务系统也撑不下去了,所以按照模块和业务,把一个系统拆分成若干子系统。若干子系统之间,数据相对独立。比如淘宝不会跟支付支付宝分享全部数据,共享同一套数据表,这也影响各自业务的发展。所以就要弄垂直切分了,把数据表归类,拆分成若干个数据库系统。

讲到这里,你仔细想想。如果过早的对数据库做了垂直切分,势必要重新构建若干独立的业务系统,工作量太巨大。水平切分并不需要业务系统做大幅度的修改,因此说应该先从水平切分开始做。

实践拆分案列 /以wordpress和wecenter为例

服务器准备:

主机名称 应用环境 外网Ip 内网Ip
web01 nginx+php 10.0.0.7 172.16.1.7
web02 nginx+php 10.0.0.8 172.16.1.8
db01 mysql 10.0.0.51 172.16.1.51

web01原数据库操作:

#web01网站服务器操作如下
    备份web01上的数据库   -B:指定导出一个数据库
mysqldump -uroot -p123 -B wp >/tmp/wp.sql
mysqldump -uroot -p123 -B wc >/tmp/wc.sql
#将web01上备份的数据库拷贝至db01服务器上
scp /tmp/wc.sql root@10.0.0.51:/tmp/
scp /tmp/wp.sql root@10.0.0.51:/tmp/
# 停止web01本机的数据库
systemctl stop mariadb

新数据库操作

# 安装数据库
yum install -y mariadb-server
# 启动服务并加入开机自启
systemctl start mariadb
systemctl enable mariadb
# 检查进程
[root@db01 ~]# ps -ef | grep mariadb
# 端口检查
[root@db01 ~]# netstat -lntup | grep 3306
# 设置数据库用户和密码123
mysqladmin -uroot password '123'
# 导入数据
mysql -uroot -p123 < /tmp/wp.sql
mysql -uroot -p123 < /tmp/wc.sql
# 查看是否导入
[root@db01 ~]# mysql -uroot -p123
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 5
Server version: 5.5.68-MariaDB MariaDB Server

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| test               |
| wc                 |      #//出现同web01数据库同名库表示导入成功
| wp                 |
+--------------------+
6 rows in set (0.00 sec)
# 创建数据库用户   <                                                >
MariaDB [(none)]> grant all on wc.* to wc_user@'%' identified by '123';
Query OK, 0 rows affected (0.00 sec)
MariaDB [(none)]> grant all on wp.* to wp_user@'%' identified by '123';
Query OK, 0 rows affected (0.00 sec)

MariaDB [(none)]> 
    #创建命令写详解;
    `grant all`: 授予所有权限。
    `on wc.*`: 授予`wc`数据库中所有表的权限(“*”表示所有表)。
    `to wc_user@'%'`: 将授权授予用户名为`wc_user` (用户名可以自定义),从任何主机(`%`)登录。
    `identified by '123'`: 指定对于用户`wc_user`的身份验证密码为`123`。
## 去web01服务器修改配置文件
#修改wordpress
[root@web01 ~]# vim /code/wordpress/wp-config.php
// ** MySQL 设置 - 具体信息来自您正在使用的主机 ** //
/** WordPress数据库的名称 */
define('DB_NAME', 'wp');
/** MySQL数据库用户名 */
define('DB_USER', 'wp_user');     #//改为创建的数据库用户
/** MySQL数据库密码 */
define('DB_PASSWORD', '123')
/** MySQL主机 */
define('DB_HOST', '10.0.0.51');   #//改为新数据库主机ip
/** 创建数据表时默认的文字编码 */
define('DB_CHARSET', 'utf8mb4')
/** 数据库整理类型。如不确定请勿更改 */
define('DB_COLLATE', '');
#修改wecenter
[root@web01 ~]# vim /code/wecenter/system/config/database.php
<?php

$config['charset'] = 'utf8';^M
$config['prefix'] = 'aws_';^M
$config['driver'] = 'MySQLi';^M
$config['master'] = array (
  'charset' => 'utf8',
  'host' => '10.0.0.51',  #//改为新数据库主机ip
  'username' => 'wc_user',    #//改为创建的数据库用户
  'password' => '123',         
  'dbname' => 'wc',
);^M
$config['slave'] = false;^M

多台web

为什么拓展web节点?

单台web服务器能抗住的访问量是有限的,配置多台web服务器能提升更高的访问速度。

解决了什么

1.单台web节点如果故障,会导致业务down

2.多台web节点能保证业务的持续稳定,扩展性高

3.多台web节点能有效的提升用户访问网站的速度

3.多台web节点技术架构组成,如下图所示

image-20230425172442776

部署web02

# 安装nginx和php  //可使用nginx和php压缩包安装
#首先下载压缩包,然后创建一个目录把压缩包上传到目录并解压
[root@web02 nginx_php]# yum localinstall -y *.rpm
# 创建www用户
groupadd www -g 666
useradd www -u 666 -g 666 -s /sbin/nologin/ -M
# 修改nginx php主配置文件
[root@web02 ~]# vim /etc/nginx/nginx.conf
user www;     #//改为www
[root@web02 ~]# vim /etc/php-fpm.d/www.conf
;   'port'                 - to listen on a TCP socket to all addresses
;                            (IPv6 and IPv4-mapped) on a specific port;
;   '/path/to/unix/socket' - to listen on a unix socket.
; Note: This value is mandatory.
listen = /opt/sock      #//此行改为套接字方式
listen.owner = www      #//改为www用户    //此两行可在原文件下文中找到这两行复制到listen下面即可
listen.group = www      #//改为www用户

; Set listen(2) backlog.
; Default Value: 511 (-1 on FreeBSD and OpenBSD)
;listen.backlog = 511
#启动服务
[root@web02 ~]# systemctl start nginx php-fpm
# web01传送.conf配置文件给web02
[root@web01 conf.d]# scp blog.conf root@10.0.0.8:/etc/nginx/conf.d/
[root@web01 conf.d]# scp zh.conf root@10.0.0.8:/etc/nginx/conf.d/
# 创建站点目录
mkdir /code
# web01传送站点目录到web02
[root@web01 ~]# scp -r /code/ root@10.0.0.8:/
# 授权  因为scp推送的一个特性,需要重新给目录修改属组
chown -R www.www /code/
# 重启服务
systemctl restart nginx php-fpm
# 本地域名解析
10.0.0.8 blog.xxx.com zh.xxx.com