MySQL主从复制与读写分离原理
目录
1 主从复制与读写分离的意义
1.1 什么是主从复制
1.2 主从数据库的好处和缺点
2 主从复制
2.1 主从复制的形式
2.2 主从复制的原理
2.3 主从复制的模式
异步模式
半同步模式 --5.5开始支持
全同步模式
3 主从复制读写分离过程
3.1 主从数据库实现同步(主从复制)
3.2 主从读写分离
4 主从复制分析
1 主从复制与读写分离的意义
企业中的业务通常数据量都比较大,而单台数据库在数据存储、安全性和高并发方面都无法满足实际的需求,所以需要配置多台主从数据服务器,以实现主从复制,增加数据可靠性,读写分离,也减少数据库压力和存储引擎带来的表锁定和行锁定问题。
1.1 什么是主从复制
MySQL 主从复制是指数据可以从一个MySQL数据库服务器主节点复制到一个或多个从节点。MySQL 默认采用异步复制方式,这样从节点不用一直访问主服务器来更新自己的数据,数据的更新可以在远程连接上进行,从节点可以复制主数据库中的所有数据库或者特定的数据库,或者特定的表。
首先,我们看一个图:
影响MySQL-A数据库的操作,在数据库执行后,都会写入本地的日志系统A中。
假设,实时的将变化了的日志系统中的数据库事件操作,在MYSQL-A的3306端口,通过网络发给MYSQL-B。
MYSQL-B收到后,写入本地日志系统B,然后一条条的将数据库事件在数据库中完成。
那么,MYSQL-A的变化,MYSQL-B也会变化,这样就是所谓的MYSQL的复制,即MYSQL replication。
在上面的模型中,MYSQL-A就是主服务器,即master,MYSQL-B就是从服务器,即slave。
日志系统A,其实它是MYSQL的日志类型中的二进制日志,也就是专门用来保存修改数据库表的所有动作,即bin log。【注意MYSQL会在执行语句之后,释放锁之前,写入二进制日志,确保事务安全】
日志系统B,并不是二进制日志,由于它是从MYSQL-A的二进制日志复制过来的,并不是自己的数据库变化产生的,有点接力的感觉,称为中继日志,即relay log。
可以发现,通过上面的机制,可以保证MYSQL-A和MYSQL-B的数据库数据一致,但是时间上肯定有延迟,即MYSQL-B的数据是滞后的。
【即便不考虑什么网络的因素,MYSQL-A的数据库操作是可以并发的执行的,但是MYSQL-B只能从relay log中读一条,执行下。因此MYSQL-A的写操作很频繁,MYSQL-B很可能跟不上。】
1.2 主从数据库的好处和缺点
优点
容灾:做数据的热备,作为后备数据库,主数据库服务器故障后,可切换到从数据库继续工作,避免数据丢失。
扩展:架构的扩展。业务量越来越大,I/O访问频率过高,单机无法满足,此时做多库的存储,降低磁盘I/O访问的频率,提高单个机器的I/O性能。
锁表:读写分离,使数据库能支撑更大的并发。当复杂sql锁表时,不会影响查询业务
缺点
成本:主从数据库的存储是冗余存储的,同一份数据多分存储是对服务器资源的消耗来满足业务需求
主从不一致:采用异步赋值模式,会出现更新的数据在从库读取有延时问题
2 主从复制
2.1 主从复制的形式
一主一从和一主多从
最常见的主从复制形式
多主一从--5.7开始支持
数据汇总,可将多个主数据库同步汇总到一个从数据库中,方便数据统计分析。
读写分离,从库只用于查询,提高数据库整体性能。
双主复制
也就是互做主从复制,每个master既是master,又是另外一台服务器的slave。这样任何一方所做的变更,都会通过复制应用到另外一方的数据库中。
级联复制
如果主节点有太多的从节点,就会损耗一部分性能用于replication(主从复制),通过级联复制缓解主节点压力
2.2 主从复制的原理
MySQL主从复制涉及到三个线程
主节点 binary log dump 线程(binlog转储线程)
主服务器创建一个线程,在从服务器连接时将二进制日志内容发送到从服务器(可以通过show processlist 命令查看) 此线程会对主节点上的bin-log加锁,当读取完成(发送从节点前)释放(为每个从机创建一个线程)
从节点I/O线程
当在从服务器上发出start slave语句时,从服务器创建一个I/O线程,该线程连接到主服务器并要求主服务器发送记录在其二进制日志中的更新。 从I/O线程读取主BINLog转储线程发送的更新(见上一项),并将它们复制到构成从服务器中继日志的本地文件,readLog。
从节点SQL线程
从服务器上面同时开启一个 SQL thread 定时检查 Realy log(这个文件也是二进制的),如果发现有更新立即把更新的内容在本机的数据库上面执行一遍。
2.3 主从复制的模式
MySQL主从复制有异步模式、半同步模式、全同步模式,MySQL默认模式是异步模式。
异步模式
只MySQL 主服务器上I/O thread 线程将二进制日志写入binlog文件之后就返回客户端结果,不会考虑二进制日志是否完整传输到从服务器以及是否完整存放到从服务器上的relay日志中。
优点:主库写性能高
缺点:数据不一致问题(数据存在延时),数据丢失问题(一旦主服务(器)宕机,数据就会发生丢失)
半同步模式 --5.5开始支持
需要安装单独插件实现
这种模式下主节点只需要接收到其中一台从节点的返回信息,就会commit;否则需要等待直到超时时间然后切换成异步模式再提交。
优点:可以使主从数据库的数据延迟缩小,可以提高数据安全性,确保了事务提交后,binlog至少传输到了一个从节点上(不保证从节点更新到db)
缺点:主库写性能上会有一定的降低,响应时间会变长
全同步模式
主节点提交一个事务,所有从节点都将该事务执行完成后才返回
优点:数据安全
缺点:性能随着从节点数量增加越来越差
3 主从复制读写分离过程
3.1 主从数据库实现同步(主从复制)
什么是主从复制?简单来说就是在主服务器上执行的语句,从服务器执行同样的语句,在主服务器上的操作在从服务器产生了同样的结果。
主从复制的基本过程如下:
Master(主数据库)将用户对数据库更新的操作以二进制格式保存到BinaryLog日志文件中。
Slave(从数据库)上面的I0进程连接上Master, 并请求从指定日志文件的指定位置(或者从最开始的日志)之后的日志内容。
Master接收到来自Slave的I0进程的请求后,通过负责复制的I0进程根据请求信息读取制定日志指定位置之后的日志信息,返回给Slave 的I0进程。返回信息中除了日志所包含的信息之外,还包括本次返回的信息已经到Master端的bin-log文件的名称以及bin-log的位置。
Slave的I0进程接收到信息后,将接收到的日志内容依次添加到Slave端的relay-log文件的最末端,并将读取到的Master端的bin-log的文件名和位置记录到master-info文件中,以便在下一次读取的时候能够清楚的告诉Master “我需要从某个bin- log的哪个位置开始往后的日志内容,请发给我”。
Slave的Sql进程检测到relay-log中新增加了内容后,会马上解析relay- log的内容成为在Master端真实执行时候的那些可执行的内容,并在自身执行。
3.2 主从读写分离
只在主服务器上写,在从服务器上读;
主数据库处理事务性查询,从数据库处理SELECT查询;
进行读操作时,是在两个从服务器上轮流读取,利用虚拟模块MySQL-Proxy做读取从服务器时的轮询。
4 主从复制分析
问题1:master的写操作,slaves被动的进行一样的操作,保持数据一致性,那么slave是否可以主动的进行写操作?
假设slave可以主动的进行写操作,slave又无法通知master,这样就导致了master和slave数据不一致了。因此slave不应该进行写操作,至少是slave上涉及到复制的数据库不可以写。实际上,这里已经揭示了读写分离的概念。
问题2:主从复制中,可以有N个slave,可是这些slave又不能进行写操作,要他们干嘛?
可以实现数据备份。
类似于高可用的功能,一旦master挂了,可以让slave顶上去,同时slave提升为master。
异地容灾,比如master在北京,地震挂了,那么在上海的slave还可以继续。
主要用于实现scale out,分担负载,可以将读的任务分散到slaves上。
【很可能的情况是,一个系统的读操作远远多于写操作,因此写操作发向master,读操作发向slaves进行操作】
问题3:主从复制中有master,slave1,slave2,...等等这么多MYSQL数据库,那比如一个JAVA WEB应用到底应该连接哪个数据库?
当 然,我们在应用程序中可以这样,insert/delete/update这些更新数据库的操作,用connection(for master)进行操作,select用connection(for slaves)进行操作。那我们的应用程序还要完成怎么从slaves选择一个来执行select,例如简单的轮循算法。
这样的话,相当于应用程序完成了SQL语句的路由,而且与MYSQL的主从复制架构非常关联,一旦master挂了,某些slave挂了,那么应用程序就要修改了。能不能让应用程序与MYSQL的主从复制架构没有什么太多关系呢?可以看下面的图:
找一个组件,application program只需要与它打交道,用它来完成MYSQL的代理,实现SQL语句的路由。
mysql proxy并不负责,怎么从众多的slaves挑一个?可以交给另一个组件(比如haproxy)来完成。
这就是所谓的MYSQL READ WRITE SPLITE,MYSQL的读写分离。
问题4:如果mysql proxy , direct , master他们中的某些挂了怎么办?
总统一般都会弄个副总统,以防不测。同样的,可以给这些关键的节点来个备份。
问题5:当master的二进制日志每产生一个事件,都需要发往slave,如果我们有N个slave,那是发N次,还是只发一次?
如果只发一次,发给了slave-1,那slave-2,slave-3,...它们怎么办?
显 然,应该发N次。实际上,在MYSQL master内部,维护N个线程,每一个线程负责将二进制日志文件发往对应的slave。master既要负责写操作,还的维护N个线程,负担会很重。可 以这样,slave-1是master的从,slave-1又是slave-2,slave-3,...的主,同时slave-1不再负责select。 slave-1将master的复制线程的负担,转移到自己的身上。这就是所谓的多级复制的概念。
问题6:当一个select发往mysql proxy,可能这次由slave-2响应,下次由slave-3响应,这样的话,就无法利用查询缓存了。
应该找一个共享式的缓存,比如memcache来解决。将slave-2,slave-3,...这些查询的结果都缓存至mamcache中。
问题7:随着应用的日益增长,读操作很多,我们可以扩展slave,但是如果master满足不了写操作了,怎么办呢?
scale on ?更好的服务器? 没有最好的,只有更好的,太贵了。。。
scale out ? 主从复制架构已经满足不了。
可以分库【垂直拆分】,分表【水平拆分】。
数据不一致问题
原因
一般的主从复制的3个线程都是单线程的 Binlog Dump(主) ‐‐> IO Thread(从) ‐‐> SQL Thread(从) 而master是并发线程提交的,在业务量巨大的情况下很容易出现主从延时
多线程复制--5.6开始支持
5.6支持每个库一个线程
5.7支持每个库多个线程
问题
多线程模式只是降低延迟,但是仍然存在。我们在程序设计中需要考虑延迟问题
使用场景
访问量较大,吞吐量需求较高的情况。
主要针对接口模块,后台模块中的访问量较小,建议读写都使用主库完成
数据量较大,变更频繁
变更不频繁,数据量较低的热数据建议使用缓存处理,变更频繁,且数据量较大可以考虑使用读写分离,如用户系统中的用户信息
无法通过简单方式优化处理的
当数据的查询性能出现瓶颈时,首先考虑是否可以通过索引和sql语句进行优化
是否是初始设计时的表结构不合理,是否可以通过中间表解决
使用主从分离是对服务器资源的消耗,也是成本的增加,我们尽量采用在低成本情况下解决问题
可以接受短时间内的数据不一致
如支付宝中的订单信息,淘宝中的购买记录。是可以接受的
如支付宝中扣款时用户的余额,确认购买商品时候的数量。是不可以接受的。这种情况还需要考虑幂等性问题
————————————————