mysql replication 实现原理
一、replication 线程
Mysql 的Replication是一个异步的复制过程,从一个 mysql Instance(master)复制到另一个mysql Instance(slave)。在Master与Slave之间的整个复制过程主要由三个线程来完成,其中两个线程 SQL线程和IO线程在Slave端,另外一个线程 IO线程在Master端。
要实现Mysql Relication,首先必须打开Master的 Binary Log功能。因为整个复制功能实际上就是Slave从Master端获取二进制日志,然后在自己身上完全按照产生的顺序依次执行日志中所记录的各种操作。
MYSQL 复制的基本过程如下:
(1)Slave的IO线程连接上Master,并请求日志文件的指定位置(或者从最开始的日志)之后的日志内容;
(2)Master接收到来自Slave的IO线程请求后,负责复制的IO线程根据请求信息读取指定日志位置之后的日志信息,返回给Slave端的IO线程。返回信息中除了日志所包含的信息外,还包括本次返回的信息在Master端的Binary Log 文件的名称和位置;
(3)Slave的IO线程接收到信息后,将日志内容依次写入Slave端的Relay Log(中继日志)文件的最末端,并将读取到的Master端的Binary Log 文件名和位置记录到master-info文件中,以便在下次读取时能清楚地告诉master “我需要从某个bin-log文件的哪个位置开始完后的日志内容,发给我”;
(4)Slave的Sql线程检测到Relay Log中新内容后,会马上解析该Log文件中的内容,还原成在Master端真实执行的可执行Query语句,并执行这些Query。实际上就是在Master端和Salve端执行了同样的Query,所以两端的数据是完全一样的。
二、复制实现级别
(1)Row Level
Binary Log会记录成每一行数据被修改的形式,然后在Slave端再对相同的数据进行修改。
优点:在Row Level模式下,Binary Log可以不记录执行的Query语句的上下文相关信息,只需要记录哪一条记录被修改了,修改成什么样了。所以 Row Level的日志内容会非常清楚地记录下每一行数据修改的细节,非常容易理解。而且不会出现某些特定情况下的存储过程,或 function,以及 Trigger的调用和触发无法正确复制的问题。
缺点:在Row Level模式下,当所有的执行语句记录到Binary Log 中时,都将被记录成每条记录被修改的形式,这样就会产生大量的日志内容。比如有这样的一条 update语句:UPDATE group_message SET group_id = 1 WHERE group_id=2,执行后,日志记录的不是该语句而是产生影响的 表中的每条记录,即:如果group_message 中有 group_id=1 的记录100条,那么Binary Log中记录了这100条记录的变化情况。尤其执行 Alter Table 之类的语句时,产生的日志量更大的惊人。因为MYSQL对于Alter Table之类的DDL变更语句是重建整个表的所有数据,即表中每一条记录都要变动,那么该表中所有记录都要记录到日志中。
(2)Statement Level
每一条会修改数据的Query都会记录到Master的Binary Log中以及执行语句时上下文的信息。Slave 在复制的时候,SQL线程会解析成和原来 Master 端执行过的相同的 Query,并再次执行。
有点:首先解决了Row Level的缺点,不需要记录每一行记录的变化,减少了Binary Log 日志量,节约了IO成本,提高了性能。
缺点:由于记录的是执行语句,为了能让这些语句在Slave端也能正确执行,就需要记录每条语句在执行时的一些相关信息,即上下文信息。另外由于MYSQL发展比较快,很多新功能的加入,也使得mysql复制遇到不少挑战,复制时涉及的内容越复杂越容易出现bug。在修改数据时使用了默写特定的函数或功能后会出现问题。如 sleep()函数在默写版本中不能正确复制,在存储过程中使用 last_insert_id()函数,可能会使Slave 和Master得到不一致的ID,由于Row Level是基于每一行变化的所以不会出现这种情况。
(3) Mixed Level
在Mixed模式下,mysql会根据执行的每一条具体的Query语句来区分对待记录的日志形式,也就是在 Statement和Row之间选择一种。从5.1.8版本开始,Mysql提供了Statement Level 和 Row Level之外的第三种复制模式 Mixed Level
三、Replication 常用架构
(1) 常规复制架构 (Master - Slave)
主从复制架构,可以实现写读分离,只要Master端和 Slave端的压力不是很大,异步复制的延时一般都很少很少。尤其是自从Slave端的复制方式改成两个线程处理之后。
(2)Dual Master复制架构(Master - Master)
如果简单用常规复制架构 切换的话,就会造成原来Master端数据的不一致性,当原来的Master启动可以正常提供服务时,不得不通过反转Master - Slave 关系重新搭建Replication 环境,并以 原 Master 作为 Slave来对外提供服务。这将增加很多工作量。为了避免这个问题,可以搭建 Dual Master (双 Master),即两个Mysql Server互相将对方作为自己的Master,自己作为对方的Slave来进行复制。这样任何一方做的变更,都会通过复制应用到另一方数据库中。
为避免循环复制,Mysql的Binary Log中记录了当前Mysql的server-id ,mysql很容易判断某个变更是从哪一个mysql server最初产生的。另外如果不打开Slave的Binary Log选项(--log-slave-update)时,mysql根本就不会记录复制过程中产生的变更多 Binary Log中。
为了避免数据冲突一般指开启一端写服务,因为两端如果同时更新一条记录,会产生冲突 覆盖。
(3)级联复制架构(Master - Slave - Slave ...)
在有些应用场景中,也许读写压力差别比较大,读压力特别大,一台Master 可能需要10台甚至更多的Slave才能支撑读的压力。
这时可利用MYSQL可以在 Slave端记录复制所产生的变更的 Binary Log 信息的功能,也就是打开 -log-slave-update 选项。然后通过二级复制来减少 Master端因为复制所带来的压力。也就是说,首先通过少数几台Mysql从Master来进行复制,这几台机器我们称为第一级Slave 集群,然后其他的Slave再通过第一级Slave集群来进行复制。从第一级Slave进行复制的Slave,称之为第二级Slave集群。如果有需要还可以继续往下增加更多层次的复制。
(4)Dual Master 与级联复制结合架构(Master-Master-Slaves)
级联复制在一定程度上确实解决了 Master因为所附属的Slave过多而成为瓶颈的问题,但并不能解决人工维护和出现异常需要切换时可能存在重新搭建 Replication的问题。
和Master - Salve - Salve 架构相比,区别只是将第一级的Slave集群换成了一台单独的Master,作为备用Master,然后再从这个Master复制到一个Slave集群,这样避免了主Master的写操作受到Slave集群的复制所带来的影响,同时主Master需要切换的时候也基本上不会出现重搭Replication的情况。但这个架构也有一个弊端,那就是备用的Master有可能成为瓶颈,如果后面Slave集群比较大的话,备用Master可能会因为过多的Slave IO线程请求而成为瓶颈。该备用Master不可供任何读服务时,瓶颈出现的可能性并不是很高。