目前在唯品会主要负责 redis/hbase 的运维和开发支持工作,也参与工具开发工作, 本文是在 Redis 中国用户组给大家分享 redis cluster 的生产实践。 |
分享大纲
本次分享内容如下:
1, 生产应用场景
2, 存储架构演变
3, 应用最佳实践
4, 运维经验总结
关于这 4 部分的内容介绍:
第 1,2 部分: 介绍 Redis cluster 在唯品会的生产应用场景, 以及存储架构的演变.
第 3 部分: Redis cluster 的稳定性, 应用成熟度, 踩到过那些坑, 如何解决这些问题? 这部分是大家比较关系的内容.
第 4 部分: 简单介绍大规模运营的一些经验, 包括部署, 监控, 管理以及 Redis 工具开发.
生产应用场景
业务范围
Redis cluster 在唯品会主要应用于后端业务, 用作内存存储服务. 主要大数据实时推荐 / ETL, 风控, 营销三大业使用.
cluster 用于取代当前 twemproxy 三层架构, 作为通用的存储架构. Redis cluster 可以大幅度简化我们的存储架构, 也解决 twemproxy 架构无法在线扩容节点的问题.
目前我们在线有生产几十个 cluster 集群, 约 2 千个 instances, 单个集群最大达到 250+instances.
这是我们的生产应用场景, 主要是后端业务的存储, 目前没有作为 cache 使用的场景.
大数据, 风控, 营销系统的特征
cluster 一般数据量大, 单个 cluster 集群在几十个 GB 到上 TB 级别内存存储量.
作为后端应用的存储, 数据来源主要以下三种方式:
Kafka → Redis Cluster,Storm/Spark 实时
Hive → Redis Cluster, MapReduce 程序
MySQL → Redis Cluster,Java/C++ 程序.
数据由离线 / 实时 job 生成, 读写请求量大, 对读写性能也要求高.
业务高峰期请求量急剧上升, 几倍的读写量增加, 需要多个 Redis 实例承担业务的读写压力.
业务需求变化快, schema 变化频繁. 如果使用 MySQL 作为存储, 那么将会是频繁的 DLL 变更, 而且需要做 online schema change.
大促销活动时扩容频繁.
为什么选择 Redis cluster
1) cluster 适合我们后端生产应用场景
在线水平扩展能力, 能够解决我们大量的扩容需求.
Failover 能力和高可用性.
虽然 cluster 不保证主从数据强一致性, 但是后端业务能够容忍 failover 后少量的数据丢失.
2) 架构简单
无中心架构, 各个节点度等. slave 节点提供数据冗余, master 节点异常时提升为 master.
取代 twemproxy 三层架构, 系统复杂性降低.
可以节约大量的硬件资源, 我们的 Lvs + Twemproxy 层 使用了近上千台物理机器.
少了 lvs 和 twemproxy 层, 读写性能提升明显. 响应时间从 100-200us 减少到 50-100us.
系统瓶颈更少. lvs 层网卡和 pps 吞吐量瓶颈; 对于请求长度较大的业务, twemproxy 单节点性能低.
总结下, 我们选择 Redis cluster 主要这两点原因: 简单, 扩展性. 另外, 我们用 cluster 取代 twemproxy 集群, 三层架构实在是很令人头疼, 复杂, 瓶颈多, 管理不方面.
存储架构演变
架构演变
在 2014 年 7 月, 为了准备当时的 814 撒娇节大促销活动, 我们把单个 Redis 的服务迁移到 twemproxy 上. twemproxy 在后端快速完成数据分片和扩容. 为了避免再次扩容, 我们静态分配足够多的资源.
之后, twemproxy 暴露出来的系统瓶颈很多, 资源使用很多, 也存在一定的浪费. 我们决定用 Redis cluster 取代这种复杂的三层架构.
Redis cluster GA 之后, 我们就开始上线使用. 最初是 3.0.2 版本, 后面大量使用 3.0.3 , 上个月开始使用 3.0.7 版本.
下面简单对比下两种架构, 解析下他们的优缺点.
Twemproxy 架构
1) 优点
sharding 逻辑对开发透明, 读写方式和单个 Redis 一致.
可以作为 cache 和 storage 的 proxy(by auto-eject).
2) 缺点
架构复杂, 层次多. 包括 lvs,twemproxy,Redis,sentinel 和其控制层程序.
管理成本和硬件成本很高.
2 * 1Gbps 网卡的 lvs 机器, 最大能支撑 140 万 pps.
流量高的系统, proxy 节点数和 Redis 个数接近.
Redis 层仍然扩容能力差, 预分配足够的 Redis 存储节点.
这是 twemproxy 的架构, 客户端直接连接最上面的 lvs(LB), 第二层是同构的 twemproxy 节点, 下面的 Redis master 节点以及热备的 slave 节点, 另外还有独立的 sentinel 集群和切换控制程序, twemproxy 先介绍到这里.
Redis Cluster 架构
1) 优点
无中心架构.
数据按照 slot 存储分布在多个 Redis 实例上.
增加 slave 做 standby 数据副本, 用于 failover, 使集群快速恢复.
实现故障 auto failover. 节点之间通过 gossip 协议交换状态信息; 投票机制完成 slave 到 master 角色的提升.
亦可 manual failover, 为升级和迁移提供可操作方案.
降低硬件成本和运维成本, 提高系统的扩展性和可用性.
2) 缺点
client 实现复杂, 驱动要求实现 smart client, 缓存 slots mapping 信息并及时更新.
目前仅 JedisCluster 相对成熟, 异常处理部分还不完善, 比如常见的 "max redirect exception".
客户端的不成熟, 影响应用的稳定性, 提高开发难度.
节点会因为某些原因发生阻塞 (阻塞时间大于 clutser-node-timeout), 被判断下线. 这种 failover 是没有必要, sentinel 也存在这种切换场景.
架构演变讲完了, 开始讲第三部分, 也是大家最感兴趣的一部分.
应用最佳实践
存在哪些坑?
develop guideline & best practice
稳定性
不扩容时集群非常稳定.
扩容 resharding 时候, 早期版本的 Jedis 端有时会出现 "max-redirect" 异常.
分析 Jedis 源码, 请求重试次数达到了上限, 仍然没有请求成功. 两方面分析: Redis 连接不上? 还是集群节点信息不一致?
存活检测机制缺陷
Redis 存活检测机制可能因为 master 节点上慢查询, 阻塞式命令, 或者其它的性能问题导致长时间没有响应, 这个节点会认为处于 failed 状态, 并进行切换. 这种切换是没必要的.
优化策略
a) 默认的 cluster-node-timeout 为 15s, 可以适当增大;
b) 避免使用会引起长时间阻塞的命令, 比如 save/flushdb 等阻塞操作, 或者 keys pattern 这种慢查询.
总体来说, Redis cluster 已经非常稳定了, 但是要注意一些应用中的小问题, 下面是 5 个坑, 大家注意了.
有哪些坑?
迁移过程中 Jedis"Max Redirect" 异常
GitHub 上讨论的结果是程序 retry.
max redirt issues: https://github.com/xetorthio/jedis/issues/1238
retry 时间应该大于 failover 时间.
Jedis 参数优化调整: 增大 jedis 中的'DEFAULT_MAX_REDIRECTIONS'参数, 默认值是 5.
避免使用 multi-keys 操作, 比如 mset/mget. multi-key 操作有些客户端没有支持实现.
长时间阻塞引起的不必要的 failover
阻塞的命令. 比如 save/flushall/flushdb
慢查询. keys *, 大 key 的操作, O(N) 操作
rename 危险操作:
- rename-command FLUSHDB REDIS_FLUSHDB
- rename-command FLUSHALL REDIS_FLUSHALL
- rename-command KEYS REDIS_KEYS
同时支持 ipv4 和 ipv6 侦听服务埋下的坑
具体现象: Redis 启动正常, 节点的协议端口只有 ipv6 socket 创建正常. 异常节点也无法加入到集群中, 也无法获取 epoch.
解决方法: 启动时指定网卡 ipv4 地址, 也可以是 0.0.0.0, 配置文件中添加: bind 0.0.0.0
这个是在 setup 集群的时候发生过的一个问题, bind 0.0.0.0 虽然存在一些安全性问题, 但是是比较简单通用的解决方法.
数据迁移速度较慢
主要使用的 Redis-trib.rb reshard 来完成数据迁移.
Redis-3.0.6 版本以前 migrate 操作是单个 key 逐一操作. 从 Redis-3.0.6 开始, 支持单次迁移多个 key.
Redis 集群内部最多只允许一个 slot 处于迁移状态, 不能并发的迁移 slots.
Redis-trib.rb reshard 如果执行中断, 用 Redis-trib.rb fix 修复集群状态.
版本选择 / 升级建议
我们已经开始使用 3.0.7 版本, 很多 3.2.0 修复的 bug 已经 backport 到这个版本.
另外我们也开始测试 3.2.0 版本, 内存空间优化很大.
Tips
Redis-trib.rb 支持 resharding/rebalance, 分配权重.
Redis-trib.rb 支持从单个 Redis 迁移数据到 cluster 集群中.
后面 2 点不算坑把, 算是不足, tips 也很实用. 开始分享下最佳实践.
最佳实践
3.1 应用做好容错机制
连接或者请求异常, 进行连接 retry 和 reconnect.
重试时间应该大于 cluster-node-time 时间
还是强调容错, 这个不是针对 cluster, 所有的应用设计都适用.
3.2 制定开发规范
慢查询, 进程 CPU 100%, 客户端请求变慢, 甚至超时.
避免产生 hot-key, 导致节点成为系统的短板.
避免产生 big-key, 导致网卡打爆, 慢查询.
TTL, 设置合理的 ttl, 释放内存. 避免大量 key 在同一时间段过期, 虽然 Redis 已经做了很多优化, 仍然会导致请求变慢.
key 命名规则.
避免使用阻塞操作, 不建议使用事务.
开发规范, 使你们的开发按照最优的方式使用 nosql.
3.3 优化连接池使用
主要避免 server 端维持大量的连接.
合理的连接池大小.
合理的心跳检测时间.
快速释放使用完的连接.
Jedis 一个连接创建异常问题 (fixed):
https://github.com/xetorthio/jedis/issues/1252
连接问题是 Redis 开发使用中最常见的问题, connection timeout/read timeout, 还有 borrow connection 的问题.
3.4 区分 Redis/twemproxy 和 cluster 的使用
Redis 建议使用 pipeline 和 multi-keys 操作, 减少 RTT 次数, 提高请求效率.
twemproxy 也支持 pipeline, 支持部分的 multi-key 可以操作.
Redis cluster 不建议使用 pipeline 和 multi-keys 操作, 减少 max redirect 产生的场景.
区分 Redis 和 cluster 的使用, 一方面是数据分片引起的; 另一方面, 与 client 的实现支持相关.
3.5 几个需要调整的参数
1) 设置系统参数 vm.overcommit_memory=1, 可以避免 bgsave/aofrewrite 失败.
2) 设置 timeout 值大于 0, 可以使 Redis 主动释放空闲连接.
3) 设置 repl-backlog-size 64mb. 默认值是 1M, 当写入量很大时, backlog 溢出会导致增量复制不成功.
4)client buffer 参数调整
- client-output-buffer-limit normal 256mb 128mb 60
- client-output-buffer-limit slave 512mb 256mb 180
运维经验总结
4.1 自动化管理
CMDB 管理所有的资源信息.
Agent 方式上报硬软件信息.
标准化基础设置. 机型, OS 内核参数, 软件版本.
Puppet 管理和下发标准化的配置文件, 公用的任务计划, 软件包, 运维工具.
资源申请自助服务.
4.2 自动化监控
zabbix 作为主要的监控数据收集工具.
开发实时性能 dashboard, 对开发提供查询.
单机部署多个 Redis, 借助于 zabbix discovery.
开发 DB 响应时间监控工具 Titan.
基本思想来源于 pt-query-degest, 通过分析 tcp 应答报文产生日志. flume agent + kafka 收集, spark 实时计算, hbase 作为存储. 最终得到 hotquery/slowquery,request source 等性能数据.
4.3 自动化运维
资源申请自助服务化.
如果申请合理, 一键即可完成 cluster 集群部署.
能不动手的, 就坚决不动手, 另外, 监控数据对开发开发很重要, 让他们了解自己服务性能, 有时候开发会更早发现集群的一些异常行为, 比如数据不过期这种问题, 运维就讲这么多了, 后面是干货中的干货, 由 deep 同学开发的几个实用工具.
Redis 开源工具介绍
1) Redis 实时数据迁移工具
1) 在线实时迁移
2)Redis/twemproxy/cluster 异构集群之间相互迁移.
- 3)GitHub:
- https://github.com/vipshop/redis-migrate-tool
2) Redis cluster 管理工具
1) 批量更改集群参数
2)clusterrebalance
3) 很多功能, 具体看 GitHub :
https://github.com/deep011/redis-cluster-tool
3) 多线程版本 Twemproxy
1) 大幅度提升单个 proxy 的吞吐量, 线程数可配置.
2) 压测情况下, 20 线程达到 50w+qps, 最优 6 线程达到 29w.
3) 完全兼容 twemproxy.
- 4)GitHub:
- https://github.com/vipshop/twemproxies
4) 在开发的中的多线 Redis
- 1)GitHub:
- https://github.com/vipshop/vire
2) 欢迎一起参与协作开发, 这是我们在开发中的项目, 希望大家能够提出好的意见.
互动环节 (陈群和申政解答)
问题 1: 版本更新, 对数据有没有影响?
答: 我们重启升级从 2.8.17 到 3.0.3/3.0.7 没有任何的异常. 3.0 到 3.2 我们目前还没有实际升级操作过.
问题 2: 请问下 sentinel 模式下有什么好的读写分离的方法吗
答: 我们没有读写分离的使用, 读写都在 maste; 集群太多, 管理复杂; 此外, 我们也做了分片, 没有做读写分离的必要; 且我们几乎是一主一从节点配置
问题 3:Redis 的 fork 主要是为了 rdb 吧, 去掉是为了什么呢
答: fork 不友好
问题 4: 如果不用 fork, 是怎么保证 rdb 快照是精确的, 有其他 cow 机制么
答: 可以通过其他方法, 这个还在探究阶段, 但目标是不用 fork
问题 5: 就是 Redis cluster 模式下批量操作会有很多问题, 可是不批量操作又会降低业务系统的性能
答: 确实存在这方面的问题, 这方面支持需要客户端的支持, 但是 jedis 的作者也不大愿意支持 pipeline 或者一些 multi key 操作. 如果是大批量的操作, 可以用多线程提高客户端的吞吐量.
来源: http://www.bubuko.com/infodetail-2962471.html