数据迁移不停服设计借鉴


jvm的gc

redis的rehash

codis的rehash

业务系统数据迁移核心是update (无状态迁移,双写)

业务模型的重构和模型改造. 层级关系. 新建字段. 原来的还是写,但是不再读,和作业务判断. 同样也是双写的机制. 另外开一张表不合适,状态什么的可以不用新改. 不然后续更新也要双更新.

hashmap 的 resize

concurrentHashMap 的 rehash

要求:

1.可遍历. 

   1.按顺序遍历

    2.不按顺序遍历

2.可拆解

两种模式.

1. 有状态.记录哪些迁移,哪些没迁移

2.无状态, insert 单向. 双读,双更新.

选择后种.

*jvm的gc

  整体迁移时的diff log. 保存更改的log. stop the world. 迁移,切换.

  改进点: 将整体迁移改成逐步迁移, 实现方案是 利用hash 后获得slot 加锁迁移,, 逐步迁移. 缺点: 降低吞吐量.

*redis的rehash

  1. 单线程,无锁.增删改查,rehash迁移线程.

  2. 修改增删改查逻辑. 当迁移时需要先迁移,再进行操作. 最小粒度原则.

  3. 遍历所有的key.(通过hash槽位遍历)

  redis的rehash,利用了双hash表。属于迁移问题.

  rehash阶段的读操作 http://www.th7.cn/db/nosql/201602/176178.shtml  rehash阶段改变了原来的读操作,含写。

 *codis的rehash

    1. 引入slot的概念,分层, zookeeper只记录slot集合信息(slot量可控),  如何遍历一个slot下所有的key ,一个key属于哪个slot有谁来记录?

     Slot模型层,代码位置在pkg/models/slot.go。

   原生Redis中并没有Slot这个概念,也就是说:虽然我们给Key分配好了Slot,但是一旦存入Redis后Key属于哪个Slot这个信息就丢失了。解决的方法有很多种,比如:

  • 1)在ZooKeeper中保存Key与Slot的对应关系,需要时就查询一下。这种方式类似HDFS中的NameNode,缺点是Key很多时会占用很多空间。
  • 2)数据迁移时遍历所有Key,用哈希函数现去算一下Key所属的Slot是否要迁移,缺点是迁移时计算量比较大,而且每个Slot迁移时都要去算可能有很多重复的计算量。
  • 3)保存到Redis之前在Key或Value中加入一些隐含信息,缺点是会改变业务的数据。
  • 4)修改Redis源码加入Slot的概念,在Redis中保存Key属于的Slot,并提供基于Slot的Migrate原子操作。Codis采取的是这种做法,缺点就是要修改Redis源码,以后升级Redis比较麻烦,尤其像Codis没有将改动封装到一个动态链接库则可能更为麻烦。
  • 5)利用Redis中database的概念替代Slot,GitHub上的xcodis采取的就是这种思想。缺点是每个Slot对应的Redis连接在使用前都要select到对应的数据库,否则就会修改到其他Slot的数据。


2.对于迁移中的Slot,如果恰好此时有客户端要访问该Slot中的某个Key该怎么办?Codis不是遇到这个问题的第一个中间件,像Taobao Tair (和jvm类似)中也有对此的解决方案:

发生迁移的时候data server如何对外提供服务?
当迁移发生的时候, 我们举个例子, 假设data server A 要把 桶 3,4,5 迁移给data server B. 因为迁移完成前, 客户端的路由表没有变化, 客户端对 3, 4, 5 的访问请求都会路由到A. 现在假设 3还没迁移, 4 正在迁移中, 5已经迁移完成. 那么如果是对3的访问, 则没什么特别, 跟以前一样. 如果是对5的访问, 则A会把该请求转发给B,并且将B的返回结果返回给客户, 如果是对4的访问, 在A处理, 同时如果是对4的修改操作, 会记录修改log.当桶4迁移完成的时候, 还要把log发送到B, 在B上应用这些log. 最终A B上对于桶4来说, 数据完全一致才是真正的迁移完成

    但Codis采取的是不同的策略。当迁移过程中发生数据访问时,Proxy会发送”slotsmgrttagone”迁移命令给Redis,强制将客户端要访问的Key立刻迁移,然后再处理客户端的请求。(ps: 需记录某个key是否已经迁移(基于slot两级隔离, 所以这个key总体不会太大). 可不用加锁, 大不了重复迁移,ps: 每个redis实例需要记录某个key是否已迁移(codis改造部分))

func (m *CodisSlotMigrator) Migrate(slot *models.Slot, fromGroup, toGroup int, task *MigrateTask, onProgress func(SlotMigrateProgress)) (err error) {
    groupFrom, err := models.GetGroup(task.zkConn, task.productName, fromGroup)
    groupTo, err := models.GetGroup(task.zkConn, task.productName, toGroup)
    fromMaster, err := groupFrom.Master(task.zkConn)
    toMaster, err := groupTo.Master(task.zkConn)

    c, err := redis.Dial("tcp", fromMaster.Addr)
    defer c.Close()

    _, remain, err := sendRedisMigrateCmd(c, slot.Id, toMaster.Addr)

    for remain > 0 {
        if task.Delay > 0 {
            time.Sleep(time.Duration(task.Delay) * time.Millisecond)
        }

        _, remain, err = sendRedisMigrateCmd(c, slot.Id, toMaster.Addr)
        if remain >= 0 {
            onProgress(SlotMigrateProgress{
                SlotId:    slot.Id,
                FromGroup: fromGroup,
                ToGroup:   toGroup,
                Remain:    remain,
            })
        }
    }
    return nil
}

func sendRedisMigrateCmd(c redis.Conn, slotId int, toAddr string) (int, int, error) {
    addrParts := strings.Split(toAddr, ":")
    if len(addrParts) != 2 {
        return -1, -1, ErrInvalidAddr
    }

    reply, err := redis.Values(c.Do("SLOTSMGRTTAGSLOT", addrParts[0], addrParts[1], MIGRATE_TIMEOUT, slotId))
    if err != nil {
        return -1, -1, err
    }

    var succ, remain int
    if _, err := redis.Scan(reply, &succ, &remain); err != nil {
        return -1, -1, err
    }
    return succ, remain, nil
}

* hashmap 的 rehash 会修改原 entry 的实体, 调换链表顺序,故并发下会死循环(1.7和1.8的 resize 方案不一致). 遍历操作会 fail-over .resize 关注的是槽位,而不是已插入的节点数.

* concurrentHashMap的 resize 不会,entrySet 是不可变的,next 值不会变. rehash 时生成新的 entrySet .故 enteySet. iterator遍历操作不加锁. 可能拿到的 不同 segment 下的 hashtable[] 是不同的.

*业务系统数据迁移核心是update

最好是记录每个id的迁移记录.

另外方案: 三级索引. 主开关,递增id号,新库初始id号.小与此都迁移过,大于此未迁移. (迁移开关一旦打开,老数据库中就不会新增数据).

    各分布式定时任务.获取迁移序列锁.迁移指定序列的数据. 最后验证下数据总量是否一致.id范围+hash进行验证排查.

两表共同迁移方案: ?

读: 根据序号判断去哪里读

写: 根据开关写入哪个新库

删: 根据开关删除哪里.

改:根据开关和序号,和加锁情况去哪里修改.





http://blog.csdn.net/dc_726/article/details/47355989?ref=myread 

你可能感兴趣的:(数据迁移)