MYSQL主从重要参数原理
最近压测一套MYSQL 8.0的主从架构数据库,一主两从,其中有一台式异地从库.
为此异地从库使用最大性能异步模式,而不是半同步插件方式.
经过压测的时候10个并发 一个小时才跑4万个订单. 3MBPS的网络带宽,实压想才有37KB数据同步,没有把带宽打满.同时从库远落后与主库.那个Seconds_Behind_Master都跑到了1万秒.
主库每20分钟产生1GB的日志,而从库却落后了20个日志量. 需要18个小时才能完成同步,这还是要在主库不再产生新的日志情况下.
而本地从库差距就胜小.
为什么呢?
mysql> show variables like '%relay%';
+---------------------------+--------------------------------------------+
| Variable_name | Value |
+---------------------------+--------------------------------------------+
| max_relay_log_size | 0 |
| relay_log | paylabs-drc-relay-bin |
| relay_log_basename | /var/lib/mysql/paylabs-drc-relay-bin |
| relay_log_index | /var/lib/mysql/paylabs-drc-relay-bin.index |
| relay_log_info_file | relay-log.info |
| relay_log_info_repository | TABLE |
| relay_log_purge | ON |
| relay_log_recovery | ON |
| relay_log_space_limit | 0 |
| sync_relay_log | 1 |
| sync_relay_log_info | 1 |
+---------------------------+--------------------------------------------+
因为我们设置了sync_relay_log =1 每1个事件就要刷盘 同步到中续日志文件中.
sync_relay_log 单位是event IO线程 当 SYNC_RELAY_LOG 设置成1000时候, 是从主库拉到 从库内存中.保存1000个后才刷入磁盘.
如果是SYNC_RELAY_LOG=1 那么每个事件都要刷入磁盘.那么因为FSYNC操作导致读取BINLOG速度变慢. 为此造成主库BINLOG无法及时同步到从库中.
如果设置成1000 读取BINLOG 不会受到FSYNC刷盘动作影响,影响会比较小.
从这里得知,这个参数影响我们的IO线程,使得它变忙,无法及时去主库拉取BINLOG.
IO 线程 :1. 读 binlog;2. 写到 relay log(文件缓存);3. 调用 fsync 刷到磁盘
读取 binlog 和刷盘 到 relay log 是两个动作 不过这两个动作是同步的相关联的.
此时此刻从库挂了,会丢失同步的999个事件吗?
那么要保证数据一致性,就得设置 relay log recovery =1 这岂不是两难啊?
这要看实际情况,如果MYSQL挂了,或者是STOP SLAVE,那么无所谓!
因为 SQL应用线程是从磁盘的中续日志OS CACHE中读入再应用.
也就是说SYNC_RELAY_LOG=1000 是积累1K事件刷盘,这1K事件已经写到了OS缓存区中,而SQL应用线程读取的时候,先读取OS缓存区中.这1K事件就被SQL线程读取到.这也不是SQL线程主动,是OS的行为.
另外不受 innodb_flush_method=O_DIRECT 影响.因为中续日志文件是普通的系统文件,不由INNODB掌控的数据文件.
如果sync_relay_log=1000 OS系统挂了怎么办?
这下就要涉及到很多参数了
sync_relay_log_info
sync_master_info
relay_log_info_repository
master_info_repository
relay_log_recovery
sync_master_info 表示IO线程写入中续日志最后MASTER位置信息.
sync_relay_log_info 表示SQL线程最后完成回放中续日志位置信息.
前面SYNC 表示同步.以前是同步到文件中,现在一般设置同步到表中
relay_log_info_repository=TABLE
master_info_repository=TABLE
由INNODB 完成持久化工作.
这样一来,系统挂了,我们MYSQL的IO线程和SQL线程就知道上一次工作结束的位置.
mysql> select * from mysql.slave_master_info\G;
*************************** 1. row ***************************
Number_of_lines: 31
Master_log_name: master_mysql-bin.000209
Master_log_pos: 38740
mysql> select * from mysql.slave_relay_log_info\G
*************************** 1. row ***************************
Number_of_lines: 7
Relay_log_name: ./mysql-relay.000015
Relay_log_pos: 621
Master_log_name: mysql-bin.000001
Master_log_pos: 2407
Sql_delay: 0
Number_of_workers: 16
Id: 1
Channel_name:
IO线程 从mysql.slave_master_info信息为起点去主库拉新的BINLOG;
SQL线程从mysql.slave_relay_log_info里的信息为起点从新恢复.
这就是完美解决了系统GAVE OVER的问题.真的吗?
这就是说sync_relay_log=1000 系统挂了丢失了999个事件,
SQL线程到底又没有把999个事件恢复了没? 这就要看mysql.slave_relay_log_info 实际上工作信息. 现在丢失了999个事件,表明当前存在的RELAY_LOG是不可用的,或者是损坏的.
那么我们就要启用relay_log_recovery=ON 参数. 它表示损坏了没有关系,把所有的中续日志给卡杀掉,重新建个新的中续日志文件.叫IO线程往这个新的中续日志文件写.
事情到此基本上就完美了,为了保证数据一致性.然并卵的事情是
sync_relay_log_info=1000
sync_master_info=1000
这两个参数默认值是1K 为了性能不设置1,是避免频繁更新INNODB. 哎! 这回带来啥问题啊?
很明显的是, 我们原本很清楚知道SQL工作结束位置, 而现在设置成1K后,就无法准确,精确了.
sync_relay_log=1000 && sync_relay_log_info=1000
SQL线程我很卖力气,实际上这1K事件我都恢复了.然系统挂了,丢失了999个事件,INFO 也丢失了888个事件.如果继续以mysql.slave_relay_log_info为标准,以前的工作岂不是白干了吗? 说真的白干不可怕,因为你要重新干过,再来一遍,就会导致数据重复,或者不一致性发生. 哦!MY GOD.
master_auto_position
master_auto_position的作用是根据从库的 Executed_Gtid_Set 自动寻找主库上对应 binlog 位置,这是在 GTID 出现后的一个功能。
-
当第一次或者 reset slave 后,执行 start slave,io thread 将从库的 Executed_Gtid_Set 发往主库,获取到对应的 File、Position,之后更新到从库的 slave_relay_log_info、slave_master_info 表中 -
当 slave_relay_log_info、slave_master_info 表中存在位置信息后,此后无论是重启复制还是重启 mysqld,都是直接从这两个地方获取 File、Position,并从这里开始读取 binlog 和回放 relay log
如果 master_auto_position=0,下次启动复制时会从新开始获取并回放主库的 binlog,造成错误。
这需要我人工完成这操作.
1 reset slave;
2 CHANGE MASTER TO ...,MASTER_PORT=3306,MASTER_AUTO_POSITION=1,..;
3 START SLAVE;
其实我们不需要执行这波人工操作,因为relay_log_info_repository=TABLE,默认值 sync_relay_log_info=1000 此时同步事件会失效,变成每回放一个事务都会在这个事务里同时更新 mysql.slave_relay_log_info 表,保证持久性,以最终保证复制的数据一致。当然 InooDB 的持久性需要 innodb_flush_log_at_trx_commit=1 来保证。
这样一来 relay_log_info_repository=TABLE & relay_log_recovery=ON
就能保证数据一致性,知道自己SQL恢复到那个位置,已经恢复对应的主库日志位置.
sync_relay_log_info=1000 & mysql.slave_master_info 就没有什么意义了.
为了提高性能 参数设置
sync_master_info=1000
sync_relay_log = 1000
为了保证数据一致性
relay_log_info_repository=TABLE
master_info_repository=TABLE
relay_log_recovery=ON
从库INNODB 这块参数:
sync_binlog=1
innodb_flush_method = O_DIRECT
innodb_flush_neighbors=1
innodb_flush_log_at_trx_commit=1
log_slave_updates = OFF