深刻解析Mysql 主从同步延迟原理及解决方案

https://www.cnblogs.com/cnmenglang/p/6393769.htmlhtml

MySQL的主从同步是一个很成熟的架构,优势为:①在从服务器能够执行查询工做(即咱们常说的读功能),下降主服务器压力;②在从主服务器进行备份,避免备份期间影响主服务器服务;③当主服务器出现问题时,能够切换到从服务器。mysql

相信你们对于这些好处已经很是了解了,在项目的部署中也采用这种方案。可是MySQL的主从同步一直有从库延迟的问题,那么为何会有这种问题。这种问题如何解决呢?sql

1. MySQL数据库主从同步延迟原理。数据库

2. MySQL数据库主从同步延迟是怎么产生的。缓存

3. MySQL数据库主从同步延迟解决方案。安全

 

1. MySQL数据库主从同步延迟原理。服务器

答:谈到MySQL数据库主从同步延迟原理,得从mysql的数据库主从复制原理提及,mysql的主从复制都是单线程的操做,主库对全部DDL和 DML产生binlog,binlog是顺序写,因此效率很高,slave的Slave_IO_Running线程到主库取日志,效率很比较高,下一步, 问题来了,slave的Slave_SQL_Running线程将主库的DDL和DML操做在slave实施。DML和DDL的IO操做是随即的,不是顺 序的,成本高不少,还可能可slave上的其余查询产生lock争用,因为Slave_SQL_Running也是单线程的,因此一个DDL卡主了,须要 执行10分钟,那么全部以后的DDL会等待这个DDL执行完才会继续执行,这就致使了延时。有朋友会问:“主库上那个相同的DDL也须要执行10分,为什 么slave会延时?”,答案是master能够并发,Slave_SQL_Running线程却不能够。网络

2. MySQL数据库主从同步延迟是怎么产生的。多线程

答:当主库的TPS并发较高时,产生的DDL数量超过slave一个sql线程所能承受的范围,那么延时就产生了,固然还有就是可能与slave的大型query语句产生了锁等待。架构

3. MySQL数据库主从同步延迟解决方案

答:最简单的减小slave同步延时的方案就是在架构上作优化,尽可能让主库的DDL快速执行。还有就是主库是写,对数据安全性较高,好比 sync_binlog=1,innodb_flush_log_at_trx_commit = 1 之类的设置,而slave则不须要这么高的数据安全,彻底能够讲sync_binlog设置为0或者关闭binlog,innodb_flushlog也 能够设置为0来提升sql的执行效率。另外就是使用比主库更好的硬件设备做为slave。

附加:

sync_binlog 配置说明:

sync_binlog”:这个参数是对于MySQL系统来讲是相当重要的,他不只影响到Binlog对MySQL所带来的性能损耗,并且还影响到MySQL中数据的完整性。对于“sync_binlog”参数的各类设置的说明以下:

sync_binlog=0,当事务提交以后,MySQL不作fsync之类的磁盘同步指令刷新binlog_cache中的信息到磁盘,而让Filesystem自行决定何时来作同步,或者cache满了以后才同步到磁盘。

sync_binlog=n,当每进行n次事务提交以后,MySQL将进行一次fsync之类的磁盘同步指令来将binlog_cache中的数据强制写入磁盘。

在MySQL中系统默认的设置是sync_binlog=0,也就是不作任何强制性的磁盘刷新指令,这时候的性能是最好的,可是风险也是最大的。由于一旦系统Crash,在binlog_cache中的全部binlog信息都会被丢失。而当设置为“1”的时候,是最安全可是性能损耗最大的设置。由于当设置为1的时候,即便系统Crash,也最多丢失binlog_cache中未完成的一个事务,对实际数据没有任何实质性影响。

从以往经验和相关测试来看,对于高并发事务的系统来讲,“sync_binlog”设置为0和设置为1的系统写入性能差距可能高达5倍甚至更多。

innodb_flush_log_at_trx_commit 配置说明:

默认值1的意思是每一次事务提交或事务外的指令都须要把日志写入(flush)硬盘,这是很费时的。特别是使用电 池供电缓存(Battery backed up cache)时。设成2对于不少运用,特别是从MyISAM表转过来的是能够的,它的意思是不写入硬盘而是写入系统缓存。日志仍然会每秒flush到硬 盘,因此你通常不会丢失超过1-2秒的更新。设成0会更快一点,但安全方面比较差,即便MySQL挂了也可能会丢失事务的数据。而值2只会在整个操做系统 挂了时才可能丢数据。 

 

 

mysql-5.6.3已经支持了多线程的主从复制。原理和丁奇的相似,丁奇的是以表作多线程,Oracle使用的是以数据库(schema)为单位作多线程,不一样的库可使用不一样的复制线程。

 

基于局域网的master/slave机制在一般状况下已经能够知足'实时'备份的要求了。若是延迟比较大,就先确认如下几个因素: 
1. 网络延迟
2. master负载
3. slave负载
通常的作法是,使用多台slave来分摊读请求,再从这些slave中取一台专用的服务器,只做为备份用,不进行其余任何操做,就能相对最大限度地达到'实时'的要求了

slave_net_timeout单位为秒 默认设置为 3600秒

参数含义:当slave从主数据库读取log数据失败后,等待多久从新创建链接并获取数据

master-connect-retry单位为秒 默认设置为 60秒

参数含义:当从新创建主从链接时,若是链接创建失败,间隔多久后重试。

一般配置以上2个参数能够减小网络问题致使的主从数据同步延迟

 

判断主从延时,一般有两个方法:

1. Seconds_Behind_Master  vs  2. mk-heartbeat,下面具体说下二者在实现功能的差异。

能够经过监控show slave status\G命令输出的Seconds_Behind_Master参数的值来判断,是否有发生主从延时。
其值有这么几种:
NULL - 表示io_thread或是sql_thread有任何一个发生故障,也就是该线程的Running状态是No,而非Yes.
0 - 该值为零,是咱们极为渴望看到的状况,表示主从复制良好,能够认为lag不存在。
正值 - 表示主从已经出现延时,数字越大表示从库落后主库越多。
负值 - 几乎不多见,只是听一些资深的DBA说见过,其实,这是一个BUG值,该参数是不支持负值的,也就是不该该出现。

Seconds_Behind_Master是经过比较sql_thread执行的event的timestamp和io_thread复制好的 event的timestamp(简写为ts)进行比较,而获得的这么一个差值。咱们都知道的relay-log和主库的bin-log里面的内容彻底一 样,在记录sql语句的同时会被记录上当时的ts,因此比较参考的值来自于binlog,其实主从没有必要与NTP进行同步,也就是说无需保证主从时钟的 一致。你也会发现,其实比较真正是发生在io_thread与sql_thread之间,而io_thread才真正与主库有关联,因而,问题就出来了, 当主库I/O负载很大或是网络阻塞,io_thread不能及时复制binlog(没有中断,也在复制),而sql_thread一直都能跟上 io_thread的脚本,这时Seconds_Behind_Master的值是0,也就是咱们认为的无延时,可是,实际上不是,你懂得。这也就是为什 么你们要批判用这个参数来监控数据库是否发生延时不许的缘由,可是这个值并非老是不许,若是当io_thread与master网络很好的状况下,那么 该值也是颇有价值的。(就比如:妈–儿子–媳妇的关系,妈与儿子亲人,媳妇和儿子也亲人,不见得媳妇与妈就很亲。开个玩笑:-)以前,提到 Seconds_Behind_Master这个参数会有负值出现,咱们已经知道该值是io_thread的最近跟新的ts与sql_thread执行到 的ts差值,前者始终是大于后者的,惟一的肯能就是某个event的ts发生了错误,比以前的小了,那么当这种状况发生时,负值出现就成为可能。

方法2. mk-heartbeat,Maatkit万能工具包中的一个工具,被认为能够准确判断复制延时的方法。

mk-heartbeat的实现也是借助timestmp的比较实现的,它首先须要保证主从服务器必需要保持一致,经过与相同的一个NTP server同步时钟。它须要在主库上建立一个heartbeat的表,里面至少有id与ts两个字段,id为server_id,ts就是当前的时间戳 now(),该结构也会被复制到从库上,表建好之后,会在主库上之后台进程的模式去执行一行更新操做的命令,按期去向表中的插入数据,这个周期默认为1 秒,同时从库也会在后台执行一个监控命令,与主库保持一致的周期去比较,复制过来记录的ts值与主库上的同一条ts值,差值为0表示无延时,差值越大表示 延时的秒数越多。咱们都知道复制是异步的ts不愿彻底一致,因此该工具容许半秒的差距,在这以内的差别均可忽略认为无延时。这个工具就是经过实打实的复 制,巧妙的借用timestamp来检查延时,赞一个!

 

===============================================

 

 

因为历史缘由,MySQL复制基于逻辑的二进制日志,而非重作日志。屡次被问到什么时候MySQL能支持基于物理的复制,其实这就看MySQL各位大佬的想法。上次和赖老师脑暴,倏地说道:MySQL会不会来个基于Paxos的redo复制?

物理复制的真正好处不在于正确性,由于基于ROW格式的日志复制也已能彻底保证复制的正确性。因为物理日志的写入是在事务执行过程当中就不断写入,而二进制日志的写入仅仅在事务提交时。所以物理日志的优点以下所示:

  • 复制架构下,大事务日志提交速度快;
  • 复制架构下,主从数据延迟小;

假设执行了1个小时的某大事务,在最后提交时,只需写入最后提交部分的重作日志(redo log可视为物理日志)。虽然此大事务重作日志写入的总量可能有1G,然而在提交时,数据主从复制仅需将最后一部分日志传输到远程从机,由于以前的重作日志已经在执行的1个小时内不断地同步到从机。

对于二进制日志,因为其写入时间发生在事务提交时,所以假设产生了1G的二进制日志,则须要事务提交时间会包含这1G日志的写入时间。在Oracle中有一种说法,事务的提交速度都是平的,不论事务的大小。这在MySQL数据库中是不成立的。即,MySQL的提交速度取决于事务产生的二进制日志的大小,事务提交的速度不是平的。

更为糟糕的是,MySQL主从复制在大事务下的延迟。一样假设1个大事务在主服务器上执行了1个小时,则须要在最后的提交时间传送到从服务器。主从延迟的时间至少为1个小时,若从服务器执行还需1个小时,则主从复制延迟的最坏状况多是2个小时。物理复制则不存在这样的限制,缘由仍是如前所述,事务提交过程当中,日志已经在传输和回放。

物理复制虽好,可是也有本身的缺陷,就我本身的实际体验来看:

  • 物理复制下,主机坏块会致使主从服务器都没法启动;相信遇到过此问题的同窗不在少数;
  • 此外,作ETL是有困难的,好比怎么将物理日志同步到Hadoop大数据平台呢?

一言以蔽之,对于MySQL数据库来讲,任什么时候刻不容许有大事务执行。若要执行,则将大事务拆成一个个小的子事务来执行。这是最基本心法口诀,但却又和Oracle有着很大不一样。总之,气宗、剑宗,本无好坏,学会理解其中的差别,融会贯通方可达风清扬般的致臻境界。

mysql 用主从同步的方法进行读写分离,减轻主服务器的压力的作法如今在业内作的很是广泛。 主从同步基本上能作到实时同步。我从别的网站借用了主从同步的原理图。

在配置好了, 主从同步之后, 主服务器会把更新语句写入binlog,   从服务器的IO 线程(这里要注意, 5.6.3 以前的IO线程仅有一个,5.6.3以后的有多线程去读了,速度天然也就加快了)回去读取主服务器的binlog 而且写到从服务器的Relay log 里面,而后从服务器的 的SQL thread 会一个一个执行 relay log 里面的sql , 进行数据恢复。

relay 就是 传递, relay race 就是接力赛的意思

1. 主从同步的延迟的缘由

咱们知道, 一个服务器开放N个连接给客户端来链接的, 这样有会有大并发的更新操做, 可是从服务器的里面读取binlog 的线程仅有一个, 当某个SQL在从服务器上执行的时间稍长 或者因为某个SQL要进行锁表就会致使,主服务器的SQL大量积压,未被同步到从服务器里。这就致使了主从不一致, 也就是主从延迟。

2. 主从同步延迟的解决办法

实际上主从同步延迟根本没有什么一招制敌的办法, 由于全部的SQL必须都要在从服务器里面执行一遍,可是主服务器若是不断的有更新操做源源不断的写入, 那么一旦有延迟产生, 那么延迟加剧的可能性就会原来越大。 固然咱们能够作一些缓解的措施。

  • a. 咱们知道由于主服务器要负责更新操做, 他对安全性的要求比从服务器高, 全部有些设置能够修改,好比sync_binlog=1,innodb_flush_log_at_trx_commit = 1 之类的设置,而slave则不须要这么高的数据安全,彻底能够讲sync_binlog设置为0或者关闭binlog,innodb_flushlog, innodb_flush_log_at_trx_commit 也 能够设置为0来提升sql的执行效率 这个能很大程度上提升效率。另外就是使用比主库更好的硬件设备做为slave。
  • b. 就是把,一台从服务器当度做为备份使用, 而不提供查询, 那边他的负载下来了, 执行relay log 里面的SQL效率天然就高了。
  • c. 增长从服务器喽,这个目的仍是分散读的压力, 从而下降服务器负载。

3. 判断主从延迟的方法

MySQL提供了从服务器状态命令,能够经过 show slave status 进行查看,  好比能够看看Seconds_Behind_Master参数的值来判断,是否有发生主从延时。

其值有这么几种:

NULL - 表示io_thread或是sql_thread有任何一个发生故障,也就是该线程的Running状态是No,而非Yes.
0 - 该值为零,是咱们极为渴望看到的状况,表示主从复制状态正常

其它的方法我也没试过, 暂时不作评论

总结

以上就是这篇文章的所有内容了,但愿本文的内容对你们的学习或者工做具备必定的参考学习价值,谢谢你们对脚本之家的支持。若是你想了解更多相关内容请查看下面相关连接

相关文章
相关标签/搜索