系统不是画出来的:一次把缓存、队列和数据库串起来的实践

# 系统不是画出来的:一次把缓存、队列和数据库串起来的实践

 

很多团队聊架构时,最容易陷入一种误区:图画得很漂亮,链路一跑就露馅。真正决定系统稳不稳的,往往不是有没有上微服务,而是几个基础问题有没有想透:热点怎么扛、写入怎么削峰、数据库什么时候该兜底。前段时间我在梳理一个高并发下单系统时,最大的感受就是,架构设计不是堆名词,而是把数据流和故障路径设计清楚。

 

先说一个常见错误:请求一进来就直打数据库。业务量小时没问题,一旦活动流量上来,数据库立刻成为共享瓶颈。更糟的是,大家往往先想到“加机器”,最后发现 CPU 没满,连接数和锁等待先爆了。这个时候最有效的动作通常不是继续横向扩容,而是把读写链路拆开。

 

一个更实用的思路是三层缓冲:**缓存抗读、队列抗峰、数据库保一致**。

 

先看读路径。对于商品详情、库存快照、活动配置这类读多写少的数据,可以把热点读请求顶到 Redis:

 

```python

import redis

import json

 

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

 

def get_product(product_id: int):

    key = f"product:{product_id}"

    cached = r.get(key)

    if cached:

        return json.loads(cached)

 

    product = query_product_from_db(product_id)

    r.setex(key, 60, json.dumps(product))

    return product

```

 

这里的关键不是“上 Redis”这四个字,而是缓存策略。热点 key 要设置过期时间,最好再加一点随机抖动,避免同一时刻集体失效。对于极热点数据,还要防缓存击穿,比如用互斥锁或逻辑过期让回源请求收敛。

 

再看写路径。下单、支付回调、日志归档这类写操作,最怕流量尖峰直接砸到数据库。一个简单但很有效的办法,是把主链路改成“先接住,再异步处理”:

 

```bash

# 观察消费堆积

kafka-consumer-groups.sh --bootstrap-server kafka:9092 \

  --describe --group order-worker

```

 

消息队列不是为了显得架构高级,而是为了把瞬时洪峰摊平。接口先快速返回“已受理”,后端消费者按数据库能承受的速度慢慢写。这样设计时要特别注意幂等,比如订单服务按 `request_id` 去重,否则消费者重试一次,业务就可能多扣一次库存。

 

最后才是数据库层。数据库不是不能扛,而是不该独自扛所有问题。表结构上至少要避免几个经典坑:长事务、没有命中索引的更新、把状态字段和高频查询条件分开存导致回表严重。像订单表这种典型场景,索引通常要围绕查询模式建,而不是围绕“看起来重要的字段”建:

 

```sql

CREATE INDEX idx_user_status_ctime

ON orders(user_id, status, created_at DESC);

```

 

这类联合索引的价值,在于把“某个用户最近的待支付订单”这种高频查询直接压在索引层解决。很多慢 SQL 不是语句复杂,而是索引和访问路径根本没对齐。

 

我现在越来越相信,系统架构设计最重要的不是选型,而是分层思维:谁负责吞吐,谁负责削峰,谁负责一致性,谁在失败时兜底。把这几个角色分清楚,架构图自然会长出来;反过来,先画一张大图,再回头找数据流补逻辑,最后大概率会变成“每层都做了一点,但没有一层真正做好”。

 

架构不是一次性设计完的,更像是持续给系统减压。先找到真正的瓶颈,再决定是加缓存、上队列,还是改索引,这比盲目追新技术靠谱得多。

posted @ 2026-07-10 09:05  fitch_liu  阅读(4)  评论(0)    收藏  举报