阿里藏经阁之-Redis最佳实践 与实战指南(阅读笔记)

672 阅读7分钟

链接地址

不得不说,阿里的藏经阁有可在线阅读,链接需要登录阿里云方可下载

  1. Redis的开发规范和常见问题-藏经阁-阿里云开发者社区 (aliyun.com)
  2. Redis 的高并发实战:抢购系统 -藏经阁-阿里云开发者社区 (aliyun.com)
  3. Redis最佳实践与实战指南-藏经阁-阿里云开发者社区 (aliyun.com)

Redis的开发规范和常见问题-藏经阁-阿里云开发者社区 (aliyun.com)

为什么要制定开发规则

Redis设计上有哪些问题?

1. 单线程问题

  1. 用户所有的来自不同的client请求,实际上每个event到来后,交由后端单线程执行,前一个event处理完成后,才处理下一下
  2. 单线程run-to-completion就是没有dispatcher,没有后端multi-worker

如果有一个慢查询(诸如keys,range,hgetall拖慢了一次查询,那么后面的请求都会被拖延)

2. 使用Sentinel判活的trick 能解决问题吗?(否)

  1. Ping 命令判活: ping命令同样受慢查询的影响,如果引擎卡住,则ping失败
  2. Duplex Failure: sentinel由于慢查询导致切备(备变主)再遇到慢查询,Redis将出现OOS

3. 扩展为集群版,可以解决问题吗? (否)

  1. 集群版解决不了单个DB被卡住的问题
  2. 查询空洞,如果用户调用了跨分片的命令,如mget,访问了出现问题的db,仍会block住

4. Protocol问题 - 大量客户端与引擎 Fully-Meshed 问题

采用Redis协议(RESP)的问题

  1. 扩展性太差 : 基于Question-Answer模式,由于在Question/Answer里面没有对应的Sequence的存在,(如果不做复杂的转换wrapper层)存储引擎端没法match请求和响应,只能采用Run-To-Completion来挂住链接

  2. C10K的问题:当引擎挂住太多active链接的时候,性能下降太多。测试结果是当有10k active连接时,性能下降30-35%,由于引擎端挂住的链接不能被返回,用户大量报错

Redis的问题3.png

存储、计算和网络的边界?

Redis的边界.png

Redis的边界

高并发不等于高吞吐

大Value问题: 高速存储并不会有特别大的高吞吐收益,反而会很危险

数据倾斜和算力倾斜

  1. bigKey的问题: break掉存储的分配律
  2. 热点的问题: 本质上是cpu上分配律不满足
  3. 大Range的问题: 对NoSQL的慢查询和导致的危害没有足够的重视

存储边界

  1. Lua使用不当造成的成本直线上升
  2. 数据倾斜带来的成本飙升,无法有效利用

对Latecy的理解问题(RT高)

  1. 存储引擎的Latency都是P99 Latency, 如99.99%在1ms以内,99.5%在3ms以内
  2. 偶然性时延高是必然的。这个根本原因在于存储引擎内部的复杂性和熵

阿里内部系统的开发规约

为什么需要开发规约?

Redis的使用建议

  1. 确定场景,是缓存(cache)还中存储型
  2. Cache的使用原则是:“无它也可,有它更强”
  3. 永远不要依赖Cache,它会丢失,也会被淘汰
  4. 优先设计合理的数据结构和逻辑
  5. 设计避免bigKey,就避免了80%的问题
  6. Keyspace能分开,就多申请几个Redis实例
  7. pubsub不适合做消息分发
  8. 尽量避免用lua做事务

不建议的做法

BigKey 的问题

数据倾斜严重,可能成为瓶颈

RedisBigKey.png

Redis LUA JIT

  1. 脚本的compile-load-run-unload非常耗费CPU
  2. 整个lua相当复杂事务推送到时Redis中执行,如果稍有不慎内存会爆,引擎算力耗光后挂住Redis

Redis Lua JIT.png

• 对于JIT技术在存储引擎中而言,“EVAL is evil”,尽量避免使用lua耗费内存和计算资源(省事不省心)

• 某些SDK(如Redisson)很多高级实现都内置使用lua,开发者可能莫名走入CPU运算风暴中,须谨慎

Pubsub/Transaction/Pipeline

Pubsub.png

Keys命令 一定会出问题,不要用不要用不要用!!!

kyes.png

其它危险命令

danger-command.png

规范总结

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内存模型

oom.png

其它阿里云上运维管理redis的手段,如果企业负担的起,建议还是使用阿里云的部署方案。原因两个字: 省心

Redis最佳实践与实战指南-藏经阁-阿里云开发者社区 (aliyun.com)

阿里的藏经阁有可在线阅读,链接需要登录阿里云方可下载

由于本人所供职的公司不使用阿里云,所以重点关注文中的标准版(具有普适性)

架构与介质(存储)选择

文中集群架构图,画得比较清晰,一目了然

1.png

标准 - 双副本

主从模式,主备位于不同物理机

  1. 主节点对外提供访问
  2. 备节点提供HA高可用

备份机可以做数据持久化,当业务需要做数据回滚时,可以从备份机上进行恢复

标准 - 单副本

不具备数据持久化和备份策略,使用场景为数据可靠性不高的纯缓存业务

读写分离

适合读多写少的场景

选型

如果业务qps低且容量低(小于32GB)选择标准版,否则选择集群版。

集群模型如何选型.png

线程模型

Redis2.x/4.x/5.x

Redis5.X之前比较知名的版本,模型没有变化过,所有的命令处理都是单线程,所有的读、处理、写都在一个主IO里运行。后台有几个BIO线程,任务主要是关闭文件、刷文件等等。

4.0之后,添加了LAZY_FREE,有些大KEY可以异步的释放,以避免阻塞同步任务处理。而在2.8上会经常会遇到淘汰或过期删除比较大的key时服务会卡顿,所以建议用户使用4.0以上的服务端,避免此类大key删除问题导致的卡顿。

1.png

Redis6.x

Redis 6.x 版本改进的模型,可以在主线程,可读事件触发之后,把“读”任务委托在IO线程处理,全读完之后,返回结果,再一次处理,然后“写”也可以分发给IO线程写,显而易见可以提升性能。

这种性能提升,运行线程还只有一个,如果有一些O(1)命令,比如简单的“读”、“写”命令,提升效果非常高。但如果命令本身很复杂,因为DB还是只有一个运行线程,提升效果会比较差。

还有个问题,把“读”任务委托之后,需要等返回,“写”也需要等返回,所以主线程有很长时间在等,且这段时间无法提供服务,所以Redis 6.x模型还有提升的空间。

线程模型2.png

分布式锁:删除与续租的Lua实现

分布式锁删除的时候比较麻烦,删除时需要一个判断,当value等于之前value时,才可以删掉。Redis目前没有这样的命令,一般通过Lua来实现。

线程模型2.png

一般来说,不建议在Redis里面使用LUA,LUA执行需要先解析、翻译,然后执行整个过程。

第一:因为 Redis LUA,等于是在C里面调LUA,然后LUA里面再去调 C,返回值会有两次的转换,先从Redis协议返回值转成LUA对象,再由LUA对象转成 C的数据返回。

第二:有很多LUA解析,VM处理,包括lua.vm内存占用,会比一般的命令时间慢。建议用LUA最好只写比较简单的,比如if判断。尽量避免循环,尽量避免重的操作,尽量避免大数据访问、获取。因为引擎只有一个线程,当CPU被耗在LUA的时候,只有更少的CPU处理业务命令,所以要慎用。

Redis同步工具 Redis-shark

1.png