Redis 缓存设计完全指南:从一次"618 大促 5 分钟 Redis 内存爆掉雪崩损失 400 万"看懂为什么 set/get 远远不够

2021 年我加入一家电商平台负责会员系统一台 Redis 主从 8G 实例平时撑 5000 QPS 一切都很顺 618 大促前我们做容量评估把 Redis 扩到 32G 加了一个从库觉得稳如老狗结果 618 开抢 5 分钟 Redis 内存爆掉主从全挂整个会员系统雪崩用户登录全部失败 30 分钟才恢复损失 400 万事后复盘我陆续踩了一堆坑第一种最让我傻眼我们用 Redis 存大量会员画像单 key 是 hash 平均 30KB 100 万会员就 30GB 大促时新增 200 万会员直接撑爆 32G 内存第二种最难缠我们的 cache aside 模式没做防穿透大促期间大量不存在的会员 ID 被恶意爬虫请求全部穿透到 MySQL 数据库 QPS 飙到 8 万 MySQL 直接挂掉第三种最离谱我们用 keys 星号做大 key 监控一次 keys 星号在 5000 万 key 的实例上跑了 8 秒 Redis 单线程被全部阻塞期间所有请求超时业务恢复 30 分钟第四种最致命我们 hot key 没识别一个明星会员的 ID 被大量查询单 key QPS 50 万单线程 Redis 顶不住整个实例 CPU 100% 其他 key 全部受影响第五种最莫名其妙我们用 INCR 做计数器用 PERSIST 持久化但 Redis 默认配置 maxmemory policy noeviction 内存满了所有写命令报错 INCR 也报错计数器卡死我盯着这一连串问题想了很久才彻底想明白第一版错在一个根本的认知上我以为 Redis 缓存就是 set 一下 get 一下内存够大就行可这个认知是错的真正能扛业务的 Redis 缓存是一个数据结构与 key 设计加缓存模式加三大穿透击穿雪崩加大 key 与 hot key 治理加内存策略与淘汰加监控与高可用的整套工程方法论

2021 年我加入一家电商平台 负责会员系统 一台 Redis 主从 8G 实例 平时撑 5000 QPS 一切都很顺。618 大促前我们做容量评估 把 Redis 扩到 32G 加了一个从库 觉得稳如老狗。结果 618 开抢 5 分钟 Redis 内存爆掉 主从全挂 整个会员系统 雪崩 用户登录全部失败 30 分钟才恢复 损失 400 万。事后复盘我陆续踩了一堆坑。第一种最让我傻眼 我们用 Redis 存大量会员画像 单 key 是 hash 平均 30KB 100 万会员就 30GB 大促时新增 200 万会员 直接撑爆 32G 内存。第二种最难缠 我们的 cache aside 模式没做防穿透 大促期间大量不存在的会员 ID 被恶意爬虫请求 全部穿透到 MySQL 数据库 QPS 飙到 8 万 MySQL 直接挂掉。第三种最离谱 我们用 keys * 做大 key 监控 一次 keys * 在 5000 万 key 的实例上跑了 8 秒 Redis 单线程被全部阻塞 期间所有请求超时 业务恢复 30 分钟。第四种最致命 我们 hot key 没识别 一个明星会员的 ID 被大量查询 单 key QPS 50 万 单线程 Redis 顶不住 整个实例 CPU 100% 其他 key 全部受影响。第五种最莫名其妙 我们用 INCR 做计数器 用 PERSIST 持久化 但 Redis 默认配置 maxmemory-policy noeviction 内存满了所有写命令报错 INCR 也报错 计数器卡死。我盯着这一连串问题想了很久才彻底想明白第一版错在一个根本的认知上我以为 Redis 缓存就是 set 一下 get 一下 内存够大就行 可这个认知是错的真正能扛业务的 Redis 缓存是一个 数据结构与 key 设计 加 缓存模式 cache-aside write-through write-behind 加 三大穿透击穿雪崩 加 大 key 与 hot key 治理 加 内存策略与淘汰 加 监控与高可用 的整套工程方法论 任何一环没做都可能让你的 Redis 挂掉拖垮整个业务本文从头梳理 Redis 缓存设计的工程要点 数据结构怎么选 缓存模式怎么用 三大问题怎么治 大 key hot key 怎么发现处理 内存策略怎么定 以及一些把 Redis 做扎实要避开的工程坑

问题背景:为什么 Redis 不是 set/get 就完事

很多人对 Redis 的认知是 set 一个 key get 一个 key 内存够大就稳 但生产里你会发现 内存爆 OOM 缓存击穿打挂 DB 大 key 慢查询阻塞单线程 hot key CPU 100% 雪崩全业务 down。问题的根源在于:

  • 数据结构选错就是浪费:存简单 KV 用 hash 浪费 30% 存计数器用 string 单 key 占 100 字节 应该用 hash 聚合 100 个字段。
  • cache-aside 必须配防护:不做空值缓存 布隆过滤器 互斥锁 一次大促就能击穿打挂 DB。
  • 大 key 是单线程杀手:单 key 超 10MB 一次 hgetall 阻塞 Redis 100ms+ 全实例 QPS 归零。
  • hot key 单线程顶不住:单 key 50 万 QPS 单实例 CPU 100% 必须本地缓存或者 key 拆分。
  • 淘汰策略决定 OOM 行为:默认 noeviction 内存满了写命令全报错 推荐 allkeys-lru。
  • 命令复杂度要算清:KEYS SMEMBERS HGETALL 等 O(N) 命令在大 collection 上是灾难。

一 数据结构与 key 设计:选错就是浪费

Redis 有 5 大基础结构 String Hash List Set ZSet 加 Stream HyperLogLog Bitmap Geo 选对结构能省 50% 内存与 5 倍 QPS。比如用户信息 错的方案是给每个字段一个 key user:123:name user:123:age user:123:gender 三个 string 占 300 字节 正确方案是一个 hash user:123 内含三个字段 占 100 字节。

import redis

r = redis.Redis(host='localhost', port=6379, decode_responses=True)

# 1 用户信息用 Hash 不要用多个 string
# 错的方案 占 300 字节
r.set('user:123:name', 'Alice')
r.set('user:123:age', 30)
r.set('user:123:vip', 'gold')

# 对的方案 占 100 字节 hash 内部压缩
r.hset('user:123', mapping={'name': 'Alice', 'age': 30, 'vip': 'gold'})
user = r.hgetall('user:123')

# 2 计数器用 String INCR 而不是各种花式
r.incr('order:count:2024-05-24')
r.incrby('user:123:login_count', 1)

# 3 排行榜用 ZSet 自动按分数排序
r.zadd('leaderboard:weekly', {'user:123': 1500, 'user:456': 2300})
top10 = r.zrevrange('leaderboard:weekly', 0, 9, withscores=True)
r.zincrby('leaderboard:weekly', 100, 'user:123')

String 计数器 Hash 对象 ZSet 排行榜是最常用的三件套 但生产经常需要更高阶的结构 比如标签集合 消息队列 大量布尔标志 海量去重 这些场景 Set Stream Bitmap HyperLogLog 才是正解 用对结构能让内存与性能差一个数量级。

# 4 标签用 Set 集合运算很方便
r.sadd('user:123:tags', 'vip', 'big_spender')
r.sadd('user:456:tags', 'vip', 'new_user')
common = r.sinter('user:123:tags', 'user:456:tags')  # 取交集

# 5 消息队列用 List 或 Stream
r.lpush('queue:notify', json.dumps({'user_id': 123, 'msg': 'hello'}))
msg = r.brpop('queue:notify', timeout=10)

# 6 大量布尔标志用 Bitmap 一个 bit 一个用户
r.setbit('user:signed_in:2024-05-24', 123, 1)
r.setbit('user:signed_in:2024-05-24', 456, 1)
total_signin = r.bitcount('user:signed_in:2024-05-24')
# 1 亿用户的签到只占 12MB

# 7 去重计数用 HyperLogLog 误差 0.81% 但只占 12KB
r.pfadd('uv:2024-05-24', 'user:123', 'user:456')
uv = r.pfcount('uv:2024-05-24')

数据结构选型的工程经验 用户信息 商品信息这种 多字段对象 必须 Hash 排行榜 必须 ZSet 大量布尔标志 必须 Bitmap UV 去重 必须 HyperLogLog 选对结构内存与性能差几倍 不要图方便全用 String。我们公司一个签到系统从 String per user 改成 Bitmap 内存从 1.2GB 降到 12MB 查询也快 5 倍。

二 缓存模式:cache-aside 不是唯一选择

缓存模式决定数据如何在 Redis 与 DB 之间流转。最常用的是 cache-aside 应用控制 但还有 read-through write-through write-behind 各有适用场景。

import time
import json

class CacheAside:
    """1 Cache-Aside 应用自己控制缓存 最常用"""

    def __init__(self, redis_client, db):
        self.r = redis_client
        self.db = db

    def get_user(self, uid: int) -> dict:
        key = f'user:{uid}'
        # 先查缓存
        cached = self.r.get(key)
        if cached:
            return json.loads(cached)
        # miss 查 DB
        user = self.db.query('SELECT * FROM users WHERE id=?', uid)
        if user:
            # 回写缓存 加随机 TTL 防雪崩
            ttl = 300 + (uid % 60)
            self.r.setex(key, ttl, json.dumps(user))
        return user

    def update_user(self, uid: int, data: dict):
        # 先更新 DB 再删缓存 删比更新可靠
        self.db.execute('UPDATE users SET ... WHERE id=?', uid, data)
        self.r.delete(f'user:{uid}')
        # 注意 不是 set 是 delete 让下次读时重新 load 从 DB
        # set 可能因为并发先后顺序导致脏数据

cache-aside 是常用模式 但要注意 update_user 是删缓存不是更新 这是经典的一致性细节 先更 DB 再删 cache 是 Cache-Aside Pattern 的标准做法 顺序反了在并发下会出脏数据。下面看另外几种模式 适合不同读写比例的业务。

class WriteThrough:
    """2 Write-Through 写穿 同步写缓存与 DB 强一致"""

    def __init__(self, redis_client, db):
        self.r = redis_client
        self.db = db

    def update_user(self, uid: int, data: dict):
        # 缓存与 DB 同时更新 在事务中
        self.db.execute('UPDATE users SET ... WHERE id=?', uid, data)
        self.r.setex(f'user:{uid}', 300, json.dumps(data))
        # 风险 db 成功 cache 失败 数据不一致
        # 解法 引入 transaction outbox 或者用 delete 而非 set

class WriteBehind:
    """3 Write-Behind 写回 先写缓存 异步写 DB 高性能"""

    def __init__(self, redis_client, queue):
        self.r = redis_client
        self.queue = queue

    def update_user(self, uid: int, data: dict):
        # 立刻写缓存 异步写 DB
        self.r.setex(f'user:{uid}', 300, json.dumps(data))
        self.queue.send('user_update', {'uid': uid, 'data': data})
        # 适合写多读少 高性能场景 比如计数 点赞
        # 风险 缓存挂了未持久化的写丢失 必须有 backup

class ReadThrough:
    """4 Read-Through 由缓存代理负责 load 应用不感知"""

    def __init__(self, redis_client, loader):
        self.r = redis_client
        self.loader = loader  # 注册的 loader 函数

    def get(self, key: str):
        cached = self.r.get(key)
        if cached:
            return json.loads(cached)
        value = self.loader(key)  # 由缓存代理调
        if value:
            self.r.setex(key, 300, json.dumps(value))
        return value

缓存模式的工程经验 90% 业务用 cache-aside + delete 而非 set 强一致写多场景用 write-through 要加事务 高性能写多场景用 write-behind 但要容忍丢数据 read-through 适合缓存抽象成中间件场景。我们公司商品信息用 cache-aside 库存用 write-through 加 Lua 原子操作 浏览计数用 write-behind 三种模式各得其所。

三 缓存三大问题:穿透 击穿 雪崩

缓存最经典的三大问题 穿透 大量查不存在的 key 击穿 hot key 过期瞬间大量请求打 DB 雪崩 大量 key 同时过期 DB 被打挂。每种都有标准解法 必须组合使用。

import hashlib
import threading
from pybloom_live import ScalableBloomFilter

class CacheProtection:
    """缓存三大问题的标准防御"""

    def __init__(self, redis_client, db):
        self.r = redis_client
        self.db = db
        # 布隆过滤器 防穿透 误判率 0.01%
        self.bloom = ScalableBloomFilter(initial_capacity=10_000_000,
                                          error_rate=0.0001)
        self._load_bloom_from_db()
        # 互斥锁 防击穿
        self.locks = {}

    def _load_bloom_from_db(self):
        """启动时把所有 user_id 加入布隆过滤器"""
        for uid in self.db.query('SELECT id FROM users'):
            self.bloom.add(str(uid))

    def get_user_with_protection(self, uid: int) -> dict | None:
        key = f'user:{uid}'

        # 第一道 布隆过滤器 防穿透
        if str(uid) not in self.bloom:
            return None  # 直接返回不查 DB

        # 第二道 缓存命中 包括空值缓存
        cached = self.r.get(key)
        if cached == '__NULL__':
            return None  # 空值缓存 防穿透
        if cached:
            return json.loads(cached)

        # 第三道 互斥锁 防击穿
        lock_key = f'lock:{key}'
        got_lock = self.r.set(lock_key, '1', nx=True, ex=10)
        if not got_lock:
            # 没拿到锁 等 100ms 重试 让别的线程 load
            time.sleep(0.1)
            return self.get_user_with_protection(uid)

        try:
            # 双重检查 拿锁后再查一次缓存
            cached = self.r.get(key)
            if cached:
                return json.loads(cached) if cached != '__NULL__' else None

            # 真正 load DB
            user = self.db.query('SELECT * FROM users WHERE id=?', uid)
            if user:
                ttl = 300 + (uid % 60)  # 随机 TTL 防雪崩
                self.r.setex(key, ttl, json.dumps(user))
            else:
                # 空值缓存短 TTL 防穿透
                self.r.setex(key, 60, '__NULL__')
            return user
        finally:
            self.r.delete(lock_key)

三大问题防御的工程经验 布隆过滤器防穿透是首选 空值缓存是兜底 互斥锁防击穿要 set NX 加超时 否则锁泄漏 随机 TTL 防雪崩 base 300s + 随机 0-60s 散开过期点。我们公司大促前必跑 cache flood 压测 故意请求 100 万不存在 ID 看 DB QPS 是否被布隆过滤器吃下来 这是上大促的硬规范。

四 大 key 与 hot key 治理

大 key 单 key 超 10MB 会让 Redis 单线程阻塞 hot key 单 key QPS 超 10 万会让单实例 CPU 100% 这两种都是 Redis 的致命问题 必须主动发现处理 不要等用户报障。

# 1 大 key 扫描 用 redis-cli --bigkeys 内置扫描
redis-cli --bigkeys -i 0.01
# 输出 找到 10 个最大的 string list hash set zset

# 2 更精细的扫描 用 --memkeys 看内存占用
redis-cli --memkeys -i 0.01

# 3 hot key 用 redis-cli --hotkeys
# 注意 必须 maxmemory-policy 设为 lfu 模式才能用
redis-cli --hotkeys

# 4 在生产环境上跑大 key 扫描必须 SCAN 不要 KEYS
# KEYS 会阻塞 SCAN 是渐进式
redis-cli --scan --pattern 'user:*' | head -100

# 5 看单个 key 的 size
redis-cli MEMORY USAGE user:123:profile
# 单位 bytes 超过 10MB 算大 key

# 6 看 hot key 实时 QPS 用 monitor 短时间
# 注意 monitor 影响性能 只在排查时用
redis-cli MONITOR | head -1000 | awk '{print $4}' | sort | uniq -c | sort -rn | head

扫描出大 key 后必须处理 单纯发现没用 这里是几种实战治理手段 拆分 hash 是最常用的 比如把 100 万字段的大 hash 按 hash slot 拆成 100 个 hash 每个 1 万字段。

def bigkey_split(r: redis.Redis, src_key: str, dst_prefix: str,
                 slot_count: int = 100):
    """大 hash 拆分 按 field hash 分散到多个小 hash"""
    cursor = 0
    while True:
        cursor, items = r.hscan(src_key, cursor=cursor, count=1000)
        pipe = r.pipeline()
        for field, value in items.items():
            slot = hash(field) % slot_count
            new_key = f'{dst_prefix}:slot:{slot}'
            pipe.hset(new_key, field, value)
        pipe.execute()
        if cursor == 0:
            break

def hot_key_local_cache(r: redis.Redis, key: str, local_cache: dict,
                         local_ttl: int = 5) -> str:
    """hot key 本地缓存 减轻 Redis 单线程压力"""
    now = time.time()
    if key in local_cache:
        value, expire_at = local_cache[key]
        if now < expire_at:
            return value
    value = r.get(key)
    local_cache[key] = (value, now + local_ttl)
    return value

def hot_key_sharding(r: redis.Redis, key: str, shard_count: int = 10):
    """hot key 拆分 在 key 后加 shard suffix 读时随机选一个"""
    # 写入时同时更新所有 shard
    value = 'hot_value'
    pipe = r.pipeline()
    for i in range(shard_count):
        pipe.set(f'{key}:shard:{i}', value)
    pipe.execute()

    # 读取时随机选一个 shard
    import random
    shard = random.randint(0, shard_count - 1)
    return r.get(f'{key}:shard:{shard}')

大 key hot key 治理的工程经验 大 key 拆分按 field hash 分到多个 small hash 单 key 控制在 10K field 以下 hot key 优先用本地缓存 5-10s TTL 能扛 90% 的 QPS shard 拆分适合极热 key 不在乎短时间不一致的场景。我们公司明星会员的 hot key 用本地 Caffeine 缓存 5s 单实例从 50 万 QPS 降到 1000 QPS Redis 压力解决。

[mermaid]flowchart TD
A[请求 get user] --> B[布隆过滤器]
B -->|不在| C[直接返回 None]
B -->|在| D[查 Redis]
D -->|命中| E[返回数据]
D -->|未命中| F[尝试获取互斥锁]
F -->|失败| G[等 100ms 重试]
F -->|成功| H[双重检查缓存]
G --> D
H -->|命中| I[返回 释放锁]
H -->|未命中| J[查 DB]
J -->|有| K[写缓存 随机 TTL]
J -->|无| L[写空值缓存 60s]
K --> M[释放锁]
L --> M
M --> E

五 内存策略与淘汰

Redis 默认 maxmemory-policy noeviction 内存满了所有写命令报错 包括 INCR DEL 都不行 业务直接挂。生产必须配 maxmemory 与淘汰策略 让 Redis 在内存压力下自动淘汰 而不是报错。

# redis.conf 内存配置
# 1 设置 maxmemory 通常是物理内存的 60%-80% 留给系统
maxmemory 24gb

# 2 淘汰策略 8 种 推荐 allkeys-lru
# noeviction 不淘汰 内存满写命令报错 默认 不推荐
# allkeys-lru 所有 key 按 LRU 淘汰 推荐通用场景
# allkeys-lfu 按 LFU 淘汰 适合有明显冷热的场景
# allkeys-random 随机淘汰
# volatile-lru 只淘汰有 TTL 的 key 按 LRU
# volatile-lfu 只淘汰有 TTL 的 key 按 LFU
# volatile-random 只淘汰有 TTL 的 key 随机
# volatile-ttl 淘汰 TTL 最近的
maxmemory-policy allkeys-lru

# 3 LFU 调优 让热度衰减更合理
maxmemory-samples 10            # 采样 10 个比较 默认 5 精度更高
lfu-log-factor 10               # 计数器对数因子
lfu-decay-time 1                # 计数器衰减时间 分钟

# 4 内存碎片整理 长期运行后 RSS 会膨胀
activedefrag yes
active-defrag-ignore-bytes 100mb
active-defrag-threshold-lower 10
active-defrag-threshold-upper 100

# 5 查看内存与碎片情况
redis-cli INFO memory | grep -E 'used_memory_|mem_fragmentation'
# used_memory 逻辑占用
# used_memory_rss 物理内存占用
# mem_fragmentation_ratio rss/used 1.0-1.5 正常 大于 1.5 碎片严重

内存策略的工程经验 maxmemory 必须设 不能让 Redis 用满物理内存 OOM kill allkeys-lru 是通用场景最佳 LFU 适合有明显冷热的场景 比如新闻 商品 activedefrag 必开 长期运行碎片率会到 2.0 以上 整理后能节省 30% 内存。我们公司每月凌晨低峰跑一次 MEMORY PURGE 配合 activedefrag 物理内存能省 5GB+ 这是被忽视的免费优化。

六 Redis 的工程坑:那些文档里学不到的

讲完原理来说几个真实生产里踩过的坑。第一个坑是 KEYS HGETALL SMEMBERS 等 O N 命令禁用 必须用 SCAN HSCAN SSCAN 渐进式扫描 一次 KEYS 在 1000 万 key 实例上跑 5 秒 单线程阻塞所有请求超时 这是 Redis 最经典的坑。第二个坑是 pipeline 不是事务 pipeline 只是批量发送 中间任何一条失败不会回滚 真正的事务用 MULTI EXEC 加 WATCH 乐观锁。第三个坑是 expire 时间在 hash 不能给单 field 设 必须给整个 hash 设 想给 field 单独 TTL 用 zset 加 score 为过期时间戳 自己扫描清理。第四个坑是 主从延迟可能秒级 读从库可能读到旧数据 强一致场景必须读主库或者用 WAIT 命令同步等从库 ack。第五个坑是 cluster 模式下 mget mset 跨 slot 报错 必须 hash tag 把相关 key 放同一 slot 比如 user 123 name 与 user 123 age 都用 user 123 作 hash tag 即 user 123 name 写成 user 123 name 加大括号

关键概念速查

概念 含义 工程价值
Hash 内部编码 ziplist 压缩 小 hash 省 70% 内存
Bitmap 位操作 1 亿用户标志 12MB
HyperLogLog 近似去重 误差 0.81% 12KB
Cache-Aside 应用控缓存 最常用 简单可靠
布隆过滤器 存在性判断 防穿透首选
互斥锁 SET NX EX 防击穿
随机 TTL 过期时间散开 防雪崩
大 key 拆分 按 field hash 分 消除单线程阻塞
hot key 本地缓存 JVM 内 caffeine QPS 降 99%
allkeys-lru LRU 淘汰 通用场景推荐

避坑清单

  1. 选对数据结构 用户对象用 Hash 排行榜用 ZSet 布尔标志用 Bitmap UV 用 HyperLogLog 不要图方便全 String。
  2. cache-aside 必配防御 布隆过滤器防穿透 空值缓存兜底 互斥锁防击穿 随机 TTL 防雪崩 四件套。
  3. 大 key 监控必做 redis-cli --bigkeys 周扫一次 超 10MB 必须拆分 否则单线程阻塞 100ms+。
  4. hot key 监控必做 redis-cli --hotkeys 配 lfu 模式 超 10 万 QPS 必须本地缓存或 shard 拆分。
  5. maxmemory-policy 必设 allkeys-lru 不要用默认 noeviction 否则内存满了写全报错业务挂。
  6. KEYS HGETALL SMEMBERS 禁用 必须 SCAN HSCAN SSCAN 单次扫 100 阻塞时间控制在 ms 级。
  7. 主从延迟监控 写主读从场景必须监控 lag 强一致场景用主库或 WAIT 命令。
  8. cluster mode 跨 slot 操作禁止 mget mset transaction 跨 slot 报错 hash tag 把相关 key 集中。
  9. activedefrag 必开 长期运行碎片率会到 2.0+ 每月 MEMORY PURGE 一次 物理内存能省 30%。
  10. pipeline 不是事务 中间失败不回滚 真事务用 MULTI EXEC WATCH 乐观锁。

总结

Redis 缓存这事 很多人的直觉是 set 一下 get 一下 内存够大就稳 这其实是把 我会调 set get 和 我能在生产用 Redis 扛住大促 50 万 QPS 业务 混为一谈。前者是会调 API 后者是懂缓存工程。中间隔着的是 数据结构选型 缓存模式 三大问题防御 大 key hot key 治理 内存策略 监控运维 整整一套工程方法论。

从原型到生产 你需要做的事远不止 set get。你要懂 不同数据结构的内存开销 不同缓存模式的一致性权衡 三大问题的标准防御 大 key hot key 的发现治理 maxmemory 与淘汰策略 SCAN 替代 KEYS hash tag 的使用。每一项单独看都不复杂 但它们组合在一起 才是一个能扛业务规模的 Redis 缓存体系。少任何一项 都可能让某个大促把 Redis 打挂连带整个业务雪崩。

我经常用一个比喻来理解 Redis 缓存 它有点像超市的临时货架。DB 是后仓 货架是 Redis 客户是用户。货架小 但取货快 Cache-Aside 是 收银员发现货架没货就去后仓拿 同时摆上货架 大 key 是一个超大箱子占了半个货架其他东西摆不下 hot key 是一个爆款 100 个客户同时抢 收银员一个人忙不过来 雪崩是货架上的特价标签同时到期 客户全去后仓拿 把后仓压死 布隆过滤器是门口的保安先确认商品在不在超市 不在直接劝退不去后仓查 写穿是收银员每次卖出立刻去后仓更新库存 写回是先记账后台异步同步 你不能因为有了货架就觉得超市能开 还要管货架结构 摆放策略 爆款分流 客流防雪崩 才是一整套超市运营。

这套架构最难的地方在于 它的复杂度在小流量时几乎完全暴露不了。你 1000 QPS 的服务 Redis 怎么用都不会出问题 觉得 Redis 真好用。但真正大促 50 万 QPS 几亿 key 上百 GB 内存 你才发现 99% 的复杂度都在 那 1% 的边角细节里 大 key 阻塞 hot key 100% CPU 缓存击穿打挂 DB 内存爆 OOM。建议任何想用 Redis 扛严肃业务的团队 上线前一定要做 真实流量压测 故意造大 key 故意造 hot key 故意大量请求不存在 ID 看防护是否生效 千万别等大促来教你 那时候损失可能就是几百万了。

—— 别看了 · 2026
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理 邮箱1846861578@qq.com。
技术教程

LLM 微调工程化完全指南:从一次"LoRA 训完法律审查飙到 85% 但日常问候也讲合同条款"看懂为什么 trainer.train 远远不够

2026-5-24 16:12:35

技术教程

RAG 检索增强生成工程化完全指南:从一次"企业知识库助手幻觉编造内容客户当场炸毛"看懂为什么 LangChain demo 远远不够

2026-5-24 16:23:04

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索