易君召
易君召
发布于 2026-07-23 / 1 阅读
0
0

高并发 Java 后端数据库完整设计方案

整体分层思路:库架构分治(分库分表)→ 读写分离 → 缓存削峰 → 事务 / 锁优化 → SQL / 索引优化 → 限流降级兜底,适配 Java SpringBoot/MyBatis/MyBatis-Plus 技术栈。

一、基础架构:读写分离(解决读多写少并发瓶颈)

1. 架构模型

1 主 N 从:主库负责写(Insert/Update/Delete),从库负责查询

  • 同步方式:MySQL 半同步复制(避免主从延迟过大)

  • 主从数量配比:常规 1 主 2 从;读压力极大 1 主 4~8 从

2. Java 层落地实现

方案 1:MyBatis-Plus / Spring AbstractRoutingDataSource 动态数据源

自定义注解 @Master(走主库)、@Slave(走从库),AOP 拦截方法切换数据源。

java

运行

// 写操作强制主库
@Master
public void createOrder(OrderDTO dto) {
    orderMapper.insert();
}
// 查询走从库
@Slave
public Page<OrderVO> listOrder(Long userId) {
    return orderMapper.selectPage();
}

方案 2:中间件代理(推荐高并发)

  • Sharding-JDBC(客户端分片,无中间件单点,Java 原生集成)

  • MyCat(独立中间件,统一数据库代理,适合多语言后端)

3. 主从延迟解决方案(高并发核心痛点)

  1. 强制读主:新增数据立刻查询场景(创建订单后查详情),注解强制走主库

  2. 业务缓存兜底:写入主库同时更新 Redis,查询优先读缓存,规避延迟

  3. 半同步复制:主库写入成功必须等至少一台从库接收 binlog 再返回客户端

  4. 避免长事务:主库大事务会阻塞 binlog 同步,导致从库严重滞后

二、水平扩展:分库分表(解决单库单表百万 / 千万级数据瓶颈)

1. 分库分表两种模式

垂直拆分(业务维度拆分)

按业务模块拆库:订单库、用户库、商品库、支付库

  • 优点:业务隔离,互不锁库,单库压力降低

  • 适用:系统业务模块清晰、耦合低

水平拆分(数据量维度拆分,高并发必备)

同一业务表,按分片键拆分多张表 / 多个库,比如订单表 t_order_00 ~ t_order_31

分片键选择黄金标准
  1. 高频查询条件(userId、orderId)

  2. 均匀分片,避免数据倾斜

  3. 极少跨分片关联查询

分片算法(Sharding-JDBC 原生支持)
  1. 取模分片(最常用)

    分片键 % 分片总数,例如用户 ID%32,均匀分布 32 张表

  2. 范围分片(时间分片,订单 / 日志)

    按创建时间分:按月分表 t_order_202601t_order_202602

    优点:范围查询友好;缺点:热点月份数据倾斜,搭配冷热数据分离

2. Sharding-JDBC Java 落地核心配置

核心能力

  • 读写分离 + 分库分表一体化

  • 分布式主键(雪花算法 Snowflake,Java 内置,全局唯一 ID)

  • 分布式分页、聚合函数、跨库关联查询适配

避坑要点

  1. 禁止无分片键查询:会触发全表扫描,并发直接打满数据库

  2. 跨分片事务:MySQL 不支持分布式强事务,优先最终一致性

  3. 分页 offset 过大:分表分页必须通过分片键过滤,禁止大偏移分页

3. 冷热数据分离(海量历史订单)

  • 热数据:近 3 个月订单,分库分表在线读写

  • 冷数据:超过 3 个月历史订单,归档至归档库 / ClickHouse,线上业务不查询

    定时任务(XXL-Job)迁移冷数据,降低主库存储、索引压力

三、缓存体系:数据库前置削峰(高并发第一道防线)

核心逻辑:缓存扛 90% 读请求,数据库只处理写和少量强一致查询

1. 多级缓存架构

  1. 本地缓存(Caffeine)

    Java 进程内存缓存,毫秒级,无网络开销;适合字典、配置、热点商品基础信息

  • 过期策略:写入更新缓存 + 定时淘汰,避免数据不一致

  1. 分布式缓存(Redis Cluster 集群)

  • 架构:Redis 3 主 3 从集群,分片存储热点数据

  • 存储内容:用户会话、订单详情、商品库存、计数类数据

  1. 数据库兜底:缓存失效 / 穿透时才查询数据库

2. 缓存三大并发问题解决方案

  1. 缓存穿透(查询不存在数据,绕过缓存打数据库)

    • 方案:布隆过滤器(Redis)、空值缓存、参数校验拦截非法 ID

  2. 缓存击穿(热点 Key 过期,大量请求击穿数据库)

    • 方案:互斥锁(Redis SETNX)、热点永不过期、逻辑过期

  3. 缓存雪崩(大量 Key 同时过期,数据库瞬间压垮)

    • 方案:过期时间随机偏移、Redis 集群高可用、熔断限流

3. 缓存与数据库双写一致性(Java 业务标准流程)

标准流程(推荐):先更新数据库,再删除缓存

@Transactional
public void updateOrder(Order order) {
    // 1. 更新主库数据库
    orderMapper.updateById(order);
    // 2. 删除Redis缓存,下次查询自动加载最新数据
    redisTemplate.delete("order:" + order.getId());
}
  • 强一致场景:分布式锁锁住读写操作,保证更新 + 删缓存原子性

四、事务与并发锁设计(避免超卖、脏写、数据不一致)

1. 事务选型

  1. 单机本地事务:单库使用 Spring @Transactional,REPEATABLE READ 隔离级别(MySQL 默认)

  2. 分布式事务(跨库 / 分库场景)

    高并发禁止强一致性 2PC(Seata AT 性能差),分层选型:

    • 最终一致性(首选):Seata TCC / 本地消息表 + 定时任务补偿(订单支付、库存扣减)

    • 可靠消息队列:RocketMQ/Kafka 事务消息,解耦削峰

2. 并发控制锁方案(按并发量从小到大)

1)乐观锁(高并发读、少写场景,无数据库阻塞)

基于版本号实现,Java MyBatis 示例:

sql

update t_order set status=1,version=version+1 
where order_id=#{id} and version=#{oldVersion}

适用:商品库存、订单状态流转,无锁竞争,性能极高

2)悲观锁(写竞争极强、不允许失败场景)

  • 行级锁:select ... for update,只锁定单行,并发友好;必须命中索引,否则升级表锁

  • 表锁:禁止高并发使用,会阻塞全表读写

3)分布式锁(跨服务 / 分库场景)

  1. Redis 分布式锁(SET + EX + NX,Redisson 封装,Java 首选),支持可重入、锁超时自动释放

  2. Zookeeper 临时节点锁:一致性更强,性能低于 Redis,适用于极低并发强一致场景

五、SQL、索引、连接池优化(Java 应用层基础优化)

1. 索引设计规范(MySQL 高并发核心)

  1. 分片键、查询条件、关联字段建立联合索引,遵循最左前缀

  2. 避免回表:覆盖索引(SELECT 只查索引字段,不查 *)

  3. 禁止索引失效操作:函数运算、隐式转换、like % xxx、not in、or

  4. 大字段(text、blob)拆分至附属表,避免索引臃肿、分页缓慢

2. SQL 编写约束(Java MyBatis 强制规范)

  1. 禁止大事务、批量超大 Inset/Update,拆分分批操作

  2. 禁止多表笛卡尔积关联,分库分表禁止跨库 join

  3. 分页禁止 limit 100000,10;改用主键游标分页 where id > lastId limit 10

  4. 批量操作使用 MyBatis batch 模式,减少网络 IO

3. Java 数据库连接池(HikariCP,SpringBoot 默认)

高并发参数调优核心:

yaml

spring:
  datasource:
    hikari:
      maximum-pool-size: 20  # 最大连接数,CPU核心数*2~5,避免过大导致MySQL连接耗尽
      minimum-idle: 5
      idle-timeout: 300000
      connection-timeout: 20000
      max-lifetime: 1800000
  • 优化点:连接数不要盲目调大;SQL 执行超时熔断;监控连接泄露

六、高并发流量兜底:限流、熔断、降级

数据库承载能力有限,不能无限承接流量,Java 微服务配套防护:

  1. 接口限流:Guava RateLimiter 本地限流 + Redis 分布式限流,限制每秒请求量

  2. 熔断降级(Sentinel)

    • 数据库查询超时 / 报错比例达到阈值,触发熔断,直接返回缓存 / 默认数据,不再访问数据库

    • 非核心业务降级:商品评论、日志查询直接返回空,保护订单、支付核心库

  3. 请求队列削峰:高并发写请求(下单、扣库存)通过 MQ 异步削峰,同步转异步,平缓写入数据库

七、高可用架构设计(避免数据库单点故障)

  1. 主库高可用:MGR MySQL 组复制 / Keepalived 虚拟 IP,主库宕机自动切换从库为主,Java 无感知

  2. 从库多副本:至少 2 台从库,一台故障自动路由其他从库

  3. 定时数据备份:全量备份(mysqldump)+ binlog 增量备份,支持故障数据回滚

  4. 监控告警:Prometheus + Grafana 监控指标:慢 SQL、连接数、锁等待、主从延迟、QPS,异常短信告警

八、不同并发量级完整落地组合方案

1. 百万日活,QPS < 2000(中小型项目)

单库主从读写分离 + Redis 分布式缓存 + HikariCP 优化 + 乐观锁,无需分库分表

2. 千万日活,QPS 2000~10000(中大型电商)

读写分离 + Sharding-JDBC 水平分表(按 ID 取模)+ Redis Cluster 多级缓存 + Seata TCC 分布式事务 + Sentinel 熔断限流

3. 超大型平台,QPS > 10000,亿级数据

多主多从分库集群 + 时间范围分片 + 冷热数据归档 ClickHouse + MQ 全异步写 + 本地消息表最终一致性 + 全链路限流熔断

九、高频踩坑总结

  1. 分表无分片键查询,全表扫描拖垮数据库

  2. 主从延迟不做缓存兜底,用户查询旧数据

  3. 热点商品无缓存,秒杀流量直接压库

  4. 大事务、长 SQL 导致行锁升级表锁,并发卡死

  5. 连接池最大连接数设置过大,MySQL 连接耗尽拒绝连接

  6. 分布式事务使用 2PC,高并发下性能雪崩


评论