最近在看Charles Bell写的《高可用MySQL》一书,发现该书本讲述了很多之前没有了解到的关于MySQL的技术内幕点,今天抽空写一篇总结性的文章来将书中自己觉得有收获的地方进行归纳总结。
在软件行业发展之处,很多简单的应用都是采用单点架构的模式来部署数据库机器,但是随着系统的访问量提升,请求变得频繁,数据量增大,在设计整个数据库架构的时候需要考虑到系统的稳定性,可扩展性,可用性等因素,于是渐渐的便出现了“复制”的思想。
认识master-slave
简单的画个图来表示,将一台机器看做为master,另外一台机器看作为slave,当我们发送写请求的时候通常都会写入到master这台机器上边,需要进行读请求的时候则是从slave机器上边读取数据。
配置master-slave架构的文档说明网上有大把的资料讲解,这里我就不做过多不必要的介绍了。
为什么要有master-slave架构
多份数据备份,提高数据的安全性。
可以将对mysql的读写请求进行分离,降低对于服务器访问的压力。
复制过程中发生了什么
我们知道mysql和slave是不同的机器,机器之间想要进行数据交流那么就需要借助网络的帮助了。那么mysql和slave之间进行数据交流和同步的方式又是如何的呢?
方案1
master每次记录的数据都通过网络的形式发送给每一台slave机器,让slave执行相同的操作,从而达到数据同步。但是这种操作实在是过于繁琐,master既要管理自己的数据,还需要处理slave的数据一致性问题,加上万一网络异常,slave机器集群中可能还会出现不同机器获取接收到的数据不一致情况。所以这种方案不实际。
方案2
master机器通过将自身的数据变动记录在一份二进制日志上边,然后slave机器通过专门的线程(I/O线程)订阅这份日志的信息记录到自己的机器上边,另存为中继日志,再通过独立的线程(SQL线程)来执行中继日志的信息从而实现slave和master的数据同步,实现主从复制。
binlog里面有什么?
首先mysql默认是没有开启binlog日志记录功能的,需要我们在my.cnf文件里面配置相应的参数才能正常开启。
通过使用mysql提供的命令,我们可以去查看mysql的binlog日志信息
这里面有几个属性我稍微解释一下
log_name
指binlog的文件名称。binlog实际上记录的时候是有两份文件的,一份叫做索引文件(例如:maset-96-bin.index),一份是对应的二进制日志文件(例如:maset-96-bin.00000x )
Pos
指日志记录的开始位置
Server_id
服务器的唯一id,需要和slave和master的唯一id不能相同
End_log_pos
日志记录的结束位置
Info
日志的备注描述信息
Event_type
指事件的类型
在mysql5.7里面事件的类型有很多种,在binlog里面记录的事件有多种,我们可以通过查看mysql的源码mysql-5.7.14/libbinlogevents/include/binlog_event.h
常见的几种事件类型
QUERY_EVENT
主要是记录一些事务操作,例如事务的开始begin,
FORMAT_DESCRIPTION_EVENT
这种事件主要是记录了mysql的版本描述信息,通常只有在binlog初始化的时候才会出现一次。
ROWS_EVENT
binlog的格式包含有rows格式,statement格式,mixed格式,当binlog的类型为rows格式的时候,所有的dml语句都会被记录到rows_events事件里面。
对于insert操作,WRITE_ROWS_EVENT包含了要插入的数据
对于update操作,UPDATE_ROWS_EVENT不仅包含了修改后的数据,还包含了修改前的值。
对于delete操作,仅仅需要指定删除的主键(在没有主键的情况下,会给定所有列)
XID_EVENT
当事务提交的时候,事件会记录一个xid来表示当前事务的id。
ROTATE_EVENT
当我们的一份binlog日志过大的时候,mysql需要更换记录到新的日志文件,那么这个时候就需要进行mysql日志记录的文件切换了。这里截取一份书中提及的mysql binlog日志结构图,如下:
binlog文件的文件尾部有加入一个轮换事件Rotate的记录,根据轮换事件来读取下一份日志的相关信息。
什么时候需要轮换?
1.服务器停止。
2.日志大小达到了相应文件大小的上限。
3.dba通过命令强制刷新日志记录的时候。出现事故的时候,dba最好切换到新的日志记录操作,单独的一份日志记录可以有利于操作命令的隔离,防止后续的slave进行读取操作。
binlog记录这么多的数据信息,其根本的目的还是在于后续能够安全地实现数据恢复。通常我们的数据恢复会有基于时间点的恢复和基于日志pos位置进行恢复的方式两种。如果需要详细查看binlog的日志还可以通过mysqlbinlog工具来细看,这款工具网上有很多的讲解,这里我只做简单的一些命令介绍。
常见的mysqlbinlog命令介绍
binlog何时进行持久化?
在binlog的相关参数设置中,有个叫做sync_binlog 的参数,主要是协调binlog进行持久化记录。这个参数的核心作用在于控制MySQL 的二进制日志(binary log)同步到磁盘的频率。
sync_binlog=0,当事务提交之后,MySQL不做fsync之类的磁盘同步指令刷新binlog_cache中的信息到磁盘,而让Filesystem自行决定什么时候来做同步,或者cache满了之后才同步到磁盘。这个是性能最好的。
sync_binlog=1,当每进行1次事务提交之后,MySQL将进行一次fsync之类的磁盘同步指令来将binlog_cache中的数据强制写入磁盘。
sync_binlog=n,当每进行n次事务提交之后,MySQL将进行一次fsync之类的磁盘同步指令来将binlog_cache中的数据强制写入磁盘。
在前边我们主要还是说了关于binlog的内容,但是实际上mysql里面的日志种类远不止binlog这一种,下边我们来深入认识一下其他的几类常见日志文件。
Undo log
undo日志主要是用于存放数据被修改前的数值,当mysql执行某些操作过程中出现了异常,那么可以通过undo日志来进行数据的回滚操作。
在mysql5.6之后的版本便开始提供了单独分配undo 表空间的功能,在mysql启动的开始我们需要手动创建undo目录以及表空间的个数,尽量将undo日志的目录单独创建出来,例如单独存放在一些高速磁盘上边,和原先的ibdata文件作分离,提升读写性能。
设置undo表空间
通过命令来查看关于undo日志的参数信息:
这里面的参数都比较好理解,可以见名知意,稍微提及一下这两个参数:
innodb_undo_log_truncate
innodb里面有一条purge线程,会根据innodb_undo_log_truncate的开启或者关闭以及innodb_max_undo_log_size参数来定期清理undo日志。
innodb_undo_tablespaces
这个参数的作用是用于设置独立表的空间个数,默认为0,表示未开启。未开启的时候,undo日志其实适合ibdata文件存储在一块的,这样容易导致ibdata文件过大,读写性能低下。所以当数据量较大的时候,可以将undo日志抽出来放置在新的磁盘空间中。在mysql5.7之后,undo的表空间还可以设置truncate参数,定期进行空间的清理。
这里需要注意一下,undo_tablespaces参数的必须要大于2,因为在进行undo日志删除的那个时间段,该undo表空间会设置为offline状态,因此此时需要有另外一份表空间供server运作。
Redo log
背景介绍
当我们需要对数据库里面的数据进行读取的时候,实际上mysql会将磁盘上边的数据页内容读取到一个buffer pool上边,然后让我们在buffer pool上边做修改,此时如果我们将buffer pool上边的数据再写入到磁盘上边的时候,这个过程实际上是一个随机io写的过程,效率较为差,而且万一写的过程中出现了数据库crash的情况,那么就会导致数据丢失的情况。
因此redo log就登场了,当buffer pool 中的data page变更结束后,把相应修改记录记录到redo log 文件中,然后再将redo log文件再将数据写入到disk上边。 假设一个大事务,对db做10万行的记录插入,在这个过程中,一直不断的往redo log顺序记录,而binary log不会记录,知道这个事务提交,才会一次写入到binary log文件中。
redo log参数调优
在redo log的相关参数调节时,有着各种不同含义的参数设置,这里我主要列举几个会需要关注到的调优参数:
innodb_flush_log_at_trx_commit 参数
这个参数是 InnoDB 引擎特有的,主要的作用是控制InooDB写入磁盘的操作策略,redo log本身其实是由两个部分组成的,分别是redo log和redo log buffer,在了解了redo log结构之后我们再来看看下边的几个参数讲解。
innodb_flush_log_at_trx_commit=0,表示每隔一秒把redo log buffer刷到文件系统中(os buffer)去,并且调用文件系统的“flush”操作将缓存刷新到磁盘上去。这样能够保证每隔一秒就将缓存里面的数据都被写入到磁盘中,如果出现了mysql crash或者os crash的事故损耗只会有一秒的数据变更。
innodb_flush_log_at_trx_commit=1,表示在每次事务提交的时候,都把redo log buffer刷到文件系统中(os buffer)去,并且调用文件系统的“flush”操作将缓存刷新到磁盘上去。和上边的不一样之处在于,此时的同步频率是每次事务提交,这个设置是innodb默认的参数设置,这样处理的好处在于能够保证每次已提交的数据都是安全的,但是对磁盘的io访问压力较大。
innodb_flush_log_at_trx_commit=2,表示在每次事务提交的时候会把redo log buffer刷到文件系统中去,但并不会立即刷写到磁盘。如果只是MySQL数据库挂掉了,由于文件系统没有问题,那么对应的事务数据并没有丢失。只有在数据库所在的主机操作系统损坏或者突然掉电的情况下才会发生数据丢失。
了解了相关的参数之后,我们可以试着调节相应的参数来提升mysql每秒插入数据的性能,感兴趣的读者可以自己做些小实验来测试一下这几个参数在不同数值的时候,数据库写数据的性能差异。
Relay log
Relay log 通常我们也称之为中继日志,它是mysql里面复制架构的核心部分,主要作用是slave端开启了io线程去读取master的binlog日志之后,将内容写入到自己机器上边的relay log中,方便本地的sql线程执行,从而实现同步的操作。
关于relay log方面我主要打算讲解它的复制方式。
1.同步复制
当master执行完一个事务之后,还要等待所有从库都将相应的事物记录并执行之后才返回给客户端。因此性能较慢,不推荐。
2.异步复制
这种方式是mysql默认的复制方式,当master复制信息之后就直接将事务提交,不管slave是否有进行相关的记录。这种方式的性能是最佳的,但是如果master挂掉了,此时将从转移为主的话,会有数据不一致性的情况。
3.半同步复制
介于异步复制和全同步复制之间,主库在执行完客户端提交的事务后不是立刻返回给客户端,而是等待至少一个从库接收到并写到relay log中才返回给客户端。相对于异步复制,半同步复制提高了数据的安全性,同时它也造成了一定程度的延迟,这个延迟最少是一个TCP/IP往返的时间。所以,半同步复制最好在低延时的网络中使用。
下面来看看半同步复制的原理图:
4.半同步复制架构后期改善点
关于主从架构的性能优化点很多时候可以通过提升mysql的版本来提升,先简单说说mysql的5.6和5.7的区别,在进行半同步复制的时候,两个版本的性能处理方式存在巨大发不同点。
5.6版本,单线程处理,dump thread处理传送binlog给slave,然后等待slave反馈信息,反馈了相应信息才会继续传送下一个events事务。
5.7版本里面则做了多线程处理,一条现场专门用于传送binlog,一条线程专门用于接收slave的数据反馈,从而提升了半同步复制的效率。
复制细节
1.事务缓存处理
在mysql的二进制日志里面,当多个会话同时访问server执行事务性sql语句的请求时候,binlog会给每个会话单独开启一个线程进行事务性sql的缓存处理。直至当相应的sql执行完毕之后再写入到binlog日志中。
这样做的好处在于能够将不同的事物进行分隔出来处理,并且保证每个写入binlog的事务sql都是完整且正常执行的一个单位。如果事务在执行的过程中发生了回滚的话,可以直接在内存中进行数据删除,不需要再在日志里面进行记录删除操作。
2.多事务交错执行场景
当同时有多个事务执行的时候,可能多个事务操作会串行化交错进行,但是binlog并不会将其交错记录,在binlog里面记录事务操作的时候会按照相应事务commit的时间点来排序记录。
3.binlog版本管理
binlog文件里面其实是有设置版本号的,不同版本的MySQL中binlog的结构格式都有不同的形式,因此如果我们在选定master机器和slave机器进行搭建主从的时候最好选择相同版本的MySQL,否则会有意想不到的事故发生。
4.特殊场景的复制处理
MySQL的binlog在记录数据修改操作的时候还需要考虑上下文信息,例如说当我们在语句中使用了某些特殊的自定义变量,或者说使用了rand随机函数,又或者说master上边执行了类似于now(),sysdate() 这类时间函数等等情况,在进行master-slave数据同步的过程中都需要进行考虑。因此mysql的master-slave背后其实隐藏了很多不为人知的技术细节点等待着我们去慢慢思考和品味。
由于时间和精力的问题,书中还有很多关于多源复制,master热备份,主从切换,mysql缓存机制等知识点都没有在本文中做过多的阐述,后续会陆续花时间进行更新,希望各位读者们喜欢。
就可以关注我们啦~
发表评论
领取专属 10元无门槛券
私享最新 技术干货