键值对使用规范
规范一:key 的命名规范
- key 的命名规范,只有命名规范,才能提供可读性强、可维护性好的 key,方便日常管理。
- 对于业务名或业务数据名,可以使用相应的英文单词的首字母表示,(比如 user 用 u 表示,message 用 m),或者是用缩写表示(例如 unique visitor 使用 uv)。
规范二:避免使用 bigkey
Redis 是使用单线程读写数据,bigkey 的读写操作会阻塞线程,降低 Redis 的处理效率。所以,在应用 Redis 时,关于 value 的设计规范,非常重要的一点就是避免 bigkey。
规范三:使用高效序列化方法和压缩方法
Redis 中的字符串都是使用二进制安全的字节数组来保存的,所以,我们可以把业务数据序列化成二进制数据写入到 Redis 中。但是,不同的序列化方法,在序列化速度和数据序列化后的占用内存空间这两个方面,效果是不一样的。比如说,protostuff 和 kryo 这两种序列化方法,就要比 Java 内置的序列化方法(java-build-in-serializer)效率更高。
规范四:使用整数对象共享池
整数是常用的数据类型,Redis 内部维护了 0 到 9999 这 1 万个整数对象,并把这些整数作为一个共享池使用。
数据保存规范
规范一:使用 Redis 保存热数据
“好钢用在刀刃上”
规范二:不同的业务数据分实例存储
规范三:在数据保存时,要设置过期时间
对于 Redis 来说,内存是非常宝贵的资源,而且,Redis 通常用于保存热数据。热数据一般都有使用的时效性。所以,在数据保存时,我建议你根据业务使用数据的时长,设置数据的过期时间。不然的话,写入 Redis 的数据会一直占用内存,如果数据持续增多,就可能达到机器的内存上限,造成内存溢出,导致服务崩溃。
规范四:控制 Redis 实例的容量
Redis 单实例的内存大小都不要太大,根据我自己的经验值,建议你设置在 2~6GB 。这样一来,无论是 RDB 快照,还是主从集群进行数据同步,都能很快完成,不会阻塞正常请求的处理。
命令使用规范
规范一:线上禁用部分命令
- KEYS,按照键值对的 key 内容进行匹配,返回符合匹配条件的键值对,该命令需要对 Redis 的全局哈希表进行全表扫描,严重阻塞 Redis 主线程;
- FLUSHALL,删除 Redis 实例上的所有数据,如果数据量很大,会严重阻塞 Redis 主线程;
- FLUSHDB,删除当前数据库中的数据,如果数据量很大,同样会阻塞 Redis 主线程。
当然,你还可以使用其它命令替代这 3 个命令。
- 对于 KEYS 命令来说,你可以用 SCAN 命令代替 KEYS 命令,分批返回符合条件的键值对,避免造成主线程阻塞;
- 对于 FLUSHALL、FLUSHDB 命令来说,你可以加上 ASYNC 选项,让这两个命令使用后台线程异步删除数据,可以避免阻塞主线程。
规范二:慎用 MONITOR 命令
MONITOR 命令会把监控到的内容持续写入输出缓冲区。如果线上命令的操作很多,输出缓冲区很快就会溢出了,这就会对 Redis 性能造成影响,甚至引起服务崩溃。
规范三:慎用全量操作命令
对于集合类型的数据来说,如果想要获得集合中的所有元素,一般不建议使用全量操作的命令(例如 Hash 类型的 HGETALL、Set 类型的 SMEMBERS)。这些操作会对 Hash 和 Set 类型的底层数据结构进行全量扫描,如果集合类型数据较多的话,就会阻塞 Redis 主线程。
如果想要获得集合类型的全量数据,三个小建议。
- 第一个建议是,你可以使用 SSCAN、HSCAN 命令分批返回集合中的数据,减少对主线程的阻塞。
- 第二个建议是,你可以化整为零,把一个大的 Hash 集合拆分成多个小的 Hash 集合。这个操作对应到业务层,就是对业务数据进行拆分,按照时间、地域、用户 ID 等属性把一个大集合的业务数据拆分成多个小集合数据。例如,当你统计用户的访问情况时,就可以按照天的粒度,把每天的数据作为一个 Hash 集合。
- 最后一个建议是,如果集合类型保存的是业务数据的多个属性,而每次查询时,也需要返回这些属性,那么,你可以使用 String 类型,将这些属性序列化后保存,每次直接返回 String 数据就行,不用再对集合类型做全量扫描了