我们前文介绍了MySQL的基本原理和工作模式之后,我们今天来学习一下MySQL的生产架构应该怎么做。
我们知道,随着互联网的发展与普及数据量的增多,数据库的数据量以及读写操作量会越来越大。我们往往会面临几个问题:
问题一:读写压力大怎么办?问题二:服务器宕机了怎么办?
任何架构的高可用的本质就是——冗余!解决上面问题的方案也是一样的。我们把一份数据存储多份。其中每一份都可以用于读,其中一份出错了,可以去读令一份。这样就解决了上述的问题。但是MySQL是如何实现数据冗余的呢?这里就不得不提到数据库的主从同步了。
问题三:数据量大怎么办?
任何架构的可拓展性的本质就是——分片!解决数据量大的方案就是如此。把一份大的数据分成多个小份存储,每次读的时候从各个小份读取汇总在一起。MySQL如何实现数据分片呢?这里就不得不说分库分表。
主从同步
主从同步概念
主从复制是指数据可以从一个数据库服务器主节点复制到一个或多个从节点。
MySQL默认采用异步复制方式,这样从节点不用一直访问主服务器来更新自己的数据,数据的更新可以在远程连接上进行,从节点可以复制主数据库中的所有数据库或者特定的数据库,或者特定的表。
主从同步的主要用途
读写分离:
在开发工作中,有时候会遇见某个sql语句需要锁表,导致暂时不能使用读的服务,这样就会影响现有业务,使用主从复制,让主库负责写,从库负责读,这样,即使主库出现了锁表的情景,通过读从库也可以保证业务的正常运作。
高可用:
当数据库系统中某个节点发生故障时,可以将其他节点切换为主节点,从而保障系统正常。
数据备份
数据存储在多个机器,从而做到数据的备份。
主从同步的形式
一主一从
MySQL架构篇一主多从
MySQL架构篇一主一从和一主多从是最常见的主从架构,实施起来简单并且有效,不仅可以实现高可用,还能读写分离,进而提升集群的并发能力。
多主一从
MySQL架构篇多主一从可以将多个数据库备份到一台存储性能比较好的服务器上。真实场景中很少使用。
双主复制
MySQL架构篇双主复制,也就是互做主从复制,每个master既是master,又是另外一台服务器的slave。这样任何一方所做的变更,都会通过复制应用到另外一方的数据库中。
级联复制
MySQL架构篇如果从节点很多的情况下,都连接主节点会损耗一些性能用于数据同步,这样会影响主节点的性能。所以就有了级联复制模式,部分slave的数据同步不连接主节点,而是连接从节点。这样不仅可以缓解主节点的压力,并且对数据一致性没有负面影响。
主从同步原理
主从同步复制有以下几种方式:
(1)同步复制,master的变化,必须等待slave-1,slave-2,...,slave-n完成后才能返回。
优点:可以保障数据的一致性;
缺点:由于同步过程有延迟,会导致吞吐量低,性能差。
(2)异步复制,master只需要完成自己的数据库操作即可,至于slaves是否收到二进制日志,是否完成操作,不用关心。MYSQL的默认设置。
优点:性能上不受影响;
缺点:主从数据有延迟,不同步。
(3)半同步复制,master只保证slaves中的一个操作成功,就返回,其他slave不管。这个功能,是由google为MYSQL引入的。
这是一个中和的方式,在性能和数据一致性方面做了一些妥协。
MySQL主从复制涉及到三个线程,一个运行在主节点(logdumpthread),其余两个(I/Othread,SQLthread)运行在从节点
MySQL架构篇整个过程如下:
1:主库db的更新事件(update、insert、delete)被写到binlog;
2:从库发起连接,连接到主库;
3:此时主库创建一个binlogdumpthread,把binlog的内容发送到从库;
4:从库启动之后,创建一个I/O线程,读取主库传过来的binlog内容并写入到relaylog;
5:还会创建一个SQL线程,从relaylog里面读取内容,将更新内容写入到slave的数据库。
通过这个执行步骤,我们可以大概知道这几个线程分别是干什么用的。这里再总结一下:
主节点binarylogdump线程:
当从节点连接主节点时,主节点会创建一个logdump线程,用于发送bin-log的内容。在读取bin-log中的操作时,此线程会对主节点上的bin-log加锁,当读取完成,甚至在发送给从节点之前,锁会被释放。如果有多个从节点连接,就需要多个线程。
从节点I/O线程:
当从节点上执行startslave命令之后,从节点会创建一个I/O线程用来连接主节点,请求主库中更新的bin-log。I/O线程接收到主节点binlogdump进程发来的更新之后,保存在本地relay-log中。
从节点SQL线程:
SQL线程负责读取relaylog中的内容,解析成具体的操作并执行,最终保证主从数据的一致性。
那么问题来了,为什么要写入relaylog呢,而不是直接写入数据库呢?
从服务器I/O线程将主服务器的Binlog日志读取过来,解析到各类Events之后记录到从服务器本地文件,这个文件就被称为relaylog。relay-log的结构和binlog非常相似,只不过他多了一个master.info和relay-log.info的文件。
master.info记录了上一次读取到master同步过来的binlog的位置,以及连接master和启动复制必须的所有信息。relay-log.info记录了文件复制的进度,下一个事件从什么位置开始,由sql线程负责更新。
然后SQL线程会读取relaylog日志的内容并应用到从服务器,从而使从服务器和主服务器的数据保持一致。中继日志充当缓冲区,这样master就不必等待slave执行完成才发送下一个事件。
总结一下,这个主从同步的完整步骤如下:
MySQL架构篇Master收到客户端请求语句,在语句结束之前向二进制日志写入一条记录,可能包含多个事件。此时,一个Slave连接到Master,Master的dump线程从binlog读取日志并发送到Slave的IO线程。IO线程从master.info读取到上一次写入的最后的位置。IO线程写入日志到relay-log中继日志,如果超过指定的relay-log大小,写入轮换事件,创建一个新的relay-log。更新master.info的最后位置SQL线程从relay-log.info读取进上一次读取的位置SQL线程读取日志事件在数据库中执行sql更新relay-log.info的最后位置Slave记录自己的binlog日志细心的小伙伴肯能会发现一个问题,这里IO和SQL线程有会产生重复事件的问题,举一个场景:
先记录中继日志,然后更新master.info位置此时服务器崩溃,写入master.info失败服务器恢复,再次同步从master.info获取到的是上一次的位置,会导致事件重复执行那如何解决呢?如果先更新master.info再记录中继日志呢?这样带来的问题就是丢失数据的问题。
而mysql认为丢失比重复更严重,所以要先刷新日志,保大还是保小mysql帮你做了决定。
主从同步的问题
介绍原理之后,细心的小伙伴可能会发现一个问题——Slave同步延迟。
因为Slave端是通过I/Othread单线程来实现数据解析入库;而Master端写Binlog由于是顺序写效率很高,当主库的TPS很高的时候,必然Master端的写效率要高过Slave端的读效率,这时候就有同步延迟的问题。
I/OThread的同步是基于库的,即同步几个库就会开启几个I/OThread。
基于Binlog的复制方式肯定有这种问题,MySQL官方也意识到,单线程不如多线程强,所以在MySQL5.7版本引入了基于组提交的并行复制(官方称为EnhancedMulti-threadedSlaves,即MTS),设置参数:
slave_parallel_workers0即可,并且global.slave_parallel_type=‘LOGICAL_CLOCK’,
即可支持一个schema(库)下,slave_parallel_workers个worker线程并发执行relaylog中主库提交的事务。
一个组提交的事务都是可以并行回放(配合binaryloggroup