根据如下pom整上一个spring-boot项目,spring-boot版本用2.3.5,shardingsphere用5.1.1。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.shardingsphere</groupId><artifactId>shardingsphere-test</artifactId><version>1.0-SNAPSHOT</version><!--引入spring boot 2.3.5 --><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.5.RELEASE</version><relativePath/></parent><dependencies><!--spring boot的web模块--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--引入shardingsphere--><dependency><groupId>org.apache.shardingsphere</groupId><artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId><version>5.1.1</version></dependency><!--引入mybatis plus--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.3.1</version></dependency><!--引入mysal connector--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><!--引入Lombok--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><!--引入spring 测试--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope><exclusions><exclusion><groupId>org.junit.vintage</groupId><artifactId>junit-vintage-engine</artifactId></exclusion></exclusions></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId></dependency><!--spring 测试类--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-test</artifactId></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><scope>compile</scope></dependency></dependencies></project>
再用mysql整一个名为shardingsphere-test的库(编码集utf8mb4,也可以选自己喜欢的编码集),再往里弄上两个测试表。
CREATE TABLE t_course_1 (`cid` BIGINT(20) NOT NULL,`user_id` BIGINT(20) DEFAULT NULL,`cname` VARCHAR(50) DEFAULT NULL,`brief` VARCHAR(50) DEFAULT NULL,`price` DOUBLE DEFAULT NULL,`status` INT(11) DEFAULT NULL,PRIMARY KEY (`cid`)
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4;CREATE TABLE t_course_2 (`cid` BIGINT(20) NOT NULL,`user_id` BIGINT(20) DEFAULT NULL,`cname` VARCHAR(50) DEFAULT NULL,`brief` VARCHAR(50) DEFAULT NULL,`price` DOUBLE DEFAULT NULL,`status` INT(11) DEFAULT NULL,PRIMARY KEY (`cid`)
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4;
项目结构如下。
application.yml的配置及解释如下:
spring:application:name: sharding-jdbc-demo # 应用名称shardingsphere:# 设置全局属性props:# 打开SQL显示功能,将会打印出执行的原始SQL和实际路由后的SQLsql-show: true# 数据源配置datasource:# 定义数据源名称列表,这里只有一个名为db1的数据源names: db1# 数据源db1的具体配置db1:# 数据源实现类,此处使用HikariCP连接池type: com.zaxxer.hikari.HikariDataSource# JDBC驱动类名,对应MySQL数据库驱动driver-class-name: com.mysql.jdbc.Driver# 数据库连接URL,连接本地MySQL服务器上的shardingsphere-test数据库,并设置字符编码和禁用SSLurl: jdbc:mysql://127.0.0.1:3306/shardingsphere-test?characterEncoding=UTF-8&useSSL=false# 数据库用户名username: root# 数据库密码password: root# 规则配置部分rules:# 分片规则相关配置sharding:# 1.定义分片表的实际分布情况tables:# 针对表t_course的分片配置t_course:# 实际数据节点分布,表明t_course表被分成两张表,分别命名为t_course_0和t_course_1,并存储在db1数据库中actual-data-nodes: "db1.t_course_$->{1..2}"# 配置策略table-strategy:# 用于单分片键的标准分片场景standard:sharding-column: cid# 分片算法名字sharding-algorithm-name: course_inline# 分布式主键生成策略配置key-generate-strategy:# 主键列名为cidcolumn: cid# 引用已定义的分布式序列生成器 alg-snowflakekey-generator-name: snowflake# 2.定义分布式序列生成器key-generators:# 定义名为alg-snowflake的分布式序列生成器 alg-snowflakesnowflake:# 类型为SNOWFLAKE算法,用于生成全局唯一IDtype: SNOWFLAKE# 3.定义分片算法sharding-algorithms:# 定义名为table-inline的分片算法course_inline:# 使用INLINE类型的行表达式分片算法type: INLINE# 算法属性配置props:# 行表达式算法具体内容,按照cid模2加1的值来确定数据落入哪张表,例如:cid%2+1=0,则落入t_course_1,等于1则落入t_course_2algorithm-expression: t_course_$->{cid % 2 + 1}# 4.定义分片策略#sharding-strategies:# 对于表t_course的分片策略定义#t_course_strategy:# 使用标准分片策略#type: STANDARD# 指定分片键为cid列#sharding-column: cid# 引用已定义的分片算法#sharding-algorithm-name: course_inline# SQL输出日志
mybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
再弄一个对应数据库的实体类。
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.ToString;@TableName("t_course")
@Data
@ToString
public class Course {//@TableId(type = IdType.AUTO)@TableIdprivate Long cid;private Long userId;private Long corderNo;private String cname;private String brief;private Double price;private Integer status;}
再搞一个mapper。
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.shardingsphere.demo.entity.Course;
import org.apache.ibatis.annotations.Mapper;@Mapper
public interface CourseMapper extends BaseMapper<Course> {}
再搞一个service接口。
public interface CoureInterface {public void addCoure();
}
按道上的规矩再搞一个service接口的实现类。
import com.shardingsphere.demo.coure.CoureInterface;
import com.shardingsphere.demo.entity.Course;
import com.shardingsphere.demo.mapper.CourseMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class CoureInterfaceImpl implements CoureInterface {@Autowiredprivate CourseMapper courseMapper;@Overridepublic void addCoure() {for (int i = 0; i < 30; i++) {Course course = new Course();// 注意: cid使用雪花算法配置了(还可以使用MybatisPlus UUID),此处不用配置course.setUserId(1000L + i);course.setCname("ShardingSphere");course.setBrief("ShardingSphere测试");course.setPrice(66.6);course.setStatus(1);courseMapper.insert(course);}}
}
再弄上一个controller,来接收远方的呼唤。
import com.shardingsphere.demo.coure.CoureInterface;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class CourseController {@Autowiredprivate CoureInterface coureInterface;@RequestMapping(path = "/demo/addCourse")public void addCourse(){coureInterface.addCoure();}
}
最后再弄上一个spring boot的启动类,用来启动这个sping boot项目。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class ShardingSphereTest {public static void main(String[] args) {SpringApplication.run(ShardingSphereTest.class, args);}
}
我们启动服务后,直接浏览器访问这个接口简单测试一下。
localhost:8080/demo/addCourse
再去数据库看一眼,发现如我们所想,数据已经被拆分到两个表中了。
趁热打铁,我们再进阶一小点,把库也给分了算了。
再找一台机器创建一个跟上面一模一样的数据库(shardingsphere-test),库里的表也跟上面建的一模一样两张表(t_course_1,t_course_2),这样我们就富裕了,有了俩数据库。
需要改造的地方就是我们的application.yml配置文件,加上分库操作。
spring:application:name: sharding-jdbc-demo-database # 应用名称shardingsphere:# 设置全局属性props:# 打开SQL显示功能,将会打印出执行的原始SQL和实际路由后的SQLsql-show: true# 数据源配置datasource:# 定义数据源名称列表,这里有两个数据源(数据的名字可以一样,也可以不一样)names: db1, db2# 数据源db1的具体配置db1:# 数据源实现类,此处使用HikariCP连接池type: com.zaxxer.hikari.HikariDataSource# JDBC驱动类名,对应MySQL数据库驱动driver-class-name: com.mysql.jdbc.Driver# 数据库连接URL,连接本地MySQL服务器上的shardingsphere-test数据库,并设置字符编码和禁用SSLurl: jdbc:mysql://127.0.0.1:3306/shardingsphere-test?characterEncoding=UTF-8&useSSL=false# 数据库用户名username: root# 数据库密码password: root# 数据源db1的具体配置db2:# 数据源实现类,此处使用HikariCP连接池type: com.zaxxer.hikari.HikariDataSource# JDBC驱动类名,对应MySQL数据库驱动driver-class-name: com.mysql.jdbc.Driver# 数据库连接URL,连接本地MySQL服务器上的shardingsphere-test数据库,并设置字符编码和禁用SSLurl: jdbc:mysql://124.223.XXX.XXX:3306/shardingsphere-test?characterEncoding=UTF-8&useSSL=false# 数据库用户名username: root# 数据库密码password: root# 规则配置部分rules:# 分片规则相关配置sharding:# 1.定义分片表的实际分布情况tables:# 针对表t_course的分片配置t_course:# 实际数据节点分布,表明t_course表被分成两张表,分别命名为t_course_0和t_course_1,并存储在db1数据库中actual-data-nodes: "db$->{1..2}.t_course_$->{1..2}"# 配置库策略database-strategy:standard:sharding-column: user_idsharding-algorithm-name: table_inline# 配置表策略table-strategy:# 用于单分片键的标准分片场景standard:sharding-column: cid# 分片算法名字sharding-algorithm-name: course_inline# 分布式主键生成策略配置key-generate-strategy:# 主键列名为cidcolumn: cid# 引用已定义的分布式序列生成器 alg-snowflakekey-generator-name: snowflake# 2.定义分布式序列生成器key-generators:# 定义名为alg-snowflake的分布式序列生成器 alg-snowflakesnowflake:# 类型为SNOWFLAKE算法,用于生成全局唯一IDtype: SNOWFLAKE# 3.定义分片算法sharding-algorithms:#定义库分片算法table_inline:type: INLINEprops:algorithm-expression: db$->{user_id % 2 + 1}# 定义名为table-inline的分片算法,表分片course_inline:# 使用INLINE类型的行表达式分片算法type: INLINE# 算法属性配置props:# 行表达式算法具体内容,按照cid模2加1的值来确定数据落入哪张表,例如:cid%2+1=0,则落入t_course_1,等于1则落入t_course_2algorithm-expression: t_course_$->{cid % 2 + 1}# 4.定义分片策略#sharding-strategies:# 对于表t_course的分片策略定义#t_course_strategy:# 使用标准分片策略#type: STANDARD# 指定分片键为cid列#sharding-column: cid# 引用已定义的分片算法#sharding-algorithm-name: course_inline# SQL输出日志
mybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
然后重启服务,重新访问localhost:8080/demo/addCourse 调用添加数据的服务接口,此时再查数据库就会发现数据已经被shardingsphere分到不同库的不同表里了。
分库查询
入库了以后,我们写个测试类尝试查询一下,看看会是怎么样。
@SpringBootTest
@RunWith(SpringRunner.class)
@Slf4j
public class MyTest {@Autowired(required = false)private CourseMapper courseMapper;/*** 水平分片:查询所有记录* 查询了两个数据源,每个数据源中使用UNION ALL连接两个表*/@Testpublic void testShardingSelectOne(){List<Course> courses = courseMapper.selectList(null);courses.forEach(System.out::println);}}
我们来看一下查询结果。
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@30b0d5a7] was not registered for synchronization because synchronization is not active
JDBC Connection [org.apache.shardingsphere.driver.jdbc.core.connection.ShardingSphereConnection@411e567e] will not be managed by Spring
==> Preparing: SELECT cid,user_id,cname,brief,price,status FROM t_course
==> Parameters:
2024-04-19 21:17:29.909 INFO 25740 --- [ main] ShardingSphere-SQL : Logic SQL: SELECT cid,user_id,cname,brief,price,status FROM t_course
2024-04-19 21:17:29.909 INFO 25740 --- [ main] ShardingSphere-SQL : SQLStatement: MySQLSelectStatement(table=Optional.empty, limit=Optional.empty, lock=Optional.empty, window=Optional.empty)
2024-04-19 21:17:29.910 INFO 25740 --- [ main] ShardingSphere-SQL : Actual SQL: db1 ::: SELECT cid,user_id,cname,brief,price,status FROM t_course_1 UNION ALL SELECT cid,user_id,cname,brief,price,status FROM t_course_2
2024-04-19 21:17:29.910 INFO 25740 --- [ main] ShardingSphere-SQL : Actual SQL: db2 ::: SELECT cid,user_id,cname,brief,price,status FROM t_course_1 UNION ALL SELECT cid,user_id,cname,brief,price,status FROM t_course_2
<== Columns: cid, user_id, cname, brief, price, status
<== Row: 1781311094111928321, 1000, ShardingSphere, ShardingSphere尝试查询, 99.0, 1
<== Row: 1781311097660309505, 1002, ShardingSphere, ShardingSphere尝试查询, 99.0, 1
<== Row: 1781311098117488641, 1004, ShardingSphere, ShardingSphere尝试查询, 99.0, 1
<== Row: 1781311098650165249, 1006, ShardingSphere, ShardingSphere尝试查询, 99.0, 1
<== Row: 1781311097660309506, 1003, ShardingSphere, ShardingSphere尝试查询, 99.0, 1
<== Row: 1781311098184597506, 1005, ShardingSphere, ShardingSphere尝试查询, 99.0, 1
<== Row: 1781311098650165250, 1007, ShardingSphere, ShardingSphere尝试查询, 99.0, 1
<== Row: 1781311096750145537, 1001, ShardingSphere, ShardingSphere尝试查询, 99.0, 1
<== Total: 8
可以看到,它是把每个库的每张表进行union操作,返回返回总结果。
多表关联查询
如果我们要是多表查询呢?先建上两张有关联的表来试一下。如下两个数据库分别创建t_order0,t_order1,t_order_item0,t_order_item1,仍然分库分表创建,只不过让t_order和t_order_item有联系,即有如此关联:SELECT o.*,i.* FROM t_order o JOIN t_order_item i ON o.order_no = i.order_no;
CREATE TABLE t_order0 (id BIGINT,order_no VARCHAR(30),user_id BIGINT,amount DECIMAL(10,2),PRIMARY KEY(id)
);
CREATE TABLE t_order1 (id BIGINT,order_no VARCHAR(30),user_id BIGINT,amount DECIMAL(10,2),PRIMARY KEY(id)
);CREATE TABLE t_order_item0(id BIGINT,order_no VARCHAR(30),user_id BIGINT,price DECIMAL(10,2),`count` INT,PRIMARY KEY(id)
);CREATE TABLE t_order_item1(id BIGINT,order_no VARCHAR(30),user_id BIGINT,price DECIMAL(10,2),`count` INT,PRIMARY KEY(id)
);
再在配置文件中将这俩表的配置搞好。
spring:application:name: sharding-jdbc-demo-database # 应用名称shardingsphere:# 设置全局属性props:# 打开SQL显示功能,将会打印出执行的原始SQL和实际路由后的SQLsql-show: true# 数据源配置datasource:# 定义数据源名称列表,这里有两个数据源(数据的名字可以一样,也可以不一样)names: db1, db2# 数据源db1的具体配置db1:# 数据源实现类,此处使用HikariCP连接池type: com.zaxxer.hikari.HikariDataSource# JDBC驱动类名,对应MySQL数据库驱动driver-class-name: com.mysql.jdbc.Driver# 数据库连接URL,连接本地MySQL服务器上的shardingsphere-test数据库,并设置字符编码和禁用SSLurl: jdbc:mysql://127.0.0.1:3306/shardingsphere-test?characterEncoding=UTF-8&useSSL=false# 数据库用户名username: root# 数据库密码password: root# 数据源db1的具体配置db2:# 数据源实现类,此处使用HikariCP连接池type: com.zaxxer.hikari.HikariDataSource# JDBC驱动类名,对应MySQL数据库驱动driver-class-name: com.mysql.jdbc.Driver# 数据库连接URL,连接本地MySQL服务器上的shardingsphere-test数据库,并设置字符编码和禁用SSLurl: jdbc:mysql://124.223.XXX.XXX:3306/shardingsphere-test?characterEncoding=UTF-8&useSSL=false# 数据库用户名username: root# 数据库密码password: 123456# 规则配置部分rules:# 分片规则相关配置sharding:# 1.定义分片表的实际分布情况tables:# 针对表t_course的分片配置t_course:# 实际数据节点分布,表明t_course表被分成两张表,分别命名为t_course_0和t_course_1,并存储在db1数据库中actual-data-nodes: "db$->{1..2}.t_course_$->{1..2}"# 配置库策略database-strategy:standard:sharding-column: user_idsharding-algorithm-name: table_inline# 配置表策略table-strategy:# 用于单分片键的标准分片场景standard:sharding-column: cid# 分片算法名字sharding-algorithm-name: course_inline# 分布式主键生成策略配置key-generate-strategy:# 主键列名为cidcolumn: cid# 引用已定义的分布式序列生成器 alg-snowflakekey-generator-name: snowflake#order表的分片分库策略t_order:# 实际数据节点分布,表明t_course表被分成两张表,分别命名为t_course_0和t_course_1,并存储在db1数据库中actual-data-nodes: "db$->{1..2}.t_order$->{0..1}"# 配置分库策略database-strategy:standard:sharding-column: user_idsharding-algorithm-name: order_inline# 配置分表策略table-strategy:# 用于单分片键的标准分片场景,根据order_no的hash值进行分片standard:sharding-column: order_no# 分片算法名字sharding-algorithm-name: order_no_mod# 分布式主键生成策略配置key-generate-strategy:# 主键列名为cidcolumn: id# 引用已定义的分布式序列生成器 alg-snowflakekey-generator-name: snowflake#order表的分片分库策略t_order_item:# 实际数据节点分布,表明t_course表被分成两张表,分别命名为t_course_0和t_course_1,并存储在db1数据库中actual-data-nodes: "db$->{1..2}.t_order_item$->{0..1}"# 配置分库策略database-strategy:standard:sharding-column: user_idsharding-algorithm-name: order_inline# 配置分表策略table-strategy:# 用于单分片键的标准分片场景,根据order_no的hash值进行分片standard:sharding-column: order_no# 分片算法名字sharding-algorithm-name: order_no_mod# 分布式主键生成策略配置key-generate-strategy:# 主键列名为cidcolumn: id# 引用已定义的分布式序列生成器 alg-snowflakekey-generator-name: snowflake# 2.定义分布式序列生成器key-generators:# 定义名为alg-snowflake的分布式序列生成器 alg-snowflakesnowflake:# 类型为SNOWFLAKE算法,用于生成全局唯一IDtype: SNOWFLAKE# 3.定义你想配置表的分片算法sharding-algorithms:#定义库分片算法table_inline:type: INLINEprops:algorithm-expression: db$->{user_id % 2 + 1}# 定义名为table-inline的分片算法,表分片course_inline:# 使用INLINE类型的行表达式分片算法type: INLINE# 算法属性配置props:# 行表达式算法具体内容,按照cid模2加1的值来确定数据落入哪张表,例如:cid%2+1=0,则落入t_course_1,等于1则落入t_course_2algorithm-expression: t_course_$->{cid % 2 + 1}order_inline:type: INLINEprops:algorithm-expression: db$->{user_id % 2 + 1}order_no_mod:# 使用HASH_MOD类型的行表达式分片算法type: HASH_MOD# 算法属性配置props:# 行表达式算法具体内容,sharding-count: 2# 4.定义分片策略#sharding-strategies:# 对于表t_course的分片策略定义#t_course_strategy:# 使用标准分片策略#type: STANDARD# 指定分片键为cid列#sharding-column: cid# 引用已定义的分片算法#sharding-algorithm-name: course_inline# SQL输出日志
mybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
OrderMapper改造一下,进行表关联。
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.shardingsphere.demo.entity.Order;
import com.shardingsphere.demo.vo.OrderVo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;import java.util.List;@Mapper
public interface OrderMapper extends BaseMapper<Order> {@Select({"SELECT o.order_no, SUM(i.price * i.count) AS amount","FROM t_order o JOIN t_order_item i ON o.order_no = i.order_no","GROUP BY o.order_no"})List<OrderVo> getOrderAmount();
}
再写个测试方法。
/*** 测试关联表查询*/@Testpublic void testGetOrderAmount(){List<OrderVo> orderAmountList = orderMapper.getOrderAmount();orderAmountList.forEach(System.out::println);}
看看执行结果:
==> Preparing: SELECT o.order_no, SUM(i.price * i.count) AS amount FROM t_order o JOIN t_order_item i ON o.order_no = i.order_no GROUP BY o.order_no
==> Parameters:
2024-04-19 21:27:52.938 INFO 21784 --- [ main] ShardingSphere-SQL : Logic SQL: SELECT o.order_no, SUM(i.price * i.count) AS amount FROM t_order o JOIN t_order_item i ON o.order_no = i.order_no GROUP BY o.order_no
2024-04-19 21:27:52.939 INFO 21784 --- [ main] ShardingSphere-SQL : SQLStatement: MySQLSelectStatement(table=Optional.empty, limit=Optional.empty, lock=Optional.empty, window=Optional.empty)
2024-04-19 21:27:52.939 INFO 21784 --- [ main] ShardingSphere-SQL : Actual SQL: db2 ::: SELECT o.order_no, SUM(i.price * i.count) AS amount FROM t_order0 o JOIN t_order_item0 i ON o.order_no = i.order_no GROUP BY o.order_no ORDER BY o.order_no ASC
2024-04-19 21:27:52.939 INFO 21784 --- [ main] ShardingSphere-SQL : Actual SQL: db2 ::: SELECT o.order_no, SUM(i.price * i.count) AS amount FROM t_order1 o JOIN t_order_item0 i ON o.order_no = i.order_no GROUP BY o.order_no ORDER BY o.order_no ASC
2024-04-19 21:27:52.939 INFO 21784 --- [ main] ShardingSphere-SQL : Actual SQL: db2 ::: SELECT o.order_no, SUM(i.price * i.count) AS amount FROM t_order0 o JOIN t_order_item1 i ON o.order_no = i.order_no GROUP BY o.order_no ORDER BY o.order_no ASC
2024-04-19 21:27:52.939 INFO 21784 --- [ main] ShardingSphere-SQL : Actual SQL: db2 ::: SELECT o.order_no, SUM(i.price * i.count) AS amount FROM t_order1 o JOIN t_order_item1 i ON o.order_no = i.order_no GROUP BY o.order_no ORDER BY o.order_no ASC
2024-04-19 21:27:52.939 INFO 21784 --- [ main] ShardingSphere-SQL : Actual SQL: db1 ::: SELECT o.order_no, SUM(i.price * i.count) AS amount FROM t_order0 o JOIN t_order_item0 i ON o.order_no = i.order_no GROUP BY o.order_no ORDER BY o.order_no ASC
2024-04-19 21:27:52.939 INFO 21784 --- [ main] ShardingSphere-SQL : Actual SQL: db1 ::: SELECT o.order_no, SUM(i.price * i.count) AS amount FROM t_order1 o JOIN t_order_item0 i ON o.order_no = i.order_no GROUP BY o.order_no ORDER BY o.order_no ASC
2024-04-19 21:27:52.939 INFO 21784 --- [ main] ShardingSphere-SQL : Actual SQL: db1 ::: SELECT o.order_no, SUM(i.price * i.count) AS amount FROM t_order0 o JOIN t_order_item1 i ON o.order_no = i.order_no GROUP BY o.order_no ORDER BY o.order_no ASC
2024-04-19 21:27:52.939 INFO 21784 --- [ main] ShardingSphere-SQL : Actual SQL: db1 ::: SELECT o.order_no, SUM(i.price * i.count) AS amount FROM t_order1 o JOIN t_order_item1 i ON o.order_no = i.order_no GROUP BY o.order_no ORDER BY o.order_no ASC
<== Columns: order_no, amount
<== Row: ShardingSphere1, 40.00
<== Row: ShardingSphere2, 40.00
<== Row: ShardingSphere5, 6.00
<== Row: ShardingSphere6, 6.00
<== Total: 4
发现了一个问题:可以看到同一个数据源中,查询的次数是t_order
和t_order_item
的笛卡尔积数量,但是t_order0
中的订单数据只会在对应数据源中t_order_item0
,不会在t_order_item1
中,所以有些关联查询是没有意义的,那么接下来就引入了绑定表的概念。
绑定表
指分片规则一致的一组分片表。 使用绑定表进行多表关联查询时,必须使用分片键进行关联,否则会出现笛卡尔积关联或跨库关联,从而影响查询效率。所以我们来配置一下,将t_order
和t_order_item绑定一下,只需要在rules这里增加如下配置,如果你的配置文件是.properties类型的,需要这样配:
spring.shardingsphere.rules.sharding.binding-tables[0]=t_order,t_order_item。
# 规则配置部分rules:# 分片规则相关配置sharding:#绑定表binding-tables:- t_order,t_order_item
再次查询:
==> Preparing: SELECT o.order_no, SUM(i.price * i.count) AS amount FROM t_order o JOIN t_order_item i ON o.order_no = i.order_no GROUP BY o.order_no
==> Parameters:
2024-04-19 21:43:08.069 INFO 24608 --- [ main] ShardingSphere-SQL : Logic SQL: SELECT o.order_no, SUM(i.price * i.count) AS amount FROM t_order o JOIN t_order_item i ON o.order_no = i.order_no GROUP BY o.order_no
2024-04-19 21:43:08.069 INFO 24608 --- [ main] ShardingSphere-SQL : SQLStatement: MySQLSelectStatement(table=Optional.empty, limit=Optional.empty, lock=Optional.empty, window=Optional.empty)
2024-04-19 21:43:08.070 INFO 24608 --- [ main] ShardingSphere-SQL : Actual SQL: db1 ::: SELECT o.order_no, SUM(i.price * i.count) AS amount FROM t_order0 o JOIN t_order_item0 i ON o.order_no = i.order_no GROUP BY o.order_no ORDER BY o.order_no ASC
2024-04-19 21:43:08.070 INFO 24608 --- [ main] ShardingSphere-SQL : Actual SQL: db1 ::: SELECT o.order_no, SUM(i.price * i.count) AS amount FROM t_order1 o JOIN t_order_item1 i ON o.order_no = i.order_no GROUP BY o.order_no ORDER BY o.order_no ASC
2024-04-19 21:43:08.070 INFO 24608 --- [ main] ShardingSphere-SQL : Actual SQL: db2 ::: SELECT o.order_no, SUM(i.price * i.count) AS amount FROM t_order0 o JOIN t_order_item0 i ON o.order_no = i.order_no GROUP BY o.order_no ORDER BY o.order_no ASC
2024-04-19 21:43:08.070 INFO 24608 --- [ main] ShardingSphere-SQL : Actual SQL: db2 ::: SELECT o.order_no, SUM(i.price * i.count) AS amount FROM t_order1 o JOIN t_order_item1 i ON o.order_no = i.order_no GROUP BY o.order_no ORDER BY o.order_no ASC
<== Columns: order_no, amount
<== Row: ShardingSphere1, 40.00
<== Row: ShardingSphere2, 40.00
<== Row: ShardingSphere5, 6.00
<== Row: ShardingSphere6, 6.00
<== Total: 4
突然一家伙少了四条查询。
- 如果不配置绑定表:测试的结果为8个SQL。 多表关联查询会出现笛卡尔积关联。
- 如果配置绑定表:测试的结果为4个SQL。 多表关联查询不会出现笛卡尔积关联,关联查询效率将大大提升。