前言

Redis持久化功能一直是影响Redis性能的高发地,简单介绍一下持久化的问题定位和优化。

正文

当Redis做RDB或AOF重写时,一个必不可少的操作就是执行fork操作创 建子进程,对于大多数操作系统来说fork是个重量级错误。

虽然fork创建的 子进程不需要拷贝父进程的物理内存空间,但是会复制父进程的空间内存页表。

例如对于10GB的Redis进程,需要复制大约20MB的内存页表,因此fork 操作耗时跟进程总内存量息息相关,如果使用虚拟化技术,特别是Xen虚拟 机,fork操作会更耗时。

fork耗时问题定位:对于高流量的Redis实例OPS可达5万以上,如果fork 操作耗时在秒级别将拖慢Redis几万条命令执行,对线上应用延迟影响非常 明显。正常情况下fork耗时应该是每GB消耗20毫秒左右。可以在info stats统 计中查latest_fork_usec指标获取最近一次fork操作耗时,单位微秒。

如何改善fork操作的耗时:

1)优先使用物理机或者高效支持fork操作的虚拟化技术,避免使用 Xen。

2)控制Redis实例最大可用内存,fork耗时跟内存量成正比,线上建议 每个Redis实例内存控制在10GB以内。

3)合理配置Linux内存分配策略,避免物理内存不足导致fork失败

4)降低fork操作的频率,如适度放宽AOF自动触发时机,避免不必要 的全量复制等。

子进程负责AOF或者RDB文件的重写,它的运行过程主要涉及CPU、内存、硬盘三部分的消耗。

子进程开销监控和优化

子进程负责AOF或者RDB文件的重写,它的运行过程主要涉及CPU、内 存、硬盘三部分的消耗。

cpu

CPU开销分析。子进程负责把进程内的数据分批写入文件,这个过程 属于CPU密集操作,通常子进程对单核CPU利用率接近90%.

CPU消耗优化。Redis是CPU密集型服务,不要做绑定单核CPU操作。 由于子进程非常消耗CPU,会和父进程产生单核资源竞争。

不要和其他CPU密集型服务部署在一起,造成CPU过度竞争。

如果部署多个Redis实例,尽量保证同一时刻只有一个子进程执行重写工作.

内存

·内存消耗分析。子进程通过fork操作产生,占用内存大小等同于父进 程,理论上需要两倍的内存来完成持久化操作,但Linux有写时复制机制 (copy-on-write)。

父子进程会共享相同的物理内存页,当父进程处理写请 求时会把要修改的页创建副本,而子进程在fork操作过程中共享整个父进程内存快照。

·内存消耗监控。RDB重写时,Redis日志输出容如下:

* Background saving started by pid 7692 * DB saved on disk * RDB: 5 MB of memory used by copy-on-write * Background saving terminated with success

如果重写过程中存在内存修改操作,父进程负责创建所修改内存页的副 本,从日志中可以看出这部分内存消耗了5MB,可以等价认为RDB重写消耗 了5MB的内存。

AOF重写时,Redis日志输出容如下:

* Background append only file rewriting started by pid 8937
* AOF rewrite child asks to stop sending diffs.
* Parent agreed to stop sending diffs. Finalizing AOF... * Concatenating 0.00 MB of AOF diff received from parent.
* SYNC append only file rewrite performed
* AOF rewrite: 53 MB of memory used by copy-on-write
* Background AOF rewrite terminated with success
* Residual parent diff successfully flushed to the rewritten AOF (1.49 MB)
* Background AOF rewrite finished successfully

父进程维护页副本消耗同RDB重写过程类似,不同之处在于AOF重写需 要AOF重写缓冲区,因此根据以上日志可以预估内存消耗为: 53MB+1.49MB,也就是AOF重写时子进程消耗的内存量。

编写shell脚本根据Redis日志可快速定位子进程重写期间内存过度消耗情况。

内存消耗优化:

1)同CPU优化一样,如果部署多个Redis实例,尽量保证同一时刻只有一个子进程在工作

2)避免在大量写入时做子进程重写操作,这样将导致父进程维护大量 页副本,造成内存消耗。

Linux kernel在2.6.38内核增加了Transparent Huge Pages(THP),支持 huge page(2MB)的页分配,默认开启。

当开启时可以降低fork创建子进程 的速度,但执行fork之后,如果开启THP,复制页单位从原来4KB变为 2MB,会大幅增加重写期间父进程内存消耗。

建议设置“sudo echo never>/sys/kernel/mm/transparent_hugepage/enabled”关闭THP。

硬盘:

·硬盘开销分析。子进程主要职责是把AOF或者RDB文件写入硬盘持久化。势必造成硬盘写入压力。根据Redis重写AOF/RDB的数据量,结合系统 工具如sar、iostat、iotop等,可分析出重写期间硬盘负载情况。

·硬盘开销优化。优化方法如下:

a)不要和其他高硬盘负载的服务部署在一起。如:存储服务、消息队 列服务等。

b)AOF重写时会消耗大量硬盘IO,可以开启配置no-appendfsync-on- rewrite,默认关闭。表示在AOF重写期间不做fsync操作。

c)当开启AOF功能的Redis用于高流量写入场景时,如果使用普通机械 磁盘,写入吞吐一般在100MB/s左右,这时Redis实例的瓶颈主要在AOF同步硬盘上。

d)对于单机配置多个Redis实例的情况,可以配置不同实例分盘存储 AOF文件,分摊硬盘写入压力。

配置no-appendfsync-on-rewrite=yes时,在极端情况下可能丢失整个AOF 重写期间的数据,需要根据数据安全性决定是否配置。

当开启AOF持久化时,常用的同步硬盘的策略是everysec,用于平衡性能和数据安全性。对于这种方式,Redis使用另一条线程每秒执行fsync同步硬盘。当系统硬盘资源繁忙时,会造成Redis主线程阻塞.

如下图:

阻塞流程分析:

1)主线程负责写入AOF缓冲区。

2)AOF线程负责每秒执行一次同步磁盘操作,并记录最近一次同步时 间。

3)主线程负责对比上次AOF同步时间:

·如果距上次同步成功时间在2秒内,主线程直接返回。

·如果距上次同步成功时间超过2秒,主线程将会阻塞,直到同步操作完成。

通过对AOF阻塞流程可以发现两个问题:

1)everysec配置最多可能丢失2秒数据,不是1秒。

2)如果系统fsync缓慢,将会导致Redis主线程阻塞影响效率

AOF阻塞问题定位:

1)发生AOF阻塞时,Redis输出如下日志,用于记录AOF fsync阻塞导致 拖慢Redis服务的行为:

Asynchronous AOF fsync is taking too long (disk is busy). Writing the AOF buffer without waiting for fsync to complete, this may slow down Redis

2)每当发生AOF追加阻塞事件发生时,在info Persistence统计中, aof_delayed_fsync指标会累加,查看这个指标方便定位AOF阻塞问题。

3)AOF同步最多允许2秒的延迟,当延迟发生时说明硬盘存在高负载问 题,可以通过监控工具如iotop,定位消耗硬盘IO资源的进程。

多实例部署

Redis单线程架构导致无法充分利用CPU多核特性,通常的做法是在一 台机器上部署多个Redis实例。当多个实例开启AOF重写后,彼此之间会产 生对CPU和IO的竞争。本节主要介绍针对这种场景的分析和优化。

Redis在info Persistence中为我们提供了监控 子进程运行状况的度量指标:

1)外部程序定时轮询监控机器(machine)上所有Redis实例。

2)对于开启AOF的实例,查看(aof_current_size- aof_base_size)/aof_base_size确认增长率。

3)当增长率超过特定阈值(如100%),执行bgrewriteaof命令手动触发 当前实例的AOF重写。

4)运行期间循环检查aof_rewrite_in_progress和 aof_current_rewrite_time_sec指标,直到AOF重写结束。

5)确认实例AOF重写完成后,再检查其他实例并重复2)~4)步操作。 从而保证机器内每个Redis实例AOF重写串行化执行。

总结

1)Redis提供了两种持久化方式:RDB和AOF。

2)RDB使用一次性生成内存快照的方式,产生的文件紧凑压缩比更 高,因此读取RDB恢复速度更快。由于每次生成RDB开销较大,无法做到实 时持久化,一般用于数据冷备和复制传输。

3)save命令会阻塞主线程不建议使用,bgsave命令通过fork操作创建子 进程生成RDB避免阻塞。

4)AOF通过追加写命令到文件实现持久化,通过appendfsync参数可以 控制实时/秒级持久化。因为需要不断追加写命令,所以AOF文件体积逐渐 变大,需要定期执行重写操作来降低文件体积。

5)AOF重写可以通过auto-aof-rewrite-min-size和auto-aof-rewrite- percentage参数控制自动触发,也可以使用bgrewriteaof命令手动触发。

6)子进程执行期间使用copy-on-write机制与父进程共享内存,避免内 存消耗翻倍。AOF重写期间还需要维护重写缓冲区,保存新的写入命令避免 数据丢失。

7)持久化阻塞主线程场景有:fork阻塞和AOF追加阻塞。fork阻塞时间 跟内存量和系统有关,AOF追加阻塞说明硬盘资源紧张。

8)单机下部署多个实例时,为了防止出现多个子进程执行重写操作, 建议做隔离控制,避免CPU和IO资源竞争。

下一节,介绍redis的复制。

redis 简单整理——持久化的问题定位和优化[二十一]的更多相关文章

  1. Redis持久化——问题定位与优化(三)

    核心知识点: 1.fork操作 a.在RDB或AOF重写时,会执行fork操作创建子进程,fork操作是一个重量级操作. b.改善fork操作耗时的手段:避免使用Xen.配置Redis实例最大使用内存 ...

  2. 微服务 - Redis缓存 · 数据结构 · 持久化 · 分布式 · 高并发

    本篇内容基于 Redis v7.0 的阐述:官网:https://redis.io/ 本篇计划用 Docker 容器辅助部署,所以需要了解点 Docker 知识:官网:https://www.dock ...

  3. Redis知识整理

    Redis知识整理 转自:https://www.cnblogs.com/rjzheng/p/9096228.html 1.单线程模型 Redis客户端对服务端的每次调用都经历了发送命令,执行命令,返 ...

  4. Redis 知识 整理

    简介 安装 启动 注意事项 使用命令 通用命令 数据结构 字符串(string) 哈希(hash) 队列(list) 集合(set) 有序集合(zset) 位图(bitcount) 事务 订阅与发布 ...

  5. redis两种持久化的方法

    Redis是一种高级key-value数据库.它跟memcached类似,不过数据可以持久化,而且支持的数据类型很丰富.有字符串,链表,集 合和有序集合.支持在服务器端计算集合的并,交和补集(diff ...

  6. .NET Web开发技术简单整理

    在最初学习一些编程语言.一些编程技术的时候,做的更多的是如何使用该技术,如何更好的使用该技术解决问题,而没有去关注它的相关性.关注它的理论支持,这种学习技术的方式是短平快.其实工作中有时候也是这样,公 ...

  7. Redis提供的持久化机制(RDB和AOF)

    Redis提供的持久化机制 Redis是一种面向"key-value"类型数据的分布式NoSQL数据库系统,具有高性能.持久存储.适应高并发应用场景等优势.它虽然起步较晚,但发展却 ...

  8. 转载:.NET Web开发技术简单整理

    在最初学习一些编程语言.一些编程技术的时候,做的更多的是如何使用该技术,如何更好的使用该技术解决问题,而没有去关注它的相关性.关注它的理论支持,这种学习技术的方式是短平快.其实工作中有时候也是这样,公 ...

  9. redis 系列17 持久化 AOF

    一.概述 除了上篇介绍的RDB持久化功能之外,Redis还提供了AOF(Append Only File)持久化功能.与RDB保存数据库中的键值对来记录数据库状态不同,AOF是通过保存redis服务器 ...

  10. redis简介与持久化

    一 . redis简介 redis属于NoSQL学名(not only sql) 特点: 存储结构与mysql这一种关系型数据库完全不同,nosql存储的是key value形式 nosql有很多产品 ...

随机推荐

  1. Java 多线程------创建多线程的方式二:实现 Runnable接口 + 比较创建线程的两种方式:

    1 package com.bytezero.threadexer; 2 3 /** 4 * 5 * 创建多线程的方式二:实现 Runnable接口 6 * 1.创建一个实现了Runnable接口类 ...

  2. STM32 LwIP学习过程问题总结(一):LwIP ping不通,抓包发现ICMP校验和为0x0000

    一.问题 今天在将之前的STM32 LwIP1.4.1版本程序移植到2.1.2版本上时,发现ping不同,但是开发板有ICMP回复包,黄颜色警告checksum为0x0000.说明LwIP移植应该是没 ...

  3. class cl表示 汇聚 集合 ss表示 阴性 这里表示抽象

    class cl表示 汇聚 集合 ss表示 阴性 这里表示抽象

  4. Android 自定义View模板代码记录

    原文地址:Android 自定义View模板代码记录 - Stars-One的杂货小窝 每次写自定义View,需要重写3个构造方法,如果使用Android Studio直接创建,会导致View代码过多 ...

  5. 25_H.264编码

    本文主要介绍一种非常流行的视频编码:H.264. 计算一下:10秒钟1080p(1920x1080).30fps的YUV420P原始视频,需要占用多大的存储空间? (10 * 30) * (1920 ...

  6. NJOPT自控第三次积分赛--风力摆小结

    NJOPT自控第三次积分赛--风力摆小结 题目 题目就不放了,百度一搜就有,就是2015国赛的风力摆.. 方案 我们队采用的主控是STM32F401CCU6(科协传统),性能完全够用:姿态传感器采用的 ...

  7. 什么是3D可视化,为什么要使用3D可视化

    虽然许多设计师听说过为什么设计的可视化在他们的审批过程中是有益的,但并不是每个人都知道3D可视化到底是什么. 3D可视化与3D图形.3D渲染.计算机生成图像和其他术语同义使用.3D可视化是指使用计算机 ...

  8. springboot 低于 2.6 版本设置 SameSite=None,springboot 1.x set SameSite=none in embedded tomcat

    speingboot 使用自带的 tomcat 运行,设置 SameSite. springboot 过低的版本没有 SameSite 的属性设置,升级到 1.5.22 版本后,虽然 Rfc6265C ...

  9. Python简单程序设计(Unicode编码与字符间转换篇)

    如题: (误以为本题存在两者之间互相转换,后附上逆过程代码) 解题方式如下: 逆过程:

  10. 记录--实现一个鼠标框选的功能,要怎么实现和设计 api?

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 前言 前两年在一家做电商的公司做了一个需求:鼠标框选商品卡片,开始拖拽的时候合成一个然后改变位置,页面上有几千个所以还要结合虚拟列表.当时 ...