Permalink: 2014-11-02 11:41:38 by ning in redis tags: all

这篇blog 里面提到2种扩容:

  1. 一个是 redis-mgr 中redis实例的迁移, 迁到一个内存大的机器
  2. 另外一个是新搭建集群, 把数据迁移过去.

这里考虑第二种思路.

1   为什么要设计这样的扩容方式

  1. 简单可依赖

  2. 从架构上, 这不需要像redis-cluster那样复杂的重新设计, 用现有的redis就可以完成扩容,
    • 不需要升级redis服务端, 也不需要升级client端(redis-cluster需要client支持)
  3. 这套方案可以用于:
    • 从单机redis迁移到 redis-mgr / twemproxy 管理的 集群.
    • 从一种集群架构迁移到另一种, 比如迁移到官方redis3.0 集群

总之, 就算redis3.0 的cluster 成熟以后, 还是需要这样的一套迁移方案.

2   怎么实现

mysql 的主从同步是基于binlog, redis主从是一个op buf, mongo主从同步是oplog.

redis里的aof就是类似binlog, 记录每个操作的log

所以, 我们可以利用aof, 把它当作binlog, 用于做迁移, 具体的迁移我们在mysql, mongo里面都多次用过了, 分三步:

  1. 迁移基准数据
  2. 追增量
  3. 追上后, 上层切流量.

redis的aof包含了基准数据和增量, 所以我们只需要把旧集群中redis实例上的aof重放到新集群, 重放追上时修改上层, 把入口换为新集群即可.

3   问题

3.1   aof不是幂等的

aof不像binlog那样可以重做 redolog, binlog 记录的操作是 幂等(idempotent) 的, 意味着如果失败了, 可以重做一次.

这是因为binlog记录的是操作的结果, 比如:

op                  log
---------------------------
set x 0             x = 0
incr x              x = 1
incr x              x = 2

但是redis的aof记录的是操作:

op                  log
---------------------------
set x 0             x = 0
incr x              incr x
incr x              incr x

这就是说, 如果我们在重放aof的过程中出错(比如网络中断):

  • 不能继续(不好找到上次同步到哪),
  • 也不能重新重放一次, (incr两次, 值就错了)

只能清除目标集群的数据重新迁移一次 ( redis-mgr 里面有 clean-keys 命令按照前缀清除)

不过, 好在redis单实例的afo数据都不大, 一般10G左右, 重放大约20min就能完成, 出错的概率也很小. (这也是redis可以这样做, 而其他持久存储比如mysql, mongo必须支持断点同步的原因)

3.2   切流量时的不一致

前面说的步骤是:

  1. 追aof
  2. 追上后, 切流量.

追aof是一个动态的过程, 追上后, 新的写操作马上就来, 所以这里追上的意思是说, 新的写入马上会被消化掉.

但是考虑这样一种场景:

假设client上做对x做两次set(一个机器上做两次, 或者两个app-server上分别做):

client          old_cluster         new_cluster
-----------------------------------------------
set x 1(a)      set x 1(客户操作)
-----------------------------------------------> 切流量到new_cluster
set x 2(b)                          set x 2 (客户操作)
                                    set x 1 (b 操作被重放到 new_cluster)

a操作还没同步到new_cluster, 流量就已经切到了new_cluster, 这时候对同一个key的更新, 会被老集群上的操作覆盖掉.

解决:

  • 这个短暂的不一致, 对多数业务, 是能容忍的(很少有业务会高速更新同一个key)
  • 如果非要达到一致, 当追aof追上后, app-server停写, 等待彻底追上(此时老集群的aof不会有更新了), 然后再切流量.

4   实现

有两份代码:

原理就是不断的看aof文件是否有更新, 有更新的话写到目标集群, 支持 按前缀过滤, 加前缀, 换前缀之类的操作.

4.1   redis-mgr 集成

redis-mgr 里面集成了一个命令来方便操作:

#从cluster1迁移到cluster5
./bin/deploy.py cluster1 replay_aof cluster5 'prefix_'

Comments