前语
最近公司某项目上反应mysql主从仿制失利,被运维部门记了一次大过,影响到了项目的检验推进,那么终究是什么原因导致的呢?而主从仿制的原理又是什么呢?本文就对排查剖析的过程做一个记载。
主从仿制原理
咱们先来简略了解下MySQL主从仿制的原理。
- 主库
master
服务器会将 SQL 记载经过dump
线程写入到 二进制日志binary log
中; - 从库
slave
服务器敞开一个io thread
线程向服务器发送恳求,向 主库master
恳求binary log
。主库master
服务器在接收到恳求之后,依据偏移量将新的binary log
发送给slave
服务器。 - 从库
slave
服务器收到新的binary log
之后,写入到本身的relay log
中,这便是所谓的中继日志。 - 从库
slave
服务器,单独敞开一个sql thread
读取relay log
之后,写入到本身数据中,从而确保主从的数据一致。
以上是MySQL主从仿制的扼要原理,更多细节不展开讨论了,依据运维反应,主从仿制失利主要在IO线程获取二进制日志bin log
超时,一看主数据库的binlog
日志竟达到了4个G,正常情况下依据配置应该是不超越300M。
binlog写入机制
想要了解binlog
为什么达到4个G,咱们来看下binlog的写入机制。
binlog
的写入机遇也十分简略,事务履行过程中,先把日志写到 binlog cache
,事务提交的时分,再把binlog cache
写到binlog
文件中。因为一个事务的binlog
不能被拆开,无论这个事务多大,也要确保一次性写入,所以体系会给每个线程分配一个块内存作为binlog cache
。
- 上图的
write
,是指把日志写入到文件体系的page cache
,并没有把数据持久化到磁盘,所以速度比较快 - 上图的
fsync
,才是将数据持久化到磁盘的操作, 生成binlog
日志中
出产上MySQL中binlog
中的配置max_binlog_size
为250M, 而max_binlog_size
是用来控制单个二进制日志巨细,当前日志文件巨细超越此变量时,履行切换动作。,该设置并不能严格控制Binlog的巨细,尤其是binlog
比较接近最大值而又遇到一个比较大事务时,为了确保事务的完整性,可能不做切换日志的动作,只能将该事务的一切$QL都记载进当前日志,直到事务完毕。一般情况下可采纳默认值。
所以说置疑是不是遇到了大事务,因此咱们需要看看binlog中的内容详细是哪个事务导致的。
查看binlog日志
咱们能够运用mysqlbinlog
这个工具来查看下binlog中的内容,详细用法参考官网:https://dev.mysql.com/doc/refman/8.0/en/mysqlbinlog.html
。
- 查看
binlog
日志
./mysqlbinlog --no-defaults --base64-output=decode-rows -vv /mysqldata/mysql/binlog/mysql-bin.004816|more
- 以事务为单位计算
binlog
日志文件中占用的字节巨细
./mysqlbinlog --no-defaults --base64-output=decode-rows -vv /mysqldata/mysql/binlog/mysql-bin.004816|grep GTID -B1|grep '^# at' | awk '{print $3}' | awk 'NR==1 {tmp=$1} NR>1 {print ($1-tmp, tmp, $1); tmp=$1}'|sort -n -r|more
出产中某个事务居然占用4个G。
- 经过
start-position
和stop-position
计算这个事务各个SQL占用字节巨细
./mysqlbinlog --no-defaults --base64-output=decode-rows --start-position='xxxx' --stop-position='xxxxx' -vv /mysqldata/mysql/binlog/mysql-bin.004816 |grep '^# at'| awk '{print $3}' | awk 'NR==1 {tmp=$1} NR>1 {print ($1-tmp, tmp, $1); tmp=$1}'|sort -n -r|more
发现最大的一个SQL居然占用了32M的巨细,那超越10M的大概有多少个呢?
- 经过超越10M巨细的数量
./mysqlbinlog --no-defaults --base64-output=decode-rows --start-position='xxxx' --stop-position='xxxxx' -vv /mysqldata/mysql/binlog/mysql-bin.004816|grep '^# at' | awk '{print $3}' | awk 'NR==1 {tmp=$1} NR>1 {print ($1-tmp, tmp, $1); tmp=$1}'|awk '$1>10000000 {print $0}'|wc -l
计算成果显示居然有200多个,毛估一下,也有近4个G了
- 依据pos, 咱们看下终究是什么SQL导致的
./mysqlbinlog --no-defaults --base64-output=decode-rows --start-position='xxxx' --stop-position='xxxxx' -vv /mysqldata/mysql/binlog/mysql-bin.004816|grep '^# atxxxx' -C5| grep -v '###' | more
依据sql,剖析了下,这个表正好有个blob
字段,计算了下blob字段总合大概有3个G巨细,然后咱们事务上有个导入操作,这是一个十分大的事务,会频繁更新这表中记载的更新时刻,导致生成binlog
十分大。
问题: 分明只是简略的修正更新时刻的句子,压根没有动blob
字段,为什么出产的binlog
这么大?因为出产的binlog选用的是row形式。
binlog的形式
binlog
日志记载存在3种形式,而出产运用的是row
形式,它最大的特色,是很精确,你更新表中某行的任何一个字段,会记载下整行的内容,这也便是为什么blob
字段都被记载到binlog
中,导致binlog
十分大。此外,binlog
还有statement
和mixed
两种形式。
- STATEMENT形式 ,根据SQL句子的仿制
-
长处: 不需要记载每一行数据的变化,削减
binlog
日志量,节约IO,进步功用。 -
缺陷: 因为只记载句子,所以,在
statement leve
l下 现已发现了有不少情况会形成MySQL的仿制出现问题,主要是修正数据的时分运用了某些定的函数或者功用的时分会出现。
- ROW形式,根据行的仿制
5.1.5版别的MySQL才开端支撑,不记载每条sql句子的上下文信息,仅记载哪条数据被修正了,修正成什么样了。
-
长处:
binlog
中能够不记载履行的sql句子的上下文相关的信息,只是只需要记载那一条被修正。所以rowlevel
的日志内容会十分清楚的记载下每一行数据修正的细节。不会出现某些特定的情况下的存储过程或function
,以及trigger
的调用和触发无法被正确仿制的问题 - 缺陷: 一切的履行的句子当记载到日志中的时分,都将以每行记载的修正来记载,会发生大量的日志内容。
- MIXED形式
从5.1.8版别开端,MySQL提供了Mixed
格局,实际上便是Statement
与Row
的结合。
在Mixed
形式下,一般的句子修正运用statment
格局保存binlog
。如一些函数,statement
无法完成主从仿制的操作,则选用row格局
保存binlog
。
总结
终究剖析下来,咱们定位到原来是因为大事务+blob字段大致binlog十分大,终究咱们选用了修正事务代码,将blob字段单独拆到一张表中解决。所以,在规划开发过程中,要尽量防止大事务,同时在数据库建模的时分特别考虑将blob字段独立成表。
欢迎重视个人大众号【JAVA旭阳】交流学习