一、MySQL高可用集群介绍
1、数据库主从架构与分库分表
随着现在互联网的应用越来越大,数据库会频繁的成为整个应用的性能瓶颈。而 我们经常使用的MySQL数据库,也会不断面临数据量太大、数据访问太频繁、数据 读写速度太快等一系列的问题。所以,我们需要设计复杂的应用架构来保护孱弱的 数据库,例如添加Redis缓存,增加MQ进行流量削峰等等。但是,数据库本身如果 不能得到提升,这就相当于是水桶理论中的最短板。
而要提升数据库的性能,一种思路,当然是对数据库本身进行优化,例如对 MySQL进行优化配置,或者干脆换成ClickHouse这一类的针对大数据的产品。另一 方面就是跟微服务架构的思路一样,从单体架构升级到集群架构,这样才能真正全 方位解放数据库的性能瓶颈。而我们后续要学习的分库分表就是一种非常常见的数 据库集群架构管理方案。
但是就像微服务架构并不是简单的将服务从单机升级为就能一样,分库分表也并 不只是字面意义上的将数据分到多个库或者多个表这么简单,他也是基于数据库产 品的一系列分布式解决方案。在不同的应用场景下,针对不同的数据库产品,分库 分表也有不同的落地方式。
2、MySQL主从同步原理
既然要解决MySQL数据库的分布式集群化问题,那就不能不先了解MySQL自身 提供的主从同步原理。这是构建MySQL集群的基础,也是后续进行分库分表的基 础,更是MySQL进行生产环境部署的基础。 其实数据库的主从同步,就是为了要保证多个数据库之间的数据保持一致。最简 单的方式就是使用数据库的导入导出工具,定时将主库的数据导出,再导入到从库 当中。这是一种很常见,也很简单易行的数据库集群方式。也有很多的工具帮助我 们来做这些事情。但是这种方式进行数据同步的实时性比较差。 而如果要保证数据能够实时同步,对于MySQL,通常就要用到他自身提供的一套 通过Binlog日志在多个MySQL服务之间进行同步的集群方案。基于这种集群方案, 一方面可以提高数据的安全性,另外也可以以此为基础,提供读写分离、故障转移 等其他高级的功能
即在主库上打开Binlog日志,记录对数据的每一步操作。然后在从库上打开 RelayLog日志,用来记录跟主库一样的Binlog日志,并将RelayLog中的操作日志在 自己数据库中进行重演。这样就能够更加实时的保证主库与从库的数据一致。
MySQL的Binlog默认是不打开的。
他的实现过程是在从库上启动一系列IO线程,负责与主库建立TCP连接,请求主 库在写入Binlog日志时,也往从库传输一份。这时,主库上会有一个IO Dump线 程,负责将Binlog日志通过这些TCP连接传输给从库的IO线程。而从库为了保证日 志接收的稳定性,并不会立即重演Binlog数据操作,而是先将接收到的Binlog日志 写入到自己的RelayLog日志当中。然后再异步的重演RelayLog中的数据操作。 MySQL的BinLog日志能够比较实时的记录主库上的所有日志操作,因此他也被 很多其他工具用来实时监控MySQL的数据变化。例如Canal框架,可以模拟一个 slave节点,同步MySQL的Binlog,然后将具体的数据操作按照定制的逻辑进行转 发。例如转发到Redis实现缓存一致,转发到Kafka实现数据实时流转等。而 ClickHouse也支持将自己模拟成一个MySQL的从节点,接收MySQL的Binlog日 志,实时同步MySQL的数据。这个功能目前还在实验阶段。
二、动手搭建MySQL主从集群
1、搭建主从集群
1》配置master主服务
首先,配置主节点的mysql配置文件: /etc/my.cnf(没有的话就手动创建一个) 这一步需要对master进行配置,主要是需要打开binlog日志,以及指定 severId。我们打开MySQL主服务的my.cnf文件,在文件中一行server-id以及一个 关闭域名解析的配置。然后重启服务。
[mysqld]
server-id=47
#开启binlog
log_bin=master-bin
log_bin-index=master-bin.index
skip-name-resolve
# 设置连接端口
port=3306
# 设置mysql的安装目录
basedir=/usr/local/mysql
# 设置mysql数据库的数据的存放目录
datadir=/usr/local/mysql/mysql-files
# 允许最大连接数
max_connections=200
# 允许连接失败的次数。
max_connect_errors=10
# 服务端使用的字符集默认为UTF8
character-set-server=utf8
# 创建新表时将使用的默认存储引擎
default-storage-engine=INNODB
# 默认使用“mysql_native_password”插件认证
#mysql_native_password
default_authentication_plugin=mysql_native_password
配置说明:主要需要修改的是以下几个属性:
server-id:服务节点的唯一标识。需要给集群中的每个服务分配一个单独的ID。
log_bin:打开Binlog日志记录,并指定文件名。
log_bin-index:Binlog日志文件
重启MySQL服务,
service mysqld restart
然后,我们需要给root用户分配一个replication slave的权限。
#登录主数据库
mysql -u root -p
GRANT REPLICATION SLAVE ON *.* TO 'root'@'%';
flush privileges;
#查看主节点同步状态:
show master status;
在实际生产环境中,通常不会直接使用root用户,而会创建一个拥有全 部权限的用户来负责主从同步。
这个指令结果中的File和Position记录的是当前日志的binlog文件以及文件中的索 引。
而后面的Binlog_Do_DB和Binlog_Ignore_DB这两个字段是表示需要记录binlog 文件的库以及不需要记录binlog文件的库。目前我们没有进行配置,就表示是针对 全库记录日志。这两个字段如何进行配置,会在后面进行介绍。
开启binlog后,数据库中的所有操作都会被记录到datadir当中,以一组 轮询文件的方式循环记录。而指令查到的File和Position就是当前日志的 文件和位置。而在后面配置从服务时,就需要通过这个File和Position通 知从服务从哪个地方开始记录binLog。
2》配置slave从服务
下一步,我们来配置从服务mysqls。 我们打开mysqls的配置文件my.cnf,修改 配置文件:
[mysqld]
#主库和从库需要不一致
server-id=48
#打开MySQL中继日志
relay-log-index=slave-relay-bin.index
relay-log=slave-relay-bin
#打开从服务二进制日志
log-bin=mysql-bin
#使得更新的数据写进二进制日志中
log-slave-updates=1
# 设置3306端口
port=3306
# 设置mysql的安装目录
basedir=/usr/local/mysql
# 设置mysql数据库的数据的存放目录
datadir=/usr/local/mysql/mysql-files
# 允许最大连接数
max_connections=200
# 允许连接失败的次数。
max_connect_errors=10
# 服务端使用的字符集默认为UTF8
character-set-server=utf8
# 创建新表时将使用的默认存储引擎
default-storage-engine=INNODB
# 默认使用“mysql_native_password”插件认证
#mysql_native_password
default_authentication_plugin=mysql_native_password
配置说明:主要需要关注的几个属性:
server-id:服务节点的唯一标识
relay-log:打开从服务的relay-log日志。
log-bin:打开从服务的bin-log日志记录。
然后我们启动mysqls的服务,并设置他的主节点同步状态。
#登录从服务
mysql -u root -p;
#设置同步主节点:
CHANGE MASTER TO
MASTER_HOST='192.168.232.128',
MASTER_PORT=3306,
MASTER_USER='root',
MASTER_PASSWORD='root',
MASTER_LOG_FILE='master-bin.000004',
MASTER_LOG_POS=156,
GET_MASTER_PUBLIC_KEY=1; #指示是否从源请求基于RSA密钥对的密码交换所需的公共密钥
#开启slave
start slave;
#查看主从同步状态
show slave status;
或者用 show slave status \G; 这样查看比较简洁
注: GET_MASTER_PUBLIC_KEY 参数在8.0之前无效
CHANGE MASTER指令中需要指定的MASTER_LOG_FILE和 MASTER_LOG_POS必须与主服务中查到的保持一致。
并且后续如果要检查主从架构是否成功,也可以通过检查主服务与从服 务之间的FilePosition这两个属性是否一致来确定。
红色方框的两个属性,与主节点保持一致,就表示这个主从同 步搭建是成功的
只有当Slave_IO_Running 和 Slave_SQL_Running 都为yes 时才搭配成功
3》主从集群测试
测试时,我们先用showdatabases,查看下两个MySQL服务中的数据库情况
然后我们在主服务器上创建一个数据库
mysql> create database syncdemo;
Query OK, 1 row affected (0.00 sec)
然后我们再用show databases,来看下这个syncdemo的数据库是不是已经同步 到了从服务。
接下来我们继续在syncdemo这个数据库中创建一个表,并插入一条数据。
然后我们也同样到主服务与从服务上都来查一下这个demoTable是否同步到了从 服。
4》全库同步与部分同步
在完成这个基本的MySQL主从集群后,我们还可以进行后续的实验: 之前提到,我们目前配置的主从同步是针对全库配置的,而实际环境中,一般并 不需要针对全库做备份,而只需要对一些特别重要的库或者表来进行同步。
那如何 针对库和表做同步配置呢? 首先在Master端:在my.cnf中,可以通过以下这些属性指定需要针对哪些库或者哪 些表记录binlog
#需要同步的二进制数据库名
binlog-do-db=masterdemo
#只保留7天的二进制日志,以防磁盘被日志占满(可选)
expire-logs-days = 7
#不备份的数据库
binlog-ignore-db=information_schema
binlog-ignore-db=performation_schema
binlog-ignore-db=sys
然后在Slave端:在my.cnf中,需要配置备份库与主服务的库的对应关系
#如果salve库名称与master库名相同,使用本配置
replicate-do-db = masterdemo
#如果master库名[mastdemo]与salve库名[mastdemo01]不同,使用以下配置[需要做映射]
replicate-rewrite-db = masterdemo -> masterdemo01
#如果不是要全部同步[默认全部同步],则指定需要同步的表
replicate-wild-do-table=masterdemo01.t_dict
replicate-wild-do-table=masterdemo01.t_num
配置完成了之后,在show master status指令中,就可以看到Binlog_Do_DB和 Binlog_Ignore_DB两个参数的作用了。
5》GTID同步集群
上面我们搭建的集群方式,是基于Binlog日志记录点的方式来搭建的,这也是最 为传统的MySQL集群搭建方式。而在这个实验中,可以看到有一个 Executed_Grid_Set列,暂时还没有用上。实际上,这就是另外一种搭建主从同步的 方式,即GTID搭建方式。这种模式是从MySQL5.6版本引入的。
GTID的本质也是基于Binlog来实现主从同步,只是他会基于一个全局的事务ID来 标识同步进度。GTID即全局事务ID,全局唯一并且趋势递增,他可以保证为每一个 在主节点上提交的事务在复制集群中可以生成一个唯一的ID 。
在基于GTID的复制中,首先从服务器会告诉主服务器已经在从服务器执行完了哪 些事务的GTID值,然后主库会有把所有没有在从库上执行的事务,发送到从库上进 行执行,并且使用GTID的复制可以保证同一个事务只在指定的从库上执行一次,这 样可以避免由于偏移量的问题造成数据不一致。
他的搭建方式跟我们上面的主从架构整体搭建方式差不多。只是需要在my.cnf中 修改一些配置。
在主节点上:
gtid_mode=on
enforce_gtid_consistency=on
log_bin=on
server_id=单独设置一个
binlog_format=row
在从节点上:
gtid_mode=on
enforce_gtid_consistency=on
log_slave_updates=1
server_id=单独设置一个
然后分别重启主服务和从服务,就可以开启GTID同步复制方式
2、集群扩容与MySQL数据迁移
我们现在已经搭建成功了一主一从的MySQL集群架构,那要扩展到一主多从的集 群架构,其实就比较简单了,只需要增加一个binlog复制就行了。
但是如果我们的集群是已经运行过一段时间,这时候如果要扩展新的从节点就有 一个问题,之前的数据没办法从binlog来恢复了。这时候在扩展新的slave节点时, 就需要增加一个数据复制的操作。 MySQL的数据备份恢复操作相对比较简单,可以通过SQL语句直接来完成。具体 操作可以使用mysql的bin目录下的mysqldump工具。
mysqldump -u root -p --all-databases > backup.sql
#输入密码
通过这个指令,就可以将整个数据库的所有数据导出成backup.sql,然后把这个 backup.sql分发到新的MySQL服务器上,并执行下面的指令将数据全部导入到新的 MySQL服务中。
mysql -u root -p < backup.sql
#输入密码
这样新的MySQL服务就已经有了所有的历史数据,然后就可以再按照上面的步 骤,配置Slave从服务的数据同步了。
3、搭建半同步复制
1》理解半同步复制
到现在为止,我们已经可以搭建MySQL的主从集群,互主集群,但是我们这个集 群有一个隐患,就是有可能会丢数据。这是为什么呢?这要从MySQL主从数据复制 分析起。
MySQL主从集群默认采用的是一种异步复制的机制。主服务在执行用户提交的事 务后,写入binlog日志,然后就给客户端返回一个成功的响应了。而binlog会由一 个dump线程异步发送给Slave从服务。
由于这个发送binlog的过程是异步的。主服务在向客户端反馈执行结果时,是不 知道binlog是否同步成功了的。这时候如果主服务宕机了,而从服务还没有备份到 新执行的binlog,那就有可能会丢数据。 那怎么解决这个问题呢,这就要靠MySQL的半同步复制机制来保证数据安全。
半同步复制机制是一种介于异步复制和全同步复制之前的机制。主库在执行完客 户端提交的事务后,并不是立即返回客户端响应,而是等待至少一个从库接收并写 到relay log中,才会返回给客户端。MySQL在等待确认时,默认会等10秒,如果超 过10秒没有收到ack,就会降级成为异步复制。
这种半同步复制相比异步复制,能够有效的提高数据的安全性。但是这种安全性 也不是绝对的,他只保证事务提交后的binlog至少传输到了一个从库,并且并不保 证从库应用这个事务的binlog是成功的。另一方面,半同步复制机制也会造成一定 程度的延迟,这个延迟时间最少是一个TCP/IP请求往返的时间。整个服务的性能是 会有所下降的。而当从服务出现问题时,主服务需要等待的时间就会更长,要等到 从服务的服务恢复或者请求超时才能给用户响应。
2》搭建半同步复制集群
半同步复制需要基于特定的扩展模块来实现。而mysql从5.5版本开始,往上的版 本都默认自带了这个模块。这个模块包含在mysql安装目录下的lib/plugin目录下的 semisync_master.so和semisync_slave.so两个文件中。需要在主服务上安装 semisync_master模块,在从服务上安装semisync_slave模块。
首先我们登陆主服务,安装semisync_master模块:
mysql> install plugin rpl_semi_sync_master soname 'semisync_master.so';
Query OK, 0 rows affected (0.01 sec)
mysql> show global variables like 'rpl_semi%';
+-------------------------------------------+------------+
| Variable_name | Value |
+-------------------------------------------+------------+
| rpl_semi_sync_master_enabled | OFF |
| rpl_semi_sync_master_timeout | 10000 |
| rpl_semi_sync_master_trace_level | 32 |
| rpl_semi_sync_master_wait_for_slave_count | 1 |
| rpl_semi_sync_master_wait_no_slave | ON |
| rpl_semi_sync_master_wait_point | AFTER_SYNC |
+-------------------------------------------+------------+
6 rows in set, 1 warning (0.02 sec)
mysql> set global rpl_semi_sync_master_enabled=ON;
Query OK, 0 rows affected (0.00 sec)
这三行指令中,第一行是通过扩展库来安装半同步复制模块,需要指定 扩展库的文件名。
第二行查看系统全局参数,rpl_semi_sync_master_timeout就是半同步 复制时等待应答的最长等待时间,默认是10秒,可以根据情况自行调 整。
第三行则是打开半同步复制的开关。
在第二行查看系统参数时,最后的一个参数
rpl_semi_sync_master_wait_point其实表示一种半同步复制的方式。
半同步复制有两种方式,一种是我们现在看到的这种默认的 AFTER_SYNC方式。这种方式下,主库把日志写入binlog,并且复制给 从库,然后开始等待从库的响应。从库返回成功后,主库再提交事务, 接着给客户端返回一个成功响应。
而另一种方式是叫做AFTER_COMMIT方式。他不是默认的。这种方式, 在主库写入binlog后,等待binlog复制到从库,主库就提交自己的本地 事务,再等待从库返回给自己一个成功响应,然后主库再给客户端返回 响应。
然后我们登陆从服务,安装smeisync_slave模块
mysql> install plugin rpl_semi_sync_slave soname 'semisync_slave.so';
Query OK, 0 rows affected (0.01 sec)
mysql> show global variables like 'rpl_semi%';
+---------------------------------+-------+
| Variable_name | Value |
+---------------------------------+-------+
| rpl_semi_sync_slave_enabled | OFF |
| rpl_semi_sync_slave_trace_level | 32 |
+---------------------------------+-------+
2 rows in set, 1 warning (0.01 sec)
mysql> set global rpl_semi_sync_slave_enabled = on;
Query OK, 0 rows affected (0.00 sec)
mysql> show global variables like 'rpl_semi%';
+---------------------------------+-------+
| Variable_name | Value |
+---------------------------------+-------+
| rpl_semi_sync_slave_enabled | ON |
| rpl_semi_sync_slave_trace_level | 32 |
+---------------------------------+-------+
2 rows in set, 1 warning (0.00 sec)
mysql> stop slave;
Query OK, 0 rows affected (0.01 sec)
mysql> start slave;
Query OK, 0 rows affected (0.01 sec)
slave端的安装过程基本差不多,不过要注意下安装完slave端的半同步插 件后,需要重启下slave服务。
3、主从集群与读写分离
我们要注意,目前我们的这个MySQL主从集群是单向的,也就是只能从主服务同 步到从服务,而从服务的数据表更是无法同步到主服务的。
所以,在这种架构下,为了保证数据一致,通常会需要保证数据只在主服务上 写,而从服务只进行数据读取。这个功能,就是大名鼎鼎的读写分离。但是这里要 注意下,mysql主从本身是无法提供读写分离的服务的,需要由业务自己来实现。 这也是我们后面要学的ShardingSphere的一个重要功能。
到这里可以看到,在MySQL主从架构中,是需要严格限制从服务的数据写入的,一旦从服务有数据写入,就会造成数据不一致。并且从服务在执行事务期间还很容易造成数据同步失败。
如果需要限制用户写数据,我们可以在从服务中将read_only参数的值设为1( set global read_only=1; )。这样就可以限制用户写入数据。但是这个属性有两个需要注意的地方:
1、read_only=1设置的只读模式,不会影响slave同步复制的功能。 所以在MySQL slave库中设定了read_only=1后,通过 "show slavestatus\G" 命令查看salve状态,可以看到salve仍然会读取master上的日志,并且在slave库中应用日志,保证主从数据库同步一致;
2、read_only=1设置的只读模式, 限定的是普通用户进行数据修改的操作,但不会限定具有super权限的用户的数据修改操作。 在MySQL中设置read_only=1后,普通的应用用户进行insert、update、delete等会
产生数据变化的DML操作时,都会报出数据库处于只读模式不能发生数据变化的错误,但具有super权限的用户,例如在本地或远程通过root用户登录到数据库,还是可以进行数据变化的DML操作; 如果需要限定
super权限的用户写数据,可以设置super_read_only=0。另外 如果要想连super权限用户的写操作也禁止,就使用"flush tables with readlock;",这样设置也会阻止主从同步复制!
三、了解MySQL的其他高可用方 案
我们之前的MySQL服务集群,都是使用MySQL自身的功能来搭建的集群。但是 这样的集群,不具备高可用的功能。即如果是MySQL主服务挂了,从服务是没办法 自动切换成主服务的。而如果要实现MySQL的高可用,需要借助一些第三方工具来 实现。
这一部分方案只需要了解即可,因为一方面,这些高可用方案通常都是运维需要考虑的事情,作为开发人员没有必要花费太多的时间精力,偶尔需要用到的时候能够用起来就够了。另一方面,随着业界技术的不断
推进,也会冒出更多的新方案。例如ShardingSphere的5.x版本的目标实际上就是将ShardingSphere由一个数据库中间件升级成一个独立的数据库平台,而将MySQL、PostGreSql甚至是RocksDB这些组件作为数据库的后端支撑。等到新版本成熟时,又会冒出更多新的高可用方案。
常见的MySQL集群方案有三种: MMM、MHA、MGR。这三种高可用框架都有 一些共同点:
对主从复制集群中的Master节点进行监控
自动的对Master进行迁移,通过VIP。
重新配置集群中的其它slave对新的Master进行同步
1、MMM
MMM(Master-Master replication managerfor Mysql,Mysql主主复制管理
器)是一套由Perl语言实现的脚本程序,可以对mysql集群进行监控和故障迁移。他
需要两个Master,同一时间只有一个Master对外提供服务,可以说是主备模式。
他是通过一个VIP(虚拟IP)的机制来保证集群的高可用。整个集群中,在主节点上
会通过一个VIP地址来提供数据读写服务,而当出现故障时,VIP就会从原来的主节
点漂移到其他节点,由其他节点提供服务。
优点:
提供了读写VIP的配置,使读写请求都可以达到高可用 工具包相对比较完善,不需要额外的开发脚本 完成故障转移之后可以对MySQL集群进行高可用监控
缺点:
故障简单粗暴,容易丢失事务,建议采用半同步复制方式,减少失败的概率 目前MMM社区已经缺少维护,不支持基于GTID的复制
适用场景:
读写都需要高可用的
基于日志点的复制方式
2、MHA
Master High Availability Manager and Tools for MySQL。是由日本人开发的一 个基于Perl脚本写的工具。这个工具专门用于监控主库的状态,当发现master节点 故障时,会提升其中拥有新数据的slave节点成为新的master节点,在此期间, MHA会通过其他从节点获取额外的信息来避免数据一致性方面的问题。MHA还提 供了mater节点的在线切换功能,即按需切换master-slave节点。MHA能够在30秒 内实现故障切换,并能在故障切换过程中,最大程度的保证数据一致性。在淘宝内 部,也有一个相似的TMHA产品。
MHA是需要单独部署的,分为Manager节点和Node节点,两种节点。其中 Manager节点一般是单独部署的一台机器。而Node节点一般是部署在每台MySQL 机器上的。 Node节点得通过解析各个MySQL的日志来进行一些操作。 Manager节点会通过探测集群里的Node节点去判断各个Node所在机器上的 MySQL运行是否正常,如果发现某个Master故障了,就直接把他的一个Slave提升
为Master,然后让其他Slave都挂到新的Master上去,完全透明。
优点:
MHA除了支持日志点的复制还支持GTID的方式 同MMM相比,MHA会尝试从旧的Master中恢复旧的二进制日志,只是未必每 次都能成功。如果希望更少的数据丢失场景,建议使用MHA架构。
缺点:
MHA需要自行开发VIP转移脚本。
MHA只监控Master的状态,未监控Slave的状态
3、MGR
MGR:MySQL Group Replication。 是MySQL官方在5.7.17版本正式推出的一种 组复制机制。主要是解决传统异步复制和半同步复制的数据一致性问题。 由若干个节点共同组成一个复制组,一个事务提交后,必须经过超过半数节点的决 议并通过后,才可以提交。引入组复制,主要是为了解决传统异步复制和半同步复 制可能产生数据不一致的问题。MGR依靠分布式一致性协议(Paxos协议的一个变 体),实现了分布式下数据的最终一致性,提供了真正的数据高可用方案(方案落地后
是否可靠还有待商榷)。 支持多主模式,但官方推荐单主模式:
多主模式下,客户端可以随机向MySQL节点写入数据
单主模式下,MGR集群会选出primary节点负责写请求,primary节点与其它节 点都可以进行读请求处理
优点:
高一致性,基于原生复制及paxos协议的组复制技术,并以插件的方式提供,提 供一致数据安全保证;
高容错性,只要不是大多数节点坏掉就可以继续工作,有自动检测机制,当不同 节点产生资源争用冲突时,不会出现错误,按照先到者优先原则进行处理,并且 内置了自动化脑裂防护机制;
高扩展性,节点的新增和移除都是自动的,新节点加入后,会自动从其他节点上 同步状态,直到新节点和其他节点保持一致,如果某节点被移除了,其他节点自 动更新组信息,自动维护新的组信息;
高灵活性,有单主模式和多主模式,单主模式下,会自动选主,所有更新操作都 在主上进行;多主模式下,所有server都可以同时处理更新操作。
缺点:
仅支持InnoDB引擎,并且每张表一定要有一个主键,用于做write set的冲突检 测; 必须打开GTID特性,二进制日志格式必须设置为ROW,用于选主与write set; 主从状态信息存于表中(--master-info-repository=TABLE 、--relay-loginfo-repository=TABLE),--log-slave-updates打开; COMMIT可能会导致失败,类似于快照事务隔离级别的失败场景 目前一个MGR集群最多支持9个节点 不支持外键于save point特性,无法做全局间的约束检测与部分事务回滚
适用的业务场景:
对主从延迟比较敏感
希望对对写服务提供高可用,又不想安装第三方软件
数据强一致的场景
四、分库分表方案介绍
1、分库分表有什么用
分库分表就是为了解决由于数据量过大而导致数据库性能降低的问题,将原来独立 的数据库拆分成若干数据库组成 ,将数据大表拆分成若干数据表组成,使得单一数据库、单一数据表的数据量变 小,从而达到提升数据库性能的目 的。
例如:微服务架构中,每个服务都分配一个独立的数据库,这就是分库。而对一些
业务日志表,按月拆分成不同的表,这就是分表。
2、分库分表的方式
分库分表包含分库和分表 两个部分,而这两个部分可以统称为数据分片,其目的都 是将数据拆分成不同的存储单元。另外,从分拆的角度上,可以分为垂直分片和水 平分片。
垂直分片: 按照业务来对数据进行分片,又称为纵向分片。他的核心理念就是转 库专用。在拆分之前,一个数据库由多个数据表组成,每个表对应不同的业务。 而拆分之后,则是按照业务将表进行归类,分布到不同的数据库或表中,从而将 压力分散至不同的数据库或表。例如,下图将用户表和订单表垂直分片到不同的
数据库:
垂直分片往往需要对架构和设计进行调整。通常来讲,是来不及应对业务需求快速 变化的。而且,他也无法真正的解决单点数据库的性能瓶颈。垂直分片可以缓解数 据量和访问量带来的问题,但无法根治。如果垂直分片之后,表中的数据量依然超 过单节点所能承载的阈值,则需要水平分片来进一步处理。
水平分片:又称横向分片。相对于垂直分片,它不再将数据根据业务逻辑分类, 而是通过某个字段(或某几个字段),根据某种规则将数据分散至多个库或表中, 每个分片仅包含数据的一部分。例如,像下图根据主键机构分片。
常用的分片策略有:
取余\取模 : 优点 均匀存放数据,缺点 扩容非常麻烦
按照范围分片 : 比较好扩容, 数据分布不够均匀
按照时间分片 : 比较容易将热点数据区分出来。
按照枚举值分片 : 例如按地区分片
按照目标字段前缀指定进行分区:自定义业务规则分片
水平分片从理论上突破了单机数据量处理的瓶颈,并且扩展相对自由,是分库分表 的标准解决方案。
一般来说,在系统设计阶段就应该根据业务耦合松紧来确定垂直分库,垂直分表 方案,在数据量及访问压力不是特别大的情况,首先考虑缓存、读写分离、索引技 术等方案。若数据量极大,且持续增长,再考虑水平分库水平分表方案。
3、分库分表要解决哪些问题
之前说过,分库分表其实并不只是字面意义上的拆分数据,他还有一系列的问题 需要解决。虽然数据分片解决了性能、可用性以及单点备份恢复等问题,但是分布 式的架构在获得收益的同时,也引入了非常多新的问题。而这些,都是一个成熟的 分库分表方案需要考虑的问题。
事务一致性问题
原本单机数据库有很好的事务机制能够帮我们保证数据一致性。但是分库分表 后,由于数据分布在不同库甚至不同服务器,不可避免会带来分布式事务问题。 跨节点关联查询问题 在没有分库时,我们可以进行很容易的进行跨表的关联查询。但是在分库后,表 被分散到了不同的数据库,就无法进行关联查询了。 这时就需要将关联查询拆分成多次查询,然后将获得的结果进行拼装。
跨节点分页、排序函数 跨节点多库进行查询时,limit分页、order by排序等问题,就变得比较复杂了。 需要先在不同的分片节点中将数据 进行排序并返回,然后将不同分片返回的结果集进行汇总和再次排序。 这时非常容易出现内存崩溃的问题。
主键避重问题
在分库分表环境中,由于表中数据同时存在不同数据库中,主键值平时使用的自 增长将无用武之地,某个分区数据 库生成的ID无法保证全局唯一。因此需要单独设计全局主键,以避免跨库主键重 复问题。
公共表处理
实际的应用场景中,参数表、数据字典表等都是数据量较小,变动少,而且属于 高频联合查询的依赖表。这一类表一般就需要在每个数据库中都保存一份,并且 所有对公共表的操作都要分发到所有的分库去执行。
运维工作量
面对散乱的分库分表之后的数据,应用开发工程师和数据库管理员对数据库的操 作都变得非常繁重。对于每一次数据读写操作,他们都需要知道要往哪个具体的 数据库的分表去操作,这也是其中重要的挑战之一。
4、什么时候需要分库分表?
在阿里巴巴公布的开发手册中,建议MySQL单表记录如果达到500W这个级别, 或者单表容量达到2GB,一般就建议进行分库分表。而考虑到分库分表需要对数据 进行再平衡,所以如果要使用分库分表,就要在系统设计之初就详细考虑好分库分 表的方案,
这里要分两种情况。
一般对于用户数据这一类后期增长比较缓慢的数据,一般可以按照三年左右的业 务量来预估使用人数,按照标准预设好分库分表的方案。 而对于业务数据这一类增长快速且稳定的数据,一般则需要按照预估量的两倍左 右预设分库分表方案。并且由于分库分表的后期扩容是非常麻烦的,所以在进行分 库分表时,尽量根据情况,多分一些表。最好是计算一下数据增量,永远不用增加 更多的表。
另外,在设计分库分表方案时,要尽量兼顾业务场景和数据分布。在支持业务场 景的前提下,尽量保证数据能够分得更均匀。
最后,一旦用到了分库分表,就会表现为对数据查询业务的灵活性有一定的影 响,例如如果按userId进行分片,那按age来进行查询,就必然会增加很多麻烦。如 果再要进行排序、分页、聚合等操作,很容易就扛不住了。这时候,都要尽量在分 库分表的同时,再补充设计一个降级方案,例如将数据转存一份到ES,ES可以实现 更灵活的大数据聚合查询。