Redis设计规范与最佳实践
更新时间:2023-12-20
作为百度首选的kv存储解决方案,在多年使用中积累了很多最佳实践,帮助业务方最大化发挥Redis的性能优势,避免不规范不合理的使用带来影响性能的问题,我们给出如下设计使用规范,供大家参考。
Key名字如何设计?
大小:
- 建议控制在30个字节内,尽可能不超过128字节,如果是MGET超过100个数据的场景,此时Key名字最好不超过20字节。
命名:
- 不使用特殊字符,比如空格、单引号、转义字符。
- 业务标识可以当做Key的前缀,标识和含义内容用“:”分隔, 比如业务标识是“AID:15”,Key就可以写成:“AID:15:player:ranking”,方便遇到问题时根据业务标识尽快定位;如果Key本身是拆分Key,可以使用"#"分隔,比如“video_info#olympic#2020-08-29#1”,“video_info#olympic#2020-08-29#2”,提高可读性。
- 常见单词尽可能使用英文简写, 比如“economic”简写为“ec”,“file path”简写为“fp”。
- 正确使用Redis的hashtag,默认符号的配置是“{}”,集群版是按照hashtag(第一个“{”和第一个“}”之间的内容)来进行路由的,比如“a{aa{xxx}bb}b”是按照“{aa{xxx}”转发到同一个数据分片来实现分布式Redis的RENAME命令的操作,要注意使用hashtag避免大量数据落入同一个数据分片, 造成多个节点数据不均衡,不需要使用hashtag的场景,禁止使用“{}”,如果有特殊原因必须使用,建议在控制台关闭hashtag的特性。
过期
- 首先我们建议Key都是需要设置过期时间的,尤其是秒杀、热门活动等集中写入的Key的场景, 在设置过期时间的时候要加上随机值, 让过期时间分散一些。
Value如何设计?
大小以及素个数:
- String类型的数据建议百字节以内,控制在1K以下, 最好不超过10K,如果是MGET或者使用Pipeline的场景,建议小于4K,Redis限制值是每个Value最大不超过512M。
这里我们给出参考值,Redis请求最大10ms延迟级别下,对应的Key大小和QPS:
单kv大小 | 100字节 | 1K | 10K | 50K | 100K | 500K |
---|---|---|---|---|---|---|
qps | 9.7w | 7.8w | 7.3w | 6.8w | 4.2w | 6400 |
- 复合类型Hash、List、Set、Zset,默认Redis配置下,控制成员数目并且控制单个成员大小是可以对应使用压缩算法的,可以降低Redis本身数据结构的开销。
类型 | 成员个数最大值 | 成员大小最大值 |
---|---|---|
Hash | 512 | 64B |
Set | 512 | 64B |
List | 512 | 64B |
Zset | 128 | 64B |
- 那么对于Set、Hash、List这类复杂数据类型,要尽量降低数据结构中的元素个数,建议元素个数控制1000以下,单Key对应Value大小不超过1M。
这里我们给出参考值,预置单Hash Key下,每个成员长度为10字节,Field个数在10、100、500、1000、3000、5000情况下,HGETALL操作对应的平响时间、qps测试结果:
kv大小:member数量*10字节 | 10*10 | 100*10 | 500*10 | 1k*10 | 3k*10 | 5k*10 |
---|---|---|---|---|---|---|
平响时间(ms) | 0.4 | 0.7 | 2.7 | 5 | 11 | 25 |
qps | 4.8w | 1w | 2500 | 870 | 250 | 120 |
使用的命令请求遵守哪些规范?
控制数量:
- 对于单个请求批量访问数据的场景,建议限制在100个Key以内(如:MGET一次不能超过100个Key、HMGET一次不能超过100个FIELD)。
- 每个Pipeline批次下Key数量 ,建议限制在200以内,最好控制在50以内。
- 对于O(n),O(log(n))以及更高复杂度的,控制元素个数以及使用频率,比如LRANGE 0 -1,HGETALL
数据删除:
- 对于复合类型的DEL,如果是已知元素个数很多的大Key,4.0以上版本建议使用UNLINK方式删除,如果是低版本的Redis,可以使用HSCAN、SSCAN、ZSCAN。
- 对于4系以上版本Redis建议开启Redis的lazyfree配置,启用异步删除的功能
LUA 使用:
- 不建议将复杂的EVAL脚本放在Redis执行,会导致占用CPU以及内存资源,带来性能下降,我们6系的版本支持基础命令以外的MODULE来满足业务场景。
- 不建议LUA中包含事务以及redis.replication相关的操作。
- LUA脚本中变量需要保证是执行的时候给定脚本和参数,建议执行EVAL将LUA的脚本缓存在Redis中,通过EVALSHA的命令执行脚本内容。
- 执行EVALSHA收到返回是脚本不存在的时候,需要重新执行EVAL。
消息队列或者通信:
- 发布订阅的业务场景,要注意发布和订阅保持平衡, 包括订阅或者取消订阅的是已有的频道,避免出现热点。
- 一次订阅建议控制在50个频道以内,以免输出缓冲区过高触发客户端连接的断连。
- 用List类型做消息队列,要注意生产和消费保持平衡,并且通过LEN检查长度,通过LTRIM对队列的长度进行控制,及时截断。
- 当出现队列异常堆积以后,控制再次消费的速度,避免大量堆积任务同一时间消费导致输出缓冲区过高触发客户端连接的断连。
慎用的命令:
- 如果不确认Key是Big-key,不确认当前Redis的内存使用量,慎用DEBUG相关的命令(如 DEBUG DIGEST/OBJECT等),避免造成阻塞,如果需要检查Key的大小,使用4系及以上高版本MEMORY命令检查,低版本建议使用复杂度是O(1)的命令检查,比如:STRLEN,HLEN(更多详见:https://redis.io/commands/)。
- 非必要不使用MONITOR命令,如果需要,预先检查Redis的网络流量,在确保网络流量在10M/s以下,安全使用MONITOR命令。
- 非必要不使用KEYS命令,如果需要,在确保自己的数据量控制在网络流量10M/s以下,安全使用KEYS命令。