7月26日,八里庄技术沙龙第10期开讲。本期分享嘉宾是58集团数据库高级技术经理于伯伟,从事数据库应用研发、数据库架构、数据库技术管理等工作11年。分享主题是Redis 应用与优化最佳实践。
Redis是一款开源的、高性能的Key-Value内存数据库,其中凭借高效稳定的访问性能,目前在各个互联网应用中得到了广泛使用。
实录大纲:
01 - Redis的核心特性
02 - Redis核心数据结构类型
03 - Redis高可用架构
04 - Redis高效开发规范
05 - 几个Redis使用踩过的坑
一、Redis的核心特性
(一)数据结构类型丰富
Redis支持存储的类型非常丰富,相对于Memcache单一的K-V存储,目前包括八大数据结构类型:string(字符串)、hash(哈希类型)、list(链表)、set(集合)、zset(有序集合)、Bitmaps(位图)、GEO(地理信息定位) 和HyperLogLog,极大的方便了各种业务场景的实现。另外Redis支持持久化存储,保证了宕机数据安全;源生的分片集群既方便了应用的简单接入,也极大的扩展了单一应用可以存储的数据量,和Memcache的对比如下:
(二)持久化
Redis持久化让数据不再仅仅存储在内存中,而是也可以写到磁盘永久存储,使之有能力避免服务器意外宕机或程序崩溃而造成的数据丢失风险。主要的持久化模式:
RDB(Redis Database)内存镜像模式:在一定触发条件下,将当前内存中的数据快照保存到磁盘文件。
AOF(Append Only File)日志追加模式:可配置定时触发,将所执行的命令刷新到磁盘文件中。
两种持久化方式的对比如下:
从Redis4.0版本开始,为了发挥两种持久化模式的优势,引入了混合持久化模式这种混合模式下数据恢复时,通过快速加载RDB格式内容,及较少的AOF操作命令,兼顾了两种持久化的优势,提高了恢复的速度并兼顾数据的完整度。
最佳实践建议:
Redis数据是全部存储在内存中的;
区分业务是缓存还是持久化应用,如无必要关闭持久化;
线上业务禁用RDB持久化,建议使用AOF持久化;
如果写入量较大,建议主库关闭持久化,从库开启持久化;
线上业务禁止自动触发AOF重写,业务低峰定时触发;
AOF重写和RDB持久化期间会占用额外内存,极端情况多使用一倍【copy-on-write】;
使用SSD磁盘进行持久化存储。
(三)主从复制
主从复制实现了数据完整副本,是故障转移和读水平扩展的基础。
基本概念:
节点类型:主节点(master),从节点(slave)。
复制流向:只能由主库流向从库,不支持双向复制。
节点拓扑:一个主库可以有多个从库,但是一个从库只能有一个主库(单主模式)。
异步复制:特殊情况会有延迟情况发生。
拓扑架构:
一主一从
一主多从(V3.2+使用)
级联树状(迁移拆分状态临时使用,线上业务禁止使用)
基于主从复制的读写分离架构:
主库提供写入服务,写入命令会复制分发到从库异步回放;
配置一个或多个从库,提供只读服务【多个从库可以使用LVS 负载均衡】。
读写分离架构问题
主从复制为异步操作,从库可能有延迟;
从库某个节点不可用导致访问错误;
读取到过期KEY【3.2+之后解决】。
二、Redis核心数据结构类型
Redis支持存储的类型非常丰富,每种类型都有自己典型的业务应用场景,但并不是绝对固定的,可根据自己的使用习惯及具体业务灵活选择,高效使用。这里不介绍每种数据结构类型的使用,给大家分享几种具体的业务实现。
1.缓存与MySQL数据一致性方案
建议方案:延时双删策略+缓存过期
1)先删除缓存数据;
2)再写数据库;
3)休眠1秒钟;
4)再次执行删除缓存数据;
5)设置写入缓存数据Key自动过期清理。
这种操作也无法全部避免缓存和数据库一致的问题,只能尽量缩短不一致的时间,具体实现可以根据业务要求灵活控制清理和过期策略了。
2.Redis的List构建可靠的消息队列
通用方案:
上游生产者将消息打入消息队列【lpush】;
下游多个消费者从队列获取消息处理【rpop】。
问题:消费者处理消息任务时突然崩溃,该消息任务将会丢失。
方案改进:
每个下游消费者维护一个私有的任务处理队列;
每次通过rpop lpush原子命令,从主消息队里获取并写入私有队列;
任务处理完成后从私有队列rpop出队列删除;
独立监控程序,如果私有队列任务超时未处理,将其再放回主消息队列继续由其它下游客户端处理。
三、Redis高可用架构
Redis高可用基本要求:
主节点故障后,不需人工介入,备节点可秒级自动升级为主库;
一主多从架构下,其它从节点要自动挂载到新主节点并完成数据同步;
对应用程序透明,故障转移后无需重新配置即可继续提供服务;
支持自动和手动的故障转移。
(一)主从高可用架构
基于Redis哨兵的主从高可用架构,目前主要有以下两种模式:
1.通过VIP模式访问Redis集群
高可用切换流程:
应用程序通过VIP获取访问主库地址;
集群监控。Sentinel哨兵集群监控所有Redis主从节点状态;
故障新主选举及重新挂载。应用程序探测到哨兵集群发生切换,选出新主并重新进行从节点挂载;
故障转移。Sentinel集群调用VIP或ZK服务接口,将老的Redis Master地址替换为新选举的主库IP地址;
应用程序访问最新的主库。
2.通过哨兵集群模式访问Redis集群
高可用切换流程:
特定应用程序通过哨兵集群API接口获取Redis集群主库地址;
集群监控,Sentinel哨兵集群监控所有Redis主从节点状态;
故障新主选举及重新挂载,应用程序探测到哨兵集群发生了切换,选出新主库并重新进行节点挂载;
应用程序通过Sentinel API接口获取最新Redis集群主库地址,新发起的请求自动切换到新主库上。
(二)分片集群高可用架构
Redis主从架构面临的一些痛点
单一节点存储数据有限,数据量较大情况下服务器无法承载;
单一节点内存过大,数据备份、新增节点、恢复或重写操作耗时巨大
读写请求量巨大,单一节点无法支撑。
一些传统的解决方案:
1.客户端分片解决方案
方案优势:
程序端灵活控制路由策略;
程序接入简单,使用标准驱动即可。
方案劣势:
Redis无法平滑扩容新节点,程序侧必须重新上线;
无法标准化,需根据特殊业务场景确定路由策略。
2. 基于twemproxy的分片解决方案
方案优势:
自动分片数据到多个服务器上,支持多个分片模式;
避免单点问题,支持失败节点自动删除;
保持与redis的长连接,减少了应用直接与Redis Server的连接数。
方案劣势:
有20%到40%的性能损耗;
多值操作mget、pipeline等性能差;
有内存泄露的严重bug,目前新版本更新迟缓;
无法平滑的扩容/缩容。
3.Redis Cluster分片集群
方案优势:
解决单实例内存、集群访问量过大情况;
自动分片数据到多个节点,支持1000个节点规模;
节点失效自动修复, 主从模式, 自动选举master;
增减节点都不需要停机, 自动reshard迁移数据。
Redis Cluster目前官方支持,经过多个版本的迭代已经非常稳定,建议生产环境使用该方案。
四、Redis高效开发规范
基础规范
数据库选型,是否必须使用Redis存储数据;
Redis必须配置访问密码,至少16位字符;
选择相对较新的版本,建议4.0以上版本。
键值设计
控制Value大小,如果超过512字节必须进行压缩存储,最大不能超过1K;
控制key长度,建议64字符以内,避免Key过多带来较大的内存开销。
内存使用
集群单节点内存上限不能超过20G;
冷热数据分离,不要将所有数据全部都放到Redis中。
操作命令
谨慎使用hgetall、smembers全量操作Hash、Set等数据结构;
慢日志阈值为1毫秒,关注命令的时间复杂度:O(n)。
集群架构
主从集群必须配置哨兵监控支持高可用;
线上业务禁止使用级联复制架构。
五、几个Redis使用踩过的坑
expire & expireat 混淆,导致key未按要求过期;
集中Key过期会带来较大的性能问题,甚至阻塞主线程响应;
Lua脚本同步复制,低于V4.0版本时,新扩容的从库不同步脚本。