链接地址
不得不说,阿里的藏经阁有可在线阅读,链接需要登录阿里云方可下载
- Redis的开发规范和常见问题-藏经阁-阿里云开发者社区 (aliyun.com)
- Redis 的高并发实战:抢购系统 -藏经阁-阿里云开发者社区 (aliyun.com)
- Redis最佳实践与实战指南-藏经阁-阿里云开发者社区 (aliyun.com)
Redis的开发规范和常见问题-藏经阁-阿里云开发者社区 (aliyun.com)
为什么要制定开发规则
Redis设计上有哪些问题?
1. 单线程问题
- 用户所有的来自不同的client请求,实际上每个event到来后,交由后端单线程执行,前一个event处理完成后,才处理下一下
- 单线程run-to-completion就是没有dispatcher,没有后端multi-worker
如果有一个慢查询(诸如keys,range,hgetall拖慢了一次查询,那么后面的请求都会被拖延)
2. 使用Sentinel判活的trick 能解决问题吗?(否)
- Ping 命令判活: ping命令同样受慢查询的影响,如果引擎卡住,则ping失败
- Duplex Failure: sentinel由于慢查询导致切备(备变主)再遇到慢查询,Redis将出现OOS
3. 扩展为集群版,可以解决问题吗? (否)
- 集群版解决不了单个DB被卡住的问题
- 查询空洞,如果用户调用了跨分片的命令,如mget,访问了出现问题的db,仍会block住
4. Protocol问题 - 大量客户端与引擎 Fully-Meshed 问题
采用Redis协议(RESP)的问题
-
扩展性太差 : 基于Question-Answer模式,由于在Question/Answer里面没有对应的Sequence的存在,(如果不做复杂的转换wrapper层)存储引擎端没法match请求和响应,只能采用Run-To-Completion来挂住链接
-
C10K的问题:当引擎挂住太多active链接的时候,性能下降太多。测试结果是当有10k active连接时,性能下降30-35%,由于引擎端挂住的链接不能被返回,用户大量报错
存储、计算和网络的边界?
Redis的边界
高并发不等于高吞吐
大Value问题: 高速存储并不会有特别大的高吞吐收益,反而会很危险
数据倾斜和算力倾斜
- bigKey的问题: break掉存储的分配律
- 热点的问题: 本质上是cpu上分配律不满足
- 大Range的问题: 对NoSQL的慢查询和导致的危害没有足够的重视
存储边界
- Lua使用不当造成的成本直线上升
- 数据倾斜带来的成本飙升,无法有效利用
对Latecy的理解问题(RT高)
- 存储引擎的Latency都是P99 Latency, 如99.99%在1ms以内,99.5%在3ms以内
- 偶然性时延高是必然的。这个根本原因在于存储引擎内部的复杂性和熵
阿里内部系统的开发规约
为什么需要开发规约?
Redis的使用建议
- 确定场景,是缓存(cache)还中存储型
- Cache的使用原则是:“无它也可,有它更强”
- 永远不要依赖Cache,它会丢失,也会被淘汰
- 优先设计合理的数据结构和逻辑
- 设计避免bigKey,就避免了80%的问题
- Keyspace能分开,就多申请几个Redis实例
- pubsub不适合做消息分发
- 尽量避免用lua做事务
BigKey 的问题
数据倾斜严重,可能成为瓶颈
Redis LUA JIT
- 脚本的compile-load-run-unload非常耗费CPU
- 整个lua相当复杂事务推送到时Redis中执行,如果稍有不慎内存会爆,引擎算力耗光后挂住Redis
• 对于JIT技术在存储引擎中而言,“EVAL is evil”,尽量避免使用lua耗费内存和计算资源(省事不省心)
• 某些SDK(如Redisson)很多高级实现都内置使用lua,开发者可能莫名走入CPU运算风暴中,须谨慎
Pubsub/Transaction/Pipeline
Keys命令 一定会出问题,不要用不要用不要用!!!
其它危险命令
规范总结
1. 造型: 用户要确定场景是cache还是内存数据库
- Cache选择单副本,关闭AOF;内存数据库选择双副本
- 如果keyspace能够分开,就申请不同的实例来隔离
2. 使用: 避免触发高速存储边界
- set/hash/zset/list/bloom/gis等大key,不要超过3000
- 避免使用keys,hgetall,lrang0 -1等大range(使用scan替代)
- 避免使用大value(10k以上就算大value)
3. SDK: 使用规范
- 严禁设置低读超时和紧密重试(200ms以下read timeout)
- 需要接受p99时延,对超时和慢做容错处理
- 尽量使用扩展数据结构,避免使用lua
- 尽量避免pubsub和blocking的API
4. 接受主动运维
常见问题处理
Tair/Redis内存模型
其它阿里云上运维管理redis的手段,如果企业负担的起,建议还是使用阿里云的部署方案。原因两个字: 省心
Redis最佳实践与实战指南-藏经阁-阿里云开发者社区 (aliyun.com)
阿里的藏经阁有可在线阅读,链接需要登录阿里云方可下载
由于本人所供职的公司不使用阿里云,所以重点关注文中的标准版(具有普适性)
架构与介质(存储)选择
文中集群架构图,画得比较清晰,一目了然
标准 - 双副本
主从模式,主备位于不同物理机
- 主节点对外提供访问
- 备节点提供HA高可用
备份机可以做数据持久化,当业务需要做数据回滚时,可以从备份机上进行恢复
标准 - 单副本
不具备数据持久化和备份策略,使用场景为数据可靠性不高的纯缓存业务
读写分离
适合读多写少的场景
选型
如果业务qps低且容量低(小于32GB)选择标准版,否则选择集群版。
线程模型
Redis2.x/4.x/5.x
Redis5.X之前比较知名的版本,模型没有变化过,所有的命令处理都是单线程,所有的读、处理、写都在一个主IO里运行。后台有几个BIO线程,任务主要是关闭文件、刷文件等等。
4.0之后,添加了LAZY_FREE,有些大KEY可以异步的释放,以避免阻塞同步任务处理。而在2.8上会经常会遇到淘汰或过期删除比较大的key时服务会卡顿,所以建议用户使用4.0以上的服务端,避免此类大key删除问题导致的卡顿。
Redis6.x
Redis 6.x 版本改进的模型,可以在主线程,可读事件触发之后,把“读”任务委托在IO线程处理,全读完之后,返回结果,再一次处理,然后“写”也可以分发给IO线程写,显而易见可以提升性能。
这种性能提升,运行线程还只有一个,如果有一些O(1)命令,比如简单的“读”、“写”命令,提升效果非常高。但如果命令本身很复杂,因为DB还是只有一个运行线程,提升效果会比较差。
还有个问题,把“读”任务委托之后,需要等返回,“写”也需要等返回,所以主线程有很长时间在等,且这段时间无法提供服务,所以Redis 6.x模型还有提升的空间。
分布式锁:删除与续租的Lua实现
分布式锁删除的时候比较麻烦,删除时需要一个判断,当value等于之前value时,才可以删掉。Redis目前没有这样的命令,一般通过Lua来实现。
一般来说,不建议在Redis里面使用LUA,LUA执行需要先解析、翻译,然后执行整个过程。
第一:因为 Redis LUA,等于是在C里面调LUA,然后LUA里面再去调 C,返回值会有两次的转换,先从Redis协议返回值转成LUA对象,再由LUA对象转成 C的数据返回。
第二:有很多LUA解析,VM处理,包括lua.vm内存占用,会比一般的命令时间慢。建议用LUA最好只写比较简单的,比如if判断。尽量避免循环,尽量避免重的操作,尽量避免大数据访问、获取。因为引擎只有一个线程,当CPU被耗在LUA的时候,只有更少的CPU处理业务命令,所以要慎用。