Redis拾遗(五)

阻塞:

Redis是典型的单线程架构,如果出现阻塞对我们的应用来说都是噩梦。导致阻塞的场景大致分为内在原因和外在原因。

内在原因:不合理的使用API或数据结构、CPU饱和、持久化阻塞等;

外在原因:CPU竞争、内存交换、网络问题等;

下边就以上两种原因分别分析:

  • 内在原因
  1. API或数据结构使用不合理:
    1. 对数据量比较大而且命令算法复杂度是O(n)的例如hgetall等执行速度势必很慢,在高并发场景赢避免使用算法复杂度超过O(n)的命令;
    2. Redis原生提供慢查询统计功能,执行slowlog get {n}可以获取最近n条慢查询命令,默认超过10ms会记录到定长队列中,线上建议设置为1ms。
    3. 修改低算法度命令,禁用keys、sort等命令;调整大对象:防止一次命令操作过多的数据。(执行redis-cli-h{ip}-p{port} bigkeys 可以扫描出大对象);
  2. CPU饱和:
    1. 可以使用redis-cli -h{ip}-p{port} --stat命令获取当前Redis使用情况;
    2. 使用info commandstats 统计分析出命令不合理的开销时间;
  3. 持久化阻塞:
    1. fork 阻塞:fork操作发生在RDB和AOF重写时,如果超过1s则需调整;
    2. AOF刷盘阻塞:文件刷盘方式一般采用每秒一次,当硬盘压力过大时,fsync操作需要等待直到写入完成。
    3. HugePage写操作阻塞;
  • 外在原因
    1. CPU竞争:
      1. 进程竞争:Redis属于典型的CPU密集型应用,不建议和其他多核CPU密集型服务部署在一起;
      2. 绑定CPU:为了充分利用多核CPU,通常一台机器部署多个实例。常见的一种优化方式是把Redis进程绑定到CPU上,降低CPU频繁上下文切换的开销。但有种例外:对于开启持久化或参与复制的节点不建议绑定CPU,因为子进程重写时对单核CPU使用率通常在90%以上。
    2. 内存交换
      1. 查询Redis进程号,根据进程号查询内存交换信息;(0~4KB属于正常现象)
      2. 防止内存交换:保证机器有充足内存;确保所有Redis实例设置最大可用内存,方式不可控增长;降低系统使用swap优先级;
    3. 网络问题:
      1. 连接拒绝:网络闪断;Redis连接拒绝:通过maxclients参数控制客户端最大连接数,默认10000.当Redis用于大量分布式节点且生命周期比较短的场景时,如map/reduce,因为客户端存在频繁启动和销毁的情况且默认Redis不会主动关闭长时间闲置连接或检查关闭无效的TCP连接,因此会导致Redis连接数快速消耗且无法释放的问题。可设置tcp-keepalive和timeout参数让redis主动检查和关闭无效连接。连接溢出:进程限制,backlog队列溢出。
      2. 网络延迟:可使用redis-cli -h{host}-p{port}后加参数进行延迟测试:--latency等;
      3. 网卡软中断:网卡软中断是指由于单个网卡队列只能使用一个CPU,高并发下网卡数据交互都集中在同一个CPU,导致无法充分利用多核CPU情况。

理解内存:

  1. 内存消耗:
    1. 内存使用统计:通过info memory命令获取内存相关指标。
    2. Redis进程内消耗包括:自身内存+对象内存+缓冲内存+内存碎片;
    3. 缓冲内存:客户端缓冲、复制积压缓冲区、AOF缓冲区
      1. 客户端输入缓冲无法控制,最大1G,超过将断开连接。输出缓冲区通过参数client-output-buffer-limit控制:
        • 普通客户端:除复制和订阅客户端之外的所有连接,一般普通客户端的内存消耗可以忽略不计,但当有大量慢连接客户端接入时这部分内存消耗就不能忽略,可通过设置maxclients做限制。
        • 从客户端:主节点会为每个从节点单独建立一条连接用于命令复制。当主从节点之间网络延迟较高或主节点挂载大量从节点时这部分内存消耗将占用很大一部分。
        • 订阅客户端:当订阅服务的消息生产快于消费速度时,输出缓冲区会产生积压造成输出缓冲区空间溢出。
      1. 复制积压缓冲区:复制积压缓冲区整个主节点只有一个,所有的从节点共享此缓冲区,可设置较大缓冲区空间,可以有效避免全量复制。
      2. AOF缓冲区:用于在Redis重写期间保存最近的写入命令。通常占用很小空间。
    4. 内存碎片:以下场景容易出现高内存碎片:
      • 频繁做更新操作
      • 大量过期键删除
      1. 解决方式:数据对齐,安全重启
    5. 子进程内存消耗:
      1. Redis产生的子进程并不需要消耗1倍的父进程内存,实际消耗根据期间写入命令量决定,依然需要预留一些内存防止溢出;
      2. 需要设置sysctl vm.overcommit_memory=1允许内核可以分配所有的物理内存,防止Redis进程执行fork时因系统剩余内存不足而失败
      3. 排查当前系统是否支持并开启THP,如果开启建议关闭,防止copy-on-write期间内存过度消耗;
  2. 内存管理
    1. 设置内存上限:
      1. 用于缓存场景;
      2. 防止所用内存超过服务器物理内存;
      3. 可以通过configset maxmemory命令动态修改内存;
    2. 内存回收策略:
      1. 删除过期键对象;
        1. 惰性删除
        2. 定时任务删除
      2. 内存溢出控制策略:支持6种策略,不在此赘述;
  3. 内存优化
    1. redisObject对象
    2. 缩减键值对象
    3. 共享对象池
    4. 字符串优化
    5. 编码优化
    6. 控制键的数量

  

原文地址:https://www.cnblogs.com/fly-to-the-sky/p/9734554.html