系统设计 | 通用高并发架构设计
引言
高并发架构是设计用来支持同时处理大量用户请求或数据操作的系统架构,如何应对高并发场景也是系统设计的重要部分之一。本文总结了高并发架构的必要条件、衡量指标和具体如何设计高并发读写方案。
高并发架构概述
实现高并发系统需要三大必要条件:高性能、高可用和可拓展
高性能
高性能(High Performance)指一个系统、应用或设备能够在最短的时间内完成大量的工作任务,特别是在处理计算密集型或数据密集型任务时的能力。在后台服务场景中,一个最基础的衡量性能的指标是响应时间。
最基础的响应时间指标是平均响应时间,平均响应时间易受到极端数据的影响。10000 个请求中 9900 个请求的响应时间都是 100ms,另外 100 个请求的响应时间是 10000ms,平均响应时间是 199ms,掩盖了其中 100 请求暴露出的问题。
因此更适合的衡量指标是 PCTn(Percentage of Completion to n Tasks)。PCTn 通常表示为完成特定百分比任务所需的时间或资源。例如,PCT90 表示系统完成90%任务所需的时间。在上述例子中 PCT99 为 10000ms,相比平均响应时间更好地暴露出了系统的问题。
基于经验数据,平均响应时间为 200ms,且 PCT99=1s 的系统基本能够满足高性能要求
高可用
高可用(High Availability)是指系统即使面对硬件故障、软件故障或其他引起服务中断的问题,也能确保服务持续无中断地提供。
可用性可以描述为 系统正常运行时间/系统总运行时间,表示系统正常运行的时间占比,也可以理解为系统对外可用的概率。一般用 N 个 9 来描述可用性
可用性 | 一年内故障时间 | 一日内故障时间 |
---|---|---|
90% | 36.5d | 2.4h |
99% | 3.65d | 14.4min |
99.9% | 8h | 1.44min |
99.99% | 52min | 8.6s |
99.999% | 5min | 0.86s |
高可用性要求系统至少保证 3 个 9 或 4 个 9 的可用性。在性能指标监控中,也通常使用两者中位数 99.95% 作为监控阈值,在低于此指标时发出预警信息。
可拓展
可拓展性(Scalability)是指系统、网络或进程能够在增加资源(如硬件、软件或带宽)时保持或提高性能的能力。最理想的情况是集群节点增加几倍,处理能力就增加几倍。通常情况下拥有 80% 可拓展性的系统即满足可拓展性需求。
高并发读场景方案
读写分离
本地缓存
业务服务一般通过调用其他服务或数据库来获取数据,为了提高效率,业务服务也可以将已经获取到的数据缓存在本地,之后收到相同请求可以从本地直接获取数据返回。
本地缓存的实现基于将数据库编码格式解码为语言内部的对象、结构体和哈希表等。因为内存资源有限,因此要求本地缓存可以自动淘汰数据,最基本的缓存淘汰策略有三种:
- FIFO(First In First Out):优先淘汰最早进入缓存的数据。FIFO 是最简单的淘汰策略,可以基于队列实现。但是越被频繁访问的数据越早进入队列,这种策略缓存命中率较低,因此实践中很少使用。
- LFU(Least Frequently Used):优先淘汰最不常用的数据。LFU 会为每条数据维护一个访问计数,数据每被访问一次,其访问计数加一。LFU 适合缓存在短时间内频繁访问的热点数据,但是最近最新缓存的数据总会被淘汰,而早期访问频率高的会一直占用缓存。
- LRU(Least Recent Used):优先淘汰缓存中最近最少使用的数据。LRU 通常使用双向链表和哈希表配合实现,双向链表用于存储数据,将最新被访问的数据放置在尾部,使得每次被淘汰的都是位于头部的数据。哈希表负责定位数据的位置,以便实现快速访问。LRU 可以有效提高短期内热点数据的命中率,但是不能很好地应对偶发性访问和批量访问。
基于三种基本策略的优缺点,发展出了一些更复杂的缓存策略,如 W-TinyLFU 策略。
W-TinyLFU 策略结合了 LFU 策略和 LRU 策略的优点,将存储空间划分为两部分:
- Window LRU:此段使用 LRU 策略缓存数据,占用空间是总缓存空间的 1%
- Segment LRU:此段进一步被划分为 protected 段和 probation 段。其中 probation 段存储最近被访问 1 次的数据,protected 段存储最近至少被访问两次的数据。
W-TinyLFU 策略的工作流程如下:
- 将首次被访问的数据存入 Window 段。
- 当 Window 段已满时,使用 LRU 策略将数据挪至 probation 段。之后再被访问时移入 protected 段。
- 当 protected 段已满时,使用 LRU 策略将被淘汰的数据挪至 probation 段。
- 当数据被挪至 probation 段但其已满时,使用 LRU 策略将被淘汰的数据和被新挪入的数据对比,淘汰两者中访问频率低的数据。
W-TinyLFU 策略通过一种高效的近似频率计数方法来保持缓存的效率和公平性,特别适用于处理具有长尾访问模式的工作负载,如内容分布和访问频率快速变化的环境。
分布式缓存
将数据缓存在服务进程的本地内存中,不需要网络开销,因此性能非常高。但本地缓存也有一些缺点:
- 无法共享
- 可拓展性差
- 内存易失
- 编程语言限制
分布式缓存即牺牲掉部分性能,换取了多进程共享、跨语言、可拓展和数据持久化的能力。主流的分布式缓存系统有 Memcached 和 Redis。相比于 Memcached 而言,Redis 更为流行,主要在于 Redis 丰富的数据类型、可持久化和原生的高可用、高可拓展支持。
在使用分布式缓存时要考虑到与数据库的一致性和缓存击穿、缓存穿透和缓存雪崩的场景问题,这里不过多赘述。
高并发写场景方案
数据分区
除数据库外,数据分区还有其他形式。如秒杀系统分布式锁中,如果大量秒杀请求竞争同一个分布式锁,串行执行秒杀请求会严重拖垮性能。优化思路也是数据分区:将产品库存拆为 N 份,同时创建 N 个分布式锁。
异步写
异步写是把写请求的交互流程从用户发起写请求并同步等待结果返回改为提交请求后异步查询结果的两阶段交互。异步写适合写请求量大,单数被请求方的系统吞吐量不足的场景,如跨公网调用和异步秒杀。
写聚合
写聚合通过合并多个写操作到一个较大的批量操作中,减少了对物理介质的访问次数和操作,从而提升整体的写入性能。
引用
李琛轩.亿级流量系统架构设计与实践.2024
转载自:https://juejin.cn/post/7375170545727815690