Redis使用规范

目录

1、key名设计

2、value设计

3、开发使用规范

4、运维规范

5、命令规范

6、客户端使用


一、制定目的

为了规范Redis的应用开发,减少开发过程中对Redis的错误使用,提高代码质量,特制订本规范

二、适用范围

所有Redis数据库,包括(codis和redis-cluster)。原则上,Redis数据库设计开发需遵循本规范说明,特殊情况可例外,但需跟SRE说明原因。

三、具体内容

1、key名设计

  • 强制: 可读性和可管理性

以业务名(或数据库名)为前缀(防止key冲突),必须使用冒号分隔,便于RDM查看,比如 “appid名称:租户号:DD_CODE”

b2b-man:tenant_code:dd_code

  • 建议:简洁性

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

user:{uid}:friends:messages:{mid}

简化为

u:{uid}:fr:m:{mid}

强制:长度10-20个字符,不要包含空格、换行,引号和一些转义字符

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

2、value设计

  • 强制:拒绝bigkey(防止网卡流量、慢查询)

string类型控制在10KB以内,hash、list、set、zset元素个数不要超过5000。

反例:一个包含200万个元素的list。

非字符串的bigkey,不要使用del删除,使用hscan、sscan、zscan方式渐进式删除,同时要注意防止bigkey过期时间自动删除问题(例如一个200万的zset设置1小时过期,会触发del操作,造成阻塞,而且该操作不会不出现在慢查询中(latency可查)),查找方法和删除方法

  • 推荐:选择适合的数据类型。

例如:实体类型(要合理控制和使用数据结构内存编码优化配置,例如ziplist,但也要注意节省内存和性能之间的平衡)

反例:

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生命周期,redis不是垃圾站

建议使用expire设置过期时间(条件允许可以打散过期时间,防止集中过期),不过期的数据重点关注idletime。

如果业务强制需求不过期,请说明具体原因。

3、开发使用规范

  • 冷热数据分离,不要将所有数据全部都放到Redis中

虽然Redis支持持久化,但是Redis的数据存储全部都是在内存中的,成本昂贵。建议根据业务只将高频热数据存储到Redis中【QPS大于5000】,对于低频冷数据可以使MySQL/ElasticSearch/MongoDB等基于磁盘的存储方式,不仅节省内存成本,而且数据量小在操作时速度更快、效率更高

  • 不同的业务数据要分开存储

不要将不相关的业务数据都放到一个Redis实例中,建议新业务申请新的单独实例。因为Redis为单线程处理,独立存储会减少不同业务相互操作的影响,提高请求响应速度;同时也避免单个实例内存数据量膨胀过大,在出现异常情况时可以更快恢复服务!

  • 存储的Key一定要设置超时时间

如果应用将Redis定位为缓存Cache使用,对于存放的Key一定要设置超时时间!因为若不设置,这些Key会一直占用内存不释放,造成极大的浪费,而且随着时间的推移会导致内存占用越来越大,直到达到服务器内存上限!另外Key的超时长短要根据业务综合评估,而不是越长越好!

  • 对于必须要存储的大文本数据一定要压缩后存储

对于大文本【超过500字节】写入到Redis时,一定要压缩后存储!大文本数据存入Redis,除了带来极大的内存占用外,在访问量高时,很容易就会将网卡流量占满,进而造成整个服务器上的所有服务不可用,并引发雪崩效应,造成各个系统瘫痪!

  • 线上Redis禁止使用Keys正则匹配操作

Redis是单线程处理,在线上KEY数量较多时,操作效率极低【时间复杂度为O(N)】,该命令一旦执行会严重阻塞线上其它命令的正常请求,而且在高QPS情况下会直接造成Redis服务崩溃!如果有类似需求,请使用scan命令代替!

  • 可靠的消息队列服务

Redis List经常被用于消息队列服务。假设消费者程序在从队列中取出消息后立刻崩溃,但由于该消息已经被取出且没有被正常处理,那么可以认为该消息已经丢失,由此可能会导致业务数据丢失,或业务状态不一致等现象发生。为了避免这种情况,Redis提供了RPOPLPUSH命令,消费者程序会原子性的从主消息队列中取出消息并将其插入到备份队列中,直到消费者程序完成正常的处理逻辑后再将该消息从备份队列中删除。同时还可以提供一个守护进程,当发现备份队列中的消息过期时,可以重新将其再放回到主消息队列中,以便其它的消费者程序继续处理。

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

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

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

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

4、运维规范

  • 事先做好容量规划
  • 不要使用rdb和aof,开启持久化影响性能。目前生产上都是主从复制结构(master-slave)模式,codis集群一台机器上运行2个redis实例,redis-cluster一台机器运行一个实例。

  • master上所有的实例大小总和应不超过物理内存的80%
  • 单个redis实例内存maxmemory不大于10G。
  • 允许最低限度使用swap,尽量不是swap。

5、命令规范

  • 推荐:O(N)命令关注N的数量

例如hgetall、lrange、smembers、zrange、sinter等并非不能使用,但是需要明确N的值。有遍历的需求可以使用hscan、sscan、zscan代替。

  • 推荐:禁用命令

禁止线上使用keys、flushall、flushdb等,通过redis的rename机制禁掉命令,或者使用scan的方式渐进式处理。

  • 推荐:避免使用select

redis的多数据库较弱,使用数字进行区分,很多客户端支持较差,同时多业务用多数据库实际还是单线程处理,会有干扰。

哨兵模式中不建议使用多db,毕竟集群模式已经不能使用多db。

  • 推荐:使用批量操作提高效率
    • 原生命令:如mget、mset。
    •  非原生命令:可以使用pipeline提高效率。
    •  但要注意控制一次批量操作的元素个数(例如500以内,实际也和元素字节数有关)。
    • 原生命令是原子操作,pipeline是非原子操作
    •  pipeline可以打包不同的命令,原生不支持
    • pipeline需要客户端和服务端同时支持
  •  建议:Redis 事物功能较弱,不建议 过多使用

Redis事务功能不支持回滚,cluster 要求事务操作的key必须在一个slot上面

  • 建议:Redis cluster 使用Lua上特殊需求:
    • 所有key都应该由 KEYS 数组来传递,redis.call/pcall 里面调用的redis命令,key的位置,必须是KEYS array, 否则直接返回error,”-ERR bad lua script for redis cluster, all the keys that the script uses should be passed using the KEYS array”
    • 所有key,必须在1个slot上,否则直接返回error, “-ERR eval/evalsha command keys must in same slot”
  • 建议 :必要情况下使用Monitor命令时,要注意不要长时间使用,造成缓冲区溢出,尽而内存抖动

6、客户端使用

  • 避免多个应用使用一个Redis实例

正例:不相干的业务拆分,公共数据做服务化。

  • 使用带有连接池的数据库,可以有效控制连接,同时提高效率
  • 高并发下建议客户端添加熔断功能(例如netflix hystrix)
  • Jedis 配置参考
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
    <!--资源池中最大连接数 默认值8 建议值-->
    <property name="maxTotal" value="50" />
    <!--资源池允许最大空闲的连接数 默认值8 建议值-->
    <property name="maxIdle" value="20" />
    <property name="minIdle" value="10" />
    <!--做空闲资源检测时,每次的采样数, 默认值3,
    可根据自身应用连接数进行微调,如果设置为-1,就是对所有连接做空闲监测-->
    <property name="numTestsPerEvictionRun" value="5" />
    <!--空闲资源的检测周期(单位为毫秒), 默认值 -1,
    建议设置,周期自行选择,也可以默认也可以使用下面JedisPoolConfig中的配置-->
    <property name="timeBetweenEvictionRunsMillis" value="5000" />
    <!--资源池中资源最小空闲时间(单位为毫秒),达到此值后空闲资源将被移除,
    默认值1000*60 *30 = 30分钟, 可根据自身业务决定,大部分默认值即可,
    也可以考虑使用下面JeidsPoolConfig中的配置-->
    <property name="minEvictableIdleTimeMillis" value="5000" />
 
 
    <!--向资源池借用连接时是否做连接有效性检测(ping),
    无效连接会被移除 默认值 false 业务量很大时候建议设置为false(多一次ping的开销)-->
    <property name="testOnBorrow" value="false" />
    <!--是否开启空闲资源监测, 默认值 false, 建议值 true-->
    <property name="testWhileIdle" value="false" />
    <!--向资源池归还连接时是否做连接有效性检测(ping),
    无效连接会被移除 默认值 false 业务量很大时候建议设置为false(多一次ping的开销)。-->
    <property name="testOnReturn" value="false" />
    <!--是否开启jmx监控,可用于监控 默认值 true 建议开启,但应用本身也要开启-->
    <property name="jmxEnabled" value="true" />
    <property name="jmxNamePrefix" value="youyuan" />
    <!--当资源池用尽后,调用者是否要等待。只有当为true时,
    下面的maxWaitMillis才会生效 默认值true 建议使用默认值-->
    <property name="blockWhenExhausted" value="true" />
    <!--当资源池连接用尽后,调用者的最大等待时间(单位为毫秒) -1:表示永不超时 不建议使用默认值-->
    <property name="maxWaitMillis" value="1500" />
</bean>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值