首页 Java知识正文

首长,Redis 性能优化十三条军规立好了,请过目~

admin Java知识 2021-01-18 70 0



  • 前言

    Redis作为高性能的内存数据库,在大数据量的情况下也会遇到性能瓶颈,日常开发中只有时刻谨记优化铁则,才能使得Redis性能发挥到极致。

    本文将会介绍十三条性能优化军规,开发过程中只要按照执行,性能必能质的飞跃。

    1. 避免慢查询命令

    慢查询命令指的是执行较慢的命令,Redis自身提供了许多的命令,并不是所有的命令都慢,这和命令的操作复杂度有关,因此必须知道Redis不同命令的复杂度。

    如说,Value 类型为 String 时,GET/SET 操作主要就是操作 Redis 的哈希表索引。这个操作复杂度基本是固定的,即 O(1)。但是,当 Value 类型为 Set 时,SORTSUNION/SMEMBERS 操作复杂度分别为 O(N+M*log(M))O(N)。其中,NSet 中的元素个数,MSORT 操作返回的元素个数。这个复杂度就增加了很多。Redis 官方文档中对每个命令的复杂度都有介绍,当你需要了解某个命令的复杂度时,可以直接查询。

    当你发现 Redis 性能变慢时,可以通过 Redis 日志,或者是 latency monitor 工具,查询变慢的请求,根据请求对应的具体命令以及官方文档,确认下是否采用了复杂度高的慢查询命令。

    如果确实存在大量的慢查询命令,建议如下两种方式:

    2. 生产环境禁用keys命令

    keys这个命令是最容易忽略的慢查询命令,因为keys命令需要遍历存储的键值对,所以操作延时很高,在生产环境使用很可能导致Redis阻塞;因此不建议在生产环境中使用keys命令

    3. keys需要设置过期时间

    Redis作为内存数据库,一切的数据都是在内存中,一旦内存占用过大则会大大影响性能,因此需要对有时间限制的数据需要设置过期时间,这样Redis能够定时的删除过期的数据。

    4. 禁止批量的给keys设置相同的过期时间

    默认情况下,Redis 每 100 毫秒会删除一些过期 key,具体的算法如下:

    ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP 是 Redis 的一个参数,默认是 20,那么,一秒内基本有 200 个过期 key 会被删除。这一策略对清除过期 key、释放内存空间很有帮助。如果每秒钟删除 200 个过期 key,并不会对 Redis 造成太大影响。

    但是,如果触发了上面这个算法的第二条,Redis 就会一直删除以释放内存空间。注意,删除操作是阻塞的(Redis 4.0 后可以用异步线程机制来减少阻塞影响)。所以,一旦该条件触发,Redis 的线程就会一直执行删除,这样一来,就没办法正常服务其他的键值操作了,就会进一步引起其他键值操作的延迟增加,Redis 就会变慢。

    频繁使用带有相同时间参数的 EXPIREAT 命令设置过期 key 将会触发算法第二条,这就会导致在一秒内存在大量的keys过期。

    因此开发中一定要禁止批量的给keys设置过期时间。

    5. 谨慎选择数据结构

    Redis 常用的数据结构一共有五种:stringhashlistsetzset(sorted set)。可以发现,大多数场景下使用 string 都可以去解决问题。但是,这并不一定是最优的选择。下面,简单说明下它们各自的适用场景:

    另外Redis还提供了几种的扩展类型,如下:

    6. 检查持久化策略

    Redis4.0之后使用了如下三种持久化策略:

    由于写入磁盘有IO性能瓶颈,因此不是将Redis作为数据库的话(可以从后端恢复),建议禁用持久化或者调整持久化策略。

    7. 采用高速的固态硬盘作为日志写入设备

    由于AOF日志的重写对磁盘的压力较大,很可能会阻塞,如果需要使用到持久化,建议使用高速的固态硬盘作为日志写入设备。

    8. 使用物理机而非虚拟机

    由于虚拟机增加了虚拟化软件层,与物理机相比,虚拟机本身就存在性能的开销,可以使用如下命令来分别测试下物理机和虚拟机的基线性能

    ./redis-cli --intrinsic-latency 120

    测试结果可以知道,使用物理机的基线性能明显比虚拟机的基线性能更好。

    9. 增加机器内存或者使用Redis集群

    物理机器的内存不足将会导致操作系统内存的Swap

    内存 swap 是操作系统里将内存数据在内存和磁盘间来回换入和换出的机制,涉及到磁盘的读写,所以,一旦触发 swap,无论是被换入数据的进程,还是被换出数据的进程,其性能都会受到慢速磁盘读写的影响。

    Redis 是内存数据库,内存使用量大,如果没有控制好内存的使用量,或者和其他内存需求大的应用一起运行了,就可能受到 swap 的影响,而导致性能变慢

    这一点对于 Redis 内存数据库而言,显得更为重要:正常情况下,Redis 的操作是直接通过访问内存就能完成,一旦 swap 被触发了,Redis 的请求操作需要等到磁盘数据读写完成才行。而且,和我刚才说的 AOF 日志文件读写使用 fsync 线程不同,swap 触发后影响的是 Redis 主 IO 线程,这会极大地增加 Redis 的响应时间。

    因此增加机器的内存 或者使用Redis集群 能够有效的解决操作系统内存的Swap,提高性能。

    10. 使用 Pipeline 批量操作数据

    Pipeline (管道技术) 是客户端提供的一种批处理技术,用于一次处理多个 Redis 命令,从而提高整个交互的性能。

    11. 客户端使用优化

    在客户端的使用上我们除了要尽量使用 Pipeline 的技术外,还需要注意要尽量使用 Redis 连接池,而不是频繁创建销毁 Redis 连接,这样就可以减少网络传输次数和减少了非必要调用指令。

    12. 使用分布式架构来增加读写速度

    Redis 分布式架构有三个重要的手段:

    使用主从同步功能我们可以把写入放到主库上执行,把读功能转移到从服务上,因此就可以在单位时间内处理更多的请求,从而提升的 Redis 整体的运行速度。

    而哨兵模式是对于主从功能的升级,但当主节点奔溃之后,无需人工干预就能自动恢复 Redis 的正常使用。

    Redis ClusterRedis 3.0 正式推出的,Redis 集群是通过将数据分散存储到多个节点上,来平衡各个节点的负载压力。

    Redis Cluster 采用虚拟哈希槽分区,所有的键根据哈希函数映射到 0 ~ 16383整数槽内,计算公式:slot = CRC16(key) & 16383,每一个节点负责维护一部分槽以及槽所映射的键值数据。这样 Redis 就可以把读写压力从一台服务器,分散给多台服务器了,因此性能会有很大的提升。

    在这三个功能中,我们只需要使用一个就行了,毫无疑问 Redis Cluster 应该是首选的实现方案,它可以把读写压力自动的分担给更多的服务器,并且拥有自动容灾的能力。

    13. 避免内存碎片

    频繁的新增修改会导致内存碎片的增多,因此需要时刻的清理内存碎片。

    Redis提供了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_rssused_memory 相除的结果。

    mem_fragmentation_ratio = used_memory_rss/ used_memory

    used_memory_rss 是操作系统实际分配给 Redis 的物理内存空间,里面就包含了碎片;而 used_memory 是 Redis 为了保存数据实际申请使用的空间。

    那么,知道了这个指标,我们该如何使用呢?在这儿,我提供一些经验阈值:

    一旦内存碎片率过高了,此时就应该采用手段清理内存碎片了,具体如何清理,参考文章:Redis清理内存碎片

    总结

    本文着重介绍了13条性能优化军规,在开发过程中还是需要针对性的具体问题具体分析,希望作者这篇文章能够帮助到你。



    版权声明

    本文仅代表作者观点,不代表本站立场。
    本文系作者授权发表,未经许可,不得转载。

    评论