1. 引言
1.1 背景及目的
目前公司在使用Redis缓存中间件时存在各种问题,如:存在多个应用共用一个Redis缓存情况,存在应用性能干扰隐患;缓存的版本不统一;架构太多(Codis代理、Redis+Keepalive、Redis Cluster、近似客户端分片等),不便于运维管理等等。为了解决上述种种问题特出具一个开发规范便于更好的使用Redis缓存中间件。
1.2 阅读对象
项目架构人员、项目开发人员、项目管理人员、Redis缓存中间件运维人员
1.3 参考资料
略
1.4 自定义
为了便于分类,本文定义了类别名词,在此进行说明。
【强制】指必须遵守项。
【建议】指一般遵守项,可以根据项目实际需求使用。
2. 使用规范
2.1 Redis有数据丢失风险,程序需要处理数据丢失时重新加载过程【强制】
使用redis有丢失数据的风险,项目架构时需要考虑到相应的解决方案,程序需要处理如果redis数据丢失时重新加载过程。
2.2 冷热数据分离【强制】
冷热数据分离,不要将所有数据全部都放到Redis中, 虽然Redis支持持久化,但是Redis的数据存储全部都是在内存中的,成本昂贵。建议根据业务只将高频热数据存储到Redis中,对于低频冷数据可以使用基于磁盘(Oracle、MySQL、MongoDB等)存储方式,不仅节省内存成本,而且数据量小在操作时速度更快、效率更高!
2.3 不同业务数据要分开存储【强制】
不要将不相关的业务数据都放到一个Redis实例中,新业务申请新的单独实例。因为Redis为单线程处理,独立存储会减少不同业务相互操作的影响,提高请求响应速度;同时也避免单个实例内存数据量膨胀过大,在出现异常情况时可以更快恢复服务。
2.4 规范Key格式【强制】
合适的Key,便于查看,统计,排错。不推荐含义不清的Key和特别长的Key。例如:系统名+应用名+业务数据+其他。不允许包含空格,tab键等特殊字符。避免使用二进制,除非特殊情况。长度控制在100字节以内。
2.5 选择合适的数据类型【建议】
在不能确定其它复杂数据结构一定优于String类型时,避免使用Redis的复杂数据结构。每种数据结构都有相应的使用场景,String类型是Redis中最简单的数据类型,推荐使用String类型。但是考虑到具体的业务场景,综合评估性能、存储、网络等方面之后使用适当的数据结构。Redis支持的数据库结构类型较多:字符串(String),哈希(Hash),列表(List),集合(Set),有序集合(Sorted Set), Bitmap, HyperLogLog和地理空间索引(geospatial)等,需要根据业务场景选择合适的类型,常见的如:String可以用作普通的K-V、计数类;Hash可以用作对象如商品、经纪人等,包含较多属性的信息;List可以用作消息队列、粉丝/关注列表等;Set可以用于推荐;Sorted Set可以用于排行榜等。
2.6 谨慎全量操作Hash、Set等集合结构【建议】
在使用HASH结构存储对象属性时,开始只有有限的十几个field,往往使用HGETALL获取所有成员,效率也很高,但是随着业务发展,会将field扩张到上百个甚至几百个,此时还使用HGETALL会出现效率急剧下降、网卡频繁打满等问题,此时建议根据业务拆分为多个Hash结构;或者如果大部分都是获取所有属性的操作,可以将所有属性序列化为一个STRING类型存储!同样在使用SMEMBERS操作SET结构类型时也是相同的情况。
2.7 对于必须要存储的大文本数据压缩后存储【建议】
对于大文本写入到Redis时,要压缩后存储。大文本数据存入Redis,除了带来极大的内存占用外,在访问量高时,很容易就会将网卡流量占满,进而造成整个服务器上的所有服务不可用,并引发雪崩效应,造成各个系统瘫痪。
2.8 禁止大String【强制】
核心集群禁用1MB的String大Key(虽然Redis支持512MB大小的String),如果1MB的key每秒重复写入10次,就会导致写入网络IO达10MB。
2.9 线上Redis禁止使用Keys正则匹配操作【强制】
Redis是单线程处理,在线上KEY数量较多时,操作效率极低,该命令一旦执行会严重阻塞线上其它命令的正常请求,而且在高QPS情况下会直接造成Redis服务崩溃。如果有类似需求,用Scan命令代替。
2.10 存储的Key要尽可能设置超时时间【建议】
如果应用将Redis定位为缓存Cache使用,对于存放的Key要设置超时时间!因为若不设置,这些Key会一直占用内存不释放,造成极大的浪费,而且随着时间的推移会导致内存占用越来越大,直到达到服务器内存上限。另外Key的超时长短要根据业务综合评估,而不是越长越好。
2.11 频繁对String进行Append操作,则使用List【建议】
如果出现频繁对String进行Append操作,则请使用List进行Push操作,取出时使用Pop。这样避免String频繁分配内存导致的延时。
2.12 线上禁止使用Monitor命令【强制】
禁止生产环境使用Monitor命令,Monitor命令在高并发条件下,会存在内存暴增和影响Redis性能的隐患。
2.13 位级别和字级别的操作【建议】
Redis 引入了位级别和字级别的操作: GETRANGE, SETRANGE, GETBIT 和 SETBIT.使用这些命令,可以把Redis的字符串当做一个随机读取的(字节)数组。例如你有一个应用,用来标志用户的ID是连续的整数,你可以使用一个位图标记用户的性别,使用1表示男性,0表示女性,或者其他的方式。这样的话,1亿个用户将仅使用12 M的内存。你可以使用同样的方法,使用 GETRANGE 和 SETRANGE 命令为每个用户存储一个字节的信息。
2.14 尽可能使用散列表【建议】
小散列表(是说散列表里面存储的数少)使用的内存非常小,所以你应该尽可能的将你的数据模型抽象到一个散列表里面。比如你的web系统中有一个用户对象,不要为这个用户的名称,姓氏,邮箱,密码设置单独的key,而是应该把这个用户的所有信息存储到一张散列表里面。
2.15 严禁使用命令【强制】
Keys * 导致 redis堵死
Flushall 丢失所有的数据
Flushdb 丢失整个db的数据
2.16 谨慎使用命令【建议】
del/hmget 非常大的key
mget/del 非常多的key
2.17 超时设置【强制】
redis server 不设置超时时间,一切由client设置。
2.18 上线时要做好容量规划【建议】
做好内存、QPS,带宽,实例密度。
2.19 程序中避免循环【建议】
尽量避免频繁访问Redis,尤其在循环中,应该尽量使用批量获取数据,减少网络拥堵。
2.20
项目上线后,产品线需要根据实际写入缓存的动态情况,及时通知运维评估容量。如版本上线需要大量写入缓存,此时需要评估现存容量是否满足需求,以便做出对应的保障计划。