把Oracle单库的数据拆散到多个实例和多个表,并不只是改一行连接串那么简单。Oracle的序列、同义词、DUAL查询和MERGE语句都会在分片环境下暴露新问题,而ShardingSphere-JDBC通过标准JDBC入口把这些复杂性收敛在配置和规则层。下面会把重点放在Oracle特有的适配细节上,而不是重复通用分片概念。

一、先确认Oracle是否真的需要分库分表
Oracle本身有RAC、分区表、物化视图和并行查询等能力,很多单库性能问题在用尽这些特性前并不需要引入分片。一般而言,如果单表行数超过5000万、写入TPS接近存储IO瓶颈、维护窗口已经无法完成索引重建或统计信息收集,再考虑水平拆分比较合适。分库分表会牺牲一部分事务一致性和SQL表达能力,不应该把它当作解决慢查询的第一选项。
如果业务确实需要拆分,还要在ShardingSphere-JDBC和ShardingSphere-Proxy之间做选择。JDBC模式以jar包形式嵌入应用,性能损耗小,适合Spring Boot这类服务直接管理数据源;Proxy模式独立部署,适合多语言客户端统一接入,但多一跳网络。对Oracle项目来说,JDBC模式通常更容易和现有连接池、XA事务管理器集成,所以后续配置以ShardingSphere-JDBC为例展开。
二、ShardingSphere分片规则与Oracle物理表准备
分片规则的核心是数据节点和算法。假设把订单表t_order按用户ID分库、按订单ID分表,两个库各放四张物理表,实际SQL会根据分片列自动路由到对应节点。下面的YAML片段配置了两个Oracle数据源,分别指向192.168.10.11和192.168.10.12上的PDB,并将t_order拆成ds0/ds1下的t_order_0到t_order_3。
spring:
shardingsphere:
mode:
type: Standalone
datasource:
names: ds0,ds1
ds0:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: oracle.jdbc.OracleDriver
jdbc-url: jdbc:oracle:thin:@//192.168.10.11:1521/ORCLPDB1
username: sharding_user
password: sharding_pwd
ds1:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: oracle.jdbc.OracleDriver
jdbc-url: jdbc:oracle:thin:@//192.168.10.12:1521/ORCLPDB1
username: sharding_user
password: sharding_pwd
rules:
sharding:
tables:
t_order:
actual-data-nodes: ds$->{0..1}.t_order_$->{0..3}
database-strategy:
standard:
sharding-column: user_id
sharding-algorithm-name: database_inline
table-strategy:
standard:
sharding-column: order_id
sharding-algorithm-name: table_inline
key-generate-strategy:
column: order_id
key-generator-name: snowflake
binding-tables:
- t_order,t_order_item
broadcast-tables:
- t_config
sharding-algorithms:
database_inline:
type: INLINE
props:
algorithm-expression: ds$->{user_id % 2}
table_inline:
type: INLINE
props:
algorithm-expression: t_order_$->{order_id % 4}
key-generators:
snowflake:
type: SNOWFLAKE
props:
worker-id: 1
props:
sql-show: true
这里的actual-data-nodes使用Groovy表达式枚举逻辑库和逻辑表,database-strategy绑定user_id做库路由,table-strategy绑定order_id做表路由。binding-tables声明t_order和t_order_item在同一分片维度下路由,这样关联查询不会跨库;broadcast-tables把t_config全量同步到每个数据源,适合小表。实际建表时需要在每个Oracle实例上手工创建t_order_0到t_order_3,并确保表结构一致,ShardingSphere本身不会自动建表。
应用侧通过JdbcTemplate写入时可以省略主键列,让SNOWFLAKE生成器自动填充order_id。下面的示例插入订单时会根据user_id和生成的order_id完成双层路由,如果打开sql-show就能在日志中看到改写后的实际SQL和命中节点。
@Service
public class OrderService {
private final JdbcTemplate jdbcTemplate;
public OrderService(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public void createOrder(Long userId, String sku, BigDecimal amount) {
String sql = "INSERT INTO t_order(user_id, sku, amount, status) VALUES (?, ?, ?, ?)";
jdbcTemplate.update(sql, userId, sku, amount, 0);
}
}
三、Oracle序列、DUAL和全局主键的改造
Oracle开发习惯里常用序列加触发器生成主键,分片后如果每个库都建同名序列,多个分片会生成重复ID,最终导致跨库合并数据冲突。ShardingSphere内置的SNOWFLAKE和NANOID生成器可以绕过数据库自增机制,由中间层直接产生全局唯一值。主键列建议改为NUMBER(19)或VARCHAR2(64),避免使用Oracle的IDENTITY列或序列触发器,除非业务能接受每片独立ID再加额外标识位。
DUAL表和rownum分页查询也需要重新审视。单库下写SELECT 1 FROM DUAL没有问题,但要注意ShardingSphere对DUAL路由的处理能力有限,不建议在真实业务SQL里频繁使用DUAL做复杂表达式。分页时Oracle常用ROWNUM或OFFSET FETCH,若分片后需要跨节点聚合排序,必须先把数据拉到合并层再做排序,性能会随分片数量下降。尽量让查询带上分片键,避免触发全库扫描。
SELECT order_id, user_id, sku, amount FROM t_order WHERE user_id = 1001 AND order_id = 9001;
上面这条SQL同时包含库分片键user_id和表分片键order_id,ShardingSphere会把请求精准路由到一个物理节点,不会产生跨库合并。若where条件只带order_id,就会扫描两个库中顺序查找目标表,虽然能定位到单表,但库级还是存在额外连接;若两个分片键都不带,则会把所有物理表都执行一遍后再合并,这在Oracle生产环境应尽量避免。
四、分布式事务与跨库查询的边界
分库后,一笔订单同时写t_order和t_order_item可能落在不同库,需要分布式事务保障一致性。ShardingSphere-JDBC支持XA事务,可以在Spring Boot中集成Atomikos或Narayana作为事务管理器,并通过配置XA数据源类型让ShardingSphere使用XAConnection。Oracle的JDBC驱动对XA支持成熟,但XA提交会引入两阶段锁等待,吞吐量明显低于本地事务,因此不是所有写操作都值得包在XA里。
对于关联查询,优先使用binding-tables让同维度的表提前绑定,避免t_order和t_order_item因为分片键不同而跨库join。broadcast-tables则适合配置、字典这类量少但到处都要用的小表。若业务确实需要跨节点聚合,ShardingSphere会执行多节点查询并在内存合并,支持SUM、COUNT、AVG等聚合,但复杂join、子查询和窗口函数可能受限。Oracle环境建议将跨库聚合放在夜间批处理或异步链路中,并对结果做二次校验。
上线前还应做一轮Oracle特性兼容检查:确认是否存在MERGE INTO、CONNECT BY、MODEL、闪回查询等方言语法;检查表级触发器是否被分片逻辑绕过;确认连接池中隔离级别和readOnly属性不会被分片重写;验证统计信息收集作业能否针对每个物理分片执行。只有把这些细节纳入测试清单,Oracle和ShardingSphere的组合才不至于在上线后频繁返工。
Oracle数据库ShardingSphere分库分表修改时间:2026-09-25 22:14:47