更新时间:2024-10-24 GMT+08:00

DCS使用规范

业务使用规范

原则

原则说明

备注

就近部署业务,避免时延过大

如果部署位置过远(非同一个region)或者时延较大(例如业务服务器与Redis实例通过公网连接),网络延迟将极大影响读写性能。

如果对于时延较为敏感,请避免创建跨AZ Redis实例。

冷热数据区分

建议将热数据加载到 Redis 中。低频数据可存储在 Mysql或者ElasticSearch中。

Redis将低频数据存入内存中,并不会加速访问,且占用Redis空间。

业务数据分离

避免多个业务共用一个Redis。

一方面避免业务相互影响,另一方面避免单实例膨胀,并能在故障时降低影响面,快速恢复。

禁止使用select功能在单Redis实例做多db区分。

Redis单实例内多DB隔离性较差,Redis开源社区已经不再发展多DB特性,后续不建议依赖该特性。

设置合理的内存淘汰(逐出)策略

合理设置淘汰策略,可以在Redis内存意外写满的时候,仍然正常提供服务。

DCS默认的逐出策略为volatile-lru,请根据业务需求选择。Redis支持的数据逐出策略

以缓存方式使用Redis

Redis事务功能较弱,不建议过多使用。

事务执行完后,不可回滚。

数据异常的情况下,支持清空缓存进行数据恢复。

Redis本身没有保障数据强一致的机制和协议,业务不能强依赖Redis数据的准确性。

以缓存方式使用Redis时,所有的key需设置过期时间,不可把Redis作为数据库使用。

失效时间并非越长越好,需要根据业务性质进行设置。

防止缓存击穿

推荐搭配本地缓存使用Redis,对于热点数据建立本地缓存。本地缓存数据使用异步方式进行刷新。

-

防止缓存穿透

非关键路径透传数据库,建议对访问数据库进行限流。

-

从Redis获取数据未命中时,访问只读数据库实例。可通过域名等方式对接多个只读实例。

核心是未命中的缓存数据不会打到主库上。

用域名对接多个只读数据库实例,一旦出现问题,可以增加只读实例应急。

不用作消息队列

发布订阅场景下,不建议作为消息队列使用。

  • 如没有非常特殊的需求,不建议将 Redis 当作消息队列使用。
  • Redis 当作消息队列使用,会有容量、网络、效率、功能方面的多种问题。
  • 如需要消息队列,可使用高吞吐的Kafka或者高可靠的RocketMQ。

合理选择规格

如果业务增长会带来Redis请求增长,请选择集群实例(Proxy集群和Cluster集群)

单机和主备扩容只能实现内存、带宽的扩容,无法实现计算性能扩容。

生产实例需要选择主备或者集群实例,不能选用单机实例

-

主备实例,不建议使用过大的规格。

Redis在执行RewriteAOF和BGSAVE的时候,会fork一个进程,过大的内存会导致卡顿

具备降级或容灾措施

缓存访问失败时,具备降级措施,从DB获取数据;或者具备容灾措施,自动切换到另一个Redis使用。

-

数据设计规范

分类

原则

原则说明

备注

Key相关规范

使用统一的命名规范。

一般使用业务名(或数据库名)为前缀,用冒号分隔。Key的名称保证语义清晰。

例如,业务名:子业务名:id

控制Key名称的长度。

在保证语义清晰的情况下,尽量减少Key的长度。有些常用单词可使用缩写,例如,user缩写为u,messages缩写为msg。

建议不要超过128字节(越短越好)。

禁止包含特殊字符(大括号“{}”除外)。

禁止包含特殊字符,如空格、换行、单双引号以及其他转义字符。

由于大括号“{}”为Redis的hash tag语义,如果使用的是集群实例,Key名称需要正确地使用大括号避免分片不均的情况。

Value相关规范

设计合理的Value大小。

设计合理的Key中Value的大小,推荐小于10 KB。

过大的Value会引发分片不均、热点Key、实例流量或CPU使用率冲高等问题,还可能导致变更规格和迁移失败。应从设计源头上避免此类问题带来的影响。

设计合理的Key中元素的数量。

对于集合和列表类的数据结构(例如Hash,Set,List等),避免其中包含过多元素,建议单Key中的元素不要超过5000个。

由于某些命令(例如HGETALL)的时间复杂度直接与Key中的元素数量相关。如果频繁执行时间复杂度为O(N)及以上的命令,且Key中的子Key数量过多容易引发慢请求、分片流量不均或热点Key问题。

选择合适的数据类型。

合理地选择数据结构能够节省内存和带宽。

例如存储用户的信息,可用使用多个key,使用set u:1:name "X"、set u:1:age 20存储,也可以使用hash数据结构,存储成1个key,设置用户属性时使用hmset一次设置多个,同时这样存储也能节省内存。

设置合理的过期时间。

合理设置Key的过期时间,将过期时间打散,避免大量Key在同一时间点过期。

设置过期时间时,可以在基础值上增减一个随机偏移值,避免在同一个时间点大量Key过期。大量Key过期会导致CPU使用率冲高。

命令使用规范

原则

原则说明

备注

谨慎使用O(N)复杂度的命令

时间复杂度为O(N)的命令,需要特别注意N的值。避免N过大,造成Redis阻塞以及CPU使用率冲高。

例如:hgetall、lrange、smembers、zrange、sinter这些命令都是做全集操作,如果元素很多,会消耗大量CPU资源。可使用hscan、sscan、zscan这些分批扫描的命令替代。

禁用高危命令

禁止使用flushall、keys、hgetall等命令,或对命令进行重命名限制使用。

请参考命令重命名的内容。

慎重使用select

Redis多数据库支持较弱,多业务用多数据库实际还是单线程处理,会有干扰。最好是拆分使用多个Redis。

-

使用批量操作提高效率

如果有批量操作,可使用mget、mset或pipeline,提高效率,但要注意控制一次批量操作的元素个数。

mget、mset和pipeline的区别如下:

  • mget和mset是原子操作,pipeline是非原子操作。
  • pipeline可以打包不同的命令,mget和mset做不到。
  • 使用pipeline,需要客户端和服务端同时支持。

避免在lua脚本中使用耗时代码

lua脚本的执行超时时间为5秒钟,建议不要在lua脚本中使用比较耗时的代码。

比如长时间的sleep、大的循环等语句。

避免在lua脚本中使用随机函数

调用lua脚本时,建议不要使用随机函数去指定key,否则在主备节点上执行结果不一致,从而导致主备节点数据不一致。

-

遵循集群实例使用lua的限制

遵循集群实例使用lua的限制。

  • 使用EVAL和EVALSHA命令时,命令参数中必须带有至少1个key,否则客户端会提示“ERR eval/evalsha numkeys must be bigger than zero in redis cluster mode”的错误。
  • 使用EVAL和EVALSHA命令时,DCS Redis集群实例使用第一个key来计算slot,用户代码需要保证操作的key是在同一个slot。

对mget,hmget等批量命令做并行和异步IO优化

某些客户端对于MGET,HMGET这些命令没有做特殊处理,串行执行再合并返回,效率较低,建议做并行优化。

例如Jedis对于MGET命令在集群中执行的场景就没有特殊优化,串行执行,比起lettuce中并行pipeline,异步IO的实现,性能差距可达到数十倍,该场景建议使用Jedis的客户端自行实现slot分组和pipeline的功能。

禁止使用del命令直接删除大Key

使用del命令直接删除大Key(主要是集合类型)会导致节点阻塞,影响后续请求

Redis 4.0后的版本可以通过UNLINK命令安全地删除大Key,该命令是异步非阻塞的。

对于Redis 4.0之前的版本:

  • 如果是Hash类型的大Key,推荐使用hscan + hdel
  • 如果是List类型的大Key,推荐使用ltrim
  • 如果是Set类型的大Key,推荐使用sscan + srem
  • 如果是SortedSet类型的大Key,推荐使用zscan + zrem

SDK使用规范

原则

原则说明

备注

使用连接池和长连接

短连接性能差,推荐使用带有连接池的客户端。

连接的频繁创建和销毁,会浪费大量的系统资源,极限情况会造成宿主机宕机。请确保使用了正确的Redis客户端连接池配置。

客户端需要对可能的故障和慢请求做容错处理

由于Redis服务可能因网络波动或基础设置故障的影响,引发主备倒换,命令超时或慢请求等现象,需要在客户端内设计合理的容错重试机制。

参考Redis客户端重试指南

合理设置重试时间和次数

合理设置容错处理的重试时间,根据业务要求设置,避免过短或者过长。

  • 如果超时重试时间设置的非常短(例如200毫秒以下),可能引发重试风暴,极易引发业务层雪崩。
  • 如果重试时间设置得较长或者重试次数设置得较大,则可能导致在主备倒换情况下业务恢复较慢。

避免使用Lettuce客户端

Lettuce客户端在默认配置下有一定性能优势,并且是spring的默认客户端,但是Jedis客户端在面对连接异常,网络抖动等场景下的异常处理和检测能力明显强于Lettuce,可靠性更强,建议使用Jedis。

Lettuce存在几个方面的问题:

  • Lettuce默认未配置集群拓补刷新的配置,会导致Cluster集群在发生拓补信息变化(主备倒换,扩容缩容)时,无法识别新的节点信息,导致业务失败。可参考使用Lettuce连接Cluster集群实例时的扩容异常处理
  • Lettuce没有连接池校验的功能,无法检测连接池中的连接是否仍然有效,获取失效连接之后会导致业务失败,存在分钟级不可用的故障风险。

运维管理规范

原则

原则说明

备注

生产开启密码保护

生产系统中需要开启Redis密码保护机制。

-

现网操作安全

禁止开发人员私自连到线上Redis服务。

-

验证业务的故障处理能力或容灾逻辑

在测试环境或者预生产环境中组织演练,验证在Redis主备倒换、宕机或者扩缩容场景下业务的可靠性。

主备倒换可以自行在页面上触发。强烈建议使用Lettuce客户端的应用进行相关的演练。

监控实践

关注Redis负载,在过载前提前扩容。

根据告警基线配置告警:配置节点cpu、内存、带宽等告警。

日常巡检

例行检查各个节点的内存使用率,查看主节点内存使用率是否有不均衡的状态。

内存使用率不均衡说明存在大Key问题,需要进行大Key拆分及优化。

开启热Key例行分析,并分析是否有Key频繁调用。

-

例行诊断Redis实例的命令,分析O(N)类命令是否存在隐患。

针对O(N)命令,即使耗时很小,建议开发分析业务增长,N是否会增长。

例行巡检Redis慢日志命令。

针对慢日志分析隐患,并尽快从业务上进行修复。