高并发设计

22次阅读
没有评论

高并发的解决方案有:

  • 拆表:按照不同功能拆分,解决以下问题(分库分表: 就是拆表的战略手段)
    • 查询慢:SELECTJOIN 等操作需要扫描大量数据,速度急剧下降。
    • 写入 / 更新慢:INSERTUPDATEDELETE 操作会创建锁,大表锁竞争激烈,阻塞其他操作。
    • 索引膨胀:索引文件会变得非常大,维护索引的成本变高,甚至可能内存都放不下。
    • 备份与恢复:备份一张几个 TB 的表需要极长时间,恢复更是噩梦。
    • ALTER 操作:给大表加个字段或修改索引可能会锁表数小时,导致服务不可用。
    • 处理大表需要更高配置的服务器(CPU、内存、磁盘 IO),成本高昂。
  • 服务分布式:服务拆分,提高解耦
  • 容错设计:当服务雪崩,请求熔断进行降级
  • 合理使用多线程:在后端服务中使用多线程处理高并发事务,减少请求事务的等待
  • 可观测性组件 (Observability):分布式系统复杂度高,必须有一套完善的监控体系。

拆表

分为垂直分表和水平拆表

 垂直分表 (Vertical Sharding/Splitting)

将一张宽表中的不常用字段、占用空间大的字段(如 TEXT, BLOB)拆分到一张“扩展表”中,而原表只保留核心、常用的字段。例如账户信息表,只保留 user_id, username, password, create_time, last_login 字段,扩展的字段表 user_id, profile_pic, biography, …

优点:减少了磁盘 I /O(因为单次读取的数据页包含了更多行核心数据),提高了热点数据的缓存效率。

缺点:需要联表查询才能获取完整信息。

水平分表 (Horizontal Sharding/Splitting)

怎么拆:按行拆分。表结构完全一样,但每张表存储一部分数据行。这也就是我们常说的 分片 (Sharding)。例如原用户表 users 有 1 亿行数据,按 user_id % 10 拆分成 10 张表:

优点:极大地解决了单表数据量过大的问题,性能提升显著。是应对海量数据的核心手段。

缺点 复杂度最高。跨分片的查询、排序、分页、事务处理变得非常复杂(例如,要查询所有分片再汇总结果)。是分布式数据库领域的核心难题。

分库分表

以 sass 系统的租户为例,可以分为多种:

  • 以租户维度,一个租户一个数据库,每个租户的数据库独立一个库,库内包含租户用到的表
    • MySQL 默认是 100 个,虽然上限较高(_操作系统对“单个目录下的子目录数量”有默认限制(如 Linux 系统的 ext4 文件系统,默认单个目录下最大子目录数约为 65536))
    • 管理复杂度飙升:过多数据库会增加权限管理(如给不同用户分配库权限)、备份恢复(需逐个或批量处理库)、监控维护(如排查某库性能问题)的难度,通常业务中会将数据库数量控制在数百个以内,避免管理成本失控。
    • InnoDB 共享表空间:若所有数据库的表共用一个 ibdata1 文件,数据库数量本身不直接增加文件数,但会导致共享表空间过大,后续维护(如收缩空间)困难,间接限制库的数量。
    • InnoDB 独立表空间:每个表对应 *.ibd 文件,数据库数量越多,磁盘上的文件数也越多,会加剧“操作系统文件数限制”和“磁盘 I/O 竞争”问题,进一步压缩可创建的库数量。
    • 100 – 500 个(常规业务),建议超过 200 个创建服务实例
  • 以业务类型时间维度创建表,一个业务多个表建立冷热数据分离,在查询的时候使用 ES(Elasticsearch)多表联查
    • 优点:简单,解决数据量大的问题。
    • 业务搜索复杂度增加
    • 缺点:无法解决单个数据库实例的连接数、CPU、磁盘 IO 等瓶颈。
  • 既分库也分表:将业务数据量比较大的分库拆分表,将数据量少或者访问量少的放一个库建表
    • 优点:这是终极方案,既解决了单表数据量大的问题,又解决了单库实例的性能瓶颈(连接数、CPU、磁盘 IO)。
    • 缺点:复杂度最高,面临全局主键、分布式事务、跨库查询等所有分布式难题。
高并发设计

分布式微服务

高并发设计

后端分布式架构已经从早期的单体架构、垂直架构,演进到今天以 微服务架构 为核心的形态。微服务架构本身就是一种分布式系统架构。

以上面的电商系统为例:

架构层面

以 nginx、HAProxy 或 LVS 等软件 / 硬件负载均衡到各个服务访问。API 网关 (API Gateway):这是微服务架构的 统一入口,是所有客户端请求的第一站,至关重要。然后以分布式拆分多个服务,每个服务熔断都不影响其他服务。使用 KafkaRabbitMQRocketMQ 等消息队列。将写数据库、发邮件、处理大文件等任务作为消息放入队列,由后台的工作进程异步消费。

API 网关 (API Gateway)功能

  • 路由转发:将请求路由到正确的后端微服务。
  • 认证鉴权:统一校验用户身份和权限,避免每个服务都重复实现。
  • 限流熔断:对恶意流量或突发高峰进行限流;当后端服务故障时,快速失败(熔断),防止雪崩效应。
  • 日志监控:收集所有入口请求的日志和指标。
  • 负载均衡:对后端多个服务实例进行负载均衡。

主流技术Spring Cloud GatewayKong(基于 OpenResty (Nginx + Lua))、Apache APISIX(新一代云原生 API 网关,性能比 Kong 更优,动态热加载能力突出。)

数据层面(重中之重)

  • Redis:几乎是大数据高并发的标配,读写性能极高(10W+ QPS)。
  • Memcached:简单的分布式内存缓存系统。
  • 数据库读写分离:主数据库负责写操作,多个从数据库负责读操作,极大提升读性能。
  • 分库分表:当单表数据量过大(千万 / 亿级)时,进行水平和垂直拆分。

使用高性能数据库

  • NewSQLTiDBOceanBase 等,兼容 MySQL 协议,同时具备分布式架构的强大扩展性和一致性。
  • NoSQL:根据场景选用合适的非关系型数据库。
    • MongoDB:处理非结构化、文档型数据。
    • Elasticsearch:专精于搜索和日志分析。
    • HBaseCassandra:适合海量数据的存储与随机读写。

代码与框架层面

  • 连接池与线程池:使用数据库连接池(如 HikariCP)、线程池,避免频繁创建和销毁连接 / 线程的开销。
  • 高性能框架:选择原生支持异步、非阻塞的框架,如 Java 的 Spring WebFluxGo 的 GinPython 的 FastAPI 等,可以用更少的资源处理更高的并发。

熔断与降级

解决方案

  • 熔断器:当失败调用达到一定阈值,熔断器打开,后续调用直接快速失败,不再请求远端服务。
  • 降级:在熔断或服务不可用时,提供一种备选方案(如返回默认值、缓存数据),保证核心流程可用。

主流技术Sentinel(阿里,功能丰富,可视化好)、Hystrix(Netflix,已逐步被 Sentinel 取代)

服务维护与排查

  • 链路追踪 :为每个请求生成一个全局唯一的 TraceID,在整条链路中传递,记录每个环节的耗时。
    • 主流技术SkyWalking(国产优秀,对 Java 应用无侵入)、ZipkinJaeger
  • 指标监控 (Metrics):收集 CPU、内存、JVM 指标、QPS、响应时间等。
    • 主流技术Prometheus(拉模式,已成为云原生监控标准)+ Grafana(可视化展示)
  • 日志收集 (Logging):集中收集所有服务的日志,方便查询和排查问题。
    • 主流技术ELK Stack (Elasticsearch, Logstash, Kibana) 或 EFK (Fluentd)。

订单高并发示例

电商系统为例假如每天订单 1000W,单库单表 => 3 亿 / 月,索引巨大,查询慢。单机 1.16 万 / 秒,写入 CPU、I/ O 扛不住。解决办法:分库分表,同时分散到不同实例,突破单机资源瓶颈、故障隔离分表减少单表数据量,提升查询性能、降低锁竞争、方便 DDL;

  • 使用订单 id 作为全局分片键
    • 主订单表存完整数据,支持详情、修改、对账
  • 分买家索引和卖家索引支持买卖双方搜索
    • 分片键 buyer_id、seller_id
    • 存储 order_id、amount、status、create_time 等 简要字段

数据唯一,避免冗余;写入一次、维护简单;索引支持买卖双方高效查询

数据库分库分表设计

1000W/ 天,一个订单数据 1kb,10G/ 天,300G/ 月。根据状况数据库 创建 8 个库,每个库 16 张表,共 126 张表,平均到每个表是 2.34G(约 234 万数据),达到了数据库的最佳性能。假设最佳销售时间长达 12 小时(非 24 小时,紧凑一点),平均每个表写入 232 条 / 秒,高峰假设:1000 条每秒,8 个库平均写入 125 条 / 秒 / 库。I/ O 写入极低,固态硬盘完全能处理

冷热数据分离

根据当前系统 1000W/ 天、3 亿 / 月、40~50 亿 / 年的情况要对数据库进行划分。根据数据访问的频率分冷热数据,最近一个月的热数据分到一个高性能的数据库中,超一个月之前的订单作为低频访问归档,或者迁移到成本更低的存储(如 HBase、ClickHouse、HDFS、对象存储)。历史订单的接口延时适当放宽一些。

复杂业务场景查询

例如用户要查询最近大于 100 元的消费分页,使用 MYSQL 的多字段组合索引效率低、范围查询 + 精确匹配索引选择性差、扩展行差无法支持聚合。

elastic:倒排索引 +BKD 树、Filter Cache、天然支持聚合分析、未来可轻松扩展

综合处理解决方式

高并发设计
正文完
 0
评论(没有评论)
验证码