代码工艺:高并发解决方案介绍

扩容方案:横向扩展

“横向扩展”就是增加更多的服务器来解决性能瓶颈问题。例如,如果应用服务器是瓶颈,就添加更多应用服务器;如果数据库服务器是瓶颈,就添加更多的从库。这种做法虽然看似简单粗暴,但在50%以上的场景中,尤其是读多写少的场景下,这种方案非常有效。

  • 举例:当系统处理1000 QPS时,使用三台应用服务器和一台数据库服务器就足够了;当处理2000 QPS时,则增加到六台应用服务器和两台数据库服务器(一主一从)。

需要注意的是,从库读取数据存在主从延迟问题,尤其在需要“写后读”的场景中,建议直接读取主库。如果从库过多,还可能导致主库压力增加,进而使延迟问题恶化。

引入缓存

在读多写少的高并发场景中,除了横向扩展外,缓存也是一种常见解决方案。与横向扩展相比,缓存策略更复杂,因为需要考虑缓存的选型和数据一致性问题。

  • 缓存选型:通常可以选择本地缓存(如HashMap或Guava Cache)或者集中式缓存(如Redis)。大部分场景下,Redis是无脑选择,只有当有大key热key,或数据几乎不变化时,本地缓存才是更合适的选择。

  • 一致性问题:缓存和数据库的数据一致性问题通常是高频面试题,解决方案众多。但较为实用的做法是“先删缓存,后写数据库,再延时双删”,这可以在大多数情况下保持数据一致性。

  • 举例:在线教育场景下的课程体系对象JSON串,基本不怎么改变,且key较大,适合使用本地缓存来减少网络瓶颈。

引入ES(Elasticsearch)

在一些需要多维复杂查询的场景下,单靠Redis和本地缓存这种Key-Value结构的存储方案无法满足需求。此时,Elasticsearch(ES)的引入是一个很好的补充。ES通过分词器倒排索引,比MySQL的B+树索引更适合处理复杂查询。

  • 数据同步:常见的方案是通过Canal或DataBus进行Binlog同步,避免双写的方式,因为写操作入口过多,难以收敛。

分库分表

分库分表的策略主要应对高并发写和读写混合场景。

  • 分库:当数据库服务器硬件成为瓶颈(如CPU使用率100%、IOPS过高、网卡满载),可以考虑分库。

  • 分表:当数据库锁(如记录锁、表锁等)成为瓶颈时,可以通过分表来解决锁竞争问题,特别是在高并发写场景下,分表可以减少资源竞争。

分表又分为垂直分表水平分表

  1. 垂直分表:将表中的大字段分离出去,减少数据页中无关数据的存储,提升查询效率。

    • 举例:用户表中,用户名、性别和年龄字段占用空间较小,而地址和个人简介字段占用较大,可将后者拆分出去,提升性能。
  2. 水平分表:当表中的数据量过大时,B+树的层级增加,查询性能下降,此时可以通过水平分表来提升性能。

MQ消峰

消息队列(MQ)是应对高并发写入的常见策略之一,主要通过两种方式:

  1. 将请求直接放入MQ中,消费者再逐步处理。
  2. 先处理重要且不耗时的操作,之后再将耗时操作放入MQ中。

这种策略在应对瞬时高并发问题时效果明显,但如果请求量长期处于高位,仍需提升系统整体处理能力。

单元化

单元化架构是一种解决高并发场景的终极方案,其核心在于将用户请求按特定规则路由到不同的“单元”,并在单元内部实现业务逻辑闭环,起到分散压力快速扩容灾备切换的作用。

  • 关键点:单元化的核心在于路由、隔离和闭环。合理的Sharding Key(分片键)策略至关重要,一旦选择失误,可能对系统架构带来灾难性影响。

  • 举例:美团外卖的Sharding Key是商家所在的城市ID。

尽管单元化方案十分强大,但其成本和风险也相对较高,适用于大厂和订单量过千万的系统,不适合小规模应用。

你可能感兴趣的:(代码工艺,1024程序员节)