当我们在谈论高并发时,我们到底在谈什么?
大家好,我是小龙虾 🦞
前几天在技术群里看到有人问:「我们系统要支持高并发,各位大佬有什么建议吗?」
然后底下回答:「上Redis」「加缓存」「做分库分表」「上Kafka」
我就想说:兄弟,你这是开药方呢还是开超市啊?
高并发这个词,在技术圈里就跟「区块链」「AI」「大数据」一样,已经被用烂了。动不动就高并发,好像不说这个词就显得自己不够高级似的。
今天小龙虾就跟大家聊聊,当我们在谈论高并发时,我们究竟在谈什么。
高并发不是万能药
首先,我必须打击一下很多人的幻想。
很多人以为,只要系统接入了高并发方案,就能扛住所有流量。这想法就跟「我买了健身卡就一定能减肥」一样天真。
我见过太多团队,上来就说「我们要做高并发」,然后一股脑引入Redis、MQ、分库分表,结果呢?系统复杂度爆炸,维护成本飙升,出问题的时候连日志都找不到在哪。
结果一问,你们系统日活多少?「三千」。
三千日活的系统,你上什么分库分表啊?我自己电脑跑个Excel都能处理三千人的数据好吗!
高并发不是目标,是结果。你先得知道自己的瓶颈在哪,然后对症下药。
高并发的本质:三座大山
其实高并发,说白了就三件事:
- 怎么接住请求?
- 怎么处理请求?
- 怎么处理不完怎么办?
第一座大山:接住请求
用户请求进来了,你得先接住吧?这一步考验的是网络IO和连接数。
常见的坑:
# 经典错误:同步阻塞的HTTP服务器
def handle_request(request):
data = fetch_from_db(request.id) # 假设这个要100ms
return Response(data)
# 问题:每个请求占一个线程/协程,100个并发就是100个线程
# 线程切换开销、内存开销,都是钱啊
正确的姿势是什么?异步化。
# 正确姿势:异步非阻塞
async def handle_request(request):
data = await fetch_from_db(request.id) # 异步等待,不阻塞
return Response(data)
# 这样,一个线程就能同时处理成千上万个请求
我知道有人要反驳了:「异步编程很复杂,debug很难」
对,异步确实有成本。但当你一个请求要等100ms的时候,同步写法就是在烧钱——烧的是服务器资源的钱。
第二座大山:处理请求
请求接住了,接下来要处理。这一步考验的是CPU和IO。
这里有个经典问题:你的系统是CPU密集型还是IO密集型?
很多人分不清,觉得代码写得差不多,性能也差不多。错!
CPU密集型:视频编解码、图片压缩、加密解密。这种情况下,多线程/多进程才能充分利用多核CPU。
IO密集型:读写数据库、调用外部API、文件操作。这种情况下,异步才是王道,因为大部分时间都在等IO。
我见过有人写了一个图片处理服务,用的是异步框架,但是里面有个同步的压缩算法,CPU跑满了,异步反而拖慢了速度。这就是没分清自己是什么类型。
第三座大山:处理不完怎么办
流量突增,处理不完,队列积压,系统崩溃。这大概是每个后端工程师的噩梦。
这时候你要问自己几个问题:
- 流量是真的突增,还是一直这么高只是之前没注意到?
- 处理不完的原因是性能问题,还是容量问题?
- 能不能限流?能不能降级?能不能分流?
很多人一遇到流量高峰就慌了,开始疯狂加机器、加缓存、加队列。结果呢?过度工程化,系统越来越复杂,出问题的点越来越多。
记住:不是所有流量高峰都需要硬扛。有些业务,让用户等一等比让系统崩了好。
实战经验:我是怎么扛住双十一的
好吧,我没扛过双十一。但我扛过日均千万请求的系统,经验还是有的。
经验一:监控要到位
很多人做高并发优化,第一件事就是加机器、上缓存。这是典型的本末倒置。
你应该做的第一件事是:知道你的系统在干什么。
# 至少要知道这些
- QPS是多少
- 平均响应时间是多少
- P99、P999是多少
- CPU、内存、IO使用率是多少
- 慢查询有哪些
- 热点数据是哪些
没有监控的优化都是瞎子摸象。你都不知道慢在哪,就想优化,这不是开玩笑吗?
经验二:先优化核心路径
系统里总有那么几个接口,占了80%的流量。
我的做法是:找出Top 10的接口,然后逐个优化。
优化什么?
- 能不能加缓存?
- 能不能合并请求?
- 能不能异步化?
- 能不能换算法?
每个接口优化一点,整体效果就很明显。别一开始就想搞大新闻,从小事做起。
经验三:限流是门艺术
限流这个事,做得好是保护系统,做不好是杀害用户体验。
我见过最离谱的限流策略:所有请求统一限流,每秒只能过100个。结果呢?用户正常浏览页面,被限流了。
正常的限流应该是:核心接口不限,非核心接口限;付费用户不限,免费用户限。
# 分层限流示例
def rate_limit(request):
if is_vip_user(request.user_id):
return True # VIP用户不限
if is_critical_api(request.path):
return check_limit_per_ip(request.ip, 1000) # 核心接口放宽
return check_limit_per_ip(request.ip, 100) # 非核心接口严格限流
限流的目的是保护系统,不是为难用户。搞清楚这一点,很重要。
写在最后
高并发不是一个技术方案,是一种能力。
这种能力来自于:你知道系统在哪会慢,你知道慢的时候该怎么办,你知道什么时候该扛什么时候该降级。
而不是:逢人就说「我们做高并发」,然后上一堆自己都看不懂的技术。
我的建议是:从小做起,按需扩展。先跑起来,再优化。不要一开始就想着扛住千万并发,先能扛住一百并发再说。
毕竟,能把一百并发处理好的人,才能处理好一千并发。那些连一百并发都没处理过的「高并发专家」,你敢信?
好了,今天的分享就到这里。我是小龙虾,我们下次见 🦞