性能不是系统上线后的“锦上添花”,而是分布式架构能否承载真实业务的基本能力。电商页面多等一秒,用户可能直接离开;物联网控制链路慢几秒,用户会选择手动按按钮。分布式系统的难点在于:慢不一定慢在代码,也可能慢在网络、数据库、缓存、队列、序列化、线程池,甚至是一次不合理的跨服务调用。
先找到瓶颈,而不是先加机器
性能优化最常见的误区,是看到响应慢就扩容。扩容当然有用,但如果瓶颈在数据库锁等待、接口串行调用、缓存击穿或连接池耗尽,单纯加机器只会把问题推迟。
一个可落地的排查顺序通常是:
- 看入口指标:P50、P95、P99 延迟,而不是只看平均值。
- 看资源指标:CPU、内存、磁盘 IO、网络带宽、连接数。
- 看依赖指标:数据库慢查询、缓存命中率、RPC 超时率、消息堆积。
- 看链路指标:一次请求经过哪些服务,每段耗时多少。
平均响应时间容易骗人。比如 99% 的请求 50ms 返回,1% 的请求 5s 返回,平均值可能还不错,但用户已经感知到卡顿。分布式系统更应该关注尾延迟。
架构层面的优化:减少不必要的远程成本
分布式系统的性能损耗,很多来自“远程调用太随意”。一次本地函数调用可能是纳秒级,一次跨服务调用则要经历网络传输、序列化、反序列化、连接池等待、服务端调度等过程。
常见优化方向包括:
- 合并读请求:商品详情页不要串行请求商品、库存、价格、优惠、评价,可以使用聚合服务或并行调用。
- 降低调用深度:避免 A 调 B,B 调 C,C 再调 D 的长链路。
- 使用缓存:对商品详情、配置、字典、权限等读多写少数据做缓存。
- 异步化非关键路径:下单后发短信、写行为日志、更新推荐特征,可以通过消息队列削峰。
- 就近访问:CDN、边缘缓存、同机房调用、读写分离都能减少网络成本。
这里的关键不是“把所有东西都缓存”,而是区分核心路径和非核心路径。用户点击“提交订单”时,库存扣减和订单创建必须可靠;发送营销消息则可以异步。
可以这样实践:给商品查询加缓存和超时保护
下面是一个可以直接改造的 Python 示例,模拟商品详情查询:优先读 Redis 缓存,缓存未命中再查数据库;同时给数据库访问设置超时思路,避免慢依赖拖垮整个接口。
运行前需要本地有 Redis:
pip install redis
redis-server
示例代码:
import json
import time
from concurrent.futures import ThreadPoolExecutor, TimeoutError
from redis import Redis
redis_client = Redis(host="localhost", port=6379, decode_responses=True)
executor = ThreadPoolExecutor(max_workers=8)
def query_product_from_db(product_id: str) -> dict:
# 模拟数据库查询。实际项目里替换成 ORM 或 SQL 查询。
time.sleep(0.2)
return {
"id": product_id,
"name": "Mechanical Keyboard",
"price": 399,
"stock": 128,
}
def get_product(product_id: str) -> dict:
cache_key = f"product:{product_id}"
cached = redis_client.get(cache_key)
if cached:
return {"source": "cache", "data": json.loads(cached)}
future = executor.submit(query_product_from_db, product_id)
try:
product = future.result(timeout=0.5)
except TimeoutError:
return {
"source": "fallback",
"data": {"id": product_id, "message": "product service is busy"},
}
redis_client.setex(cache_key, 60, json.dumps(product))
return {"source": "db", "data": product}
if __name__ == "__main__":
print(get_product("10001"))
print(get_product("10001"))
第一次请求通常来自数据库,第二次请求来自缓存。真实系统里还要继续补上三件事:
- 缓存空值:避免不存在的商品 ID 反复打到数据库。
- 随机过期时间:避免大量 key 同时失效导致缓存雪崩。
- 热点保护:对爆款商品使用本地缓存、请求合并或限流。
数据库优化:让查询少做无用功
数据库往往是分布式系统里最昂贵、最容易被打爆的共享资源。优化数据库不是只会“加索引”,而是要让查询路径更短、锁冲突更少、返回数据更小。
可以优先检查这些点:
- SQL 是否只查询需要的字段,避免
select *。 - where 条件是否命中合适索引。
- 分页是否使用深分页,例如
limit 100000, 20。 - 是否把高频统计、聚合查询压在主库上。
- 是否存在长事务导致锁等待。
例如商品列表页可以用游标分页替代深分页:
-- 不推荐:页数越深,扫描越多
select id, name, price
from product
order by id
limit 100000, 20;
-- 推荐:用上一页最后一个 id 继续向后取
select id, name, price
from product
where id > 100000
order by id
limit 20;
如果业务需要按销量、价格、时间复杂排序,可以考虑把检索压力交给搜索引擎,数据库负责事务和权威数据。
缓存、队列和限流不是银弹
缓存能降低数据库压力,但会引入一致性问题;队列能削峰,但会引入延迟和补偿逻辑;限流能保护系统,但会拒绝部分请求。性能优化永远伴随取舍。
几个边界要提前说清楚:
- 强一致业务不要随意读缓存,例如支付状态、账户余额。
- 队列消费必须支持幂等,否则重试会造成重复扣减或重复通知。
- 限流要区分用户、接口、租户、来源,不能只做全局一刀切。
- 超时要配合重试预算,盲目重试会放大故障。
一个简单的 Nginx 限流配置可以这样改造:
http {
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
server {
listen 8080;
location /api/ {
limit_req zone=api_limit burst=20 nodelay;
proxy_pass http://backend_service;
proxy_connect_timeout 1s;
proxy_read_timeout 3s;
}
}
}
这段配置按客户端 IP 限制 /api/ 请求速率,并设置后端连接和读取超时。生产环境里还需要结合网关、用户身份、业务优先级做更细粒度控制。
落地清单:把优化做成闭环
分布式系统性能优化应该是一条闭环,而不是一次专项运动:发现慢请求,定位瓶颈,做小步改造,压测验证,上线观察,再继续迭代。
建议从这份清单开始:
- 为核心接口建立 P95、P99 延迟目标。
- 接入链路追踪,能看到每个服务和依赖的耗时。
- 给所有远程调用设置超时、熔断和降级策略。
- 把读多写少的数据纳入缓存,但设计好失效和一致性方案。
- 用消息队列处理非关键路径,并保证消费幂等。
- 定期分析慢 SQL、缓存命中率、队列堆积和错误重试。
真正有效的性能优化,不是把某个指标压到极致,而是在成本、稳定性和用户体验之间找到可持续的平衡点。