- 规范参考
- 总体要求
- 键名设计
- key名设计
- value设计
- 命令使用
- 禁止使用keys、flushall、hmgetall等命令
- 慎用hgetall、lrange、smembers、zrange等命令
- 【推荐】使用批量操作提高效率
- 【建议】Redis事务功能较弱,不建议过多使用
- 缓存设计
- 多个库的使用
- 避免多个应用公用一个redis实例
- 合理评估业务场景,并设置最大内存以及内存淘汰策略(maxmemory和maxmemory-policy)
- 使用带有连接池的数据库,可以有效控制连接,同时提高效率
- 给redis设置一个密码
- 冷热数据区分
- 缓存非特殊情况不做中间态
- 场景实战问题
- 【推荐】
- 项目redis使用问题
- 慎用laravel自带的cache功能
- 注意key的过期时间设置
- 小心缓存穿透
- 慎用缓存层层包裹
- 慎用将redis做为消息队列
- 查询使用问题
- 线上Redis禁止使用Keys正则匹配操作
- 其他
- redis同步工具
- 大key查询
规范参考
- 总体规约以《阿里云Redis开发规范》为主,请开发人员至少阅读一遍该手册。
总体要求
Redis主要用于缓存处理,加快读取效率,但在使用过程中需要注意合理的使用,一般存储全局配置数据和一些访问非常频繁的较为静态的数据,另外注意过期时间控制,减少资源的不必要消耗。
- 模块级固定段:服务简码:模块简码: 例:
auth:iam:
- 服务级固定段:服务简码: 例:
pai:
在项目中 redis
属于高频使用,在使用时我们遇到了各种各样的redis问题,于是针对自身情况梳理了一个使用规范。
键名设计
key名设计
- 【建议】: 可读性和可管理性
以业务名(或数据库名)为前缀(防止key冲突),用冒号分隔,比如业务名:表名:id
示例:teach:leeson_id:21
- 【建议】: 简洁性
保证语义的前提下,控制key的长度,当key较多时,内存占用也不容忽视,例如:
示例:user:{uid}:friends:messages:{mid}
简化为u:{uid}:fr:m:{mid}。
- 【强制】:不要包含特殊字符(比如空格、换行、单双引号以及其他转义字符)
反例:包含空格、换行、单双引号以及其他转义字符
value设计
- 拒绝大key操作
禁用超过10K的string大key(虽然redis支持512MB大小的string),如果1mb的key每秒重复写入10次,就会导致写入网络IO达10MB。
错误示范:直接将laravel的整个模型或者对象当成value存储
设计key时使用合适的数据类型(在资源利用和性能之间作平衡)
错误示范:一个普通字符串弄成hash类型进行存储反例:
set user:1:name tom set user:1:age 19 set user:1:favor football
正例:
hmset user:1 name tom age 19 favor football
一定要控制key的生命周期
建议使用expire设置过期时间(条件允许可以打散过期时间,防止集中过期),不过期的数据重点关注idletime。
错误示范:key设置为永不过期控制value长度
比如string类型:- 如果value为’8个字节的长整型’则内部使用int类型
- 如果value为’小于等于39个字节的字符串’则内部使用embstr类型
- 如果value为’大于39个字节的字符串’则内部使用raw类型
这样能很好的利用redis的性能。
数据按需存储
不需要的数据千万不要存储在redis,只会浪费内存空间
命令使用
禁止使用keys、flushall、hmgetall等命令
为防止业务研发的误操作,通常可以在交付redis实例之前将默认命令rename掉;而真正需要删除或者遍历key时可以使用scan家族命令
慎用hgetall、lrange、smembers、zrange等命令
除非业务场景需要,尽量不要使用这些命令。如果没有控制好会导致操作量过大,形成阻塞。
【推荐】使用批量操作提高效率
原生命令:例如mget、mset。
非原生命令:可以使用pipeline提高效率。
但要注意控制一次批量操作的元素个数(例如500以内,实际也和元素字节数有关)。
注意两者不同:
1. 原生是原子操作,pipeline是非原子操作。
2. pipeline可以打包不同的命令,原生做不到
3. pipeline需要客户端和服务端同时支持。
【建议】Redis事务功能较弱,不建议过多使用
Redis的事务功能较弱(不支持回滚),而且集群版本(自研和官方)要求一次事务操作的key必须在一个slot上(可以使用hashtag功能解决)
缓存设计
多个库的使用
如果应用中会涉及到各种不同的redis数据存储,应该分库存储,最好是一种业务使用一个库
比如:课程缓存:库1;订单队列:库2;日志处理:库3
避免多个应用公用一个redis实例
避免一个应用出现问题或者错误使用拖累其他应用
合理评估业务场景,并设置最大内存以及内存淘汰策略(maxmemory和maxmemory-policy)
目前我们用的阿里云redis,不太存在这个问题
使用带有连接池的数据库,可以有效控制连接,同时提高效率
给redis设置一个密码
目前我们用的阿里云redis,不太存在这个问题
冷热数据区分
虽然 Redis支持持久化,但将所有数据存储在redis中,成本非常昂贵。
建议将热数据 (如 QPS超过 5k) 的数据加载到redis中。
低频数据可存储在Mysql、ElasticSearch中。
缓存非特殊情况不做中间态
redis大多数时候都是做缓存用,去掉后业务逻辑不应发生改变,万不可切入到业务里。
- 第一、缓存的高可用会影响业务;
- 第二、产生深耦合会发生无法预料的效果;
- 第三、会对维护产生负效果。
场景实战问题
【推荐】
避免多个应用使用一个Redis实例
正例:不相干的业务拆分,公共数据做服务化。
项目redis使用问题
当前的使用方式是,每个接入的应用要配置核心项目的redis配置。这样是不合理的,核心项目的redis应该只能在核心项目中使用,对外应该是提供api接口或者rpc进行访问。
慎用laravel自带的cache功能
laravel自带的cache功能最容易导致大key,经常由于简单使用至今将整个对象模型存储到redis,造成大key。
注意key的过期时间设置
在报名等高峰期的时候,key值设置过短容易造成缓存穿透,导致大量请求直接打到mysql数据库。
小心缓存穿透
经常使用会只给有数据的结果进行缓存,结果导致空数据无法缓存,相同查询直接每次都到达数据库,所以空值也应该被缓存。
慎用缓存层层包裹
缓存里面的数据还有一层缓存数据,会导致问题排查麻烦,出问题也不容易处理。
慎用将redis做为消息队列
如没有非常特殊的需求,严禁将 Redis 当作消息队列使用。redis 当作消息队列使用,会有容量、网络、效率、功能方面的多种问题。
如需要消息队列,可使用高吞吐的 Kafka 或者高可靠的 RocketMQ,nsq,(同步有时间前后要求,且量不大才使用的)。
查询使用问题
线上Redis禁止使用Keys正则匹配操作
redis是单线程处理,在线上Key数量较多时,操作效率极低【时间复杂度为O(N)】,该命令一旦执行会严重阻塞线上其它命令的正常请求,而且在高QPS情况下会直接造成redis服务崩溃!如果有类似需求,请使用scan命令代替。
其他
redis同步工具
阿里云的redis-shake工具,方便快速
大key查询
阿里云有大key分析工具