一.影响Redis快慢的原因之一------文件系统和操作系统
原因:
- Redis会持久化保存数据到磁盘,这个过程要依赖文件系统完成。
- 文件系统将数据写回磁盘的机制,将会直接影响到Redis持久化的效率。
- 持久化的过程中,Redis也在接收其他请求,持久化机的效率高低又会影响到Redis处理请求的性能。
- 在持久化的过程中,Redis 也还在接收其他请求,持久化的效率高低又会影响到 Redis 处理请求的性能。
操作系统内存机制
- Redis是内存数据库,内存操作非常频繁,所以操作系统的内存机制会直接影响到Redis的处理效率。
- Redis内存不够 用了,操作系统会启动 swap机制,直接拖慢Redis。
文件系统AOF文件模式
Redis是个内存 数据库,为什么它的性能还和文件系统有关呢?
AOF写回策略 | 执行的系统调用 | no | 调用 write写日志文件,由操作系统周期性的讲日志写回磁盘 | everysec | 每秒调用一次fsync,将日志写回磁盘 | always | 每执行一次操作,就调用fsync将日志写回磁盘 |
- 当写回策略配置为everysec时,redis会使用后台子线程异步完成fsync操作。
- always策略,Redis需要确保每个操作记录日志都写回磁盘,如果用后台子线程异步完成,主线程就无法及时知道每个操作是否完成,因此always策略并不使用后台子线程来执行。
- 使用 AOF日志时,为了避免日志文件不断增大,Redis会执行AOF重写,生成缩小的新AOF日志文件。重写本身需要很长时间,所以用子线程重写。
风险点:
- AOF重写会对磁盘进行大量IO操作,同时,fsync有需要等到数据写回磁盘后才能返回,所以AOF重写的压力比较大时,就会导致fysnc被阻塞。虽然fsync是由于、后台子线程负责执行,但是主线程会监控fsync执行进度。
- 当主线程使用后台子线程执行了 一次fsync,需要把新接收的操作记录写回磁盘时,如果主线程发现了上一次fsync还没执行完,那么他就会阻塞,所以fsync 频繁阻塞的话(比如 AOF 重写占用了大量的磁盘 IO 带宽)主线程也会阻塞,导致 Redis 性能变慢。
由于fsync后台子进程和AOF重写子进程的存在,主要IO线程一般不会被阻塞。如果在重写日志时,AOF重写子进程的写入量比较大,fsync线程也会被阻塞,进而阻塞主线程,导致延迟增加。
- ? ? 如果AOF写回策略使用everysec或always配置,请先确认下业务方对数据可靠性的要求,明确是否需要每一秒或每一个操作都记日志。
- ? ? 如果业务应用对延迟非常敏感,但是同时允许一定量的数据丢失,那么可以把配置项no-appendfsync-rewirte设置为yes
no-appendfsync-on-rewrite yes
- ?这个配置项设置为yes时,表示再进行AOF重写时,不进行fsync操作。也就是说,Redis实例把写命令写到内存后,不调用后台线程进行faync操作,就可以直接返回了。
- 如果此时实例发生宕机,就会导致数据丢失。反之,如果这个配置项设置为no,在AOF重写时,Redis实例仍然会调用后台线程进行fsync操作,这会给实例带来阻塞。
如果确实需要高性能,同时也需要高可靠性数据保证,我建议你采用高速固态硬盘作为AOF日志写入设备。
- 高速固态盘的带宽和并发度比传统机械硬盘的高出10倍以上。
- 在AOF重写和fsync后台线程同时执行时,固态硬盘可以提供较为充足的磁盘IO资源,让AOF重写和fsync后台线程的磁盘IO资源竞争减少,从而降低对Redis的性能影响。
操作系统:swap
- 内存swap是操作系统里将内存数据在内存和磁盘间来回换入和换出的机制,涉及到磁盘的读写 ,所以,一旦触发swap,无论是被换入数据的进程买还是被换出数据的进程,其性能都会受到慢速磁盘读写的影响。
- Redis是内存数据库,内存使用量大,如果没有控制 好内存的使用量,或者和其他内存需求量大的应用一起运行,就可能 受到swap的影响进而导致性能变慢。
- 正常情况下,Redis的操作是直接通过访问内存就能完成的,一旦swap被触发了,Redis的请求操作需要等到磁盘数据读写完成才行,极大增加Redis响应时间。
触发SWAP的原因
? ? 触发swap的原因主要是物理机器内存不足
- Redis实例自身使用大量内存,导致物理机器的可用内存不足;
- 和Redis实例在同一机器上运行的其他进程,在大量文件读写操作,文件读写本身会占用系统内存,这回导致分配给Redis实例的内存量变少,进而触发Redis发生swap。
增加机器内存或者使用Redis集群。
操作系统:内存大页
内存大页机制(Transparent Huge Page, THP)
- Linux内核支持内存大页机制,该机制支持2MB大小的内存页分配,而常规的内存页分配是按照4KB的粒度来执行的。
- 虽然内存大页给Redis带来内存分配方面的收益
- 但是Redis为了提供数据可靠性的保证,需要将数据做持久化保存。这个写入的过程由额外的线程执行,所以此时 Redis主线程仍然可以接收客户端的写请求。
- 客户端的写请求可能会修改正在进行持久化的数据。
- Redis会采用写时复制的机制,一旦有数据要修改,Redis并不会直接修改内存中的数据,而是将这些数据拷贝一份,然后再进行修改。
存在问题:
- 几十客户端请求只修改100B的数据,Redis也需要拷贝2MB的大页
- 如果是常规内存页机制,只用拷贝4KB。
- 当客户端请求修改或者写入数据较多时候,内存大页机制将会导致大量的拷贝,这就会影响Redis的访存操作,最终导致性能变慢。
解决方案:
??关闭内存大页
Redis变慢9步法
- 获取Redis实例在当前环境下的基线性能。
- 查看是否使用 慢查询命令 ,如果已经使用就用其他命令 替代慢查询命令 ,将聚合计算放在客户端中。
- 查看是否对过期key设置相同过期时间,通过过期时间加上随机数的方式避免同时删除。
- 查看是否存在bigkey,对于bigkey删除操作,4.0以上 异步线程机制减少主线程阻塞,4.0以前版本SCAN命令迭代删除,bigkey 的集合查询和聚合操作,可以使用 SCAN 命令在客户端完成。
- Redis AOF配置级别,如果需要高性能也允许数据丢失,no-appendfsync-on-rewrite 设置为 yes,避免 AOF 重写和 fsync 竞争磁盘 IO 资源,如果既要高性能又要高可靠性,最好使用高速固态盘作为 AOF 日志的写入盘。
- Redis 实例的内存使用是否过大?发生 swap 了吗?如果是的话,就增加机器内存,或者是使用 Redis 集群,分摊单机 Redis 的键值对数量和内存压力。同时,要避免出现 Redis 和其他内存需求大的应用共享机器的情况。
- 在 Redis 实例的运行环境中,是否启用了透明大页机制?如果是的话,直接关闭内存大页机制就行了。
- 是否运行了 Redis 主从集群?如果是的话,把主库实例的数据量大小控制在 2~4GB,以免主从复制时,从库因加载大的 RDB 文件而阻塞。
- 是否使用了多核 CPU 或 NUMA 架构的机器运行 Redis 实例?使用多核 CPU 时,可以给 Redis 实例绑定物理核;使用 NUMA 架构时,注意把 Redis 实例和网络中断处理程序运行在同一个 CPU Socket 上。
|