前言
Redis
作为高性能的内存数据库,在大数据量的情况下也会遇到性能瓶颈,日常开发中只有时刻谨记优化铁则,才能使得Redis性能发挥到极致。
本文将会介绍十三条性能优化军规,开发过程中只要按照执行,性能必能质的飞跃。
1. 避免慢查询命令
慢查询命令指的是执行较慢的命令,Redis自身提供了许多的命令,并不是所有的命令都慢,这和命令的操作复杂度有关,因此必须知道Redis不同命令的复杂度。
如说,Value
类型为 String
时,GET/SET
操作主要就是操作 Redis 的哈希表索引。这个操作复杂度基本是固定的,即 O(1)
。但是,当 Value
类型为 Set
时,SORT
、SUNION/SMEMBERS
操作复杂度分别为 O(N+M*log(M))
和 O(N)
。其中,N
为 Set
中的元素个数,M
为 SORT
操作返回的元素个数。这个复杂度就增加了很多。Redis 官方文档中对每个命令的复杂度都有介绍,当你需要了解某个命令的复杂度时,可以直接查询。
当你发现 Redis 性能变慢时,可以通过 Redis 日志,或者是 latency monitor
工具,查询变慢的请求,根据请求对应的具体命令以及官方文档,确认下是否采用了复杂度高的慢查询命令。
如果确实存在大量的慢查询命令,建议如下两种方式:
-
用其他高效的命令代替:比如说,如果你需要返回一个 SET
中的所有成员时,不要使用SMEMBERS
命令,而是要使用SSCAN
多次迭代返回,避免一次返回大量数据,造成线程阻塞。 -
当你需要执行排序、交集、并集操作时,可以在客户端完成,而不要用 SORT
、SUNION、SINTER
这些命令,以免拖慢 Redis 实例。
2. 生产环境禁用keys命令
keys
这个命令是最容易忽略的慢查询命令,因为keys命令需要遍历存储的键值对,所以操作延时很高,在生产环境使用很可能导致Redis阻塞;因此不建议在生产环境中使用keys
命令。
3. keys需要设置过期时间
4. 禁止批量的给keys设置相同的过期时间
100
毫秒会删除一些过期
key
,具体的算法如下:
-
采样 ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP
个数的 key,并将其中过期的 key 全部删除; -
如果超过 25%
的key
过期了,则重复删除的过程,直到过期key
的比例降至 `25%`` 以下。
ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP
是 Redis 的一个参数,默认是
20
,那么,一秒内基本有
200
个过期
key
会被删除。这一策略对清除过期 key、释放内存空间很有帮助。如果每秒钟删除 200 个过期 key,并不会对 Redis 造成太大影响。
Redis 4.0
后可以用异步线程机制来减少阻塞影响)。所以,一旦该条件触发,Redis 的线程就会一直执行删除,这样一来,就没办法正常服务其他的键值操作了,就会进一步引起其他键值操作的延迟增加,Redis 就会变慢。
keys
设置过期时间。
5. 谨慎选择数据结构
string
、
hash
、
list
、
set
、
zset
(sorted set)。可以发现,大多数场景下使用 string 都可以去解决问题。但是,这并不一定是最优的选择。下面,简单说明下它们各自的适用场景:
-
string
:单个的缓存结果,不与其他的 KV 之间有联系 -
hash
:一个 Object 包含有很多属性,且这些属性都需要单独存储。注意:这种情况不要使用 string,因为 string 会占据更多的内存 -
list
:一个 Object 包含很多数据,且这些数据允许重复、要求有顺序性 -
set
:一个 Object 包含很多数据,不要求数据有顺序,但是不允许重复 -
zset
:一个 Object 包含很多数据,且这些数据自身还包含一个权重值,可以利用这个权重值来排序
-
HyperLogLog
:适合用于基数
统计,比如PV,UV的统计,存在误差
问题,不适合精确统计。 -
BitMap
:适合二值状态
的统计,比如签到打卡,要么打卡了,要么未打卡。
6. 检查持久化策略
-
AOF日志
:一种采用文件追加的方式将命令记录在日志中的策略,针对同步和异步追加还提供了三个配置项,有兴趣的可以查看官方文档。 -
RDB快照
:以快照的方式,将某一个时刻的内存数据,以二进制的方式写入磁盘。 -
AOF
和RDB
混用:Redis4.0新增的方式,为了采用两种方式各自的优点,在RDB快照的时间段内使用的AOF日志记录这段时间的操作的命令,这样一旦发生宕机,将不会丢失两段快照中间的数据。
7. 采用高速的固态硬盘作为日志写入设备
8. 使用物理机而非虚拟机
基线性能
:
./redis-cli --intrinsic-latency 120
9. 增加机器内存或者使用Redis集群
Swap
。
swap
是操作系统里将内存数据在内存和磁盘间来回换入和换出的机制,涉及到磁盘的读写,所以,一旦触发
swap
,无论是被换入数据的进程,还是被换出数据的进程,其性能都会受到慢速磁盘读写的影响。
swap
的影响,而导致性能变慢。
swap
被触发了,Redis 的请求操作需要等到磁盘数据读写完成才行。而且,和我刚才说的
AOF
日志文件读写使用
fsync
线程不同,
swap
触发后影响的是 Redis 主
IO
线程,这会极大地增加 Redis 的响应时间。
Swap
,提高性能。
10. 使用 Pipeline 批量操作数据
Pipeline
(管道技术) 是客户端提供的一种批处理技术,用于一次处理多个 Redis 命令,从而提高整个交互的性能。
11. 客户端使用优化
Pipeline
的技术外,还需要注意要尽量使用 Redis 连接池,而不是频繁创建销毁 Redis 连接,这样就可以减少网络传输次数和减少了非必要调用指令。
12. 使用分布式架构来增加读写速度
-
主从同步 -
哨兵模式 -
Redis Cluster
集群
Redis Cluster
是
Redis 3.0
正式推出的,Redis 集群是通过将数据分散存储到多个节点上,来平衡各个节点的负载压力。
Redis Cluster
采用虚拟哈希槽分区,所有的键根据哈希函数映射到
0 ~ 16383
整数槽内,计算公式:
slot = CRC16(key) & 16383
,每一个节点负责维护一部分槽以及槽所映射的键值数据。这样 Redis 就可以把读写压力从一台服务器,分散给多台服务器了,因此性能会有很大的提升。
Redis Cluster
应该是首选的实现方案,它可以把读写压力自动的分担给更多的服务器,并且拥有自动容灾的能力。
13. 避免内存碎片
INFO memory
可以查看内存的使用信息,如下:
INFO memory
# Memory
used_memory:1073741736
used_memory_human:1024.00M
used_memory_rss:1997159792
used_memory_rss_human:1.86G
…
mem_fragmentation_ratio:1.86
mem_fragmentation_ratio
的指标,它表示的就是 Redis 当前的内存碎片率。那么,这个碎片率是怎么计算的呢?其实,就是上面的命令中的两个指标
used_memory_rss
和
used_memory
相除的结果。
mem_fragmentation_ratio = used_memory_rss/ used_memory
used_memory_rss
是操作系统实际分配给 Redis 的物理内存空间,里面就包含了碎片;而
used_memory
是 Redis 为了保存数据实际申请使用的空间。
-
mem_fragmentation_ratio
大于 1 但小于 1.5。这种情况是合理的。这是因为,刚才我介绍的那些因素是难以避免的。毕竟,内因的内存分配器是一定要使用的,分配策略都是通用的,不会轻易修改;而外因由 Redis 负载决定,也无法限制。所以,存在内存碎片也是正常的。 -
mem_fragmentation_ratio
大于 1.5 。这表明内存碎片率已经超过了 50%。一般情况下,这个时候,我们就需要采取一些措施来降低内存碎片率了。
有道无术,术可成;有术无道,止于术
欢迎大家关注Java之道公众号
好文章,我在看❤️
本文分享自微信公众号 - Hollis(hollischuang)。
如有侵权,请联系 [email protected] 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。