云原生内存数据库Tair可提供高效的数据库服务,当内存不足时,可能导致Key频繁被逐出、响应时间上升、QPS(每秒访问次数)不稳定等问题,进而影响业务运行。通常情况下,当内存使用率超过95%时需要及时关注。

Tair内存占用介绍

Redis的内存占用主要由以下三部分组成:

内存占用 说明
链路内存(动态) 主要包括Input Buff、Output Buff、JIT Overhead、Fake Lua Link、Lua执行缓存等,例如可执行INFO命令,通过返回结果的Clients中查看客户端缓存信息。
说明 Input buff与Output buff与每个客户端的连接有关,通常较小。当执行客户端Range类操作或大Key收发较慢时,Input buff与Output buff占用的内存会增大,从而影响数据区,甚至会造成内存溢出OOM(Out Of Memory)。
数据内存 用户数据区,即实际存储的Value信息,通常作为重点分析的对象。
管理内存(静态) 启动时较小且相对恒定,该区域由管理数据的Hash内存开销、Repl-buff与aof-buff的内存开销(约32 MB~64 MB)等构成。
说明 当Key数量特别多时(例如几亿个),会占用较大的内存。
说明 大部分OOM场景是由于动态内存管理失效引起,例如限流时请求堆积导致动态内存快速上升、过于复杂或不合理的Lua脚本也可能导致OOM。

步骤一:分析内存使用情况

  1. 查询指定时段的内存使用率信息,具体操作,请参见查看监控数据

    以下示例中,内存使用率(即Memory Usage监控指标)持续接近100%,如下图所示:

    图 1. 内存使用率示例
    内存使用率示例
    说明 对于集群架构读写分离架构的实例,当选择展示数据节点聚合指标时,Memory Usage展示的是该实例下所有数据节点内存使用率的平均值(不含只读节点)。
  2. 查询历史累计逐出的Key总数和命令的最大时延,分析是否呈现明显的上升趋势。

    以下示例中,在同一时间点(2021年1月7日16点整),历史累计逐出的Key总数和命令的最大时延均呈现显著上升趋势,表明存在内存不足的问题。

    说明 需关注的监控指标为Evicted Keys(历史累计逐出的Key总数)和Max Rt(数据节点从接收命令到发出响应最大时延)。
    图 2. 性能监控示例
    性能监控示例
  3. 可选:Tair的内存使用率不符合预期时,可执行下述步骤详细分析内存使用情况。
    1. 通过redis-cli连接Tair
    2. Tair命令行中,执行MEMORY STATS命令查询内存使用详情。
      Tair实例的内存开销主要由两部分组成:
      • 业务数据的内存开销,该部分一般作为重点分析对象。
      • 非业务数据的内存开销,例如主备复制的积压缓冲区、Tair进程初始化消耗的内存等。

      返回示例及各参数对应的解释如下:

      说明 下述返回信息中,内存数值的单位为字节。
       1) "peak.allocated" //Tair进程自启动以来消耗内存的峰值。
       2) (integer) 79492312
       3) "total.allocated" //Tair使用其分配器分配的总字节数,即当前的总内存使用量。
       4) (integer) 79307776
       5) "startup.allocated" //Tair启动时消耗的初始内存量。
       6) (integer) 45582592
       7) "replication.backlog" //复制积压缓冲区的大小。
       8) (integer) 33554432
       9) "clients.slaves" //主从复制中所有从节点的读写缓冲区大小。
      10) (integer) 17266
      11) "clients.normal" //除从节点外,所有其他客户端的读写缓冲区大小。
      12) (integer) 119102
      13) "aof.buffer" //AOF持久化使用的缓存和AOF重写时产生的缓存。
      14) (integer) 0
      15) "db.0"  //业务数据库的数量。
      16) 1) "overhead.hashtable.main" //当前数据库的hash链表开销内存总和,即元数据内存。
          2) (integer) 144
          3) "overhead.hashtable.expires" //用于存储key的过期时间所消耗的内存。
          4) (integer) 0
      17) "overhead.total" //数值=startup.allocated+replication.backlog+clients.slaves+clients.normal+aof.buffer+db.X。
      18) (integer) 79273616
      19) "keys.count" //当前Tair实例的key总数
      20) (integer) 2
      21) "keys.bytes-per-key" //当前Tair实例每个key的平均大小,计算公式:(total.allocated-startup.allocated)/keys.count。
      22) (integer) 16862592
      23) "dataset.bytes" //纯业务数据占用的内存大小。
      24) (integer) 34160
      25) "dataset.percentage" //纯业务数据占用的内存比例,计算公式:dataset.bytes*100/(total.allocated-startup.allocated)。
      26) "0.1012892946600914"
      27) "peak.percentage" //当前总内存与历史峰值的比例,计算公式:total.allocated*100/peak.allocated。
      28) "99.767860412597656"
      29) "fragmentation" //内存的碎片率。
      30) "0.45836541056632996"
    3. Tair命令行中,执行MEMORY USAGE命令查询指定Key消耗的内存(单位为字节)。

      命令执行示例:

      MEMORY USAGE Key0089393003

      返回信息如下:

      (integer) 1000072
    4. Tair命令行中,执行MEMORY DOCTOR命令获取内存诊断建议。
      图 3. 诊断结果示例
      诊断结果示例
      MEMORY DOCTOR会从以下维度为Tair实例的提供内存诊断建议,您可以根据诊断建议制定相应的优化策略:
          int empty = 0;     /* Instance is empty or almost empty. */
          int big_peak = 0;       /* Memory peak is much larger than used mem. */
          int high_frag = 0;      /* High fragmentation. */
          int high_alloc_frag = 0;/* High allocator fragmentation. */
          int high_proc_rss = 0;  /* High process rss overhead. */
          int high_alloc_rss = 0; /* High rss overhead. */
          int big_slave_buf = 0;  /* Slave buffers are too big. */
          int big_client_buf = 0; /* Client buffers are too big. */
          int many_scripts = 0;   /* Script cache has too many scripts. */

步骤二:优化内存使用率

  1. 查询现有的Key是否符合业务预期,及时清理无用的Key。
  2. 通过缓存分析功能,分析大Key分布和Key的TTL过期策略。具体操作,请参见离线全量Key分析
    1. 分析Key是否有合理的TTL策略。
      说明 以下示例中,所有的Key均未设置过期时间,建议根据业务需求来衡量,并在应用端设置合理的过期时间。
      图 4. Key的过期时间分布示例
      Key的过期时间分布示例
    2. 对大Key进行评估,然后从业务方向对大Key进行拆分。
      图 5. 大Key分析示例
      大Key分析示例
  3. 根据业务需求,设置合理的数据逐出策略(即调整maxmemory-policy参数的值)。具体操作,请参见设置实例参数
    说明 Tair默认的数据逐出策略为volatile-lru,更多信息,请参见参数支持
  4. 根据业务需求,设置合理的过期Key主动删除的执行频率(即调整hz参数的值)。具体操作,请参见调整Redis定期任务的执行频率
    说明 hz的取值建议在100以内,如果该值过大将对CPU的使用率产生较大影响。您也可以设置为自动调整(要求实例为内存型或持久内存型),具体操作,请参见实现Redis定期任务执行频率的自动调整
  5. 经过上述步骤优化后,流量使用率依旧较高,可评估升级至更大内存的规格,以承载更大的网络流量。具体操作,请参见变更实例配置
    说明 在正式升级实例的规格前,您可以先购买一个按量付费的实例,测试要升级到的目标规格是否能够满足业务的负载需求,测试完成后可将其释放。关于如何释放实例,请参见释放按量付费实例