亿级分布式系统架构演进实战(一)- 总体概要
亿级分布式系统架构演进实战(二)- 横向扩展(服务无状态化)
核心目标
分散数据库压力,提升读性能
1. 数据库架构设计
数据库由原理的单实例变成主从模式,主主要负责写,从负责读。
1.1 主从角色定义
节点类型 | 数据流向 | 核心职责 |
---|---|---|
主库 | 读写(Write) | 处理事务性写操作(INSERT/UPDATE/DELETE)/部分读 |
从库 | 只读(Read) | 承担查询请求(SELECT),支持水平扩展 |
1.2 半同步复制实现
目标:确保主从数据强一致性,避免异步复制导致数据丢失。
我司系统是强一致优先,各位可以根据业务情况去选择
MySQL半同步配置
-- 主库配置
INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
SET GLOBAL rpl_semi_sync_master_enabled = 1;
SET GLOBAL rpl_semi_sync_master_timeout = 1000; -- 超时1秒后降级为异步
-- 从库配置
INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';
SET GLOBAL rpl_semi_sync_slave_enabled = 1;
数据可靠性保障:
• 主库提交事务前,至少一个从库ACK确认收到日志
• 超时未确认则自动降级为异步,记录告警日志
2. 透明路由实现
选择ShardingSphere客户端模式实现
2.1 数据源配置(Druid连接池)
dataSources:
master:
dataSourceClassName: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://master:3306/db?useSSL=false
username: root
password: 123456
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
validationQuery: SELECT 1
slave1:
dataSourceClassName: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://slave1:3306/db?useSSL=false
# 其他参数同master
slave2:
dataSourceClassName: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://slave2:3306/db?useSSL=false
# 其他参数同master
2.2 读写分离规则
rules:
- !READWRITE_SPLITTING
dataSources:
pr_ds:
writeDataSourceName: master
readDataSourceNames:
- slave1
- slave2
loadBalancerName: round_robin # 轮询负载均衡
路由逻辑:
• 默认SELECT路由到从库
• 事务内的所有操作(包括SELECT)强制走主库
3. 事务内强制读主库实现
虽然数据库采用了半同步机制,但是从库还是有几率因为延迟使得请求读取了旧数据,对于对数据实时性很高的场景,可以强制走主库
3.1 基于Hint强制路由
代码示例
// 事务方法中强制读主库
@Transactional
public Order getOrderWithMasterRead(String orderId) {
// 开启Hint强制路由主库
HintManager hintManager = HintManager.getInstance();
hintManager.setPrimaryRouteOnly();
try {
Order order = orderRepository.findById(orderId); // 强制走主库
return order;
} finally {
hintManager.close(); // 关闭Hint
}
}
Spring AOP全局配置
@Aspect
@Component
public class MasterRouteAspect {
@Around("@annotation(transactional)")
public Object forceMasterRead(ProceedingJoinPoint joinPoint, Transactional transactional) throws Throwable {
HintManager hintManager = HintManager.getInstance();
hintManager.setPrimaryRouteOnly();
try {
return joinPoint.proceed();
} finally {
hintManager.close();
}
}
}
还可以自定义注释去实现,这里就不展开。
3.2 事务传播控制
配置项:
spring:
shardingsphere:
props:
sql-show: true
# 强制事务内读写均走主库
transaction-type: BASE
query-with-cipher-column: true
4. 主从同步延迟监控与治理
4.1 延迟监控(Prometheus + Grafana)
监控指标:
• mysql_slave_status_seconds_behind_master
• mysql_global_status_wsrep_flow_control_paused
告警规则:
groups:
- name: mysql_alerts
rules:
- alert: HighSlaveLag
expr: mysql_slave_status_seconds_behind_master > 5
for: 2m
labels:
severity: warning
annotations:
summary: "从库同步延迟超过阈值 ({{ $value }}秒)"
4.2 延迟解决方案
• 强制主库读
• 缓存兜底:
@Cacheable(value = "orderCache", key = "#orderId", unless = "#result == null")
public Order getOrder(String orderId) {
return orderRepository.findById(orderId); // 从库查询
}
• 通过Prometheus监测,触发告警动态摘除节点
5. 从库水平扩展(ShardingSphere负载均衡)
5.1 动态权重配置
loadBalancers:
weighted_round_robin:
type: WEIGHT
props:
slave1: 2
slave2: 1 # slave1的权重是slave2的2倍
5.2 自动扩缩容流程
这里比较繁琐可以选择手动实现,我们目前就是手动
6. 连接池优化(Druid高级配置)
我们采用的是Druid连接池,这里介绍此中间件的高级优化
6.1 动态参数调整
// 根据负载动态调整连接数
public void adjustDruidPool() {
DruidDataSource masterDataSource = (DruidDataSource) dataSource.getDataSource();
// 监控线程动态调整
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(() -> {
int active = masterDataSource .getActiveConnections();
if (active > 80) {
masterDataSource.setMaxActive(120); // 扩容
} else if (active < 20) {
masterDataSource.setMaxActive(80); // 缩容
}
}, 0, 5, TimeUnit.SECONDS);
}
6.3应用层连接池核心连接数、最大连接数设置
CPU 密集型任务(如计算、压缩)
• 核心线程数:CPU 核心数 + 1
(+1 是为了防止线程偶发缺页中断导致的 CPU 空闲)
• 最大线程数:核心线程数 × 1.5
(避免过多线程竞争 CPU,引发上下文切换开销)
示例:
• 服务器:8 核 CPU
• 配置:
int corePoolSize = Runtime.getRuntime().availableProcessors() + 1; // 9
int maxPoolSize = (int) (corePoolSize * 1.5); // 13
IO 密集型任务(如数据库查询、HTTP 请求)
• 核心线程数:CPU 核心数 × 2
(利用 CPU 在 IO 等待时的空闲时间)
• 最大线程数:CPU 核心数 × (2 + 平均 IO 等待时间占比)
(例如 IO 等待占 50% → 2 + 0.5/0.5 = 3 → 最大线程数 = 8×3=24)
示例:
• 服务器:8 核 CPU,IO 平均等待时间占比 70%
• 配置:
int corePoolSize = 8 * 2; // 16
double ioWaitFactor = 0.7 / (1 - 0.7); // 2.33
int maxPoolSize = 8 * (2 + ioWaitFactor); // ≈ 34
混合型任务(CPU + IO 混合)
• 核心线程数:CPU 核心数 × 1.5
• 最大线程数:核心线程数 × (1 + IO 占比)
(例如 CPU 占 60%,IO 占 40% → 1 + 0.4 = 1.4 倍)
动态调优与监控指标
• 性能监控关键指标
指标 | 健康阈值 | 异常处理建议 |
---|---|---|
CPU 使用率 | < 70% | 若持续 >80%,需减少线程数或优化代码 |
线程池活跃线程数 | 接近核心线程数 | 若长期 >核心数,考虑增大队列或最大线程数 |
任务队列堆积量 | < 队列容量的 50% | 若队列常满,需扩容队列或增大最大线程数 |
平均任务处理时间 | TP99 < 1秒 | 优化慢任务(如 SQL、缓存) |
• 动态调整策略
使用 Resilience4j 或 Spring Cloud Gateway 实现自适应线程池:
// 根据 CPU 负载动态调整核心线程数
ScheduledExecutorService monitor = Executors.newSingleThreadScheduledExecutor();
monitor.scheduleAtFixedRate(() -> {
double cpuLoad = ManagementFactory.getOperatingSystemMXBean().getSystemLoadAverage();
int newCoreSize = (cpuLoad > 80) ?
threadPool.getCorePoolSize() - 1 :
threadPool.getCorePoolSize() + 1;
threadPool.setCorePoolSize(Math.min(maxPoolSize, Math.max(1, newCoreSize)));
}, 5, 5, TimeUnit.SECONDS);
行业最佳实践与配置示例
• 电商秒杀系统(IO 密集型)
硬件:16 核 CPU,128GB 内存
配置:
# Spring Boot 配置
task:
execution:
pool:
core-size: 32 # 16核 × 2
max-size: 64 # 16核 × 4
queue-capacity: 1000
• 拒绝策略:CallerRunsPolicy
(避免雪崩)
实时数据分析(CPU 密集型)
硬件:32 核 CPU,256GB 内存
配置:
ThreadPoolExecutor executor = new ThreadPoolExecutor(
33, // 32核 + 1
49, // 33 × 1.5 ≈ 49
30, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(5000),
new ThreadPoolExecutor.AbortPolicy()
);
微服务网关(混合型)
硬件:4 核 CPU,8GB 内存
配置:
// Netty 风格事件循环组(IO 密集型 + 少量计算)
EventLoopGroup bossGroup = new NioEventLoopGroup(4); // 核心线程 = CPU核数
EventLoopGroup workerGroup = new NioEventLoopGroup(8); // 最大线程 = 核数 × 2
6.3 数据库监控集成
启用Druid监控界面:
@Bean
public ServletRegistrationBean<StatViewServlet> druidServlet() {
ServletRegistrationBean<StatViewServlet> reg = new ServletRegistrationBean<>();
reg.setServlet(new StatViewServlet());
reg.addUrlMappings("/druid/*");
reg.addInitParameter("loginUsername", "admin");
reg.addInitParameter("loginPassword", "admin");
return reg;
}
7. 慢SQL治理方案
开启慢sql会对性能造成一定影响
7.1 监控采集
Druid内置监控:
spring:
datasource:
druid:
filter:
stat:
enabled: true
slow-sql-millis: 1000 # 定义慢SQL阈值
7.2 慢sql优化流程
8 升级效果
总结:
1、mysql实现主从架构,采用ShardingSphere路由实现读写分离
以上系统已经实现了应用层、数据库读层性能横向扩张。