Redis使用规范的最佳实践:打造高性能与稳定性的关键法则

       

目录

一、键值对使用规范

        1.1 key 的命名规范

        1.2 避免使用 bigkey 

        1.3 使用高效的序列化方法和压缩算法

        1.4 使用整数对象共享池

二、数据保存使用规范

        2.1 使用 Redis 保存热数据

        2.2 不同的业务数据分实例存储

        2.3 在数据保存时,要设置过期时间

        2.4 控制 Redis 实例的容量

三、命令使用规范

        3.1 线上禁用部分命令

        3.2 慎用 monitor 命令

        3.3 慎用全量操作命令

        在工作中 Redis 的使用非常广泛,它是提升性能的利器,那怎么样才能发挥出 Redis 的最大优势呢?那就是良好的使用规范,遵循 Redis 使用规范不仅能提升系统性能、节省资源、保障数据安全,还能极大地改善系统的可维护性、可靠性和长期可持续发展能力。

        今天就来介绍一下 Redis 的使用规范问题。

一、键值对使用规范

        关于键值对的使用规范,主要注意下面两点:

  • key 命名规范,只有命名规范,才能提供可读性强、可维护性好的 Key,方便日常管理;
  • Value 涉及规范:包括避免使用 bigKey、选择高效序列化和压缩方法、使用整数对象共享池、数据类型选择等。

        1.1 key 的命名规范

        一个 Redis 实例默认可以支持 16 个数据库,我们可以把不同的业务数据分散保存到不同的数据库中。

        但是,在使用不同数据库时,客户端需要使用 SELECT 命令进行数据库切换,相当于增加了一个额外的操作。

        其实,我们可以通过合理命名 key,减少这个操作。具体的做法是,把业务名作为前缀,然后用冒号分隔,再加上具体的业务数据名。这样一来,我们可以通过 key 的前缀区分不同的业务数据,就不用在多个数据库间来回切换了。

        比如说,如果我们要统计网页的独立访客量,就可以用下面的代码设置 key,这就表示,这个数据对应的业务是统计 unique visitor(独立访客量),而且对应的页面编号是 1024。

        这里有一个地方需要注意一下。key 本身是字符串,底层的数据结构是 SDS。SDS 结构中会包含字符串长度、分配空间大小等元数据信息。从 Redis 3.2 版本开始,当 key 字符串的长度增加时,SDS 中的元数据也会占用更多内存空间。

        所以,我们在设置 key 的名称时,要注意控制 key 的长度。否则,如果 key 很长的话,就会消耗较多内存空间,而且,SDS 元数据也会额外消耗一定的内存空间。

        SDS 结构中的字符串长度和元数据大小的对应关系如下表所示:

字符串大小(字节)SDS结构元数据大小(字节)
1 ~ 2^5 - 11
2^5 ~ 2^8 -1 3
2^8 ~ 2^16 -1 5
2^16 ~ 2^32 -1 9
2^32 ~ 2^64 -1 11

        为了减少 key 占用的内存空间,对于业务名或业务数据名,可以使用相应的英文单词的首字母表示,(比如 user 用 u 表示,message 用 m),或者是用缩写表示(例如 unique visitor 使用 uv)。

        1.2 避免使用 bigkey 

        Redis 使用单线程读写数据,bigkey 的读写操作会阻塞线程,降低 Redis 的处理效率。所以在使用 Redis 时关于 value 的设计规范,非常重要的一点就是避免使用 bigkey。

        bigkey 通常有两种情况:

  1. 键值对的值大小本身就很大:例如 value 为几 MB 的 String 类型的数据,为了避免 String 类型的 bigkey,在业务层,要尽量把 String 类型的数据压缩到 10KB 一下;
  2. 键值对的值是集合类型:集合元素个数非常多,例如包含上百万个元素的 Hash 集合类型数据。为了避免集合类型的 bigkey,建议的设计规范是,尽量把集合类型的元素个数控制在 1 万一下。

        当然,这些建议只是为了尽量避免 bigkey,如果业务层的 String 类型数据确实很大,可以通过数据压缩来减小数据大小。如果集合元素的确很多,可以将一个大集合拆分成多个小集合来保存。

        这里,还有个地方需要注意下,Redis 的 4 种集合类型 List、Hash、Set 和 Sorted Set,在集合元素个数小于一定的阈值时,会使用内存紧凑型的底层数据结构进行保存,从而节省内存。例如,假设 Hash 集合的 hash-max-ziplist-entries 配置项是 1000,如果 Hash 集合元素个数不超过 1000,就会使用 ziplist 保存数据。

        紧凑型数据结构虽然可以节省内存,但是会在一定程度上导致数据的读写性能下降。所以,如果业务应用更加需要保持高性能访问,而不是节省内存的话,在不会导致 bigkey 的前提下,就不用刻意控制集合元素个数了。

        1.3 使用高效的序列化方法和压缩算法

       为了节省内存,除了采用紧凑型数据结构以外,我们还可以遵循两个使用规范,分别是使用高效的序列化方法和压缩方法,这样可以减少 value 的大小。

        Redis 中的字符串都是使用二进制安全的字节数组来保存的,所以,我们可以把业务数据序列化成二进制数据写入到 Redis 中。

        但是,不同的序列化方法,在序列化速度和数据序列化后的占用内存空间这两个方面,效果是不一样的。比如说,protostuff 和 kryo 这两种序列化方法,就要比 Java 内置的序列化方法(java-build-in-serializer)效率更高。

        此外,业务应用有时会使用字符串形式的 XML 和 JSON 格式保存数据。这样做的好处是,这两种格式的可读性好,便于调试,不同的开发语言都支持这两种格式的解析。缺点在于,XML 和 JSON 格式的数据占用的内存空间比较大。为了避免数据占用过大的内存空间,建议使用压缩工具(例如 snappy 或 gzip),把数据压缩后再写入 Redis,这样就可以节省内存空间了。

        1.4 使用整数对象共享池

        整数是常用的数据类型,Redis 内部维护了0到9999这1万个整数对象,并把这些整数作为一个共享池使用。

        换句话说,如果一个键值对中有0到9999范围的整数,Redis 就不会为这个键值对专门创建整数对象了,而是会复用对象池中的整数对象。

        这样一来,即使大量键值对保存了0到9999范围内的整数,在 Redis 实例中其实只保存了一份整数对象,可以节省空间。

        基于这个特点,在满足业务数据需求的前提下,能用整数时就尽量用整数,这样可以节省实例内存。

        那什么时候不能用整数对象共享池呢?主要有两种情况。

  • 如果 Redis 中设置了 maxmemory,而且启用了 LRU 策略(allkeys-lru 或 volatitle-lru),那么整数对象共享池就无法使用了。因为,LRU 策略需要统计每个键值对的使用时间,如果不同的键值对都共享使用一个整数对象,LRU 策略就无法进行统计了。
  • 如果集合类型采用了 ziplist 编码,而集合元素是整数,这时也不能使用共享池。因为 ziplist 使用了紧凑型内存结构,判断整数对象的共享情况时效率低。

二、数据保存使用规范

        2.1 使用 Redis 保存热数据

        为了提供高性能访问,Redis 是把所有数据保存到内存中的。

        虽然 Redis 支持使用 RDB 快照和 AOF 日志持久化保存数据,但是,这两个机制都是用来提供数据可靠性保证的,并不是用来扩充数据容量的。而且,内存成本本身就比较高,如果把业务数据都保存在 Redis 中,会带来较大的内存成本压力。

        所以,一般来说,在实际应用 Redis 时,我们会更多地把它作为缓存保存热数据,这样既可以充分利用 Redis 的高性能特性,还可以把宝贵的内存资源用在服务热数据上。

        2.2 不同的业务数据分实例存储

        虽然我们可以使用 key 的前缀把不同业务的数据区分开,但是,如果所有业务的数据量都很大,而且访问特征也不一样,我们把这些数据保存在同一个实例上时,这些数据的操作就会相互干扰。

        你可以想象这样一个场景:假如数据采集业务使用 Redis 保存数据时,以写操作为主,而用户统计业务使用 Redis 时,是以读查询为主,如果这两个业务数据混在一起保存,读写操作相互干扰,肯定会导致业务响应变慢。

        建议把不同的业务数据放到不同的 Redis 实例中。这样一来,既可以避免单实例的内存使用量过大,也可以避免不同业务的操作相互干扰。

        2.3 在数据保存时,要设置过期时间

        对于 Redis 来说,内存是非常宝贵的资源,而且,Redis 通常用于保存热数据。热数据一般都有使用的时效性。所以,在数据保存时,建议根据业务使用数据的时长,设置数据的过期时间。不然的话,写入 Redis 的数据会一直占用内存,如果数据持续增多,就可能达到机器的内存上限,造成内存溢出,导致服务崩溃。

        2.4 控制 Redis 实例的容量

        Redis 单实例的内存大小都不要太大,根据经验值,建议设置在 2~6GB 。这样一来,无论是 RDB 快照,还是主从集群进行数据同步,都能很快完成,不会阻塞正常请求的处理。

三、命令使用规范

        3.1 线上禁用部分命令

        Redis 是单线程处理请求操作,如果我们执行一些涉及大量操作、耗时长的命令,就会严重阻塞主线程,导致其它请求无法得到正常处理,这类命令主要有 3 种

  1. keys命令:按照键值对的 key 内容进行匹配,返回匹配条件的键值对,该命令需要对 Redis 全局的 hash 表进行全表扫描,严重阻塞 Redis 主线程
  2. flushall命令:删除 Redis 实例上的所有数据,如果数据量很大,会严重阻塞 Redis 主线程
  3. flushdb命令:删除当前数据库中的数据,如果数据量很大,同样会阻塞 Redis 主线程

        所以,我们在线上应用 Redis 时,就需要禁用这些命令。

        3.2 慎用 monitor 命令

        Redis 的 MONITOR 命令在执行后,会持续输出监测到的各个命令操作,所以,我们通常会用 MONITOR 命令返回的结果,检查命令的执行情况。

        但是,MONITOR 命令会把监控到的内容持续写入输出缓冲区。如果线上命令的操作很多,输出缓冲区很快就会溢出了,这就会对 Redis 性能造成影响,甚至引起服务崩溃。

        所以,除非十分需要监测某些命令的执行(例如,Redis 性能突然变慢,我们想查看下客户端执行了哪些命令),你可以偶尔在短时间内使用下 MONITOR 命令,否则,我建议你不要使用 MONITOR 命令。

        3.3 慎用全量操作命令

        对于集合类型的数据来说,如果想要获得集合中的所有元素,一般不建议使用全量操作的命令(例如 Hash 类型的 HGETALL、Set 类型的 SMEMBERS)。这些操作会对 Hash 和 Set 类型的底层数据结构进行全量扫描,如果集合类型数据较多的话,就会阻塞 Redis 主线程。

        综上所述,遵循Redis使用规范不仅能提升系统性能、节省资源、保障数据安全,还能极大地改善系统的可维护性、可靠性和长期可持续发展能力。

往期经典推荐

实时数据传输的新里程——Server-Sent Events(SSE)消息推送技术-CSDN博客

Redis 6.0进化之路:关键新特性详解_redis 6.0 特性-CSDN博客

决胜高并发战场:Redis并发访问控制与实战解析-CSDN博客

Redis持久化(AOF、RDB)用到的写时复制到底是什么_rdb写时复制如何实现的-CSDN博客

透视Redis大key背后的I/O挑战-CSDN博客

Redis高性能的秘密原来在这,超越想象-CSDN博客

  • 25
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

超越不平凡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值