MySQL主从问题及解决方案

参考"高性能MySQL一书"
主库意外关闭:
如果没有设置sync_binlog选项,就可能在数据库崩溃前没将最后的部分二进制事件刷新至磁盘,备库IO系线程可能一直处于读不到未写入磁盘的状态.
当主库重新启动,备库重新连接到主库,被再次去读取这些事件,主库会告诉备库没有这个二进制日志偏移量.
解决方法:
从下一个日志的开头读取日志,但是一些日志事件将丢失,主从恢复正常后使用pt-table-checksum来检测数据的一致性,以便修复.
修复可以使用工具pt-table-sync或者mysqldump
建议事项:
sync_binlog=1 
innodb_flush_log_at_trx_commit=1 
innodb_flush_log_at_trx_commit = 0,Innodb 中的Log Thread 每隔1 秒钟会将log buffer中的数据写入到文件,同时还会通知文件系统进行文件同步的flush 操作,保证数据确实已经写入到磁盘上面的物理文件.但是,每次事务的结束(commit 或者是rollback)并不会触发Log Thread 将log buffer 中的数据写入文件.所以,当设置为0 的时候,当MySQL Crash 和OS Crash 或者主机断电之后,最极端的情况是丢失1 秒时间的数据变更.
innodb_flush_log_at_trx_commit = 1,这也是Innodb 的默认设置.我们每次事务的结束都会触发Log Thread 将log buffer 中的数据写入文件并通知文件系统同步文件.这个设置是最安全的设置,能够保证不论是MySQL Crash 还是OS Crash 或者是主机断电都不会丢失任何已经提交的数据.
innodb_flush_log_at_trx_commit = 2,当我们设置为2 的时候,Log Thread 会在我们每次事务结束的时候将数据写入事务日志,但是这里的写入仅仅是调用了文件系统的文件写入操作.而我们的文件系统都是有缓存机制的,所以Log Thread 的这个写入并不能保证内容真的已经写入到物理磁盘上面完成持久化的动作.文件系统什么时候会将缓存中的这个数据同步到物理磁盘文件Log Thread 就完全不知道了.所以,当设置为2 的时候,MySQL Crash 并不会造成数据的丢失,但是OS Crash 或者是主机断电后可能丢失的数据量就完全控制在文件系统上了.各种文件系统对于自己缓存的刷新机制各不一样,大家可以自行参阅相关的手册.


备库意外关闭
备库意外关比并重启后,会去重新读取master.info文件以找到上次停止复制的位置.但是此时该文件并没有同步写入到磁盘,这时候文件中的信息就可能是错误的.
备库常常会重新执行一些二进制事件,这可能会导致主键冲突,唯一索引冲突等.唯一的办法就是忽略错误.
解决办法:
可以使用工具pt-slave-restart,如果是innodb表可以重启后观察错误日志
找到二进制日志坐标,可以使用这个值重新指向主库的便宜量,然后再用pt-table-checksum检查数据是否一致
建议事项:
skip_slave_start=1
#能够阻止备库崩溃后自动启动复制,这样可以为你提供足够的时间修复可能出现的问题 
sync_master_info     =1 #默认值为10000即每10000次sync_relay_log事件会刷新到磁盘
sync_relay_log         =1 #如果不介意额外的fsync()导致的性能问题可以都设置
sync_relay_log_info  =1 #成1 即1次事件就刷新至磁盘


二进制日志损坏
主库二进制日志损坏
解决方法:
只能忽略损坏的位置,可以在主库上执行flush logs 然后备库指向该文件的开始位置.或者试着发现损坏的结束位置,然后使用SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1来忽略一个错误,如果多个损坏事件就要执行多次.
备库二进制日志损坏
解决方法:
通过CHANGE MASTER TO 丢弃并重新获取损坏的事件,只要将备库指向它当前正在复制的位置(根据Relay_Master_Log_File/Exec_Master_Log_Pos确定),这样会导致丢弃所有在磁盘上的中继日志.


对复制数据的依赖性
如果在主库上有备库不存在的表
此时操作该表复制就会报错
或者备库存在主库上不存在的表
此时创建该表复制也会报错
解决方法:
避免主备这些差异,可以禁止不相干的人访问备库,进行一些操作


丢失的临时表
临时表与基于语句的复制是不相容的,如果备库崩溃或者正常关闭,任何复制线程拥有的临时表都回丢失,重启备库后所有依赖于临时表的语句都会失败.
解决方法:
1)手动创建一个名字和结构相同的表来代替临时表
2)直接跳过错误
以上方法虽然解决报错,但是扔可能导致数据的不一致,可以使用pt-table-checksum及pt-table-sync来检测并同步异常数据.
建议事项:
1)采用实体表代替临时表,当不再使用的时候将其删除
2)在关闭备库前确保状态变量Slave_open_temp_tables为0,正确的流程是先stop slave 然后检查Slave_open_temp_tables 最后关闭备库.


慎用binlog-do-db,binlog-ignore-db,replicate-do-db,replicate-ignore-db
假如设置了binlog-ignore-db = garbage
mysql> delete from garbage.junk; #此时从库没有garbage.junk表报错
mysql> use garbage;
mysql> update production.users set disabled = 1 where user = "root"; #由于设置了binlog-ignore-db = garbage 此条语句在从库上不会重放
换句话说, 过滤不是基于 查询的字符串的, 而实际于你used的数据库.
建议事项:
使用replicate-wild-* 选项
 
Innodb加锁引起的锁争用
在使用基于语句复制的时候,INSERT ...SELECT ...会在锁定源表的所有行,只有加锁才能保证该语句的执行结果在主库和备库上一致的.加锁实际上会导致主库上语句的串行化.
建议事项:
1)分解命令,把事务控制的尽可能小
2)主库使用SELECT INTO OUTFILE备库使用LOAD DATA INFILE代替INSERT ... SELECT...这个问题最大的麻烦就是为输出文件选择名称唯一的处理,可以用CONNECT_ID()来保证文件的唯一.
3)关闭锁机制innodb_locks_unsafe_for_binlog=1 这样使得依赖的数据不再加锁,但是如果其它查询修改了数据,并在第一条语句之前提交,则可能造成主备数据不一致,建议不用


过大的复制延迟
复制是单线程的,如果在备库上执行查询可能会堵塞住复制线程,这时候复制线程将无法做别的事.
建议事项:
1)备库上打开慢查询日志,如果打开log_slow_slave_statements选项,慢查询语句中也会记录复制线程执行的语句
2)innodb_flush_log_at_trx_commit 设置为2
3)禁用备库的二进制日志
4)主库上尽量少用昂贵的写操作,如REPLACE INTO TB(COL1,COL2...)SELECT COL1,SUM() FROM ... GROUP BY ...可以在备库执行这些操作然后通过SELECT INTO OUTFILE和LOAD DATA INFILE写回主库
5)对REPLACE INTO TB(COL1,COL2...)SELECT COL1,SUM() FROM ... GROUP BY ...进行分离,把SELECT 结果返回给应用程序处理,应用程序逐条处理REPLACE INTO TB(...) VALUES(...)
mysql主从复制,经常会遇到错误而导致slave端复制中断,这个时候一般就需要人工干预,跳过错误才能继续
跳过错误有两种方式:
跳过指定数量的事务
mysql>slave stop;
mysql>SET GLOBAL SQL_SLAVE_SKIP_COUNTER = 1        #跳过一个事务
mysql>slave start


修改mysql的配置文件,通过slave_skip_errors参数来跳所有错误或指定类型的错误
vi /etc/my.cnf
[mysqld]
slave-skip-errors=1062,1053,1146 #跳过指定error no类型的错误
slave-skip-errors=all #跳过所有错误


其他注意事项
1)避免使用事务型与非事务型表
2)非事务型引擎尽量或避免使用kill,因为在主的上面执行了查询,会在从的上面重放,此时kill,从的不会回滚
3)主库备库尽量使用相同的引擎
4)尽量不要在备库上操作 可以设置read_only选项
5)@@servier_id确保唯一,而且必须显示指定(my.cnf).

你可能感兴趣的:(MYSQL)