在互联网业务快速发展的今天,单机系统已经很难支撑海量数据和高并发访问的需求。我经历过多个从单体架构到分布式系统的迁移项目,深刻体会到分布式架构带来的扩展性优势,同时也面临着诸多技术挑战。
分布式系统的核心价值在于:
但在享受这些优势的同时,我们也必须解决分布式环境带来的新问题:
根据我的经验,当出现以下指标时就应该考虑分库分表:
| 策略类型 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 哈希分片 | 数据分布均匀 | 难以范围查询 | 用户ID等离散值 |
| 范围分片 | 支持范围查询 | 可能热点集中 | 时间序列数据 |
| 列表分片 | 灵活可控 | 需要维护映射表 | 地域等固定分类 |
选择分片键时需要重点考虑:
我在电商项目中采用"用户ID哈希+订单创建时间范围"的复合分片策略,既保证了用户维度的查询效率,又避免了时间维度的热点问题。
ShardingSphere通过四大核心模块实现分库分表透明化:
SQL解析引擎
路由引擎
执行引擎
结果归并引擎
重要提示:分库分表后,跨分片JOIN操作性能会显著下降,建议通过以下方式优化:
- 数据冗余:将关联数据冗余存储
- 应用层JOIN:先查询再内存合并
- 使用宽表:提前关联好数据
| 模式 | 一致性 | 性能 | 复杂度 | 适用场景 |
|---|---|---|---|---|
| XA | 强一致 | 低 | 高 | 金融支付 |
| TCC | 最终 | 中 | 高 | 电商订单 |
| SAGA | 最终 | 高 | 中 | 长事务 |
| AT | 最终 | 高 | 低 | 常规业务 |
AT模式是Seata框架的核心特性,其工作流程分为三个阶段:
一阶段:
二阶段提交:
二阶段回滚:
关键设计要点:
事务恢复服务定期扫描超时事务,处理流程如下:
java复制// 伪代码展示事务恢复核心逻辑
while (true) {
List<GlobalTransaction> timeoutTxns =
transactionDao.selectTimeoutTransactions();
for (GlobalTransaction txn : timeoutTxns) {
if (txn.getStatus() == BEGIN) {
// 超时未完成的事务
if (allBranchesCommitted(txn)) {
txn.setStatus(COMMITTED);
} else {
txn.setStatus(ROLLBACKED);
sendRollbackToBranches(txn);
}
transactionDao.update(txn);
}
}
Thread.sleep(RECOVERY_INTERVAL);
}
熔断器状态机包含三个状态:
Hystrix实现示例:
java复制public class ServiceCircuitBreaker {
private AtomicInteger failureCount = new AtomicInteger(0);
private long lastFailureTime = 0;
public Response invoke(Request request) {
if (state == State.OPEN) {
if (System.currentTimeMillis() - lastFailureTime > resetTimeout) {
state = State.HALF_OPEN;
} else {
return fallbackResponse();
}
}
try {
Response response = actualService.call(request);
if (state == State.HALF_OPEN) {
failureCount.set(0);
state = State.CLOSED;
}
return response;
} catch (Exception e) {
failureCount.incrementAndGet();
lastFailureTime = System.currentTimeMillis();
if (failureCount.get() >= threshold) {
state = State.OPEN;
}
return fallbackResponse();
}
}
}
| 类别 | 指标 | 预警阈值 |
|---|---|---|
| 数据库 | QPS | > 2000 |
| 连接数使用率 | > 80% | |
| 慢查询比例 | > 1% | |
| JVM | GC时间 | > 200ms |
| 老年代使用率 | > 75% | |
| 线程数 | > 500 | |
| 中间件 | 消息堆积 | > 1000 |
| 响应时间 | > 500ms |
Trace数据模型:
java复制public class Span {
private String traceId; // 全局唯一
private String spanId; // 当前跨度
private String parentId; // 父跨度
private String serviceName;
private long startTime;
private long duration;
private Map<String,String> tags;
private List<Log> logs;
}
采样策略建议:
问题1:事务悬挂
问题2:空回滚
问题3:幂等控制
问题1:跨库JOIN性能差
问题2:分布式ID冲突
问题3:分片键变更
索引优化原则:
分页查询优化:
sql复制-- 反例:全表扫描
SELECT * FROM orders LIMIT 1000000, 10;
-- 正例:使用覆盖索引
SELECT * FROM orders WHERE id > 1000000 LIMIT 10;
关键参数配置建议:
code复制-server
-Xms4g -Xmx4g // 堆内存
-XX:MetaspaceSize=256m
-XX:MaxMetaspaceSize=256m
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:ParallelGCThreads=4
-XX:ConcGCThreads=2
-XX:InitiatingHeapOccupancyPercent=45
多级缓存架构设计:
| 中间件 | 优点 | 缺点 | 适用版本 |
|---|---|---|---|
| ShardingSphere | 功能全面 | 学习曲线陡 | 5.x |
| MyCat | 简单易用 | 性能一般 | 2.0 |
| Seata | 支持AT模式 | 需要TC服务 | 1.5 |
推荐技术栈组合:
背景:
改造方案:
效果:
挑战:
解决方案:
单体架构:
服务化:
分布式:
云原生:
关键技术能力培养路径:
初级阶段:
中级阶段:
高级阶段:
在分布式系统建设过程中,最大的体会是:没有银弹方案,必须根据业务特点选择合适的技术组合。比如对于一致性要求极高的金融核心系统,可能需要牺牲部分性能选择XA模式;而对于互联网高并发场景,最终一致性+补偿机制可能是更合理的选择。