Redis Cluster 迁移案例
背景简介
Grab 是东南亚的打车巨头,app 下载量已有 5500 万,司机有 120 万
app 与 server 通信时需要使用一个认证 token,Grab 使用 Redis 来缓存 token,使用 Mysql 来持久化备份
之前 Redis 是单节点结构,今年年初时 Grab 意识到这个结构很快就会支撑不住,因为用户增长太快
选择解决方案
备选方案
(1)使用多节点复制结构
之前的单点结构是设计上的缺陷,容错性很差,现在正是个修补的机会,可以使用 Redis 复制结构来提升容错性
但这也点问题,当 master 出现问题时,选择 slave 提升为 master 这个过程需要时间,这段时间内的写操作会受到影响
(2)自建 Redis Cluster
Redis Cluster 的确能够解决可用性问题,但会有其他麻烦:
- 分片依赖客户端,所以客户端的复杂度增加了
- 添加新的分片时比较麻烦,需要自己设计迁移逻辑,选择好一批用户信息,从现有节点上移动到新的节点
(3)使用 AWS 的弹性缓存服务
可以按需添加每个分片的复制节点,可以在服务端完成数据切分,AWS 来为我们操心分片策略,但不支持添加新的分片
选择
Grab 最想要的是水平扩展能力,在请求压力加大时可以轻松的增加处理能力
AWS 的弹性缓存服务是最适合的,每个分片可以动态添加复制节点,很好的支持了读数据能力的水平扩展,但不能够添加分片,这就限制了写数据能力的扩展
读写能力都需要很好的扩展性吗?经过统计发现,主要压力是在读上
写负载:
读负载:
写负载并不太高,提前规划好容量就可以了,Grab 统计了过去6个月的增长率,对容量进行了评估,最后决定使用3个分片,每个分片2个复制节点,一共9个节点
迁移过程
决定使用 AWS Redis Cluster 弹性缓存服务之后,就需要把现有的单点 Redis 中的数据迁移到 AWS,并把读写操作也转过去
Grab 把整个迁移过程拆分成了6步,来保证绝对的安全稳定
第1步
把数据从老的 Redis 节点迁移到 Redis Cluster,这个过程比较简单,因为 cluster 还没有开始处理线上流量
需要考虑的就是不要影响老节点的性能,Grab 使用了 scan
,dump
,restore
这些高效的命令把影响降到最低
第2步
应用开始向 cluster 中写数据,写入老节点的时候异步写入 cluster
这个过程对原有业务流程没有任何影响,可以验证是否出错、写入过程是否符合预期
第3步
上一步没问题后,使用同步模式向 cluster 中写入,真正参与到业务流程中,如果出现问题,就会影响真实的 API 调用结果,在真实环境中检验
第4步
读操作时,异步读取 cluster 中的数据,与老节点中的结果进行对比验证
这个过程对原有流程也没有影响,就是用来验证新老数据源是否同步
第5步
把所有读操作完全转到 cluster,停止对老Redis的读取,至此,API 完全依赖于新的 redis-cluster
第6步
停止向老 Redis 写,彻底停掉与其的任何交互,迁移完成
每个步骤都是使用配置进行控制,如果出现了不可预知的情况,便可以快速的回退到初始状态
小结
Grab 这次 Redis 迁移的过程并不复杂,但他们的分析思路和严谨的态度很值得借鉴
本文翻译正自理 Grab 的技术文章
http://engineering.grab.com/migrating-existing-datastores
- 数据库中间件 Sharding-JDBC 源码分析 —— 结果归并
- PhalGo-Request
- PhalApi-Excel
- PhalGo-Viper获取配置
- Dubbo 源码解析 —— 集群容错架构设计
- PhalGo-ADM思想
- 数据库中间件 Sharding-JDBC 源码分析 —— JDBC实现与读写分离
- Pytorch 0.3.0 发布:新增张量函数,支持模型移植
- 数据库分库分表中间件 Sharding-JDBC 源码分析 —— SQL 执行
- PhalGo-初识PhalGO
- 【学术】如何在神经网络中选择正确的激活函数
- PhalGo-Echo路由
- PhalGo-介绍
- 数据库分库分表中间件 Sharding-JDBC 源码分析 —— 分布式主键
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- [记录点滴]在Ionic和Android中上传Blob图片
- [源码解析] GroupReduce,GroupCombine 和 Flink SQL group by
- [记录点滴] 小心 Hadoop Speculative 调度策略
- [白话解析] 通过实例来梳理概念 :准确率 (Accuracy)、精准率(Precision)、召回率(Recall)和F值(F-Measure)
- [记录点滴] OpenResty中Redis操作总结
- [源码解析] 从TimeoutException看Flink的心跳机制
- [记录点滴] 一个解决Lua 随机数生成问题的办法
- [记录点滴] 记录一次用 IntelliJ IDEA遇到scope provided 的坑
- [记录点滴] 一个Python中实现flatten的方法
- [源码解析]Oozie来龙去脉之提交任务
- [记录点滴]Ionic编译过程的研究
- [记录点滴]OpenResty 支持http v2的问题
- [源码解析]Oozie来龙去脉之内部执行
- [记录点滴]编译安装luarocks、luacheck、luautf8
- [笔记整理] 一维搜索