MySQL 复制原理详解[转载]

转载:https://cloud.tencent.com/developer/article/1005270

导语

mysql作为一个开源的数据库,有着广泛的应用。本文主要讲述了mysql复制的原理,以及异步复制,同步复制和并行复制。

1、mysql复制的原理

Mysql有两种复制原理:基于行的复制和基于语句的复制。最早出现的是基于语句的复制,而基于行的复制方式在5.1版本中才被引入。这两种方式都是通过在主库上记录二进制日志(binlog)、在备库上重放日志的方式来实现异步的数据复制。这意味着、在同一时间点备库上的数据可能和主库不一致,并且无法保证主库备库之间的延迟。

1.1 、基于语句的复制

基于语句的复制模式下,主库会记录那些造成数据更改的查询,当备库读取并重放这些事件时,实际上只把主库上执行过的SQL再执行一遍。

优点:

最明显的好处是实现相当简单。理论上讲,简单地记录和执行这些语句,能够让备库保持同步。另外好处是binlog日志里的事件更加紧凑,所以相对而言,基于语句的模式不会使用太多带宽。一条更新好几兆数据的语句在二进制日志里可能只占用几十字节。

缺点:

有些数据更新语句,可能依赖其他因素。例如,同一条sql在主库和备库上执行的时间可能稍微或很不相同,因此在传输的binlog日志中,除了查询语句,还包括一些元数据信息,如当前的时间戳。即便如此,还存在着一些无法被正确复制的SQL,例如,使用CURRENT_USER()函数语句。存储过程和触发器在使用基于语句的复制模式时也可能存在问题。另外一个问题是更新必须是串行的。这需要更多的锁。并且不是所有的存储引擎都支持这种复制模式。

1.2、基于行的复制

MySQL5.1开始支持基于行的复制,这种方式会将实际数据记录在二进制日志中,跟其他数据库的实现比较相像。

优点:

最大的好处是可以正确的复制每一行,一些语句可以呗更加有效地复制。由于无需重放更新主库数据的查询,使用基于行的复制模式能够更高效地复制数据。重放一些查询的代价会很高。例如,下面有一个查询将数据从一个大表中汇总到小表

<pre class="prism-token token language-javascript">mysql> INSERT INTO summary_table(col1,col2,sum_col3)

     ->SELECT col1,col2,sum(col3) from enormous_table GROUP BY col1,col2;

</pre>

缺点:

但是另外一方面,下面这条语句使用基于语句的复制方式代价会小很多:

<pre class="prism-token token language-javascript">mysql> UPDATE enormous_table SET col1 =0 ;
</pre>

由于这条语句做了全表更行,使用基于行的复制开销会大很多,因为每一行的数据都会呗记录到二进制日志中,这使得二进制日志时间非常庞大。另外由于语句并没有在日志里记录,因此无法判断执行了哪些sql,除了需要知道行的变化外,这在很多情况下很重要。执行基于行的过程像一个黑盒子,你无法知道服务器正在做什么。

由于没有哪一种模式对所有情况都是完善的,mysql能够在这两种复制模式间动态切换。默认情况下使用的是基于语句的复制方式,但如果发现语句无法呗正确地复制,就切换基于行的复制模式。还可以根据需要来设置会话级别的变量binlog_format,控制二进制日志格式。

2、异步复制过程

总体来说,复制有3个步骤:

1、主服务器把数据更改记录到二进制日志中。(这叫做二进制日志事件)

2、从服务器把主服务器的二进制日志拷贝到自己的中继日志中。

3、从服务器重放中继日志中的事件,把更改应用到自己的数据上。

这只是概述,每一个步骤都很复杂。下图更清晰描述了复制的过程。

image

第一步、在主服务器上记录二进制日志。在每个更新数据的事务完成之前,主服务器都会将数据更改记录到二进制日志中。即使事务在执行期间是交错的,mysql也会串行地将事务写入到二进制日志中。在把事件写入二进制日志之后,主服务器告诉存储引擎提交事务。

第二步、从服务器把主服务器的二进制日志拷贝到自己的硬盘上,进入所谓的“中继日志”中。首先,它启动一个工作线程,叫I/O线程,这个I/O线程开启一个普通的客户端连接,然后启动一个特殊的二进制日志转储进程(它没有相应的SQL命令)。这个转储进程从主服务器的二进制日志中读取数据。它不会对事件进行轮询。如果3跟上了主服务器,就会进入休眠状态并等待有新的事件发生时主服务器发出的信号。I/O线程把数据写入从服务器的中继日志中。

第三步、SQL线程读取中继日志,并且重放其中的事件,然后更新从服务器的数据。由于这个线程能跟上I/O线程,中继日志通常在操作系统的缓存中,所以中继日志的开销很低。SQL线程执行事件也可以被写入从服务器自己的二进制日志中,它对于有些场景很实用。

3、半同步复制

一般情况下,异步复制就已经足够应付了,但由于是异步复制,备库极有可能是落后于主库,特别是极端情况下,我们无法保证主备数据是严格一致的。比如,当用户发起commit命令时,Master并不关心Slave的执行状态,执行成功后,立即返回给用户。试想下,若一个事务提交后,Master成功返回给用户后crash,这个事务的binlog还没来得及传递到Slave,那么Slave相对于Master而言就少了一个事务,此时主备就不一致了。对于要求强一致的业务是不可以接受的,半同步复制就是为了解决数据一致性而产生的。

为什么叫半同步复制?先说说同步复制,所谓同步复制就是一个事务在Master和Slave都执行后,才返回给用户执行成功。这里核心是说Master和Slave要么都执行,要么都不执行,涉及到2PC(2 Phrase Commit)。而MySQL只实现了本地redo-log和binlog的2PC,但并没有实现Master和Slave的2PC,所以不是严格意义上的同步复制。而MySQL半同步复制不要求Slave执行,而仅仅是接收到日志后,就通知Master可以返回了。这里关键点是Slave接受日志后是否执行,若执行后才通知Master则是同步复制,若仅仅是接受日志成功,则是半同步复制。对于Mysql而言,我们谈到的日志都是binlog,对于其他的关系型数据库可能是redo log或其他日志。

半同步复制如何实现?半同步复制实现的关键点是Master对于事务提交过程特殊处理。目前实现半同步复制主要有两种模式,AFTER_SYNC模式和AFTER_COMMIT模式。两种方式的主要区别在于是否在存储引擎提交后等待Slave的ACK。先来看看AFTER_COMMIT模式,如下图,Start和End分别表示用户发起Commit命令和Master返回给用户的时间点,中间部分就是整个Commit过程Master和Slave做的事情。

image

Master提交时,会首先将该事务的redo log刷入磁盘,然后将事务的binlog刷入磁盘(这里其实还涉及到两阶段提交的问题,这里不展开讲),然后进入innodb commit流程,这个步骤主要是释放锁,标记事务为提交状态(其他用户可以看到该事务的更新),这个过程完成后,等待Slave发送的ACK消息,等到Slave的响应后,Master才成功返回给用户。看到图中红色虚线部分,这段是Master和Slave的同步逻辑,是Master-Slave一致性的保证。

半同步复制是否能保证不丢数据?我们通过几种场景来简单分析下。第一种情况:假设Master第1,2步执行成功后,binlog还没来得及传递给Slave,此时Master挂了,Slave作为新Master提供服务,那么备库比主库要少一个事务(因为主库的redo 和binlog已经落盘),但是不影响用户,对于用户而言,这个事务没有成功返回,那么提交与否,用户都可以接受,用户一定会进行异常捕获而重试。第二种情况,假设第3步innodb commit执行成功后,binlog还没来得及传递给Slave,此时Master挂了,此时与第一种情况一样,备库比主库少一个事务,但是其他用户在3执行完后,可以看到该事务的更新,而切换到备库后,却发现再次读这个更新又没了,这个就发生了“幻读”,如果其他事务依赖于这个更新,则会对业务逻辑产生影响。当然这仅仅是极端情况。

对于第二种情况产生的影响,AFTER_SYNC模式可以解决这一问题。与AFTER_COMMIT相比,master在AFTER_SYNC模式下,Fsync binlog后,就开始等待SLAVE同步。那么在进行第5步innodbcommit后,即其它事务能看到该事务的更新时,Slave已经成功接收到binlog,即使发生切换,Slave拥有与Master同样的数据,不会发生“幻读”现象。但是对于上面描述的第一种情况,结果是一样的。

所以,在极端情况下,半同步复制的Master-Slave会有一个事务不一致,但是对于用户而言,由于这个事务并没有成功返回给用户,所以无论事务提交与否都是可以接受的,用户有必要进行查询或重试,判读是否更新成功。或者我们想想,对于单机而言,若事务执行成功后,返回给用户时,网络断了,用户也是面临一样的问题,所以,这不是半同步复制的问题。对于提交返回成功的事务,版同步复制保证Master-Slave一定是一致的,从这个角度来看,半同步复制不会丢数据,可以保证Master-Slave的强一致性。下图是AFTER_SYNC模式,事务提交过程。

image

4、并行复制

半同步复制解决了Master-Slave的强一致问题,那么性能问题呢?从图1中可以看到参与复制的主要有两个线程:IO线程和SQL线程,分别用于拉取和回放binlog。对于Slave而言,所有拉取和解析binlog的动作都是串行的,相对于Master并发处理用户请求,在高负载下, 若Master产生binlog的速度超过Slave消费binlog的速度,导致Slave出现延迟。如下图,可以看到,Users和Master之间的管道远远大于Master和Slave之间的管道。

image

那么如何并行化,并行IO线程,还是并行SQL线程?其实两方面都可以并行,但是并行SQL线程的收益更大,因为SQL线程做的事情更多(解析,执行)。并行IO线程,可以将从Master拉取和写Relay log分为两个线程;并行SQL线程则可以根据需要做到库级并行,表级并行,事务级并行。库级并行在mysql官方版本5.6已经实现。如下图,并行复制框架实际包含了一个协调线程和若干个工作线程,协调线程负责分发和解决冲突,工作线程只负责执行。图中,DB1,DB2和DB3的事务就可以并发执行,提高了复制的性能。有时候库级并发可能不够,需要做表级并发,或更细粒度的事务级并发。

image

并行复制如何处理冲突?并发的世界是美好的,但不能乱并发,否则数据就乱了。Master上面通过锁机制来保证并发的事务有序进行,那么并行复制呢?Slave必需保证回放的顺序与Master上事务执行顺序一致,因此只要做到顺序读取binlog,将不冲突的事务并发执行即可。对于库级并发而言,协调线程要保证执行同一个库的事务放在一个工作线程串行执行;对于表级并发而言,协调线程要保证同一个表的事务串行执行;对于事务级而言,则是保证操作同一行的事务串行执行。

是否粒度越细,性能越好?这个并不是一定的。相对于串行复制而言,并行复制多了一个协调线程。协调线程一个重要作用是解决冲突,粒度越细的并发,可能会有更多的冲突,最终可能也是串行执行的,但消耗了大量的冲突检测代价。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容