本文介绍CacheServer的主从节点是如何进行binlog同步的,涉及到MKBinLogTimeThread和MKBinLogThread 2个线程。
概述
为保证存储的可靠性,Master节点会同步将本次数据操作(如set/delete)记录到binlog文件中,然后迅速将binlog同步到Slave节点,以保证多节点数据的最终一致性。
DCahce的binlog同步是由Slave发起的,调用Master的rpc方法拉取binlog并记录到本地。Slave每次写完本地binlog后,都会将本次同步的时间点写入sync_time.data文件中,用于评测主从同步质量(数据同步的时间差超过300ms会禁止主从切换);将本次同步过来的binlog文件名和指针位置写入sync_point.data中。后续,Slave无论是正常处理还是异常恢复,都会从sync_point.data中读取当前同步的文件名和指针位置,调用Master的服务继续同步binlog。
记录binlog同步时间点(sync_time.data)和同步位置(sync_point.data)分别由线程MKBinLogTimeThread和MKBinLogThread线程来完成的。下面分别介绍下这2个线程。
MKBinLogTimeThread
功能:获取备份源最后记录binlog时间的定时类。
该线程在CacheServer初始化时启动。
文件:
MKBinLogTimeThread.h
MKBinLogTimeThread.cpp
配置解析
#保存synctime文件的间隔(秒)
SaveSyncTimeInterval=10
每隔SaveSyncTimeInterval秒记录一次binlog同步时间点到sync_time.data中。
Master的处理流程
每隔100ms上报一次binlog的同步时间差到Dcache的OptServer,用于监控主从同步质量。
Slave的处理流程
- 初始化BinLogObj服务的代理;
- 线程启动时从文件sync_time.data中获取当前同步时间(g_binLogSyncTime.tSync)和上一次同步时间(g_binLogSyncTime.tLast);
- 休眠3秒,防止同步binlog线程还没有同步binlog 而上报同步差异;
- 线程循环开始,每隔500ms执行一次该线程;
- 距上次保存sync_time.data时间超过阈值(SaveSyncTimeInterval)时,再次保存 sync_time.data;
- 如果处于备机恢复状态(g_app.gstat()->isSlaveCreating()),则休眠100ms,回到4;
- 如果Master(备份源)地址发生变化,则重新创建BinLogObj服务的代理,更改binlog源。
-
如果没有主节点了,则上报binlog的同步时间差,休眠100s,回到4;
- 调用BinLogObj服务的getLastBinLogTime方法,获取备份源内存中最后记录binlog的时间;
- 更新备份源的binlog最新记录时间到本地的内存(g_binLogSyncTime.tSync和g_binLogSyncTime.tLast)中;
- 上报binlog的同步时间差,回到4;
MKBinLogThread
功能:Slave向Master(备份源)发起请求,拉取binlog。
该线程在CacheServer初始化时启动。
文件:
MKBinLogThread.h
MKBinLogThread.cpp
配置解析
涉及到配置文件中的信息如下:
<BinLog>
#binlog日志文件名后缀
LogFile=binlog
#每次同步binlog的行数
MaxLine=10000
#是否记录binlog
Record=Y
#是否记录key binlog
KeyRecord=N
#主备同步使用key binlog
KeySyncMode=N
#同步binlog是否开启压缩
SyncCompress=Y
#采用gzip压缩格式
IsGzip=Y
#备机同步binlog缓存buffer的szie
BuffSize=10
#保存synctime文件的间隔(秒)
SaveSyncTimeInterval=10
#active binlog产生的周期(秒)
HeartbeatInterval=600
</BinLog>
<Cache>
#insert一个数据是在主key链的头部还是尾部,Y/N -- 头/尾
InsertOrder=N
#数据更新时,同时更新主key链下的顺序
UpdateOrder=N
#主key最大记录条数限制,只在无源cache时有效,超过最大记录限制将删除旧的数据,0表示无限制
MkeyMaxDataCount=0
</Cache>
<DbAccess>
#是否存在DB,Y/N
DBFlag=Y
#DbAccess的obj名称
ObjName=DCache.TestDbAccessServer.DbAccessObj
#当Cache中没有数据时,是否从DB或文件查询, Y/N
ReadDbFlag=Y
</DbAccess>
线程读取的配置:
MaxLine,每次同步binlog的最大行数
LogFile
SyncCompress
Record
KeySyncMode
BuffSize
InsertOrder
UpdateOrder
MkeyMaxDataCount
ReadDbFlag,如果配置为Y/y,则MkeyMaxDataCount重置为0(无数量限制)
DBFlag,Slave是否写binlog文件
正常情况下我们采用默认配置即可。
可能会修改到的配置是BuffSize。当binlog的队列中数据量大于该值时,会提示:
[MKBinLogThread::syncBinLog] queue is full!
并结束本次同步。我们可以通过修改这个值来增加binlog缓存。
处理流程
会启动2个线程:
- run线程,写binlog线程队列,并更新binlog的时间;
- WriteData线程,从binlog线程队列中取出数据写入binlog文件。
Slave处理流程
run线程:
1.初始化binlog服务代理
2.循环处理(100ms)
3.如果为备机恢复状态(SlaveCreating.dat),则休眠100ms,再次执行2;
5.从sync_point.data文件中获取binlog的文件名称以及指针位置;
6.确认备份源地址(一般情况下都是Master)是否有变化,有变化则重新获取;
7.执行binlog同步;两种同步方式:压缩同步(syncCompress)和正常同步(syncBinLog);
syncBinLog:
- binlog线程队列中的数据量大于阈值(BuffSize配置)时,结束同步,回到2;
- 调用主节点的 BinLogObj服务getLog;
- 更新binlog的时间(这里只更新了内存中的值);
- 写入binlog线程队列_binlogDataQueue,重置指针位置的值(内存中);
syncCompress:
- SyncCompress=Y/y时调用
- 与syncBinLog流程基本一致,调用BinLogObj的getLogCompressWithPart服务;
- 获取经过压缩的Binlog日志,如果返回的数据超过8M,会分块返回;
WriteData线程:
1.循环开始
2.binlog线程队列_binlogDataQueue中有数据时,pop取出;如果没数据,休眠100ms回到1;
3.如果是压缩同步,则解压;
3.写binlog(MKBinLogThread::wirteBinLog)
- 如果binlog类型是 BINLOG_ACTIVE,则跳过 (保活日志,确保每个小时都有binlog生成);
- 将binlog数据写入内存;如果接了db,就进行相应处理(g_HashMap);
- 如果Record配置为Y/y,则将binlog内容写入磁盘文件;
4.保存binlog的文件名以及已经同步的指针位置到文件sync_point.data中。
Master处理流程
run线程:删除sync_point.data文件,休眠100ms,再次执行。
WriteData线程:Master节点由于binlog队列中没有数据,休眠100ms,再次执行。
为什么是100ms就扫描一次?
由于网络故障而发生主从切换时,原来的Master(现在变成了Slave)恢复服务后,就可以立即进入Slave状态、进行binlog同步。
总结
DCache在做binlog同步时,由CacheServer启动2个独立的线程进行处理,由Slave调用Master的服务同步binlog。将同步时间点记录到sync_time.data文件中,用于判断主从同步质量;将当前同步的binlog文件名以及文件中的指针位置记录到sync_point.data文件中,用于“断点续传”。
DCache使用的是“同步刷盘,异步复制”的策略,非强一致性。但是其默认100ms的同步周期(这个值是写死的)将主从数据差异控制在了一定范围内。