Redis Note(五)性能优化

(五)redis性能问题

A. redis客户端

  • redis客户端通信
    • redis新版本对于网络请求进行多线程处理,收到请求后redis实际处理数据依然为单线程模式。
    • redis客户端同服务端间的通信基于tcp协议。
  • jedis客户端
    • 开发阶段将jedis的jar包加入project中,或使用maven添加依赖。
    • jedis直连模式
      • 是默认方式,但每次使用都会新建tcp连接。
      • jedis直连简单方便,但存在对象线程不安全的问题,适用于少量连接的情况。
    • jedisPool模式
      • 生产多采用该方式,预先生成几个jedis对象于pool中,这些对象保持长连接状态。
      • 具体使用时,用getJedisFromPool方法获取对象,再完成后续实际操作。
      • 向jedisPool借用jedis对象是本地操作,并无网络开销。控制pool的大小可以有效确定并发的压力,远小于新建tcp的方式。

B. redis内存理解

  • 概述:redis所有实时使用的数据都存储于内存中,持久化文件用于备份恢复。本小节九内存消耗、内存管理 & 优化进行阐述分析。

1. 内存消耗

  • 内存使用统计

    • info memory:查看内存相关指标,重点需要关注的是used_memory_rss, used_memory & 它们的比值mem_fragmentation_ratio

    • 阈值注意点

      • mem_fragmentation_ratio > 1时,说明redis占用OS的内存并非全是数据存储消耗的,是内存碎片消耗的,两者相差很大则说明碎片率很严重
      • mem_fragmentation_ratio < 1时,说明内存不足时OS进行swap,让硬盘临时替代成内存给redis使用,这导致redis性能急剧下降
    • 核心指标说明

      属性名 属性说明
      used_memory_human used_memory可读显示
      used_memory_rss 操作系统下,redis进程占用的物理内存总量
      mem_fragmentation_ratio used_memory_rss / used_memory,大于1就有碎片;小于1则说明有swap给redis用
  • 内存消耗分类

    • 自身内存:redis空进程自身消耗内存很少,通常used_memory_rss在3M左右,used_memory则约800kb。
    • 对象内存:最主要消耗内存的部分,存储用户所有数据。一个对象由key和value两部分组成。
    • 缓冲内存:缓冲内存包括客户端缓冲、复制积压缓冲区 & AOF缓冲区。在客户端连接数过大时,可能会造成redis内存飙升。
    • 内存碎片:redis默认内存分配器是jemalloc,一般采用固定范围的内存块分配,将内存分为小、大、巨大三个范围。频繁的更新操作(append、setrange等)、大量过期key删除易导致碎片出现。建议重启节点完成碎片整理。
  • 子进程内存消耗

    • 场景:在AOF或RDB时,fork的子进程理论上会需要和父进程一样的内存空间。基于linux的copy-on-write技术,父子进程可共享物理内存页。但在接受写请求时,父进程仍然需要对修改内容所在的内存页进行复制以完成写操作。
    • THP:transparent huge pages机制在linux2.6.38后出现,默认开启,导致复制内存页从4kb变成2mb。若有大量写请求,这会导致内存消耗急剧上升。

2. 内存管理

  • 设置内存上限目的:防止所用内存超出服务器物理内存,一般情况下设置合理的maxmemory,保证机器预留20 - 30%闲置内存。

  • 动态调整内存上限:config set maxmemory 10g & config rewrite。注意,一旦使用内存上限调整就会激活内存回收

  • 内存回收策略

    • 过期对象回收:精准维护每个key过期会消耗大量cpu资源,对单线程的redis来说成本过高,一般有两种模式解决。

      1. 惰性删除:客户端读取到expired的key时,会执行删除操作并返回空值。该方式虽然节约cpu,但存在内存泄漏问题。若expired的key一直未被读取就不会被删除,内存无法释放。
      2. 定时任务删除:redis内部维护一个定时任务,默认每秒运行10次,通过配置hz完成。默认使用慢模式运行,每个数据库空间随机找20个key检查,发现过期时删除对应的key-value。若本次检查中超过25%的key过期则继续检查20个key,直到低于25%或者运行超时才结束回收的循环。
    • 内存溢出控制策略:基于mexmemory-policy进行策略的选取 & 确认,可使用config set maxmemory-policy xxx进行动态配置。

      策略名 策略具体内容
      noeviction 默认策略,不删除任何数据,拒绝所有写入操作并向客户端返回OOM错误信息。
      volatile-lru 根据LRU算法删除expired的key,直到空间足够为止。若没有可删除对象,则回退成noeviction策略。
      allkeys-lru 根据LRU删除key,不管有无设置超时属性。
      allkeys-random 随机删除所有key。
      volatile-random 随机删除expired的key。
      volatile-ttl 根据key-value的ttl属性,删除最近将要过期的数据,若没有复合的对象回退noeviction策略。

4. 内存优化

  • 概述:redis所有存储数据都是用redisObject封装,有下方几个字段
字段 含义
type 当前对象数据类型,主要为string,hash,list,set & zset。注意,key都是string类型。
encoding redis内部编码类型,代表其内部采用的数据结构。
lru 记录对象最后一次被访问的时间。
refcount 记录当前对象被引用的次数,若refcount=0就可以被安全回收。
*ptr 和对象的数据内容相关,若为整数直接存储数据;若非整数则表示指向数据的指针。
  • 缩减对象:缩减key和value的长度是最直接的方法。
  • 共享对象池:redis内部维护0-9999的整数对象池,可通过object refcount查看引用次数验证是否启动整数对象池技术。
    • 效果:使用该技术后,数据内存使用率可降低30%以上
    • 限制:使用maxmemory-policy中的lru策略时(无论allkeys或volatile),redis禁止使用共享对象池。因为共享时,lru字段也需要共享,导致无法获取每个对象最后的访问时间。
  • 其他:字符串的优化(预分配机制、字符串重构),encoding类型优化 & 控制key数量。

C. redis缓存设计

  • 概述
    • 缓存收益
      • 加速读写。
      • 降低后端负载压力,例如减少数据库访问量和复杂计算。
    • 成本代价
      • 数据不一致性:缓存层和存储层数据在一定时间窗口内有数据不一致问题,需要存储层更新至缓存层。
      • 代码维护成本:新增对于缓存层的代码 & 缓存与存储逻辑的代码。
      • 运维成本增加,维护集群状态。

1. 缓存更新策略

  • LRU、LFU或FIFO算法
    • 使用场景:key数量过多,消耗内存达到设置的maxmemory阈值时,会对key-value的剔除。
    • 问题:产生一致性问题较严重,删除后导致缓存层数据缺失且运维人员无法及时得知删除信息。
    • 配置方法:设置maxmemory大小 & maxmemory-policy算法即可。
  • 超时剔除
    • 使用场景:缓存数据配置了expire命令,保障key-value在一段时间后失效删除。
    • 问题:在一定窗口内有一致性问题。
  • 主动更新
    • 使用场景:对数据一致性要求较高时使用,若真实数据更新立即更新缓存层。
    • 问题:维护成本较高,需开发正完成更新逻辑。
  • 案例实践建议
    • 低一致性业务:使用最大内存淘汰算法即可。
    • 高一致性业务:超时剔除+主动更新,结合使用。

2. redis雪崩

  • 现象:redis的大量的key在某个时间段失效redis服务不可用,导致需要直接访问数据库,大量请求冲击database。
  • 解决方案
    • 不要设置固定过期时间,尽量在setex时采用随机的ttl
    • 保证缓存层服务高可用性,基于分布式集群架构。

3. redis缓存穿透

  • 现象:redis和database都不存在该key对应的数据,但用户不断发起对该类数据的请求导致database挂掉,比如持续大量请求uid的-1的对应数据。
  • 解决方案
    • 缓存空对象,后续再访问该key可基于缓存层返回null值,存在问题是导致缓存了更多的无用key & 数据不一致问题。该策略适用于数据命中不高,数据频繁变化实时性高的场景。
    • 设置database的并发锁,防止大量请求在database上进行。
    • 设置拦截器,类似bitmapsbloom filter,不存在的uid直接拦截在redis层,不可达database。适用于数据命中不高,数据相对固定的场景。

4. redis击穿

  • 现象:redis中没有该key但是database中有,一般出现场景为某个hot key过期,大量用户并发查询该key导致直接冲击database。
  • 解决方案
    • hot key重建
      • 定义:缓存失效的瞬间,有大量线程来重建缓存。
      • 负面效应:重建时后段附在加大,所以需要减少重建缓存的次数。
    • 设置互斥锁
      • 互斥锁通过竞争对资源独占使用,执行顺序是乱序的。该方法利用setex或setnx实现,确保只有一个线程重建缓存,其他线程都在等待状态
      • 同步锁更高级,能保证执行顺序。
    • 设置hot key永不过期
      • 直接不设置过期时间,或者设置一个逻辑过期时间,超过该周期后单独使用一个线程重建缓存。
      • 但容易出现数据一致性问题。

D. pipeline & transaction

  • pipeline
    • 概述:管道技术是客户端行为对redis server透明,server不知道发过来的请求是普通的还是pipeline形式的。
    • 使用效果:类似批量的方式缺失降低了网络开销,利用多个命令一次网络发送提升了效率。基于hgetall的操作并没有对应的mhgetall可用,此时pipeline可派上用场。
  • transaction
    • 概述:事务是redis server行为,当客户端使用了MULTI命令后把客户端对象设置为特殊状态。客户端发出的命令会被缓存到server,等到EXEC再执行。
    • 使用效果:客户端在exec前的命令,都会被server返回一个queued,事务模式能提高网络使用效率,但不支持回滚

E. 异常排查 & 配置优化

1. Jedis客户端常见异常

  • 无法从连接池获取到连接:jedisPool的jedis个数有限(默认8个),若他们都被使用时,那么第9个来调用的就会报错。

    • 报错信息:redis.clients.jedis.exceptions.JedisConnectionException: Could not get get a resource from the pool
    • 高并发场景下,连接池的jedis个数可进行调整。若我们认为一个jedis每条命令处理时间5ms,1s单个jedis对象极限能处理200个命令,8个jedis的qps极限约为200*8=1600。若qps预计在1.6w的话,需将jedis数量调整至80。
    • 若存在慢查询,那么这个jedis也会被长时间占用不归还,可能造成该异常。
  • 读写超时

    • 报错信息:redis.clients.jedis.exceptions.JedisConnectionException & java.net.SocketTimeoutException: read timed out
    • 读写超时设置的太短。
    • 读写操作耗时太久,需优化具体逻辑。
  • 连接超时

    • 报错信息:redis.clients.jedis.exceptions.JedisConnectionException & java.net.SocketTimeoutException: connect timed out
    • 连接超时设置太短,修改参数jedis.getClients().setConnectionTimeout(A larger value)
    • redis发生阻塞,tcp-backlog满了导致新连接失败。
    • 服务端与客户端间的网络存在问题。
  • 客户端缓冲区异常

    • 报错信息:redis.clients.jedis.exceptions.JedisConnectionException: Unexpected end of stream
    • jedis输出的缓冲区大小偏小,若set bigkey,可能就会报错.
    • jedis不正常并发读写,被多个线程操作。
  • 其他场景异常

    • lua脚本正在执行:正在运行lua脚本时会报错。

    • 加载持久化文件:正在加载rdb文件时会报错。

    • 内存使用情况超出maxmemory:若redis使用内存过多,超出预设的maxmemory会报错OOM,需要扩容调整。

    • 客户端连接数过大:超出预设的maxclients时,会报错,具体解决手段有两个大方向

      1. 客户端:若maxclients设置的阈值不小,则一般是客户端使用不当导致的,可下线部分应用节点先降低总连接数。

      2. 服务端:基于服务端一般是高可用模式,可采用故障转移机制,下线有问题的redis节点,完成主从切换后再排查。

  • 实际案例

    • redis内存陡增:master内存狂增几乎打满maxmemory,但slave内存几乎无变化,导致无法写入新数据,报出OOM的错误。
      • 确有大量key写入:基于slave内存无变化的现象,可初步判定主从间复制出现问题,比较两侧dbsize进行复核。
      • 其他原因:造成master压力大可能是jedis等客户端缓冲区导致的,比如客户端使用monitor命令,可使用info clients确认。
    • 客户端周期性超时:redis服务端无明显异常,可发现部分慢查询。
      • 网络原因导致该现象出现。
      • redis服务端自身问题,需查log文件再确认。
      • 查询慢查询时间点,查看客户端日志中是否存在redis.clients.jedis.exceptions.JedisConnectionException & java.net.SocketTimeoutException: connect timed out的错误信息。

2. 运维配置优化

a. Linux - 内存分配控制overcommit
  • 定义:Linux对于大部分申请内存的请求回复为yes,便于运行更多的程序。大多数情况下,申请完内存后并不会马上使用内存,该技术即为overcommit。本处内存代表,物理内存+swap之和

  • 参数具体可选值

    含义
    0 表示内核将检查是否有足够的可用内存,若有足够内存,则申请通过;若没有足够的可用内存,则申请失败。
    1 表示内核允许超量使用内存直到耗尽。
    2 表示内核绝不过量使用内存(never overcommit),系统整个内存地址空间不超过swap+50%RAM值。
  • 常见场景:redis启动时,日志中出现WARNING overcommit is set to 0!...

  • redis中的解决方案:上述日志提示修改vm.overcommit_memory=1,便于持久化操作的fork能在低内存下执行成功。

    • 获取参数:cat /proc/sys/vm/overcommit_memory
    • 设置参数:echo "vm.overcommit_memory=1" >> /etc/sysctl.conf,并进行sysctl vm.overcommit_memory=1
b. Linux - 内存交换swappiness
  • 定义:物理内存不足时将一部分内存页进行swap操作——将硬盘暂时作内存使用。若遇到高并发、高吞吐的应用,此时磁盘IO会成为系统瓶颈。在Linux 下,并不是物理内存使用完才会使用swap,具体何时使用swap基于swappiness参数。

  • 参数含义:swappiness取值范围在 0 -100间,默认值为60,该值越大意味着操作系统使用swap的概率越高。

    策略
    0 Linux3.5及更新版本,宁愿OOM killer也不用swap。
    1 Linux3.5及更新版本,宁愿用swap也不用OOM killer。
    60 默认值。
    100 Linux会主动使用swap。
  • 设置方法

    • 配置swappiness:使用echo {bestvalue} > /proc/sys/vm/swapiness
    • 确保重启依然生效:使用echo vm.swappiness={bestvalue} >> /etc/sysctl.conf
  • 常见场景

    • redis在物理内存充足时运行极快,物理内存不足时该配置可避免redis死掉。
    • 若redis时高可用的情况下,宁愿死掉也不要使用swap,因为这会导致阻塞。
    • 除了直接free -h查看swap情况,亦可参考mem_fragmentation_ratio,若 < 1则存在redis使用swap的情况。
c. Linux - THP
  • 定义:Linux kernel在2.6.38后增加了THP特性,默认开启。全称Transparent Huge Pages,支持大内存页2MB分配,可加快fork子进程的速度,但fork操作后每个内存页从4KB增加到2MB,会大幅度增加重写期间父进程的内存消耗,每次写命令引起的复制内存页单位放大了512倍。
  • 参数设置
    • 禁用THP特性:使用命令echo never > /sys/kernel/mm/transparent_hugepage/enabled
    • 确保重启依然生效:编辑开机配置文件/etc/rc.local,追加禁用THP特性命令——echo never > /sys/kernel/mm/transparent_hugepage/enabled
  • 常见场景
    • redis启动时日志中出现WARNING you have Transparent Huge Pages (THP) support enabled in your kernel...,需手动禁用THP。
    • 部分版本的Linux 未把THP放入/sys/kernel/mm/transparent_hugepage/enabled,例如Red Hat 6以上的版本即将THP配置放入/sys/kernel/mm/redhat_transparent_hugepage/enabled,但redis将检查THP的位置写死导致不能成功检测THP问题并将之暴露于日志中,但THP的问题依旧存在需要手动调整。
d. Linux - OOM Killer
  • 定义:在内存不足时选择性地杀掉用户进程,OOM Killer会为每个用户进程设置一个权值,该值越高越容易被优先干掉
  • 参数设置
    • 参数位置:每个进程的权值位于/proc/{pid}/oom_score中,受到/proc/{pid}/oom_adj的控制。
    • oom_adj
      • 当oom_adj设置为最小值时,该进程不会被干掉。不同Linux 版本的最小值不同,可参考Linux 源码中的oom.h(-15到-17)。
      • 设置方法为echo {value} > /proc/{pid}/oom_adj
  • 适用场景:为保证redis在主机内存使用率接近满载时仍然能存活下来,可将redis进程对应的oom_adj设置成最小值。
e. Linux - 其他参数
  • NTP

    • 定义:网络时间协议,全称network time protocol,保证不同机器的时钟一致性。
    • 场景:redis集群有多节点组成,涉及多主机问题。时钟不一致会导致redis集群内问题排查难度提升,例如无法有效判别clutser故障转移。
    • 解决手段:建议每小时使用一次时钟同步,保障其集群内时钟一致性。具体为0 * * * * /usr/sbin/ntpdate ntp.xx.com > /dev/null 2>&1
  • ulimit

    • 定义:查看和设置系统当前用户进程的资源数,常用命令ulimit -a,其展示效果可见下方。

      category unit value
      core file size (blocks, -c) 0
      data seg size (kbytes, -d) unlimited
      scheduling priority (-e) 0
      file size (blocks, -f) unlimited
      pending signals (-i) 61370
      open files (-n) 4096
      POSIX message queues (bytes, -q) 819200
      max user processes (-u) 61370
    • 场景

      • redis允许同时多个客户端通过网络进行连接,可通过服务端配置maxclients参数来进行具体客户端数的限制。

      • redis除了供给客户端连接数所需的10000FD句柄,还需要32个FD供redis自身内部使用。

      • Linux系统下,所有网络连接都是文件句柄,若当前open files为4096,则启动redis时会有下方日志

        You requested maxclients of 10000 requiring at least 10032 max file descriptors...Current maximum open files is 4096...

    • 解决手段:将open files的值设置为65535,通过命令ulimit -Sn 65535实现。

  • TCP backlog

    • 定义
      • backlog是一个连接队列,在Linux内核2.2之前,backlog包括半连接状态和全连接状态两种队列大小;在Linux内核2.2之后,分离为两个backlog来分别限制半连接(SYN_RCVD状态)队列大小和全连接(ESTABLISHED状态)队列大小。
      • 半连接状态:服务器处于Listen状态时收到客户端SYN报文时放入半连接队列中,即SYN queue(服务器端口状态为:SYN_RCVD)。
      • 全连接状态:TCP的连接状态从服务器(SYN+ACK)响应客户端后,到客户端的ACK报文到达服务器之前,则一直保留在半连接状态中;当服务器接收到客户端的ACK报文后,该条目将从半连接队列搬到全连接队列尾部,即 accept queue (服务器端口状态为:ESTABLISHED)。
    • 场景:redis默认tcp-backlog值为511,若Linux系统的值小于511则会在启动日志中出现WARNING: THE TCP backlog setting of 511 cannot be ...
    • 解决手段
      • 查看系统值:通过命令cat /proc/sys/net/core/somaxconn查看accept queue具体值,redis不涉及修改syn queue的问题,若想查看可通过cat /proc/sys/net/ipv4/tcp_max_syn_backlog进行查询。
      • 修改:使用echo 511 > /proc/sys/net/core/somaxconn进行变更。
f. 数据恢复手段
  • AOF机制恢复
    • 场景:若误操作flushall或flushdb导致数据丢失,需借助持久化文件进行恢复。若开启了appendonly yes,则误操作仅在AOF文件中追加了一条操作记录。
    • 解决手段
      • 若AOF文件重写了,则之前的数据就无法顺利找回,所以需要调整auto-aof-rewrite-percentage & auto-aof-rewrite-min-size阻止AOF自动重写,并拒绝手动bgrewriteaof
      • 确保上方参数设置后,将AOF文件中的flush操作去掉,并确保AOF文件格式正常以保障数据的顺利恢复。
  • RDB机制恢复
    • 前置配置:若rdb持久化设置中有自动策略,例如save 900 1这类,则因为flush一般涉及key value都较多,会导致RDB恢复基本无望。除非并无开启rdb自动策略,否则面对flush误操作,rdb不能有效恢复数据。
    • 解决手段:若redis没有进行rdb自动策略,仅有手动的bgsave所得的rdb文件,那么即可使用rdb文件恢复数据,但相对数据完整性不如aof机制。

F. redis阻塞问题

  • 概述
    • redis属于典型的单线程架构,读写操作皆于唯一的主线程中完成。
    • redis处于高并发场景时,主线程若出现阻塞则问题严重,阻塞时jedis会抛出JedisConnectionException异常。
    • 阻塞内因:不合理使用api或数据结构,cpu饱和、持久化阻塞(basave的fork瞬间或save持久化)。
    • 阻塞外因:cpu竞争、内存交换(swap)、网络问题。

1. 内因详解

a. api或数据结构使用不合理
  • 场景案例:redis正常场景下执行命令速度极快(微妙级),若执行hgetall命令且该hash类型的key是big key,则执行速度就会很慢,属于是典型的不合理使用api & 数据结构。

  • 慢查询

    • 定义:默认配置slowlog-log-slower-than 10000,即10ms以上的操作被判定为慢查询slowlog-max-len 128默认存储慢查询日志长度为128条。
    • 获取结果:slowlog get 10,即获得最近的10条慢查询命令。
  • 解决方案

    • 定制慢查询阈值:线上一般可设置1ms为慢查询阈值,即slowlog-log-slower-than 1000,该速度下单机redis的qps将被限制为1000左右。

    • 拓展慢查询日志长度:增加日志长度帮助排查故障的原因,将slowlog-log-slower-than可设置为1000

    • 其他:使用快速命令hgetall改成hmget,并禁用keys、sort命令,并将big key拆分。

  • 大键big key问题

    • 定义
      • big key指value所占空间很大的key,一般地,string类型的key,若其value大于10KB即为big key。其他类型的big key,是因为包含的元素过多。
      • 一个string类型的key,value最大为512MB;一个list类型的key,其value最多可存储(2的32次方-1)个元素。
    • 危害
      • 内存空间不均:若在redis集群模式下,bigkey必须被分配至某个节点,导致内存空间使用不均匀。
      • 阻塞:因为操作big key容易耗时过高,导致阻塞时间偏长。
      • 网络拥塞:每次获取big key产生的网络流量较大,占用带宽。
    • 检测big key
      • 被动收集:big key在被访问时,出现异常日志。通过这种情况探测出的big key,即为被动收集的模式。
      • 主动检测:使用redis-cli -h xxx.xxx.xxx.xxx -p 6379 --bigkeys探测,其本质是使用scan渐进式遍历rdr也是很好的redis分析工具,在从节点上操作时更合适的手段。
    • 删除big key
      • string类型:该类的big key一般可以直接del key即可,不会阻塞。
      • hash、list、set & sorted set:直接删除依然会导致阻塞,若为hash类型的场景下,可结合hscan逐步获取进行删除。
      • Redis4.0后特性:使用lazy delete free模式,删除big key不会阻塞。
  • 热键hot key问题

    • 常见场景:热点突发新闻、热销大卖商品会给系统带来巨大流量,在集群模式下存储这些信息的redis节点会出现流量不均匀的情况,存储热点消息的节点出现QPS压力偏大的问题。
    • 判别方法
      • 代理端:若客户端对redis服务端的请求,是基于Twemproxy/Codis这样的分布式架构,则所有的请求都通过代理端再达到服务端,可在代理处完成数据统计分析big key。
      • 服务端:使用monitor在server处分析客户端大量请求中的hot key,但是使用monitor会影响redis性能,且该方法是针对redis单节点进行的,若为集群模式还需要聚合统计。
    • 解决方案
      • 拆分复杂数据结构:若为二级结构(list、hash等),等可拆分成若干key-value,分散至redis集群的各个节点上。
      • 迁移:将hot key所在的slot单独放置到一个redis节点。
      • 本地缓存:将hot key放在业务端的本地缓存中,处理速度更快且降低redis压力,但需要注意数据一致性问题。
b. cpu饱和
  • 简述:redis处理命令时为单线程,仅有主线程处理业务,所以可用cpu数为1。若单核cpu的主机,则使用率接近100%,可使用top命令或redis-cli -h xxx.xxx.xxx.xxx -p 6379 --stat查看使用情况,stat中connections是历史总连接数累计,实时数据可看lsof -i:6379 | wc -l
  • 解决方案
    1. 若看到qps已经足够高(一般可达6w),那么此时垂直优化较困难,建议通过集群化完成水平扩展。
    2. 若发现qps仅数千甚至更低,那么大概率使用了高时间复杂度的命令,需修改业务方法。
    3. 内存优化过度,可用info commandstats检查,比如hset耗时过长,为节约内存空间放宽ziplist的使用条件,可修改hash-max-ziplist-entries & hash-max-ziplist-values
c. 持久化导致堵塞
  • 前置说明:不考虑使用save的场景,这种模式会导致长时间redis堵塞。

  • fork阻塞:rdb和aof重写恢复数据时,主线程会fork生成子进程,子进程再完成实际的重写。若fork耗时过长,则可能导致阻塞出现;

  • aof阻塞:开启aof后,文件刷盘的方式一般为1s一次,后台线程对aof文件进行fsync操作,当磁盘压力大时fsync会需要等待,其主要缘由是磁盘压力偏大。

2. 外因详解

  • cpu竞争
    • 进程竞争:redis属于cpu密集型应用,虽然一般情况下cpu不是性能瓶颈,但是不建议和其他服务部署在一台主机上。
    • 绑定cpu:redis为尽可能地充分利用多核cpu,通常会一台机器部署多个redis实例。此场景下,不同redis进程绑定对应的cpu可降低上下文切换的开销,但是某个实例进行rdb或者aof持久化时,可能因为绑定cpu会导致压力过大。
  • 内存交换swap
    • 简述:swap空间即内存空间不足时,将磁盘充当内存使用。
    • 甄别手段
      1. 找到redis pid,通过redis-cli -p 6379 info server | grep process_id
      2. 接着根据id查询swap信息,使用cat /proc/xxxx/smaps | grep swap。正常情况下应该都是0kb或者少量4kb。
  • 网络问题
    • 拒绝连接:网络割接或带宽耗尽时的网络闪断(sar -n DEV查看),redis超过默认1w的maxclients连接数,或者是进程限制打开文件数(ulimit -n)、backlog队列溢出。
    • 网络延迟:客户端到redis服务器之间网络延迟,可使用redis-cli -h xxx.xxx.xxx.xxx -p 6379 --latency核验。
    • 网卡软中断:高并发下单个网卡队列只能使用一个cpu,新版redis已经改进网络方面效率问题。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容