广告引擎Redis优化:提升资源利用效率的实践

背景

目前广告引擎会频繁使用Redis,之前Redis内存已经从32GB扩展到64GB,并扩容后产生了一定的额外费用。又达到了容量80%告警通知,DBA不建议我们进一步扩展至128GB,因为再扩展后会导致一半的内存浪费,另外还会有成本会增加。因此,我们需要对Redis进行数据结构深入优化分析和内存管理,通过分析数据具体占用情况,并合理的将Redis进行业务模块拆分,通过精细化的存储设计提升内存使用效率,从而降低资源的费用。

目标

  1. 降低Redis内存使用量:通过分析和优化Redis存储结构,减少无效数据占用,提升内存使用效率。
  2. 控制费用增长:通过精细化管理Redis内存,避免因无序扩展导致的资源浪费和成本上升。
  3. 优化Redis存储:在保证业务响应速度和性能的前提下,尽可能优化内存结构、减少内存消耗和提高访问效率。
  4. 模块归属分析与拆分:按照业务模块划分Redis中的数据,对不同业务模块的数据进行分析,是否进行分离存储,避免互相影响,降低大内存扩容的资源浪费。

Redis 存储模型及原理

存储模型

Redis作为广告引擎中的核心存储组件,采用的是内存键值对存储模型,会根据不同的数据类型采用相应的数据结构和编码方式(如ziplisthashtableskiplist等)来进行内存管理和优化。

如下是广告引擎在Redis使用的主要数据结构:

  • String:用于存储广告的访问计数、时间戳等单一字段值,常用于广告用户访问频率控制,以增加广告最大收益;
  • Hash:可用于存储复杂广告对象的多个字段,适合复杂的数据结构,比如:广告场景对应的订单数据等缓存、交易订单快照信息等;
  • Set:用于存储唯一值集合、按顺序排序的对象集合,用于存储广告RTA集合数据;

内存原理

Redis的数据存储完全在内存中,这使得其具有极高的读写速度。内存的管理和使用主要涉及以下几个方面:

  • 内存分配:Redis使用jemalloc作为内存分配器,以优化内存分配和释放的性能,避免内存碎片问题。根据对象大小的不同,Redis会在不同的区间中分配内存,以提高内存使用效率。
  • 数据编码:为减少内存使用,Redis支持多种数据编码方式。例如:
    • ziplist:一种压缩数据结构,适合存储小型hashlist等;
    • intset:用于存储小整型集合,避免过多的内存占用;
    • quicklist:用于list类型,结合了链表和压缩存储的优势。
  • 过期机制:Redis支持多种过期策略,包括:
    • 主动过期:Redis定期扫描一部分Key(默认每秒10次),主动淘汰过期的Key。
    • 被动过期:当访问一个Key时,发现该Key已经过期,则立即清理该Key。
    • 内存淘汰策略:当Redis内存到达配置的最大上限时,会根据淘汰策略(如LRU、LFU)清理一些Key以释放内存。

Redis使用分析

Redis节点信息

image-20241010001620168

image-20241010141957212

信息显示:

  1. 内存使用率高:已使用内存比例为80.33%,已经触发预警通知线。
  2. Key数量大:总计约252.61M个key,大量的key会导致内存碎片和可能的性能问题。
  3. 已过期Key比例低:仅5.77%的key已过期,占用4.29%的内存,说明当前的过期设计策略可能还不够优化。
  4. 有一部分永不过期Key:约1.30M个key永不过期,占用266.81 MB内存,占用内存比例较小可控范围。

Redis Key分析

Top 500 Key前缀

image-20241010002322468

Top 500 Big Key(按内存)

image-20241010002547171

Top 500 Big Key(按数量)

image-20241010002453782

从上述数据分析表可以看出,这些数据可能具有复杂的业务逻辑或需要频繁存储大量的数据项:

  1. cola前缀的数据主要使用了hash类型,Key数量197.95M,占用内存较大达到34.82GB,可以考虑拆分独立,并深入该数据结构分析优化;
  2. titan前缀的数据则多为string类型,Key数量51.46M,占用了6.17GB内存;
  3. 部分Big key 在命名上使用uuid的命名,若数据成倍的增长也会造成内存大量消耗;
  4. data:ticker:open 部分Big Key 存储占用192.09KB,需要分析存储的数据是否全都用到,以及是否有优化的空间;

Redis Key 分析方法

数据采样与内存占用分析

我们使用的阿里云Redis,阿里云有相对成熟的分析工具来协助分析,在自建Redis时以下命令可以进行参考使用。

  • 使用 redis-cliINFO MEMORYMEMORY STATS 命令获取Redis的整体内存使用情况。
  • 通过 redis-cli --bigkeys 命令分析Redis中的大Key,找到占用内存最大的Key。
  • 使用 MEMORY USAGE <key> 命令查看特定Key的内存使用情况,并记录数据类型、长度、内存消耗等信息。
  • RDB工具,使用redis-rdb-tools分析Redis的RDB文件,生成详细的内存使用报告,包括每个Key的内存占用、过期时间和数据类型分布等。

分析Key设计是否合理

  • 使用更紧凑的Key设计方案,避免过长的Key名称,可以考虑使用缩写、哈希等方式减少Key的长度。
  • 统计高频Key的TTL(Time to Live)设置,排查是否存在未设置过期时间或过期时间过长导致内存膨胀的问题
  • 针对大字段(如长字符串、JSON)可以考虑采用压缩存储方式(如GzipLZ4)。
  • 将长时间未访问的Key迁移到磁盘数据库中,减少内存占用。

Redis拆分策略

  • 在Redis Key命名时通常会有类似 module:submodule:key 的结构,可以根据 module 划分业务模块。
  • 按照业务模块划分Redis实例,将不同业务模块的数据分别存储到不同的实例中,减少业务之间的耦合度,我们可以考虑将广告投放数据、用户行为数据、缓存数据等分配到不同的Redis实例中。
  • 另外根据业务模块和数据类型(如热数据和冷数据)选择不同的Redis实例和分片策略。

问题分析

根据对 Redis 内存与Top Key分析,得到以下结论:

内存占用情况 在广告引擎使用的 Redis 集群中,与 “cola” 相关的 Key 占用了约 34.82 GB 的内存,且 Key 数量高达 197.95M。这部分数据是可口可乐活动扫码点单期间产生的缓存信息。

Key前缀 占用内存 Key数量 元素数量
cola 34.82 GB 197.95M 395.90M

Redis 内存占用情况与过期Key分析

  • 当前 Redis 集群中,所有节点的内存使用率约为 80.33%,大部分被 “cola” 活动相关数据占用。
  • Key 总数为 252.61M,其中已过期的 Key 数量为 14.58M,占比 5.77%。这些过期 Key 占用了约 2.21 GB 的内存,占总内存的 4.29%。
  • 永不过期的 Key 数量为 1.30M,内存占用为 266.81 MB,占用内存不大。

我们发现 “cola” 前缀key的数据存储对 Redis 内存消耗巨大,随着可口可乐联合收钱吧广告运营活动的结束,这些 Key 已经成为无效数据,继续保留在 Redis 中没有意义,需要应当尽快进行清理

解决方案

为了高效地完成对 Redis 中 “cola” 相关 Key 的批量清理,采用了以下解决方案:

使用 Lua 脚本进行批量 Key 删除

  • Lua 脚本通过 SCAN 指令逐批扫描匹配 “cola:*” 前缀的所有 Key,并使用 DEL 指令删除匹配的 Key。这样可以避免 KEYS 指令引起的 Redis 主线程阻塞,提升删除操作的安全性与效率。

  • Lua 脚本代码:

    local cursor = ARGV[1]
    local match_pattern = ARGV[2]
    local count = 1000
    local deleted = 0
    
    local function delete_keys(keys)
        if #keys > 0 then
            redis.call("DEL", unpack(keys))  -- 批量删除提高效率
            deleted = deleted + #keys
        end
        redis.call("SLEEP", 0.02)  -- 休眠 20 毫秒,降低对 Redis 的压力
    end
    local scan_result = redis.call("SCAN", cursor, "MATCH", match_pattern, "COUNT", count)
    local new_cursor = scan_result[1]
    local keys = scan_result[2]
    delete_keys(keys)
    return {new_cursor, deleted}
    
  • 删除进度与内存释放情况监控

    • 在执行批量删除操作时,实时监控 Redis 内存的变化情况,以确认内存释放的效果。同时通过统计 Key 的删除数量来掌握删除的进度。
    • 删除结束后,再次分析 Redis 中内存使用情况与 Key 数量变化,验证操作的实际效果。

实施结果

image-20241011162830356

image-20241011162844201

image-20241011162725309

通过上述方案的实施,我们清理了 Redis 中所有与 “cola” 活动相关的 Key,释放了约 34 GB 的内存空间,Redis 集群整体内存占用率将从 80.33% 降至 45.21%,大幅提升了集群的响应速度和稳定性。

结论

批量删除已过期和无效数据是优化 Redis 内存使用的有效手段。此次对 “cola” 活动相关 Key 的清理,不仅释放了宝贵的内存资源,还提高了 Redis 集群的性能和稳定性。

在未来的Redis内存管理中,还将持续分析并优化 Redis 中的数据存储,确保系统的高效运行和资源的合理利用。

0%