Why
项目要求快速上线,因为redis支持丰富的数据结构,所以开发初期便把部分数据直接存储到redis,几个月之后用户量上来了,累积了很多数据,其中绝大多数都是冷数据,用户一百年也不会访问一次的.相比磁盘来说,内存虽然快但是很贵,而这部分数据没必要放在内存中.于是就要想办法把这部分数据迁移出来.
How
这种事情以前干过一次,把这部分数据迁移到mysql,但是这样做的麻烦之处在于要修改整个存储逻辑,因为使用sql和nosql的整个设计思路都是不一样的,很多地方要用完全不同的实现方式,工作量巨大且风险高.
其实仔细思考,redis的协议并不只是适用于纯内存数据库,我的需求就是兼容redis协议,但是能把部分冷数据落地到磁盘,然后从内存中清除,当有请求过来时再把这部分数据load到内存.
所以最开始的思路是使用redis-rdb-tools导出所有key以及key占用的size,分析当前redis内究竟是哪种类型的key占用了最大的内存,然后做一个proxy,把这部分key的redis请求发送到proxy, 在proxy上把相关命令的协议实现,然后实现刚才提到的缓存的逻辑.
后来,我google了一下,发现了pika数据库,似乎可以完美的适应我的需求,看到测试数据,性能大概是redis的50%,完全够用了.
Converting&Importing
pika项目组提供了一个数据导入的工具,功能和redis-cli –pipe一样,通过管道批量执行命令,这种导入只适用于appendonly.aof文件,并不适用于dump.rdb文件
其实如果是正常自建的redis,完全可以开启appendonly落地模式,然后使用这个工具,在导入完完整的appendonly文件后,继续导入新增的数据,可以实现线上不停机的平滑迁移.
可惜阿里云的Redis服务不提供appendonly文件, 也不提供sync/psync命令,所以没法做平滑迁移.
最后采用的方案是在迁移过程中让业务侧停止写入,迁移完成后再开放.
从阿里云上拿到的redis数据文件是dump文件,如何把他变成appendonly格式的文件也是个问题.自然而然的想到先用redis-server读取dump文件,然后关掉save,开启appendonly模式,执行bgrewriteaof命令生成一个appendonly.aof文件.
这个方案在迁移第一个库的时候管用,在迁移第二个库时就不行了,因为第二个库太大,load到内存后,没有办法再开启一个同样内存大小的异步进程去rewrite.
为了解决这个问题,我使用了redis-rdb-tools,这个工具可以读取dump文件把aof格式的协议数据输出到标准输出流.
Check&Test
在线上环境找了一台负载不高的机器,创建了一个测试库,给线上redis做个快照,然后把数据导入到pika,利用redis-copy工具,把线上环境的redis收到的所有命令都copy到测试库上,以此来检测我们目前用到的命令有哪些是pika不兼容的.
通过这个办法,我发现rename命令在pika里面是没有的,使用到rename命令的代码需要修改,只能把要rename的key的value get出来,然后再重新用新的key set回去.
Done
删除原库数据时一定要仔细核对,确保迁移过去后,原库的待删除key没有增加,以此保证所有的服务器都指向到了新的pika服务.
通过这个流程,最终成功完成了两个业务库的迁移,现在线上环境已经使用pika跑了一周了,暂时没有出现任何异常.