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

背景
目前广告引擎会频繁使用Redis,之前Redis内存已经从32GB扩展到64GB,并扩容后产生了一定的额外费用。又达到了容量80%告警通知,DBA不建议我们进一步扩展至128GB,因为再扩展后会导致一半的内存浪费,另外还会有成本会增加。因此,我们需要对Redis进行数据结构深入优化分析和内存管理,通过分析数据具体占用情况,并合理的将Redis进行业务模块拆分,通过精细化的存储设计提升内存使用效率,从而降低资源的费用。
目标
- 降低Redis内存使用量:通过分析和优化Redis存储结构,减少无效数据占用,提升内存使用效率。
- 控制费用增长:通过精细化管理Redis内存,避免因无序扩展导致的资源浪费和成本上升。
- 优化Redis存储:在保证业务响应速度和性能的前提下,尽可能优化内存结构、减少内存消耗和提高访问效率。
- 模块归属分析与拆分:按照业务模块划分Redis中的数据,对不同业务模块的数据进行分析,是否进行分离存储,避免互相影响,降低大内存扩容的资源浪费。
Redis 存储模型及原理
存储模型
Redis作为广告引擎中的核心存储组件,采用的是内存键值对存储模型,会根据不同的数据类型采用相应的数据结构和编码方式(如ziplist、hashtable、skiplist等)来进行内存管理和优化。
如下是广告引擎在Redis使用的主要数据结构:
- String:用于存储广告的访问计数、时间戳等单一字段值,常用于广告用户访问频率控制,以增加广告最大收益;
- Hash:可用于存储复杂广告对象的多个字段,适合复杂的数据结构,比如:广告场景对应的订单数据等缓存、交易订单快照信息等;
- Set:用于存储唯一值集合、按顺序排序的对象集合,用于存储广告RTA集合数据;
内存原理
Redis的数据存储完全在内存中,这使得其具有极高的读写速度。内存的管理和使用主要涉及以下几个方面:
- 内存分配:Redis使用
jemalloc作为内存分配器,以优化内存分配和释放的性能,避免内存碎片问题。根据对象大小的不同,Redis会在不同的区间中分配内存,以提高内存使用效率。 - 数据编码:为减少内存使用,Redis支持多种数据编码方式。例如:
ziplist:一种压缩数据结构,适合存储小型hash、list等;intset:用于存储小整型集合,避免过多的内存占用;quicklist:用于list类型,结合了链表和压缩存储的优势。
- 过期机制:Redis支持多种过期策略,包括:
- 主动过期:Redis定期扫描一部分Key(默认每秒10次),主动淘汰过期的Key。
- 被动过期:当访问一个Key时,发现该Key已经过期,则立即清理该Key。
- 内存淘汰策略:当Redis内存到达配置的最大上限时,会根据淘汰策略(如LRU、LFU)清理一些Key以释放内存。
Redis使用分析
Redis节点信息


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

Top 500 Big Key(按内存)

Top 500 Big Key(按数量)

从上述数据分析表可以看出,这些数据可能具有复杂的业务逻辑或需要频繁存储大量的数据项:
cola前缀的数据主要使用了hash类型,Key数量197.95M,占用内存较大达到34.82GB,可以考虑拆分独立,并深入该数据结构分析优化;titan前缀的数据则多为string类型,Key数量51.46M,占用了6.17GB内存;- 部分Big key 在命名上使用uuid的命名,若数据成倍的增长也会造成内存大量消耗;
- data:ticker:open 部分Big Key 存储占用192.09KB,需要分析存储的数据是否全都用到,以及是否有优化的空间;
Redis Key 分析方法
数据采样与内存占用分析
我们使用的阿里云Redis,阿里云有相对成熟的分析工具来协助分析,在自建Redis时以下命令可以进行参考使用。
- 使用
redis-cli的INFO MEMORY和MEMORY 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)可以考虑采用压缩存储方式(如
Gzip、LZ4)。 - 将长时间未访问的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 数量变化,验证操作的实际效果。
实施结果



通过上述方案的实施,我们清理了 Redis 中所有与 “cola” 活动相关的 Key,释放了约 34 GB 的内存空间,Redis 集群整体内存占用率将从 80.33% 降至 45.21%,大幅提升了集群的响应速度和稳定性。
结论
批量删除已过期和无效数据是优化 Redis 内存使用的有效手段。此次对 “cola” 活动相关 Key 的清理,不仅释放了宝贵的内存资源,还提高了 Redis 集群的性能和稳定性。
在未来的Redis内存管理中,还将持续分析并优化 Redis 中的数据存储,确保系统的高效运行和资源的合理利用。
