2014-12-02

SSDB源码分析 – 主从和多主同步原理解析

Views: 114696 | 53 Comments

SSDB 的主从同步策略非常简单, 就是把主(Master)上的所有写操作(Binlogs), 在从(Slave)上再执行一遍. MySQL 的主从同步也是一样. 而多主可以理解为互为主从.

把 Master 上的所有操作(Binlogs)在 Slave 上执行一遍, 说来很简单, 但还是会遇到一些难题, 例如 Binlogs 不可能无限地永久保留. SSDB 只保留最新的 1000 万次写操作. 对于熟悉 MySQL 的同学可能也知道这样的例子: 在有 Binlogs 之前, 数据库内已经有了一部分数据. 也就是说, 这部分数据是无法通过 Binlog 来获得的.

为此, 要有一个基础数据的拷贝(Copy)过程. 对于 MySQL 来说, 必须由 DBA 手动拷贝. 而对于 SSDB 来说, 这是自动的.

COPY 状态

因为 Copy 过程不是立即完成的, 所以如何处理 Copy 过程中的新的 Binlog 又成为一个问题. 当然, 问题也很容易解决. 不过, 在解决之前, 要先了解 SSDB 的 Copy 过程.

SSDB 数据库中的所有数据都是排好序的, 所以你可以把整个数据库理解为一个链表, SSDB 从表头开始 Copy, 一次一个节点, 游标一直往后. 这时, 如果有新的 Binlog, SSDB 会先判断这条 Binlog 对应的节点在链表中的什么位置, 是在游标的前面还是后面?

如果在游标的前面, 那么会把这条 Binlog 发给 Slave 执行. 如果在游标的后面, 就会直接忽略掉, 因为游标最终会移动到更新的位置. 从这个描述也可以知道, 处于 Copy 阶段的 Slave, 有可能无法立即知道 Master 上的更新.

当游标移动到了链表的末端之后, Copy 过程就结束了, 主从同步流程进入到 Sync 阶段, 也就是即时(毫秒级)更新阶段.

SYNC 状态

Sync 其实非常简单, 就是 Master 每收到一条写指令, 就把这条指令发给 Slave 执行.

Master 会把 Binlog 进行编号(序号), 所以, Slave 在重启之后是可以断点续传的. 因为有序号的原因, Master 上就可以看到 Binlog 最大序号(用 ssdb-cli 执行 info 命令可以看到 binlogs.max_seq):

binlogs
        capacity : 10000000
        min_seq  : 113033
        max_seq  : 113055

而在 Slave 上, 就能看到当前已经收到的 Binlog 的序号(replication.slaveof.last_seq).

replication
    slaveof 127.0.0.1:8888
        id         : svc_2
        type       : sync
        status     : INIT
        last_seq   : 113055
        copy_count : 0
        sync_count : 0

Master 上的 binlogs.max_seq 应该和 Slave 上的 replication.slaveof.last_seq 相等, 不然说明有一部分更新还没有来得及同步. 注意: 这两个数据是在不同的服务器上获取的!

如果在 Master 上用 info 命令查看, 你还会看到这样的信息:

replication
    client 127.0.0.1:55158
        type     : sync
        status   : SYNC
        last_seq : 113055

这是从 Master 的角度看, 当前连接上来的 Slave 的信息, replication.client.last_seq 一般是和 replication.slave.last_seq 是相等的. 注意, 这两个数据是在不同的服务器上获取的!

关于 slaveof.type 同步的类型

  • 对于主从同步, Slave 上的 ssdb.conf 中的 slaveof.type 必须配置成 sync.
  • 对于双主或者多主, 所有的服务器的 ssdb.conf 都必须将 slaveof.type 配置成 mirror!

对于双主或者多主, 如果不配置成 mirror 会出现什么情况? 那就是死循环! 一条 Binlog 会在所有服务器上不停地来回传递放大.

关于 slaveof.id

你必须知道, SSDB 节点是不知道自己的 ID 的, 你没法配置节点的 ID.

至于配置文件中的 replication.slaveof.id, 假设是在服务器 B 上的配置, B 想从 A 同步数据, 是指 B 认为它所连接的 A 的名字叫 id, 至于 A 叫什么(A 其实没有名字), 这没关系.

事实上, 这个 id 是为了标识一个数据库, 而不是一台机器, 无论这个库你把它拷贝到哪台机器上跑都没关系. B 只认这个库, 即使你把它迁移到了另一台机器, B 仍然认得, 不会认错, 还是会继续从上一次的断点继续同步.

Related posts:

  1. SSDB 的双主和多主配置
  2. MySQL 在线增加从库
  3. SSDB 配置文件
  4. MySQL 数据库双主配置
  5. SSDB源码分析 – 服务器的启动过程
Posted by ideawu at 2014-12-02 20:02:42

53 Responses to "SSDB源码分析 – 主从和多主同步原理解析"

  • ssdb 的 binlog 是基于 leveldb 底层的 binlog?还是说 ssdb 的 binlog 通过 leveldb 接口层进行保存? Reply
    @qyvlik: 不是基于leveldb的, 是自己定义的binlog日志模块 Reply
  • 有没有可能出现,双主同步时差问题,导致取时取成错误的信息,如A,B两台主,已经存在的key1信息,这是通过A更新key1了,程序中去get key1,连的key2取,但是取成未更新前的。 Reply
    程序中去get key1,连的B取 Reply
    @一诺: 嗯, 是可能的, 主从同步不是强一致的模型 Reply
  • 现在我们用两台服务器做双主,其中一台服务器磁盘故障,恢复后没有数据了,因为数据量比较大,有10亿的key,只靠slaveof比较慢。我能否直接将正常服务器的var/data目录和var/meta目录拷贝过来启动 Reply
  • hi,我又来打扰了。最近对ssdb做改造,(主要是把ssdb改成支持可动态扩展的多主集群)发现一些同步方面的问题,想跟你分享一下。
    1.假如一开始只有一个ssdb节点A.已写了部分数据在里面.这时候启动一个节点B,立即与节点A建立mirror的多主关系。发现节点A,B会flushdb一次(这是因为两者都没同步过,一开始同步的时候last_seq都是0,会触发双方flushdb)..发现这里比较危险,如果不控制好两节点间的slave先后启动问题,有可能会导致数据丢失。
    2.假如现在已有两个节点A,B.它们已是多主关系,正常同步中。此时启动节点C,立即与节点A,B建立mirror的多主关系。此时发现C会flushdb两次(这是因为C没同步过,而建立起的两个slave线程的last_seq都是0,然后跟A同步一次flushdb一次,跟B同步一次又flushdb一次。)…发现这里假如如果再多N个节点,岂不是flushdb N次。如果同步数据多的时候,有可能出现slave A同步到的数据,被slave B flushdb

    不知道你看懂我的意思不?我猜测你设计的时候,应该没考虑动态扩展的。但对于以上的问题,你有什么看法? Reply
    @lkp: 谢谢你的反馈.
    1. 我也发现此风险, 最新版本已经移除了自动flush的特性.
    2. 根据新版本, 此问题不讨论. Reply
    @ideawu 第二点最后的表述有误,应该说:发现这里假如如果再多N个节点,岂不是flushdb N次。如果同步数据多的时候,会全量同步很多次,效率很成问题。 Reply
  • 你好,我想请教一下你,我看了一下ssdb同步方面的代码,master sync时候没有管发出成不成功,我想问ssdb是否无法获知跨机房容同步丢包问题?
    打给极端比方说:
    master –>A,B,C–>slave
    假如跨机房网络抖动,B的包丢了,网络连接也没断开,master也不知道。造成。
    master –>A,C–>slave
    ssdb 无法感知??需要第三方校验检测? Reply
    @lkp: slave会通过序号检查有没有丢包. Reply
    @ideawu: 噢。。你意思这个一步检查,但这个目测是master的代码?
    if(this->last_seq != 0 && log.seq() != expect_seq){
    this->status = Client::OUT_OF_SYNC;
    return 1;
    }
    然后还有一个问题。就是关于多主的时候,我发现copy阶段slave会从每个master里面都copy一次。假如节点多,数据量大了的时候,扩容一个ssdb节点的话,感觉那得copy好久时间来着。
    谢谢你的回答。 Reply
    @lkp: 问下对于OUT_OF_SYNC状态的处理,主机为什么是不再管,不应该是重新copy吗(希望加下我qq,请教一下) Reply
    @lkp: 哦。。不对。。我看错日志了。。只是copy了一次。。有序号在。。目测会去重。 Reply
  • binlog源码的一点疑问:

    int BinlogQueue::del_range(uint64_t start, uint64_t end){
    while(start <= end){
    leveldb::WriteBatch batch;
    for(int count = 0; start <= end && count < 1000; start++, count++){
    batch.Delete(encode_seq_key(start));
    }
    //这里会实际删除数据库的数据吧?log_clean_thread_func的定位应该是清理min/last seq ,维护一个范围而已,为何要实际调用本函数删除数据呢?况且,set的kv时候,在commit时候,value实际写write入了db了(然后清理log时候再给从db删除)?实在不明白!

    leveldb::Status s = db->Write(leveldb::WriteOptions(), &batch);
    if(!s.ok()){
    return -1;
    }
    }
    return 0;
    } Reply
  • 处于copy状态时,如果新的binlog在游标之前会发送到slave去执行,那么会不会copy完之后,旧的数据替换掉了新的binlog? Reply
    @veklip: copy状态完成之后, 才会开始发送增量binlog Reply
  • 如果做双主同步,主机分别是A和B,如果A挂了,把B的ip改为A,不重启SSDB(那么就是自己mirror自己),会不会出现死循环或者别的什么问题。 Reply
  • 你好,目前我打算在项目中使用ssdb,处于测试阶段。单节点的访问量和写入量都还不错。但是发现键值对个数过多的情况下,如果要建立个全新的slave,那么从copy到sync状态的过程会很长很长,实测1.3亿key-value需要2个小时才能完全同步(此时master没有写入了),然而整个ssdb数据的总大小不过1.4GB,感觉这样小的数据量应该很快同步完成才合理。不知能否像mysql那样,先dump出一个基础备份,直接复制这个物理文件过去,然后再主从同步后面的?估计这样会快一些。不知你们在实际使用过程中,应该怎么应付这种键值对多但总数据量不多时,新slave建立缓慢的情况? Reply
    @bupa: 我的问题跟你比较类似,是否能使用ssdb-dump备份出data文件后,使用备份的data文件启动后再slave备份和启动过程中的差异数据 Reply
    @bupa: 这个受网速和带宽的影响比较大. Reply
  • 请教一下楼主,多主的时候,每个实例的binlog的序号是怎么生成的呢?我看同步过程是根据序号来判断的,会不会因为两台机同时写的时候,碰巧有同样的序号,而不会进行同步呢 Reply

« [1][2] » 1/2

Leave a Comment