关于高并发的一连串问题分析(未完成)
高并发场景如何处理
一、流量分层
- 首先我们可以做冷热数据的处理,比如把超过三个月的历史数据放到另一个表甚至历史数据库中,同时页面上把查询历史数据的接口独立出来,不影响近期数据的查询;
- 然后就是做流量分摊,通过分库分表(比如通过用户id,哈希值分多个库)+读写分离的方式把压力坟山到不同的数据库当中;
- 做好缓存,我们可以把一些不经常i需改的数据(如订单数据)缓存到redis中,同时也可以做好缓存的预热,把预计会成为热点的数据提前缓存到redis里。(详情见下文补充)
二、突破搜索瓶颈
使用es做搜索引擎解决分库分表带来的搜索难题;(详情见下文补充)
三、使用消息队列优化响应
高并发场景下,我们可以将请求异步处理,先把已在处理的消息返回给前端,然后把一个个的请求丢到消息队列中(Kafka、RocketMq、RabbitMq)一个个进行处理,这样可以缓解短时间高并发带来的影响;(详情见下文补充)
四、系统监控自保
- 监控预警:慢SQL监控,CPU、网络、IO等的监控,实时返回监控数据,超过阈值发出预警;
- 流量管控:非核心的业务如果超过阈值可以限流,不影响主程序的运行;
- 熔断降级:如果请求量过大,数据库压力剧烈增长超过设定的阈值,非核心业务可以自动降级,修改为只查询缓存,或者返回业务繁忙等信息;
前面提到的redis缓存,可能会存在一些问题,首先就是:
数据库和缓存如何保证数据的一致性(MySQL和redis为例)
- 先更新MySQL,再更新redis,但是这存在风险,如果更新redis失败,MySQL无从得知,这样可能就会导致数据不一致;
- 缓存旁路模式(Cache-Aside模式):删除redis缓存再更新MySQL,再次查询的时候将数据添加到缓存当中,这种方案解决了1方案的维妮塔,但是在该并发下性能相对1较低,而且仍然会出现数据不一致的问题。比如线程1删除了redis的数据,正在更新MySQL数据,此时另外一个线程在查询,查询的时候把更新前的数据更新到redis当中,也会导致数据的不一致;
- 延时双删:步骤是先删除redis缓存的数据,再更新MySQL,延时几百毫秒再删除redis缓存数据,这样就算在更新MySQL是,其他线程读取了MySQL,也会把读取后的数据删除,下次查询过来时查询到的是更新后的数据,这样就可以保证数据的一致性;
- 在3的基础上,我们也可以通过添加分布式锁来保证同时只有一个线程更新缓存;
实际工作场景中如何取舍:
根据业务场景选择策略
场景 | 推荐方式 | 原因 |
---|---|---|
读多写少 | 缓存旁路模式+过期实际 | 简单高效 |
高一致性要求 | 分布式锁+延迟双删 | 保证强一致性 |
高并发写 | 异步更新+消息队列 | 提升性能 |
关键业务数据 | 多重保障策略 | 降低风险 |