Pipeline一次性塞千万条会压垮Redis 6,因其不原子落盘,超50万key(尤其value>1KB)将致内存飙升3–5GB、主从缓冲区溢出、连接数超限;需按内存增量≤300MB、主从延迟

不能用 pipeline 一次性塞千万条,也不能用 SCAN + SETEX 扫全表——这两者都会在 Redis 主从同步阶段引发 IO 阻塞、内存暴涨、甚至 OOM。
为什么 pipeline 批量写入千万 key 会压垮 Redis 6?
Redis 6 的 pipeline 是原子性写入队列,但不等于“原子性落盘”。当批量写入超过 50 万 key(尤其 value >1KB),会导致:
• used_memory_human 在几秒内飙升 3–5GB,触发 maxmemory 策略(如 allkeys-lru)频繁驱逐,缓存命中率归零
• 主从复制缓冲区(repl_backlog_size)溢出,从节点断连重同步,整个集群进入“假死”状态
• 客户端连接数激增(每个 pipeline 请求独占一个连接),超出 maxclients 限制,新请求直接被拒
如何分批次控制写入节奏?关键看三个指标
每批写完必须检查并做流控,不能只按数量切分:
- 用
redis.info("memory")["used_memory_human"]监控内存增长,单批增量建议 ≤300MB - 查
redis.info("replication")["master_last_io_seconds_ago"],若 >2 秒说明主从延迟已起,立即暂停 - 调用
redis.info("stats")["instantaneous_ops_per_sec"],持续 >8000 就降速(Redis 6 单线程吞吐理论上限约 1w ops/s)
千万级数据该从哪读?别碰全表 SCAN
MySQL 里 SELECT * FROM product 扫千万行会锁表、拖慢线上查询。正确做法是:
- 用业务方提供的轻量集合:比如
hot_sku_ids(最近 7 天销量 Top 10w SKU)、recent_login_uids(最近 24 小时活跃用户) - 读取前加
WHERE updated_at > DATE_SUB(NOW(), INTERVAL 1 HOUR),避免加载陈旧数据 - 用游标分页(
LIMIT 1000 OFFSET xxx)替代SCAN,配合应用层去重,防止重复预热
value 结构和 TTL 必须跟线上代码对齐
预热写进去的 key,如果结构或过期时间跟运行时不一致,等于白干:
- 如果线上代码用
hgetall("user:1001")读 HASH,预热就不能用setex("user:1001", ...)写字符串 - TTL 不能设固定值,得用类似
setWithRandomExpiry(key, value, base_ttl=3600, jitter=0.1),让实际过期时间落在 [3240, 3960] 秒区间 - 主从同步完成前,禁止开放客户端读写——可通过
redis.info("replication")["connected_slaves"]和"master_sync_in_progress":0双条件确认
真正卡点不在“怎么写快”,而在“怎么停准”:内存涨到阈值、主从延迟刚冒头、OPS 接近瓶颈——这三个信号任何一个出现,就得立刻 pause,等 3–5 秒再 resume。很多人预热失败,不是因为没分批,而是分了批却没监控这些实时指标。











