Redis 开发规范

规范内容部分源于阿里云 Redis 操作规范

总体要求

Redis 主要用于缓存处理,加快读取效率,但在使用过程中需要注意合理的使用,一般存储全局配置数据和一些访问非常频繁的较为静态的数据,另外注意过期时间控制,减少资源的不必要消耗。

模块级固定段:服务简码:模块简码: 例:hpfm:fnd:
服务级固定段:服务简码: 例:hpfm:

命名规约

可读性和可管理性:

以业务名(或数据库名)为前缀(防止 key 冲突),用冒号分隔,比如平台服务:基础模块:配置文件(Hash 结构的 key),或者采用 系统名 + 业务名 + 业务数据 + 其他,不可变的前缀在前,可变的放在最后。

hpfm : fnd : profile

简洁性:

保证语义的前提下,控制 key 的长度,当 key 较多时,内存占用也不容忽视。

例如:user : friends : messages : {uid} : {mid}
简化:u : fr : m : {uid} : {mid}

不要包含特殊字符:

反例:包含空格、换行、单双引号以及其他转义字符。

开发操作规约

命令操作:

● 禁止使用 * 匹配表达式,如:KEYS *

● 设置超时时间,不要设置 key 不过期

说明:控制 key 的生命周期,Redis 不是垃圾桶。
建议使用 expire 设置过期时间(条件允许可以打散过期时间,防止集中过期),不过期的数据重点关注 idletime 。

● 使用批量操作
mget、hmget 而不是 get 和 hget,对于 set 也是如此。
lpush 向一个 list 一次性导入多个元素,而不用 lset 一个个添加。
lrange 一次取出一个范围的元素,而不用 lindex 一个个取出。

谨慎全量操作 Hash、Set 等集合结构

说明:在使用 hash 结构存储对象属性时,开始只有有限的十几个 field,往往使用 hgetall 获取所有成员,效率也很高,但是随着业务发展,会将 field 扩张到上百个甚至几百个,此时还使用 hgetall 会出现效率急剧下降、网卡频繁打满等问题【时间复杂度O(N)】,此时建议根据业务拆分为多个 hash 结构;或者如果大部分都是获取所有属性的操作,可以将所有属性序列化为一个 string 类型存储!
同样在使用 smembers 操作 set 结构类型时也是相同的情况!

合理利用管道操作

说明:Redis 提供一个 pipeline 的管道操作模式,将多个指令汇总到队列中批量执行,可以减少 tcp 交互产生的时间,一般情况下能够有 10%~30% 不等的性能提升。但是需要注意的是,pipeline 与 multi 不同,无法保证请求之间的原子性,因此需要考虑使用场景。如果业务场景允许,这也是一个性能提升的点。

使用统一封装的类来操作 Redis 实例命令操作场景,涉及到锁的需要采用统一的 lock 类

说明: Redis 自由度非常大,key 有各种各样的形式,将所有的操作集中在一个类中,便于管理。

根据业务场景合理使用不同的数据结构类型

说明:目前 Redis 支持的数据库结构类型较多:字符串(String),哈希(Hash),列表(List),集合(Set),有序集合(Sorted Set),Bitmap,HyperLogLog 和地理空间索引(geospatial)等,需要根据业务场景选择合适的类型,常见的如:String 可以用作普通的 K – V 、计数类;Hash 可以用作对象如商品、经纪人等,包含较多属性的信息;List 可以用作消息队列、粉丝/关注列表等;Set 可以用于推荐;Sorted Set 可以用于排行榜等!

减少不必要的请求

说明:多的代码框架都会产生大量的不必要请求,这个在 MySQL 使用上很严重,在 Redis 上也经常出现。但是 Redis 的使用通常是在一些高并发低延迟的场景中,因此不必要的请求会大大的拉低有效请求数。比如你的 Redis 的吞吐大约是 2w 的 qps,如果你的不必要请求大约占了 40%,那意味着有效的用户请求才 12000 次/秒,如果干掉这部分不必要请求,那意味着有 20000 次/秒的有效用户请求,实际的业务吞吐量能提升 67% 。

案例一:某天某系统发现请求响应十分缓慢,查看 php 日志发现了大量的 Redis 超时请求,再查看 Redis 发现流量以及 tcp 请求均属于正常状态,但是 Redis 使用的 CPU 出现100% 现象。于是我们对该实例进行了请求采样,很快就发现了问题:该实例的平均 qps 为69.24,大约 26.54% 的请求是 keys 请求,每次 keys 请求平均耗时约为 13w 微秒,key 的总请求时间为该实例总请求时间的 99.99% 。把一个 Redis 实例搞成 69.24 的 qps,这证明复杂度为 O(N) 的请求很快就能整跨一个 Redis 实例、整垮一个系统。屏蔽掉 keys 请求之后,Redis 的 qps 直线上升,运营同学马上报来好消息,系统正常了系统正常了,玩家可以访问了。

案例二:故事依然是那样的开头,故障 / 排查 / 解决。其中一个优化点就是:将复杂度为 O( log( N ) + M ) 的 zrevrange 操作转换为 list 的 O( 1 ) 操作,单单这点优化 qps 从 5000 上升到 6500,约提升了 30% 。

常用场景:

● 对于数据量较大的集合,不要轻易进行删除操作,这样会阻塞服务器,一般采用重命名 + 批量删除的策略。

● 取最新 N 个数据的操作

说明:比如典型的取你网站的最新文章,通过下面方式,我们可以将最新的 5000 条评论的 ID 放在 Redis 的 List 集合中,并将超出集合部分从数据库获取。使用 lpush latest.comments 命令,向 list 集合中插入数据插入完成后再用 ltrim latest.comments 0 5000 命令使其永远只保存最近 5000 个 ID 。如果你还有不同的筛选维度,比如某个分类的最新 N 条,那么你可以再建一个按此分类的 list,只存 ID 的话,Redis 是非常高效的。

● 排行榜应用,取 TOP N 操作

这个需求与上面需求的不同之处在于,前面操作以时间为权重,这个是以某个条件为权重,比如按顶的次数排序,这时候就需要我们的 sorted set 出马了,将你要排序的值设置成 sorted set 的 score,将具体的数据设置成相应的 value,每次只需要执行一条 ZADD 命令即可。

Uniq 操作,获取某段时间所有数据排重值

这个使用 Redis 的 set 数据结构最合适了,只需要不断地将数据往 set 中扔就行了,set 意为集合,所以会自动排重。

安全规范:

信任的内网运行,绑定 Redis 监听的网络接口。

禁止 root 用户启动 Redis 。

● 限制 Redis 文件目录访问权限。

● 避免使用默认端口。

● 开启 Redis 密码认证,并设置高复杂度密码(需要重启 Redis 才能生效)。

● 服务精细化授权。

# 禁用或重命名危险命令(FLUSHDB, FLUSHALL, KEYS,PEXPIRE, DEL, CONFIG, SHUTDOWN, BGREWRITEAOF, BGSAVE, SAVE, SPOP, SREM, RENAME,DEBUG, EVAL等) rename-command CONFIG CONFIG_b9fc8327c4dee7 rename-command SHUTDOWN SHUTDOWN_b9fc8327c4dee7 rename-command FLUSHDB “” rename-command FLUSHALL “” # 重启生效

● 防火墙屏蔽 Redis 端口。

● 禁止在 Redis 中存储敏感的明文数据。