sharding-jdbc分库分表规则(2)-多表查询
来源:互联网 发布:多益网络客服电话 编辑:程序博客网 时间:2024/06/03 14:41
前言
上一篇介绍了sharding-jdbc单表的基本查询逻辑,本篇介绍一下多表的查询,包括多表查询的BindingTable机制。
建库建表
订单表逻辑语句:
CREATE TABLE IF NOT EXISTS t_order
(order_id
INT NOT NULL, user_id
INT NOT NULL, status
VARCHAR(50), PRIMARY KEY (order_id
))
订单项逻辑语句:
CREATE TABLE IF NOT EXISTS t_order_item
(item_id
INT NOT NULL, order_id
INT NOT NULL, user_id
INT NOT NULL, PRIMARY KEY (item_id
))
配置
为简单起见,使用基本的jdbc进行操作,最精简的代码如下:
public final class MultiTableSelect { public static void main(final String[] args) throws SQLException { DataSource dataSource = getOrderShardingDataSource(); printMultiTableSelect(dataSource); } private static void executeUpdate(final DataSource dataSource, final String sql) throws SQLException { try ( Connection conn = dataSource.getConnection(); PreparedStatement preparedStatement = conn.prepareStatement(sql)) { preparedStatement.executeUpdate(); } } private static ShardingDataSource getOrderShardingDataSource() { DataSourceRule dataSourceRule = new DataSourceRule(createDataSourceMap()); TableRule orderTableRule = TableRule.builder("t_order").actualTables(Arrays.asList("t_order_0", "t_order_1")).dataSourceRule(dataSourceRule).build(); TableRule orderItemTableRule = TableRule.builder("t_order_item").actualTables(Arrays.asList("t_order_item_0", "t_order_item_1")).dataSourceRule(dataSourceRule).build(); ShardingRule shardingRule = ShardingRule.builder().dataSourceRule(dataSourceRule).tableRules(Arrays.asList(orderTableRule,orderItemTableRule)) .databaseShardingStrategy(new DatabaseShardingStrategy("user_id", new ModuloDatabaseShardingAlgorithm())) .tableShardingStrategy(new TableShardingStrategy("order_id", new ModuloTableShardingAlgorithm())).build(); Properties prop = new Properties(); prop.setProperty(ShardingPropertiesConstant.SQL_SHOW.getKey(), "true"); ShardingDataSource shardingDataSource = new ShardingDataSource(shardingRule , prop); return shardingDataSource; } private static void printMultiTableSelect(final DataSource dataSource) throws SQLException { String sql = "SELECT i.* FROM t_order o JOIN t_order_item i ON o.order_id=i.order_id WHERE o.user_id=? AND o.order_id=?"; try ( Connection conn = dataSource.getConnection(); PreparedStatement preparedStatement = conn.prepareStatement(sql)) { preparedStatement.setInt(1, 10); preparedStatement.setInt(2, 1001); try (ResultSet rs = preparedStatement.executeQuery()) { while (rs.next()) { System.out.println(rs.getInt(1)); System.out.println(rs.getInt(2)); System.out.println(rs.getInt(3)); } } } } private static DataSource createDataSource(final String dataSourceName) { BasicDataSource result = new BasicDataSource(); result.setDriverClassName(com.mysql.jdbc.Driver.class.getName()); result.setUrl(String.format("jdbc:mysql://127.0.0.1:3306/%s", dataSourceName)); result.setUsername("root"); result.setPassword("123456"); return result; } private static Map<String, DataSource> createDataSourceMap() { Map<String, DataSource> result = new HashMap<>(2); result.put("ds_jdbc_0", createDataSource("ds_jdbc_0")); result.put("ds_jdbc_1", createDataSource("ds_jdbc_1")); return result; }}
我们的目标是要执行下面这个sql语句:
SELECT i.* FROM t_order o JOIN t_order_item i ON o.order_id=i.order_id WHERE o.user_id=? AND o.order_id=?
运行时参数【10,1001】。
流程分析
有了单表查询逻辑的分析,我们直接来研究多表联合查询的流程是怎样的。
通过sql解析发现有两张逻辑表名称:t_order 和 t_order_item,
发现两个条件: t_order.userId = 10 和 t_order.order_id = 1001发现sql中有多张表,创建复合路由引擎:ComplexRoutingEngine
单表路由及合并
遍历2张逻辑表 t_order 和 t_order_item
根据逻辑表名获取TableRule,构建单表路由引擎SimpleRoutingEngine,
然后获取单表路由结果,最后合并两个单表的路由结果此时两个表的路由如下:
逻辑表 路由结果 t_order TableUnit(dataSourceName=ds_jdbc_0, logicTableName=t_order, actualTableName=t_order_1) t_order_item [TableUnit(dataSourceName=ds_jdbc_0, logicTableName=t_order_item, actualTableName=t_order_item_0),
TableUnit(dataSourceName=ds_jdbc_0, logicTableName=t_order_item, actualTableName=t_order_item_1),
TableUnit(dataSourceName=ds_jdbc_1, logicTableName=t_order_item, actualTableName=t_order_item_0),
TableUnit(dataSourceName=ds_jdbc_1, logicTableName=t_order_item, actualTableName=t_order_item_1)]
4. 笛卡尔路由
找出两个单表路由结果集的数据源交集,即t_order和t_order_item共有的数据源,在这里为 ds_jdbc_0。
在共有数据源中,找出涉及到的所有逻辑表:
{ds_jdbc_0=[t_order, t_order_item]}
转换为物理表:
[[t_order_1], [t_order_item_0, t_order_item_1]]
利用google提供的工具类 Sets.cartesianProduct 进行笛卡尔相乘,得到两组数据:
ds_jdbc_0.t_order_1 + ds_jdbc_0.t_order_item_0
ds_jdbc_0.t_order_1 + ds_jdbc_0.t_order_item_1
5. 生成执行单元
对于步骤4得到的两组数据,分别生成对应的SQLExecutionUnit,也就是最终我们要执行两条sql语句,最后执行后就是合并结果了,跟单表差不多,此处不再赘述。
其实,简单的多表查询跟单表查询差别并不是太大,主要是先利用了单表路由,然后合并路由,最后进行一个笛卡尔计算得到多条执行语句,这也告诉了我们,如果t_order和t_order_item分表非常多,而且你的查询条件并不能够减少最终路由到的物理表,那么结果就是这个笛尔卡积算出来的结果会非常的吓人,这也是官方提到的要注意的一个问题点,而为了解决这个问题,sharding-jdbc提供了BindingTable的机制来优化这个问题。
表的绑定
先来看看业务场景:订单记录t_order为一个大的订单,像购物车购买这种一下可以买多种商品的话,最终一般会进行拆单,拆成多条t_order_item记录,如果我们能够确保这两个表的路由规则是完全一样的话,实际上是可以避免完全的笛卡尔积的。
比如在每个数据源中,都有如下的物理表:
t_order_0,t_order_1,t_order_item_0,t_order_item_1
当一个订单id映射到 t_order_0的时候,业务能够确保其对应的t_order_item一定映射到t_order_item_0,这种情况下,t_order_0永远没有必要与t_order_item_1之类的物理表进行联合查询,BindingTable就是用于配置这种情况的。
首先要配置需要绑定在一起的表,如下代码所示:
ShardingRule shardingRule = ShardingRule.builder().dataSourceRule(dataSourceRule).tableRules(Arrays.asList(orderTableRule,orderItemTableRule)) .bindingTableRules(Collections.singletonList(new BindingTableRule(Arrays.asList(orderTableRule, orderItemTableRule)))) .databaseShardingStrategy(new DatabaseShardingStrategy("user_id", new ModuloDatabaseShardingAlgorithm())) .tableShardingStrategy(new TableShardingStrategy("order_id", new ModuloTableShardingAlgorithm())).build();
我们新增了BindingTableRule这句代码,把两张表绑定在一起,接着重新来看下解析的流程:
通过sql解析发现有两张逻辑表名称:t_order 和 t_order_item。
路由
在真正路由之前,引擎会做一个判断,如果解析出来的所有逻辑表都在一个绑定规则中,那么取出一张逻辑表,然后走单表的路由,在这里,两张逻辑表绑定在一个规则中,也就是所谓的全绑定,这时候,引擎使用其中一张表走单表路由逻辑。此示例用t_order单表路由解析出如下结果:
ds_jdbc_0, t_order, t_order_1那么问题来了,怎么处理逻辑表t_order_item的物理映射呢?
路由重写
在路由完t_order之后,引擎会进入sql重写阶段,要把sql中的t_order和t_order_item替换为物理表名,前面我们知道,我们找到了t_order_1,在重写的时候,引擎会用t_order去查找绑定规则,当找到了之后,计算t_order_1在所有物理表[t_order_0,t_order_1]中的位置索引,然后根据索引在物理表中[t_order_item0,t_order_item1]找到t_order_item的物理名表,这样便可以替换所有的逻辑表了。
混合绑定
前面介绍的要不就是非绑定多表关联,要不就是全绑定多表关联,如果一个查询中涉及到3张逻辑表,两张是绑定关系,另外一张没有绑定关系,那又该如何呢?
经过前面的分析,其实可以猜想到,由于有些表不是绑定表,所以肯定走复合路由,单表去路由后再合并笛卡尔计算,然而,由于绑定表的存在,在单表路由的时候,绑定的表只要路由任何其中一张就可以了,然后在最后重写sql的时候再根据彼此的索引去计算物理表名。
总结
表的关联有时候不可避免,但有时候我们也会业务上转化为单表多次去查询,这个要结合自身的业务去选择实现方案,sharding-jdbc的绑定表机制在某些场景下确实带来了高效性。
- sharding-jdbc分库分表规则(2)-多表查询
- sharding-jdbc分库分表规则(1)-单表查询
- 分库分表sharding jdbc
- 解读分库分表中间件Sharding-JDBC
- 解读分库分表中间件Sharding-JDBC
- 解读分库分表中间件Sharding-JDBC
- 解读分库分表中间件Sharding-JDBC
- 解读分库分表中间件Sharding-JDBC
- 解读分库分表中间件Sharding-JDBC
- 当当分库分表中间件-sharding-jdbc
- 解读分库分表中间件Sharding-JDBC
- 解读分库分表中间件Sharding-JDBC
- 解读分库分表中间件Sharding-JDBC
- 解读分库分表中间件Sharding-JDBC
- 数据分库分表Sharding JDBC学习
- Sharding-JDBC分库分表使用实例
- 解读分库分表中间件Sharding-JDBC
- Sharding-JDBC
- 孩子们的游戏(圆圈中最后剩下的数)
- 网络五层模型
- Android TV 官方教程简读1-Building Apps for TV
- Linux打包免安装的Qt程序
- 扑克牌顺子
- sharding-jdbc分库分表规则(2)-多表查询
- day1对象
- Surround the Trees HDU
- Gitolite + repo 搭建安卓源码开发环境
- 翻转单词顺序列
- 使用python在Window10下配置tensorflow
- Codeforces Round #436 (Div. 2) E
- 左旋转字符串
- SpringMVC简例HelloWorld~