扩展之ShardingSphere

张开发
2026/5/20 19:08:11 15 分钟阅读
扩展之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 解析引擎。这是一个强大的语法分析器生成工具。词法解析首先将 SQL 字符串拆分成一个个不可再分的“单词”Token如SELECT、*、FROM、t_order、WHERE等。语法解析然后根据 SQL 的语法规则将这些 Token 组织成一个抽象语法树AST。这棵树清晰地表达了 SQL 的结构和语义。性能优化为了避免重复解析相同的 SQL 模板如SELECT * FROM t_order WHERE id ?ShardingSphere 会缓存解析后的 AST从而大幅提升性能。经过解析ShardingSphere 就知道了这是一条查询t_order表的语句查询条件是user_id 1和order_id 100。2. SQL 路由 (SQL Route)路由是 ShardingSphere 的“大脑”它根据解析出的上下文和用户配置的分片规则计算出这条 SQL 应该在哪些真实的数据库和表上执行。核心任务根据分片键的值通过分片算法确定目标数据节点。路由过程匹配分片键从解析上下文中找到user_id和order_id这两个分片键。执行分片算法分库路由根据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_orderSQL 会被路由到所有数据节点执行。应尽量避免因为它效率较低。最终路由引擎确定了这条 SQL 的真实执行路径ds1.t_order0。3. SQL 改写 (SQL Rewrite)SQL 改写的目的是将面向“逻辑表”的 SQL转换成可以在“真实数据节点”上执行的“物理 SQL”。核心任务修正 SQL 中的逻辑信息使其适配真实的数据库环境。改写内容正确性改写这是必须的。表名修正将逻辑表名t_order替换为真实表名t_order0。补列如果使用自动生成主键ShardingSphere 会改写 SQL 以获取生成的主键值。优化改写这是为了提升性能。单节点优化如果一次查询被路由到同一个数据库的多个表ShardingSphere 可能会将它们合并成一条UNION ALL语句减少网络交互次数。经过改写原始 SQL 变成了可以在真实数据库执行的物理 SQLSELECT * 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 BY、GROUP 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 后再分别对每个连接执行commit或rollback。核心缺陷由于每个数据节点各自管理自己的事务它们之间没有任何协调和通信。如果一个节点提交成功另一个节点提交失败就会导致数据不一致。适用场景对性能要求极高且业务层面能够容忍或通过其他方式处理数据不一致的场景。XA 事务强一致性保障XA 事务基于 X/Open 组织定义的 DTP 模型通过两阶段提交2PC协议来保证分布式事务的强一致性ACID。工作原理准备阶段 (Prepare)ShardingSphere 作为事务管理器TM会向所有参与的数据库资源管理器 RM发送xa prepare指令。每个数据库会执行 SQL并将变更记录到事务日志中然后向 TM 汇报“准备成功”或“准备失败”。在此阶段数据库会锁定相关资源。提交/回滚阶段 (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 模式为例一阶段提交业务 SQL 在本地数据库中被正常执行并提交。但在执行前后Seata 会拦截 SQL并自动生成用于回滚的undo_log包含数据变更前后的镜像然后将undo_log与业务数据在同一个本地事务中提交。全局锁为了保证隔离性Seata 会在提交前获取全局锁避免不同全局事务间的写写冲突。二阶段异步补偿如果全局事务需要回滚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 AT1. 引入 Maven 依赖shardingsphere-transaction-base-seata-at的版本最好与shardingsphere-jdbc保持一致而seata-all的版本应与你的 Seata Server 版本一致。!-- ShardingSphere JDBC 核心依赖 -- dependency groupIdorg.apache.shardingsphere/groupId artifactIdshardingsphere-jdbc/artifactId version5.5.1/version /dependency !-- ShardingSphere 集成 Seata AT 模式的核心模块 -- dependency groupIdorg.apache.shardingsphere/groupId artifactIdshardingsphere-transaction-base-seata-at/artifactId version5.5.1/version /dependency !-- Seata 客户端全量依赖 -- dependency groupIdorg.apache.seata/groupId artifactIdseata-all/artifactId version2.3.0/version /dependency2. 准备数据库必须创建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) ) ENGINEInnoDB DEFAULT CHARSETutf8;3. 配置 Seata 客户端需要在项目的resources目录下创建两个核心配置文件seata.conf和registry.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 兼容的数据库提供强大的分片和治理能力。

更多文章