高并发
一、什么是高并发?
高并发是指系统在短时间内能够同时处理大量用户请求或任务的能力,是衡量分布式系统、互联网应用性能的重要指标之一。它的核心目标是确保系统在高负载下仍能稳定、高效运行,同时提供良好的用户体验。
1、高并发系统的必要条件?
-
高性能:
性能代表一个系统的并行处理能力,在同样的硬件设备条件下,性能越高,越能节约硬件资源;同时性能关乎用户体验,如果系统响应时间过长,用户就会产生抱怨。
-
高可用:
系统可以长期稳定、正常地对外提供服务,而不是经常出故障、宕机、崩溃。
-
可扩展:
系统可以通过水平扩容的方式,从容应对请求量的日渐递增乃至突发
的请求量激增。
2、衡量指标
- 高性能指标:响应时间尽可能的短
- 高可用指标:可用性=系统正常运行时间/系统总运行时间
- 可扩展性指标:可扩展性=吞吐量提升比例/集群节点增加比例
3、场景分类
我们使用计算机实现各种业务功能,最终将体现在对数据的两种操作上,即读和写, 于是高并发请求可以被归类为高并发读和高并发写。比如有的业务场景读多写少,需要重点解决高并发读的问题;有的业务场景写多读少,需要重点解决高并发写的问题;而有的业务场景读多写多,则需要同时解决高并发读和高并发写的问题。将高并发场景划分为高并发读场景和高并发写场景,是因为在这两种场景中往往有不同的高并发解决方案
二、高并发读场景解决方案
1、数据库读写分离
主节点master作为写库,其他从节点slave作为读库
在主从架构可能会涉及主从数据不一致或延迟问题,为了解决该问题常见的方法有:
- 同步数据复制
在进行主从复制时主库master同步等待从库slave复制完成在返回,但是会导致吞吐量下降,影响系统并发性能,优点是无业务侵入,只需将主从复制从异步模式改为同步模式。 - 强制读主
当面对用户修改之后想立马看到修改结果,主要取决与业务对主从延迟的容忍度。 - 会话分离
比如某会话在数据库中执行了写操作,那么在接下来极短的一段时间内,此会话的读请求暂时被强制路由到数据库Master,与“强制读主”方案中的例子很像,保证每个用户的写操作立刻对自己可见。暂时强制读主的时间可以被设定为略高于数据库完成主从数据复制的延迟时间,尽量使强制读主的时间段覆盖主从数据复制的实际延迟时间。
2、本地缓存
对于本地缓存,由于其内存有限,所以在使用时要考虑到及时的清除一些冷数据,并且应该设置过期时间作为兜底策略避免内存溢出。
- 常见内存淘汰策略
- FIFO ( First In First Out)策略:优先淘汰最早的缓存的数据。
- LFU(Least Frequently Used)策略:优先淘汰最不常用的数据(计数)。
- LRU(Least Recently Used)策略:优先淘汰最近最少使用的数据(双向链表+哈希表)
- W-TinyLFU 策略
相关论文,W-TinyLFU结合了LFU和LRU策略的优点。
(1) Window LRU段(对应图中的LRU):此内存段使用LRU策略缓存数据,其占用的内存空间是总缓存内存空间的1%。
(2)Segment LRU段(简称SLRU):此内存段使用SLRU策略缓存数据,具体是将缓存段进一步划分为protected段(保护段)和probation段(试用段),其中probation段负责存储最近被访问1次的缓存数据protected段负责存储最近被访问至少2次的缓存数据。Segment LRU段内存空间的80%被分配给protected段,剩余20%的内存空间被分配probation段。
工作流程:
W-TinyLFU策略使用Count-Min Sketch近似算法来保存每条缓存数据的访问频率。
3、分布式缓存
本地缓存将数据缓存在服务进程的内存当中,因此会存在一些局限性问题。
1)无法共享:多个服务进程之间无法共享本地缓存。
2)可扩展性差:由于服务进程携带了数据,因此服务是有状态的。有状态的服务不具备较好的可扩展性。
3)内存易失性:服务进程重启,缓存数据全部丢失。
4)编程语言限制:本地缓存与程序绑定,用Golang语言开发的本地缓存组件不可以直接为用Java语言开发的服务器所使用。
- 主流的分布式缓存技术
对比维度 | Redis | Memcached |
---|---|---|
数据结构 | 支持丰富数据结构:String、Hash、List、Set、Sorted Set、Bitmap、Geo、HyperLogLog 等 | 仅支持简单 Key-Value 键值对 |
持久化 | 支持 RDB(快照)和 AOF(日志)持久化,数据可存储到磁盘并重启恢复 | 不支持持久化,数据仅存于内存,服务重启或失效后数据丢失 |
集群与高可用性 | 原生支持 Redis Cluster 集群(自动分片、故障转移),支持 Sentinel 哨兵实现主从复制和自动故障转移 | 不支持原生集群,需通过客户端(如一致性哈希)或代理层实现分片,无内置高可用机制 |
内存管理 | 支持动态内存分配(如jemalloc/tcmalloc),可配置多种淘汰策略(LRU/LFU/随机淘汰/过期优先等) | 使用固定大小的 Slab Allocation 机制,仅支持近似 LRU 淘汰策略(不可配置),可能产生内存碎片 |
单个 Value 大小限制 | 最大支持 512MB | 最大支持 1MB(超过会报错) |
附加功能 | 支持事务、Lua 脚本、发布订阅(Pub/Sub)、管道(Pipeline)、分布式锁、计数器、消息队列等 | 仅提供基础的增删改查(GET/SET/DELETE 等),无复杂功能 |
应用场景 | 复杂场景:分布式缓存、分布式锁、排行榜、地理位置查询、实时数据分析、消息中间件等 | 简单场景:页面缓存、API 结果缓存、临时数据存储(需快速读取、数据无需持久化) |
性能特点 | 单线程模型(基于 IO 多路复用),功能复杂但性能优异,适合中等复杂度场景 | 多线程模型(后期版本),极简设计,纯内存 Key-Value 操作,性能极致,适合超高并发的简单缓存场景 |
4、缓存问题
1)缓存雪崩
- 缓存雪崩是指在同一时间Redis缓存中的数据大面积过期,导致请求涌向数据库。
- 解决方案:在设置缓存过期时间时,使用随机过期时间策略或者在小范围内随机分布。
2)缓存击穿
- 缓存击穿是指单个热点 Key 失效,大量并发请求集中访问该 Key,击穿缓存大量请求访问数据库。
- 解决方案:
– 热Key不过期
–SingleFlight :请求合并,响应共享。
3)缓存穿透
- 用户频繁查找一个即不存在数据库当中也不在Redis的Key,这些请求会全部穿透Redis直达数据库导致数据库崩溃。
- 解决方案:布隆过滤器,将存在的数据通过k个hash函数进行映射将对应数组对应位置置为1,查询时通过hash函数获取对应数组内的值,判断,若任意一个位置存在0即数据一定不存在,若都为1,则可能存在,因为存在hash碰撞问题,所以布料过滤器存在一定的误判。
4)缓存不一致问题
解决方案:先更新数据库再删除缓存
5、高并发读场景总结:CQRS
CQRS(Command Query Responsibility Segregation,命令职责查询分离)
三、高并发写场景解决方案
1、数据分片
- 分库分表
- 垂直拆分:根据业务进行拆分
- 水平拆分
1)范围分区
2)哈希分区
3)一致性哈希分区
2、异步写和写聚合
-
异步写是指用户将操作提交后立即得到响应,异步查询结果
-
写聚合
将多次操作融合到一起,例如对数据库的写操作可以将一次一次的写操作融合成一个批处理操作,减少与数据库的交互次数。
四、总结
一、高并发定义与核心要素
- 定义:
系统在短时间内处理大量并发请求的能力,目标是确保高负载下稳定高效运行,核心体现为高性能、高可用、可扩展。 - 必要条件:
- 高性能:提升并行处理能力,降低响应时间(如优化算法、减少IO)。
- 高可用:系统长期稳定运行,通过冗余、故障转移等机制减少宕机(可用性=正常运行时间/总时间)。
- 可扩展性:通过水平扩容(增加节点)线性提升吞吐量(可扩展性=吞吐量提升比/节点增加比)。
二、高并发场景分类与解决方案
(一)高并发读场景
-
数据库读写分离:
- 主从架构:主库(Master)写、从库(Slave)读。
- 主从延迟解决方案:
- 同步数据复制:主库等待从库复制完成后返回(牺牲吞吐量,保证强一致)。
- 强制读主:写操作后强制当前会话读主库(适用于用户需立即看到更新的场景)。
- 会话分离:写操作后短时间内强制会话读主库,覆盖主从复制延迟(保证用户视角一致性)。
-
本地缓存:
- 淘汰策略:
- FIFO(先入先出)、LFU(最少使用)、LRU(最近最少使用)。
- W-TinyLFU:结合LFU和LRU优点,通过Count-Min Sketch计数+SLRU分段(保护段/试用段)优化热点数据缓存。
- 淘汰策略:
-
分布式缓存(Redis vs Memcached):
- Redis:支持复杂数据结构、持久化、集群(Cluster)和哨兵(Sentinel)高可用,适合分布式锁、排行榜等复杂场景。
- Memcached:纯Key-Value、高性能、无持久化,适合简单高频读(如页面缓存)。
-
缓存常见问题:
- 雪崩:大量Key同时过期,请求涌向后端。
- 解决方案:随机化过期时间(避免集中失效)。
- 击穿:单个热点Key失效,并发请求直达数据库。
- 解决方案:热点Key永不过期、加锁(如SETNX分布式锁)、请求合并(SingleFlight)。
- 穿透:查询不存在的Key,缓存和数据库均无数据。
- 解决方案:布隆过滤器(概率性判断Key存在,减少无效数据库查询)。
- 不一致:缓存与数据库数据不一致。
- 解决方案:先更新数据库再删除缓存(需注意并发问题,最终一致性)。
- 雪崩:大量Key同时过期,请求涌向后端。
-
架构模式:
- CQRS(命令查询职责分离):读写链路分离,读端可结合缓存、搜索引擎优化,写端专注事务处理。
(二)高并发写场景
-
数据分片:
- 垂直拆分:按业务模块分库(如订单库、用户库),降低单库复杂度。
- 水平拆分:
- 范围分区:按数据范围(如时间、ID区间)分片(适合有序查询)。
- 哈希分区:通过哈希函数分散数据(均匀分布,但范围查询困难)。
- 一致性哈希:节点增减时仅影响邻近数据,减少迁移量(需虚拟节点优化均衡性)。
-
异步写与写聚合:
- 异步写:用户提交后立即响应,后台通过消息队列(如Kafka)异步处理写操作(解耦流量,削峰填谷)。
- 写聚合:将多次写操作合并为批处理(如批量插入/更新),减少数据库连接开销和网络IO(提升吞吐量)。
场景 | 核心技术 | 典型工具/方案 | 核心目标 |
---|---|---|---|
高并发读 | 读写分离、缓存、布隆过滤器 | 主从复制、Redis/Memcached、CQRS | 分流数据库压力,提升读性能 |
高并发写 | 数据分片、异步写、写聚合 | 分库分表、消息队列、批量操作API | 突破单节点写入瓶颈,提升吞吐量 |
缓存问题应对 | 过期策略、锁、布隆过滤器 | 随机过期、SETNX、Count-Min Sketch | 避免缓存失效导致的数据库压力 |