修改从库配置
vim /etc/mysql/mysql.conf.d/mysqld.cnf gtid_mode=on enforce_gtid_consistency=on #重启服务 systemctl restart mysql
#关闭同步 stop slave; #配置同步信息 使用MASTER_AUTO_POSITION自动获取同步信息 CHANGE MASTER TO MASTER_HOST='192.168.1.13', MASTER_PORT=3306, MASTER_USER='slave', MASTER_PASSWORD='1qaz@WSX0okm(IJN', MASTER_AUTO_POSITION=1; #开启同步 start slave; #查看状态 show slave status\G
查看主库状态
mysql> show master status; +-------------------+----------+--------------+----------------------+-------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +-------------------+----------+--------------+----------------------+-------------------+ | master-bin.000004 | 154 | demo | ignore_db,ignore_db2 | | +-------------------+----------+--------------+----------------------+-------------------+
咦,咋还是没有勒?
别急,因为该同步方式是基于事务id的,我们插入一条数据试试。
insert into demo_table values (3);
查看状态
mysql> show master status; +-------------------+----------+--------------+----------------------+------------------------------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +-------------------+----------+--------------+----------------------+------------------------------------------+ | master-bin.000004 | 676 | demo | ignore_db,ignore_db2 | 7fb11262-7654-11eb-9930-0242c0296f15:1-2 | +-------------------+----------+--------------+----------------------+------------------------------------------+ 1 row in set (0.00 sec)
发现已生效!
加入新节点
安装MySQL并修改配置
- vim /etc/mysql/mysql.conf.d/mysqld.cnf
[mysqld] server-id=3 relay-log-index=slave-relay-bin.index relay-log=slave-relay-bin log-bin=mysql-bin log-slave-updates=1 gtid_mode=on enforce_gtid_consistency=on read_only=1
- 重启MySQL
systemctl restart mysql
配置主从
#登陆MySQL mysql -uroot -p #配置主从,由于我们使用gtid同步,所以不再需要查看master节点状态 CHANGE MASTER TO MASTER_HOST='192.168.1.13', MASTER_PORT=3306, MASTER_USER='slave', MASTER_PASSWORD='1qaz@WSX0okm(IJN', MASTER_AUTO_POSITION=1; #开启同步 start slave; #查看状态 show slave status\G
问题解决
查看状态会发现以下错误
Got fatal error 1236 from master when reading data from binary log: 'The slave is connecting using CHANGE MASTER TO MASTER_AUTO_POSITION = 1, but the master has purged binary logs containing GTIDs that the slave requires. Replicate the missing transactions from elsewhere, or provision a new slave from backup. Consider increasing the master's binary log expiration period. The GTID set sent by the slave is '', and the missing transactions are '7fb11262-7654-11eb-9930-0242c0296f15:1-2'.'
看报错信息是因为我们清除了主库上的二进制文件,但我好像啥也没干
- 解决办法
在主库上执行以下命令,查询gtid_purged,记录下该值
show global variables like '%gtid%'\G
*************************** 1. row *************************** Variable_name: binlog_gtid_simple_recovery Value: ON *************************** 2. row *************************** Variable_name: enforce_gtid_consistency Value: ON *************************** 3. row *************************** Variable_name: gtid_executed Value: 7fb11262-7654-11eb-9930-0242c0296f15:1-2 *************************** 4. row *************************** Variable_name: gtid_executed_compression_period Value: 1000 *************************** 5. row *************************** Variable_name: gtid_mode Value: ON *************************** 6. row *************************** Variable_name: gtid_owned Value: *************************** 7. row *************************** Variable_name: gtid_purged Value: 7fb11262-7654-11eb-9930-0242c0296f15:1-2 *************************** 8. row *************************** Variable_name: session_track_gtids Value: OFF 8 rows in set (0.00 sec)
- 在从库上同样执行该命令,记录
gtid_executed
值
show global variables like '%gtid%'\G
*************************** 1. row *************************** Variable_name: binlog_gtid_simple_recovery Value: ON *************************** 2. row *************************** Variable_name: enforce_gtid_consistency Value: ON *************************** 3. row *************************** Variable_name: gtid_executed Value: 7fb11262-7654-11eb-9930-0242c0296f15:1-2 *************************** 4. row *************************** Variable_name: gtid_executed_compression_period Value: 1000 *************************** 5. row *************************** Variable_name: gtid_mode Value: ON *************************** 6. row *************************** Variable_name: gtid_owned Value: *************************** 7. row *************************** Variable_name: gtid_purged Value: 7fb11262-7654-11eb-9930-0242c0296f15:1-2 *************************** 8. row *************************** Variable_name: session_track_gtids Value: OFF 8 rows in set (0.00 sec)
咱这没有,就算了
- 重置从节点
stop slave; reset slave; #此时会将gtid_executed置空,因为我们这里本来就是空,如果有一定要置空,不然执行下面的命令将报错 reset master;
- 设置清除的事务id
set @@global.gtid_purged='7fb11262-7654-11eb-9930-0242c0296f15:1-2'; #再次查看 show global variables like '%gtid%'\G #此时会发现gtid_purged多了我们加的事务id
*************************** 1. row *************************** Variable_name: binlog_gtid_simple_recovery Value: ON *************************** 2. row *************************** Variable_name: enforce_gtid_consistency Value: ON *************************** 3. row *************************** Variable_name: gtid_executed Value: *************************** 4. row *************************** Variable_name: gtid_executed_compression_period Value: 1000 *************************** 5. row *************************** Variable_name: gtid_mode Value: ON *************************** 6. row *************************** Variable_name: gtid_owned Value: *************************** 7. row *************************** Variable_name: gtid_purged Value: *************************** 8. row *************************** Variable_name: session_track_gtids Value: OFF 8 rows in set (0.00 sec)
- 重新开启同步
CHANGE MASTER TO MASTER_HOST='192.168.1.13', MASTER_PORT=3306, MASTER_USER='slave', MASTER_PASSWORD='1qaz@WSX0okm(IJN', MASTER_AUTO_POSITION=1; #开启同步 start slave; #查看状态 show slave status\G
复制机制
以上我们已经搭建了1主2从的集群,但是仍然会有一个隐患,那就是我们常说的数据丢失问题
这是因为MySQL主从集群默认采用的是一种异步复制的机制。主服务在执行用户提交的事务后,写入binlog日志,然后就给客户端返回一个成功的响应了。而binlog会由一个dump线程异步发送给从服务。
由于这个发送binlog的过程是异步的。主服务在向客户端反馈执行结果时,是不知道binlog是否同步成功了的。这时候如果主服务宕机了,而从服务还没有备份到新执行的binlog,那就有可能会丢数据。
这时,半同步复制机制就要登场啦
半同步复制
半同步复制机制是一种介于异步复制和全同步复制之前的机制。主库在执行完客户端提交的事务后,并不是立即返回客户端响应,而是等待至少一个从库接收并写到relay log中,才会返回给客户端。MySQL在等待确认时,默认会等10秒,如果超过10秒没有收到ack,就会降级成为异步复制。
优点:相比异步复制,能够有效的提高数据的安全性。
缺点:会造成一定程度的延迟,这个延迟时间最少是一个TCP/IP请求往返的时间。整个服务的性能是会有所下降的。而当从服务出现问题时,主服务需要等待的时间就会更长,要等到从服务的服务恢复或者请求超时才能给用户响应。
配置半同步
- 安装扩展模块
查看扩展模块文件,一般是在/usr/lib/mysql/plugin
目录下
ls /usr/lib/mysql/plugin adt_null.so connection_control.so keyring_file.so locking_service.so mysql_no_login.so semisync_master.so validate_password.so auth_socket.so innodb_engine.so libmemcached.so mypluglib.so rewriter.so semisync_slave.so version_token.so
会看到有两个扩展模块
semisync_master.so
和semisync_slave.so
- 登陆主节点安装
semisync_master.so
-- 安装扩展模块 mysql> install plugin rpl_semi_sync_master soname 'semisync_master.so'; Query OK, 0 rows affected (0.17 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 (0.00 sec) # 打开半同步 mysql> set global rpl_semi_sync_master_enabled=ON; Query OK, 0 rows affected (0.00 sec)
rpl_semi_sync_master_enabled: 开启半同步
rpl_semi_sync_master_timeout:应答时间,单位毫秒,这里是10s
rpl_semi_sync_master_trace_level: 输出监控信息的级别
rpl_semi_sync_master_wait_for_slave_count: 等待的从库数量
rpl_semi_sync_master_wait_no_slave: 开启后从库同步跟上主库时,自动切换到同步模式
rpl_semi_sync_master_wait_point: 同步方式
AFTER_SYNC:默认方式,这种方式下,主库把日志写入binlog,并且复制给从库,然后开始等待从库的响应。从库返回成功后,主库再提交事务,接着给客户端返回一个成功响应。
AFTER_COMMIT:在主库写入binlog后,发送binlog复制到从库,主库就提交自己的本地事务,再等待从库返回给自己一个成功响应,然后主库再给客户端返回响应,由于主库并未等待从库同步成功就已经提交了事务,所以会造成其他会员能在主库上看到数据,在从库上却看不到数据的情况(数据不一致)。
- 登陆从库安装扩展模块
semisync_slave.so
mysql> install plugin rpl_semi_sync_slave soname 'semisync_slave.so'; Query OK, 0 rows affected (0.02 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 (0.02 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 (0.00 sec) -- 重新启动 mysql> stop slave; Query OK, 0 rows affected (0.00 sec) mysql> start slave; Query OK, 0 rows affected (0.00 sec)
- 查看同步状态
登陆主节点查看
SHOW STATUS LIKE '%semi%'; +--------------------------------------------+-------+ | Variable_name | Value | +--------------------------------------------+-------+ | Rpl_semi_sync_master_clients | 2 | 半同步复制客户端的个数 | Rpl_semi_sync_master_net_avg_wait_time | 0 | 平均等待时间,毫秒 | Rpl_semi_sync_master_net_wait_time | 0 | 总共等待时间 | Rpl_semi_sync_master_net_waits | 2 | 等待次数 | Rpl_semi_sync_master_no_times | 0 | 关闭半同步复制的次数 | Rpl_semi_sync_master_no_tx | 0 | 没有成功接收slave提交的次数 | Rpl_semi_sync_master_status | ON | 异步模式还是半同步模式,ON为半同步 | Rpl_semi_sync_master_timefunc_failures | 0 | 调用时间函数失败的次数 | Rpl_semi_sync_master_tx_avg_wait_time | 381 | 事务的平均传输时间 | Rpl_semi_sync_master_tx_wait_time | 381 | 事务的总共传输时间 | Rpl_semi_sync_master_tx_waits | 1 | 事物等待次数 | Rpl_semi_sync_master_wait_pos_backtraverse | 0 | 后来的先到了,而先来的还没有到的次数 | Rpl_semi_sync_master_wait_sessions | 0 | 有多少个session因为slave的回复而造成等待 | Rpl_semi_sync_master_yes_tx | 1 | 成功接受到slave事务回复的次数 +--------------------------------------------+-------+ 14 rows in set (0.00 sec)
数据延迟问题
在我们搭建的这个主从集群中,有一个比较隐藏的问题,就是这样的主从复制之间会有延迟。这在做了读写分离后,会更容易体现出来。即数据往主服务写,而读数据在从服务读。这时候这个主从复制延迟就有可能造成刚插入了数据但是查不到。
出现这个问题的根本在于:面向业务的主服务数据都是多线程并发写入的,而从服务是单个线程慢慢拉取binlog,这中间就会有个效率差。所以解决这个问题的关键是要让从服务也用多线程并行复制binlog数据。
登陆从节点修改配置
#停止同步 mysql> stop slave; Query OK, 0 rows affected (0.00 sec) mysql> show global variables like 'slave_parallel_workers%'; +------------------------+-------+ | Variable_name | Value | +------------------------+-------+ | slave_parallel_workers | 0 | +------------------------+-------+ 1 row in set (0.00 sec) mysql> set global slave_parallel_workers = 4; mysql> show global variables like 'slave_parallel_type%'; -- 默认是基于数据库的同步,即不同db下的操作可以在从库并行回放 +---------------------+----------+ | Variable_name | Value | +---------------------+----------+ | slave_parallel_type | DATABASE | +---------------------+----------+ 1 row in set (0.00 sec) -- LOGICAL_CLOCK方式,在主库对一个db或一张表并发执行的事务到slave端也可以并行回放 mysql> set global slave_parallel_type = LOGICAL_CLOCK; mysql> start slave; mysql> show processlist; +----+-------------+-----------+------+---------+------+--------------------------------------------------------+------------------+ | Id | User | Host | db | Command | Time | State | Info | +----+-------------+-----------+------+---------+------+--------------------------------------------------------+------------------+ | 8 | root | localhost | demo | Query | 0 | starting | show processlist | | 11 | system user | | NULL | Connect | 7 | Waiting for master to send event | NULL | | 12 | system user | | NULL | Connect | 7 | Slave has read all relay log; waiting for more updates | NULL | | 13 | system user | | NULL | Connect | 7 | Waiting for an event from Coordinator | NULL | | 14 | system user | | NULL | Connect | 7 | Waiting for an event from Coordinator | NULL | | 15 | system user | | NULL | Connect | 7 | Waiting for an event from Coordinator | NULL | | 16 | system user | | NULL | Connect | 7 | Waiting for an event from Coordinator | NULL | +----+-------------+-----------+------+---------+------+--------------------------------------------------------+------------------+
可以看到现在已经有4个同步线程了