当前位置: 首页 > news >正文

扩展之ShardingSphere

之前做社交的时候用的是这个,后来的工作也是一直借鉴其中的思想;当然这类中间件思想都那个什么,咱们这篇文章中主角大佬的知名程度咱就不吹了,下面正式进入正文:

要深入理解 ShardingSphere 的底层原理,关键在于掌握其处理 SQL 的完整生命周期。你可以把它想象成一个精密的“数据库代理”,它在应用程序和真实数据库之间,对每一条 SQL 进行拦截、解析、改写、执行和归并

SQL 解析 (Parse) → SQL 路由 (Route) → SQL 改写 (Rewrite) → SQL 执行 (Execute) → 结果归并 (Merge)

下面就让我们结合一个具体的分库分表场景,来深度剖析这五个步骤的内部运作机制!

场景设定

假设我们有一个t_order订单表,根据user_id进行分库(2个库ds0,ds1),根据order_id进行分表(每个库4张表t_order0~t_order3)。

SELECT * FROM t_order WHERE user_id = 1 AND order_id = 100;

1. SQL 解析 (SQL Parse)

这是 ShardingSphere 处理 SQL 的第一步,其目标是将一条文本形式的 SQL 转换成程序可以理解和操作的内部对象。

  • 核心任务:将 SQL 字符串拆解,提取出关键的“解析上下文”,包括表名、查询条件、排序字段、聚合函数等。
  • 底层实现:ShardingSphere 使用ANTLR作为其 SQL 解析引擎。这是一个强大的语法分析器生成工具。
    1. 词法解析:首先,将 SQL 字符串拆分成一个个不可再分的“单词”(Token),如SELECT*FROMt_orderWHERE等。
    2. 语法解析:然后,根据 SQL 的语法规则,将这些 Token 组织成一个抽象语法树(AST)。这棵树清晰地表达了 SQL 的结构和语义。
  • 性能优化:为了避免重复解析相同的 SQL 模板(如SELECT * FROM t_order WHERE id = ?),ShardingSphere 会缓存解析后的 AST,从而大幅提升性能。

经过解析,ShardingSphere 就知道了:这是一条查询t_order表的语句,查询条件是user_id = 1order_id = 100

2. SQL 路由 (SQL Route)

路由是 ShardingSphere 的“大脑”,它根据解析出的上下文和用户配置的分片规则,计算出这条 SQL 应该在哪些真实的数据库和表上执行。

  • 核心任务:根据分片键的值,通过分片算法,确定目标数据节点。
  • 路由过程
    1. 匹配分片键:从解析上下文中找到user_idorder_id这两个分片键。
    2. 执行分片算法
      • 分库路由:根据user_id = 1,应用分库算法(例如1 % 2 = 1),确定目标数据库为ds1
      • 分表路由:根据order_id = 100,应用分表算法(例如100 % 4 = 0),确定目标表为t_order0
  • 路由类型:ShardingSphere 支持多种路由类型,以适应不同的查询场景:
    • 单片路由:当分片键使用=操作符时,精确路由到唯一的数据节点。这是最高效的方式。
    • 多片路由:当分片键使用IN操作符时(如WHERE user_id IN (1, 2)),会路由到多个数据节点。
    • 范围路由:当分片键使用BETWEEN操作符时,会路由到范围内的所有相关节点。
    • 广播路由:当 SQL 中不包含分片键时(如SELECT * FROM t_order),SQL 会被路由到所有数据节点执行。应尽量避免,因为它效率较低。

最终,路由引擎确定了这条 SQL 的真实执行路径:ds1.t_order0

3. SQL 改写 (SQL Rewrite)

SQL 改写的目的是将面向“逻辑表”的 SQL,转换成可以在“真实数据节点”上执行的“物理 SQL”。

  • 核心任务:修正 SQL 中的逻辑信息,使其适配真实的数据库环境。
  • 改写内容
    • 正确性改写:这是必须的。
      • 表名修正:将逻辑表名t_order替换为真实表名t_order0
      • 补列:如果使用自动生成主键,ShardingSphere 会改写 SQL 以获取生成的主键值。
    • 优化改写:这是为了提升性能。
      • 单节点优化:如果一次查询被路由到同一个数据库的多个表,ShardingSphere 可能会将它们合并成一条UNION ALL语句,减少网络交互次数。

经过改写,原始 SQL 变成了可以在真实数据库执行的物理 SQL:

SELECT * FROM ds1.t_order0 WHERE user_id = 1 AND order_id = 100;

4. SQL 执行 (SQL Execute)

执行引擎负责将改写后的物理 SQL 安全、高效地发送到底层数据库执行。

  • 核心任务:管理数据库连接和线程,执行 SQL 并获取结果集。
  • 执行模式:ShardingSphere 提供了两种执行模式,以平衡资源消耗和执行效率:
    • 内存限制模式:每个 JDBC 连接在同一时间只执行一个 SQL。适用于 OLTP 场景,配合流式归并,内存占用低。
    • 连接限制模式:严格控制 JDBC 连接的使用数量,一个连接会执行多个 SQL。适用于 OLAP 场景,配合内存归并,可以更快地获取全部数据。

执行引擎会自动选择合适的模式,将 SQL 发送到ds1数据库执行。

5.结果归并 (Result Merge)

最后一步!负责将来自一个或多个数据节点的执行结果,合并成一个完整的结果集返回给客户端。

  • 核心任务:将分散的结果集整合成一个,对应用层透明。
  • 归并方式:根据 SQL 的类型和路由结果,选择不同的归并策略:
    • 流式归并:适用于ORDER BYGROUP BY等场景。它像拉链一样,逐条从各个结果集中取出数据进行比较和排序,内存占用非常少。
    • 内存归并:适用于SUM()COUNT()等聚合查询。它需要将所有结果集的数据全部加载到内存中,然后再进行计算。
    • 装饰者归并:用于处理分页等特殊情况,通过装饰者模式叠加处理。

最终,经过归并引擎处理后的单一结果集被返回给应用程序,整个过程对应用层完全透明,仿佛操作的是一个未分片的单表。

分布式事务

分布式事务一直的关键中的敏感话题,咱们ss为开发者提供一套统一且易于使用事务接口begin/commit/rollback),并在底层整合了三种主流的事务模式,以适应不同的业务场景!这三种模式分别是:LOCAL(本地事务)XA(强一致性事务)BASE(柔性事务)。它们各自在一致性、性能和适用场景上做出了不同的权衡。都为你想到了、妥妥滴……

三种模式核心对比

模式一致性隔离性并发性能适合场景
LOCAL不支持不支持无影响业务方自行处理不一致
XA支持(强一致)支持严重衰退短事务 & 低并发
BASE最终一致业务方保证略微衰退长事务 & 高并发
模式核心特点适用场景不适用场景
XA强一致性,基于两阶段提交(2PC)。对数据一致性要求极高的短事务,如金融转账、账户扣款。高并发、长流程的业务,因其会长时间锁定资源,严重影响性能。
BASE (Seata AT)最终一致性,性能高,对代码侵入性低。大多数高并发的互联网业务,如电商下单、库存扣减。对实时一致性有严格要求的场景。
BASE (Saga)最终一致性,通过补偿机制实现,适合长流程。跨多个服务的长事务业务流程,如机票/酒店预订(订票→支付→出票)。需要强一致性的短事务。
LOCAL无分布式事务保证,性能最高。业务逻辑本身能保证最终一致性,或可以容忍短暂不一致的场景。任何需要跨分片数据一致性的场景。

LOCAL 事务:性能优先

LOCAL 模式是 ShardingSphere 中最简单、性能最高的事务模式,但它不保证分布式环境下的数据一致性。

  • 工作原理:当开启一个 LOCAL 事务时,ShardingSphere 会在每个涉及到的真实数据库连接上,独立地执行begin指令。在执行完 SQL 后,再分别对每个连接执行commitrollback
  • 核心缺陷:由于每个数据节点各自管理自己的事务,它们之间没有任何协调和通信。如果一个节点提交成功,另一个节点提交失败,就会导致数据不一致
  • 适用场景:对性能要求极高,且业务层面能够容忍或通过其他方式处理数据不一致的场景。

XA 事务:强一致性保障

XA 事务基于 X/Open 组织定义的 DTP 模型,通过两阶段提交(2PC)协议来保证分布式事务的强一致性(ACID)。

  • 工作原理
    1. 准备阶段 (Prepare):ShardingSphere 作为事务管理器(TM),会向所有参与的数据库(资源管理器 RM)发送xa prepare指令。每个数据库会执行 SQL,并将变更记录到事务日志中,然后向 TM 汇报“准备成功”或“准备失败”。在此阶段,数据库会锁定相关资源
    2. 提交/回滚阶段 (Commit/Rollback):TM 收集所有分支事务的准备结果。如果所有节点都准备成功,则向所有节点发送xa commit指令,事务正式提交;只要有一个节点准备失败,则向所有节点发送xa rollback指令,回滚整个事务。
  • 核心缺陷:在整个事务期间,所需资源会被长时间锁定,这在高并发场景下会严重阻塞其他请求,导致性能急剧下降。因此,它更适用于执行时间短、并发量不高的场景。
  • 实现支持:ShardingSphere 不仅内置了 XA 事务管理器,还支持集成 Atomikos、Bitronix 等成熟的第三方 XA 事务管理器。

BASE 事务:高并发下的最终一致

BASE 事务是一种柔性事务,它通过放宽对强一致性的要求,来换取系统吞吐量的提升。BASE 是基本可用(Basically Available)柔性状态(Soft state)最终一致性(Eventually consistent)的缩写。

ShardingSphere 的 BASE 事务主要通过集成Seata框架来实现,其中最常用的是AT (Auto Transaction)模式。

  • 工作原理(以 Seata AT 模式为例)
    1. 一阶段提交:业务 SQL 在本地数据库中被正常执行并提交。但在执行前后,Seata 会拦截 SQL,并自动生成用于回滚的undo_log(包含数据变更前后的镜像),然后将undo_log与业务数据在同一个本地事务中提交。
    2. 全局锁:为了保证隔离性,Seata 会在提交前获取全局锁,避免不同全局事务间的写写冲突。
    3. 二阶段异步补偿:如果全局事务需要回滚,Seata 的事务协调器(TC)会异步地调用各个分支事务,根据undo_log生成反向补偿 SQL 并执行,从而实现数据的最终一致性。
  • 核心优势:由于一阶段就提交了本地事务,资源锁定的时间极短(仅在获取全局锁时),因此并发性能远高于 XA 模式。
  • 适用场景:适用于高并发、长流程的业务,且业务可以容忍短暂的数据不一致。

如何使用

spring: shardingsphere: # 配置事务类型,可选 LOCAL, XA, BASE props: transaction: type: XA # 如果选择 XA,可以指定事务管理器 xa-transaction-manager-type: atomikos

通过这种方式,ShardingSphere 将分布式事务的复杂性封装起来,让开发者可以根据业务需求,灵活地在一致性、性能和开发成本之间做出选择。

通用实践

1. 精细化控制事务边界

事务的粒度应尽可能小。将非数据库操作(如参数校验、ID生成、远程调用)移出事务范围,可以有效缩短事务执行时间,减少资源锁定。

@Service public class OrderService { @Autowired private TransactionTemplate transactionTemplate; public OrderResult createOrder(OrderCreateRequest request) { // 1. 在事务外进行参数校验 validateOrderRequest(request); // 2. 在事务外生成订单ID Long orderId = idGenerator.generateId(); // 3. 仅在需要保证原子性的数据库操作周围开启事务 return transactionTemplate.execute(status -> { // 创建订单 orderMapper.insert(new Order(orderId, request)); // 扣减库存 inventoryMapper.deduct(request.getSkuId(), request.getQuantity()); return new OrderResult(orderId); }); } }
2. 设计健壮的异常处理与重试机制

明确区分业务异常和系统异常。业务异常通常意味着需要回滚,而系统异常(如网络抖动)可能适合重试。

  • 异常处理:在try-catch块中,对于可重试的异常,应标记事务为回滚状态,然后抛出,由外部的重试框架处理。
  • 重试机制:使用如 Spring Retry 等框架,为远程调用或临时性失败的操作配置重试策略(如最多重试3次,间隔1秒)。
3. 优化连接池配置

合理的数据库连接池配置是高性能的基础。确保连接池的最大连接数、最小空闲连接数等参数与你的业务并发量相匹配。

BASE (Saga) 模式专项实践

当业务流程复杂且漫长时,Saga 模式是理想选择。其核心在于“正向操作”和“补偿操作”的设计

1. 补偿操作设计原则
  • 原子性:每个补偿操作本身必须是一个独立的本地事务,确保其能成功执行。
  • 可追溯:记录每个步骤的执行状态和上下文信息,便于问题排查和人工干预。
  • 幂等性:补偿操作必须支持幂等,即多次执行相同补偿的结果与执行一次相同。可以通过版本号或唯一业务键来实现。
  • 逆向而非删除:补偿操作应优先采用逆向业务操作(如“增加库存”)而非物理删除数据,以保留审计线索。
2. 典型补偿场景处理
场景处理策略
部分补偿失败记录失败点,通过定时任务或人工干预继续执行剩余补偿。
业务逻辑版本冲突在补偿操作中携带业务逻辑版本号,确保补偿逻辑与原始操作兼容。
资源不足导致补偿失败采用分级补偿策略,优先回滚核心资源,非核心资源可延迟补偿。

Seata AT

1. 引入 Maven 依赖

shardingsphere-transaction-base-seata-at的版本最好与shardingsphere-jdbc保持一致,而seata-all的版本应与你的 Seata Server 版本一致。

<!-- ShardingSphere JDBC 核心依赖 --> <dependency> <groupId>org.apache.shardingsphere</groupId> <artifactId>shardingsphere-jdbc</artifactId> <version>5.5.1</version> </dependency> <!-- ShardingSphere 集成 Seata AT 模式的核心模块 --> <dependency> <groupId>org.apache.shardingsphere</groupId> <artifactId>shardingsphere-transaction-base-seata-at</artifactId> <version>5.5.1</version> </dependency> <!-- Seata 客户端全量依赖 --> <dependency> <groupId>org.apache.seata</groupId> <artifactId>seata-all</artifactId> <version>2.3.0</version> </dependency>
2. 准备数据库

必须创建undo_log表。这张表是 Seata AT 模式实现回滚的核心,用于存储数据变更前的镜像和回滚所需的信息

-- 用于 AT 模式回滚的日志表 CREATE TABLE `undo_log` ( `id` BIGINT(20) NOT NULL AUTO_INCREMENT, `branch_id` BIGINT(20) NOT NULL, `xid` VARCHAR(100) NOT NULL, `context` VARCHAR(128) NOT NULL, `rollback_info` LONGBLOB NOT NULL, `log_status` INT(11) NOT NULL, `log_created` DATETIME NOT NULL, `log_modified` DATETIME NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
3. 配置 Seata 客户端

需要在项目的resources目录下创建两个核心配置文件:seata.confregistry.conf

3.1 配置seata.conf

启用 ShardingSphere 对 Seata AT 模式的支持,并定义客户端的基本信息。

# 启用 Seata AT 模式 shardingsphere.transaction.seata.at.enable = true # 设置全局事务超时时间(秒) shardingsphere.transaction.seata.tx.timeout = 120 # 客户端配置 client { # 应用ID,通常与 spring.application.name 一致 application.id = order-service # 事务服务组,自定义名称,但需与 Seata Server 配置保持一致 transaction.service.group = default_tx_group } # 服务配置(如果使用 Nacos 作为注册中心,此处的 grouplist 配置可省略,以 Nacos 中的为准) service { # 将事务组映射到集群 vgroupMapping.default_tx_group = "default" # 指定 Seata Server 的地址(仅在非注册中心模式下需要) default.grouplist = "127.0.0.1:8091" }
3.2 配置registry.conf

指定 Seata Server 的注册中心和配置中心,确保客户端能找到并连接到服务端。

registry { # 注册中心类型,如 nacos, eureka, consul 等 type = "nacos" nacos { serverAddr = "127.0.0.1:8848" namespace = "" group = "SEATA_GROUP" username = "nacos" password = "nacos" } } config { # 配置中心类型 type = "nacos" nacos { serverAddr = "127.0.0.1:8848" namespace = "" group = "SEATA_GROUP" username = "nacos" password = "nacos" # 指向 Nacos 中 Seata Server 的配置文件 dataId data-id = "seataServer.properties" } }
4. 传递全局事务 ID (XID)

在微服务架构中,发起方服务需要将自己的全局事务 ID (XID) 传递给下游被调用的服务,才能让所有服务加入同一个全局事务。

4.1 在调用方配置 Feign 拦截器

配置一个RequestInterceptor,将 XID 放入 HTTP 请求头中。

import feign.RequestInterceptor; import feign.RequestTemplate; import io.seata.core.context.RootContext; import org.springframework.context.annotation.Configuration; import org.springframework.util.StringUtils; @Configuration public class TransferTxXidInterceptor implements RequestInterceptor { @Override public void apply(RequestTemplate requestTemplate) { // 获取当前线程绑定的 XID String xid = RootContext.getXID(); if (StringUtils.hasText(xid)) { // 将 XID 放入请求头 requestTemplate.header(RootContext.KEY_XID, xid); } } }
4.2 在被调用方配置 Web 拦截器

配置一个 Web MVC 拦截器,从请求头中解析 XID 并绑定到当前线程。

import io.seata.spring.annotation.datasource.SeataAutoDataSourceProxyCreator; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration @ConditionalOnClass(name = "org.springframework.web.servlet.config.annotation.WebMvcConfigurer") public class SeataWebConfig { /** * 配置 Seata 的事务传播拦截器,用于从请求头中获取并绑定 XID */ @Bean public SeataAutoDataSourceProxyCreator seataAutoDataSourceProxyCreator() { return new SeataAutoDataSourceProxyCreator(); } }

完成以上所有步骤后,你就可以在 Service 层的方法上使用@GlobalTransactional注解来开启一个分布式事务啦 ;

XA 模式专项实践

使用 XA 模式时,核心是控制其性能影响。

  • 限制事务范围:确保 XA 事务只包含最核心、必须强一致的数据库操作。
  • 避免大事务:长时间运行的事务会长时间持有资源锁,严重阻塞并发。务必将 XA 事务的执行时间控制在秒级。

监控与运维

建立完善的监控体系是保障分布式事务稳定运行的关键。

  • 监控核心指标
    • 事务成功率成功事务数 / 总事务数
    • 事务平均耗时:监控事务执行时间,及时发现性能劣化。
    • 回滚率:高回滚率可能意味着业务逻辑或系统存在潜在问题。
  • 集成监控工具:将 ShardingSphere 和 Seata 的指标接入 Prometheus + Grafana 、herzsbeat等监控平台,实现可视化告警。

总结

ShardingSphere 的底层原理,本质上是一个围绕 SQL 生命周期的精密处理框架。它通过解析理解意图,通过路由定位数据,通过改写适配环境,通过执行完成操作,最后通过归并呈现结果。这套机制使其能够以无侵入的方式,为任何 JDBC 兼容的数据库提供强大的分片和治理能力。

http://www.cnnetsun.cn/news/1644341.html

相关文章:

  • 茉莉花插件完整指南:3步让Zotero中文文献管理效率提升90%
  • FGA智能战斗引擎:Fate/Grand Order自动化效率提升方案
  • FigmaCN界面本地化工具:基于人工翻译校验的设计效率提升方案
  • Langflow API实战:从零构建你的第一个AI对话流程
  • 保姆级避坑指南:树莓派GPIO控制LED闪烁,从wiringPi到RPi.GPIO的完整代码与常见错误排查
  • 深入解析猫抓扩展的资源嗅探引擎:从网络监控到媒体解析的完整技术栈
  • Tao-8k模型推理性能深度评测:不同硬件配置下的表现对比
  • Java多态实战:如何用Shape接口计算三角形、矩形和圆的周长(附完整代码)
  • Firefox vs Chrome:哪个浏览器更适合你的开发需求?2024实测对比
  • Dify插件离线安装避坑指南:手把手教你搞定无网服务器的OpenAI-API-compatible插件
  • 千问3.5-2B在企业文档处理中的应用:自动读取报表图表+中文摘要生成
  • DeepSeek-R1-Distill-Qwen-1.5B效果展示:实测代码生成与数学推理能力
  • Windows右键菜单清理终极指南:告别臃肿,提升300%工作效率
  • Mojo嵌入Python解释器的安全反模式(附2024最新CVE-2024-XXXX PoC规避清单)
  • Gromacs实战:从零构建空蛋白体系的分子动力学模拟流程
  • 别再只盯着准确率了!用sklearn的`f1_score`搞定多标签分类的Macro-F1和Micro-F1
  • DriverStore Explorer完整指南:Windows驱动管理实战攻略
  • Chandra开源模型部署:Gemma:2b作为轻量级LLM在私有环境中的价值验证
  • 生物信息学新手必看:5分钟搞定scran安装与细胞周期分析入门
  • BUUCTF-Misc实战:从图片分离到brainfuck解密的全流程指南(附7z解压vmdk技巧)
  • PyCharm高级技巧:配置Qwen3.5-2B作为本地代码审查与生成助手
  • 电阻电容组合:微分、积分、低通、高通电路实战解析
  • Cursor 高级技巧:@符号、Chat 模式与多文件编辑
  • ViGEmBus完整指南:Windows游戏手柄兼容性驱动的终极解决方案
  • 告别官方工具臃肿体验:轻量级替代方案如何重塑华硕设备性能
  • 3大维度解析开源下载工具:如何让网盘效率提升80%
  • 告别黑盒:用KAN的可解释性,5分钟看懂你的神经网络到底在学什么
  • 摩根士丹利裁员2500人,金融业AI替代潮来了
  • 在Ubuntu 22.04上编译BlueZ 5.66,我踩过的那些依赖包的坑(附完整解决方案)
  • 免费高效的Windows驱动清理工具:DriverStore Explorer完整操作指南