mysql主从架构
开局准备
主从理论
- MySQL的主从集群搭建,可以解决MySQL的数据存储和访问压力。
- 保证数据安全:MySQL主从配置相当于多了一个备份数据。
- 实现读写分离:在互联网项目中很多都是读多写少的场景,那么就可以进行读写分离,主数据库写入数据,从数据库读数据。实现读写分离需要一些中间件,比如ShardingSphere。
- 保证高可用:当主数据库宕机了后,可以在从数据库中选举出新的主数据库,保证服务的可用性。实现高可用也需要一些中间件,比如MMM,MHA,MGR等。
同步原理
- MySQL主从架构的数据同步是通过binlog日志文件实现的。在主服务器上打开binlog日志记录每一步的数据库操作,然后从服务器会有一个IO线程和主服务器建立TCP连接请求主服务器上的binlog日志传输过来。主库(主数据库服务器,下同)上dump线程会通过这个TCP连接把binlog日志文件内容传输给从服务器,接着从库(从数据库服务器,下同)会把读取到的binlog日志数据写入自己的relay日志文件中。然后从库上另一线程会读取relay文件内容进行操作重演,还原数据。如下图
- MySQL的binlog日志出来用于主从数据同步,还可以用于缓存数据同步。比如在redis中的缓存数据可以模拟一个从节点向MySQL发起binlog数据同步请求,然后将数据写入到redis,实现缓存和数据库一致。
- 在搭建主从架构时,双方的MySQL版本必须一致,或者主服务器版本低于从服务器。还有两节点时间需要同步。
主从搭建
主数据库搭建
- 首先打开主节点上的配置文件:/etc/my.cnf。配置打开binlog日志和指定serverId。如下图
[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
- 这里可以通过命令查看主节点同步状态:
show master status;
- file:指当前日志的binlog文件。
- position:指文件中的索引。
- binlog_do_db和binlog_ignore_db指记录binlog文件的库和不需要记录binlog文件的库。(为空表示没有配置)
从数据库搭建
- 同样打开mysql配置文件进行配置: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-bin:打开binlog日志记录。
-
然后启动MySQL从服务器,并设置他的主节点同步状态:
#登录从服务
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;
#开启slave
start slave;
#查看主从同步状态
show slave status;
或者用 show slave status \G; 这样查看比较简洁
- 注意:CHANGE MASTER必须要指定MASTER_LOG_FILE和MASTER_LOG_POS与主数据库保持一致。后续检查主从架构是否配置成功也需要使用file和position两个属性是否一致来确定。
- 关注上图中两个圈起来的属性,如果和主节点保持一致那就说明主从同步搭建成功。
主从同步测试
- 可以先用show databases命令,查看两个mysql服务器中数据库的情况。如下图
- 然后在主库上用以下命令添加一个数据库,发现从库上也有对应的数据库。如下图
mysql> create database syncdemo;
Query OK, 1 row affected (0.00 sec)
- 接着在syncdemo数据库中添加一张表和一条数据,发现在从库上也有对应的表和数据。如下图
mysql> use syncdemo;
Database changed
mysql> create table demoTable(id int not null);
Query OK, 0 rows affected (0.02 sec)
mysql> insert into demoTable value(1);
Query OK, 1 row affected (0.01 sec)
- 所以在主库上的写操作会同步到从库上面去。
- 如果在从库上查看状态时发现Slave_sql_runing=no,就表示主从同步失败。可能是因为从库上进行写操作时,与同步过来的sql操作冲突了,也可能是从库重启后回滚了事务操作。
- 这里数据不能由从库同步到主库。如果想要实现由从库同步到主库,只需要在主库上配置节点同步状态CHANGE MASTER TO…,那这就是双主模式。由此还可以配置互主集群模式。
主从同步扩展
- 上面都是对主从的全库同步操作,而在实际操作中可能只是对某个数据库或表进行同步。那也是可以通过配置的。
- 在主库的配置文件my.cnf 里配置如下:
#需要同步的二进制数据库名
binlog-do-db=masterdemo
#只保留7天的二进制日志,以防磁盘被日志占满(可选)
expire-logs-days = 7
#不备份的数据库
binlog-ignore-db=information_schema
binlog-ignore-db=performation_schema
binlog-ignore-db=sys
- 在从库的配置文件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
- 这里配置完成后使用shou master status命令,就可以看到binlog_do_db和binlog_lgnore_db两个参数的作用了。
- 这里为了保证数据一致性,通常数据写入主库,数据读取从库,这就是MySQL的读写分离了。如果要实现MySQL的读写分离,那么就需要依赖第三方插件ShardingSphere。
- 为了限制从库写入数据,可以配置read_only参数(set global read_only=1;)这样就可以限制用户直接写入数据,但是不会影响从库读取主库binlog日志操作同步数据。还有read_only只能限制普通操作用户的写入数据,如果拥有super权限的用户还是可以写入数据的,如果也要限定super用户的写数据可以使用super_read_only=0命令。
GTID同步集群模式
- GTID本质也是使用binlog日志实现同步数据的,只是它会基于一个全局事务ID来标识从库同步进度。所以GTID是全局事务ID,全局唯一且递增,保证为每一个主库上提交的事务在复制集群中科院生成一个唯一的ID。
- 在基于GTID的同步数据中,从库会告诉主库已经在从库执行完事务的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同步复制。
集群扩容
- 在上面的一主一从集群模式中,如果需要扩展成一主多从模式,只需要增加从库的binlog复制即可。
- 如果之前的集群是已经运行一段时间,这时需要扩展从服务器时,之前的主库的binlog文件没办法复制到新的从服务器。那么就为新的从服务器增加一个数据复制操作。MySQL的数据备份恢复操作比较简单,可以先生成原始数据库的sql文件,然后放入新从服务器执行,然后在重复上面的配置从库的同步配置即可。
半同步复制
- 在MySQL的主从数据同步时,使用的是异步复制方式。主可以通过在用户commit提交事务后会写入到binlog文件,然后就返回客户端了。主库的另一个线程dump会发送binlog数据给从库。这里的异步会导致主库插入数据并返回后宕机了,这时dump线程还没有发送数据给从库,导致数据的不一致。
- 为了解决这个问题,MySQL有一种半同步复制数据模式来保证数据安全,就是把异步发送binlog数据变成同步发送。
- 当用户把事务提交到binlog文件后,不立即返回,而是等待从库接受数据并把数据写入relay-log文件响应给主节点后才返回成功响应到客户端,主库在等待从库的ack响应是有超时机制,默认等10秒,如果还没有得到从库的ack就会降级为异步复制数据。
- 这种同步复制数据相比异步复制数据,能提高数据一致性。但是也不是绝对一致性,因为只能保证事务提交后binlog至少能传输到一个从库,但并不保证从库这个事务是执行成功的。而且因为有同步等待,所以会有一定的延迟,服务性能会有下降。
搭建半同步复制模式
- 在MySQL5.5以上版本中已经自带了这个模块,包含在MySQL安装目录的lib/plugin目录下的semisync_master.so和semisync_salve.so两个文件中。需要在主服务器上安装semisync_master模块,在从服务器上安装semisync_salve模块。
- 首先安装主服务器的模块
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就是半同步复制的超时等待时间。可自行配置。
- 第三行命令:打开半同步复制开关
- 再安装从服务器的模块
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)
- 从服务器和主服务器安装过程差不多,这里安装完了后需要重启从服务器。
主从架构数据延迟
- 在主从架构中,关于数据同步复制间和MySQL读写分离会有延迟。在主数据插入数据时,又在从数据库读取数据,主从数据复制的延迟会导致数据不一致。
- 这个问题在于在用户写入数据提交事务时,以Java后端为例都是多线程并发写入,然后从服务器单线程拉取binlog文件,中间就由效率差。
- MySQL在5.7版本后支持服务器并行复制,可以在从服务器上设置slave_parallel_workers为一个大于0的数,并把slave_parallel_type参数设置为LOGLCAL_CLOCK就可以了。
mysql高可用方案
- 上面的MySQL集群都是基于自身功能搭建的,不具备高可用功能。即如果master主服务器挂了后,从服务器没法自动切换为主服务器。如果要使用MySQL的高可用,就需要一些第三方工具来实现。
- 常见的有:MMM,MHA,MGR。它们的一些共同点:
- 对主从集群架构的mater主节点进行监控。
- 自动对master主节点进行迁移。
- 重新配置从节点对新的主节点数据进行同步。
- 提一下目前在大型互联网公司中MHA高可用方案用的比较多。因为MMM已经快淘汰了,而MGR是新出来的工具,缺乏权威认证。
- 最后就不细讲其中三种的执行原理,有兴趣的可以自行研究一下。