Sharding-Sphere
簡介
1、ShardingSphere是一套開源的分布式數據庫中間件的解決方案
2、它由三個產品組成:Sharding-JDBC、Sharding-Proxy和Sharding-Sidecar。
3、是關系型數據庫中間件,合理在分布式環境下使用關系型數據庫操作
Sharding-JDBC
輕量級Java框架,可理解為增強版的JDBC驅動,完全兼容JDBC和各種ORM框架(如:JPA、Hibernate、mybatis、Spring JDBC Template或直接使用JDBC),
支持任何第三方的數據庫連接池(如:DBCP、C3P0、BoneCP、Druid、HikariCP等),支持仍以實現JDBC規范的數據庫(目前支持MySQL、Oracle、SQLServer、
PostgreSQL以及任何遵循SQL92標准的數據庫)。
ShardingSphere-Proxy
定位為透明化的數據庫代理端,提供封裝了數據庫二進制協議的服務端版本,用於完成對異構語言的支持。 目前提供 MySQL 和 PostgreSQL 版本,它可以
使用任何兼容 MySQL/PostgreSQL 協議的訪問客戶端(如:MySQL Command Client, MySQL Workbench, Navicat 等)操作數據,對 DBA 更加友好。
- 向應用程序完全透明,可直接當做 MySQL/PostgreSQL 使用。
- 適用於任何兼容 MySQL/PostgreSQL 協議的的客戶端。
分庫分表介紹
數據庫的數據量是不可控的,隨着時間和業務發展,造成表中數據越來越多,如果再去對數據庫表CRUD操作時,造成性能問題
解決方案
- 從硬件上解決,增加硬盤、內存。(治標不治本)
- 分庫分表
為了解決由於數據量過大而造成數據庫性能降低問題。
![]()
分庫分表方式
分庫分表有兩種方式:垂直切分和水平切分
垂直切分:垂直分表和垂直分庫
水平切分:水平分表和水平分庫
垂直切分
垂直分表
操作數據庫中某張表,把這張表中一部分字段數據存到一張新表里面,再把這張表另一部分字段數據存到另外一張表里面
![]()
垂直分庫
把單一數據庫按照業務進行划分,專庫專表。
![]()
水平切分
水平分表
水平分庫
![]()
這種方式帶來了一些問題,如何將數據插入這兩個庫?可以采用根據id取余的方式來插入數據庫,比如,這里有兩個庫,用
id%數據庫個數
,如果余數為0,就在A庫,余數為1,就在B庫
分庫分表的應用和問題
應用
- 在數據庫設計時候考慮垂直分庫和垂直分表
- 隨着數據庫數據量增加,不要馬上考慮做水平拆分,首先考慮緩存處理、讀寫分離,使用索引等方式,如果這些方式都不能根本解決問題了,再考慮做水平拆分
分庫分表帶來的問題
- 跨節點連接查詢問題(分頁、排序)
- 多數據源管理問題
Sharding-JDBC
簡介
定位為輕量級 Java 框架,在 Java 的 JDBC 層提供的額外服務。 它使用客戶端直連數據庫,以 jar 包形式提供服務,無需額外部署和依賴,可理解為增強版的 JDBC 驅動,完全兼容 JDBC 和各種 ORM 框架。
- 適用於任何基於 JDBC 的 ORM 框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template 或直接使用 JDBC。
- 支持任何第三方的數據庫連接池,如:DBCP, C3P0, BoneCP, Druid, HikariCP 等。
- 支持任意實現 JDBC 規范的數據庫,目前支持 MySQL,Oracle,SQLServer,PostgreSQL 以及任何遵循 SQL92 標准的數據庫。

Sharding-JDBC實現水平分表
搭建環境
-
創建shardingjdbcdemo項目(SpringBoot2.2.1)
-
引入依賴
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.20</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.apache.shardingsphere</groupId> <artifactId>sharding-jdbc-spring-boot-starter</artifactId> <version>4.0.0-RC1</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.0.5</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> </dependencies>
-
創建數據庫
按照水平分表的放肆,創建數據庫和數據庫表。
-
創建數據庫course_db
-
在數據庫中創建兩張表course_1和course_2
-
約定規則,課程id是偶數把數據添加到course_1,奇數添加到course_2
CREATE TABLE course_1( cid BIGINT(20) PRIMARY KEY, cname VARCHAR(50) NOT NULL, user_id BIGINT(20) NOT NULL, cstatus VARCHAR(10) NOT NULL ) CREATE TABLE course_2( cid BIGINT(20) PRIMARY KEY, cname VARCHAR(50) NOT NULL, user_id BIGINT(20) NOT NULL, cstatus VARCHAR(10) NOT NULL )
-
-
編寫代碼
-
實體類
@Data @AllArgsConstructor @NoArgsConstructor public class Course { private Long cid; private String cname; private Long userId; private String cstatus; }
-
Mapper
@Mapper public interface CourseMapper extends BaseMapper<Course> { }
-
啟動類
@SpringBootApplication @MapperScan("om.atguigu.shardingjdbcdemo.mapper") public class ShardingjdbcdemoApplication { public static void main(String[] args) { SpringApplication.run(ShardingjdbcdemoApplication.class, args); } }
-
-
配置Sharding-JDBC分片策略
- 在
application.yml
配置文件中進行配置
注: course是表名前綴
# 配置分片策略 spring: shardingsphere: datasource: #配置數據源名字 names: ds1 # 配置數據源具體內容 ds1: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://192.168.245.128:3306/course_db username: root password: 123456 # 指定course表分布情況,配置表在哪個數據庫里面,表名稱都是什么 sharding: tables: course: actual-data-nodes: ds1.course_$->{1..2} #指定course表里面主鍵生成策略 key-generator: column: cid # SNOWFLAKE 雪花算法生成的id type: SNOWFLAKE # 指定分片策略, 約定cid值偶數添加到course_1表中,奇數添加到course_2表中 table-strategy: #inline 的方式不支持范圍查詢 inline: sharding-column: cid algorithm-expression: course_$->{cid % 2 + 1} # 打開SQL輸出日志 props: sql: show: true
- 在
-
測試
@RunWith(SpringRunner.class) @SpringBootTest class ShardingjdbcdemoApplicationTests { @Autowired private CourseMapper courseMapper; @Test void addCourse() { Course course = new Course(); course.setCname("Java"); course.setUserID(100L); course.setCstatus("NOrmal"); int insert = courseMapper.insert(course); } }
會出現如下錯誤:
這個錯誤是由於我們有兩張表,只有一個實體類,雖然字段一樣,但是不能映射,只要添加上面的配置紅框的配置即可。
spring: main: allow-bean-definition-overriding: true
再次測試:
2021-03-16 16:57:48.550 INFO 17616 --- [ main] ShardingSphere-SQL : Actual SQL: ds1 ::: INSERT INTO course_2 (cname, user_id, cstatus, cid) VALUES (?, ?, ?, ?) ::: [Java, 100, NOrmal, 578627173078794241]
可以看到cid為奇數,在course_2表
成功插入到2號表
Sharding-JDBC實現水平分庫
需求分析
創建兩個數據庫
約定分片規則:
數據庫規則:
- userid為偶數的數據添加到edu_db_1數據庫中
- 奇數數據添加到edu_db_2數據庫中
表規則:
- cid為偶數數據添加到course_1表
- 奇數數據添加到course_2表中
創建數據庫
// 建表SQL
CREATE TABLE course_1(
cid BIGINT(20) PRIMARY KEY,
cname VARCHAR(50) NOT NULL,
user_id BIGINT(20) NOT NUll,
cstatus VARCHAR(10) NOT NULL
)
配置數據庫分片規則
# 配置分片策略
spring:
shardingsphere:
datasource:
#配置數據源名字
names: ds1,ds2
# 配置數據源具體內容
ds1:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.245.130:3306/edu_db_1
username: root
password: 123456
# 第二個數據源
ds2:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.245.130:3306/edu_db_2
username: root
password: 123456
# 指定數據庫分布情況,配置表在哪個數據庫里面,表名稱都是什么
# m1 m2 course_1 course_2
sharding:
tables:
course:
actual-data-nodes: ds$->{1..2}.course_$->{1..2}
#指定course表里面主鍵生成策略
key-generator:
column: cid
# SNOWFLAKE 雪花算法生成的id
type: SNOWFLAKE
# 指定表分片策略, 約定cid值偶數添加到course_1表中,奇數添加到course_2表中
table-strategy:
inline:
sharding-column: cid
algorithm-expression: course_$->{cid % 2 + 1}
#指定庫分片策略
# 1. userid為偶數的數據添加到edu_db_1數據庫中
# 2. 奇數數據添加到edu_db_2數據庫中
database-strategy:
standard:
inline:
sharding-column: user_id
algorithm-expression: ds$->{user_id % 2 + 1}
#指定庫分片策略
# 1. userid為偶數的數據添加到edu_db_1數據庫中
# 2. 奇數數據添加到edu_db_2數據庫中
# default-database-strategy:
# inline:
# sharding-column: user_id
# algorithm-expression: ds$->{user_id % 2 + 1}
# 打開SQL輸出日志
props:
sql:
show: true
main:
allow-bean-definition-overriding: true
編寫測試代碼
@Test
void addCourseDb() {
Course course = new Course();
course.setCname("JavaDemo");
course.setUserId(100L);
course.setCstatus("Normal");
int insert = courseMapper.insert(course);
}
根據分片規則,user_id為偶數是在ds1(edu_db_1)庫,cid偶數是在course_1表,我們來看看結果:
cid是奇數,插入了course_2表。user_id是偶數,插入了ds1庫
@Test
public void findCourseDb() {
QueryWrapper<Course> wrapper = new QueryWrapper<>();
wrapper.eq("user_id", 100L);
wrapper.eq("cid",579491782740410369L);
Course course = courseMapper.selectOne(wrapper);
System.out.println("-------------------------->" + course);
}
Sharding-JDBC實現垂直分庫
需求分析
垂直分庫就是專庫專表,如下

創建數據庫和表
編寫代碼
-
創建user實體類和UserMapper
@Data public class User { private Long userId; private String username; private String ustatus; }
@Mapper public interface UserMapper extends BaseMapper<User> { }
-
配置垂直分庫策略
# 配置分片策略 spring: shardingsphere: datasource: #配置數據源名字 names: ds1,ds2,ds3 # 配置數據源具體內容 ds1: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://192.168.245.130:3306/edu_db_1 username: root password: 123456 # 第二個數據源 ds2: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://192.168.245.130:3306/edu_db_2 username: root password: 123456 # 第三個數據源 ds3: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://192.168.245.130:3306/user_db username: root password: 123456 # 指定數據庫分布情況,配置表在哪個數據庫里面,表名稱都是什么 # m1 m2 course_1 course_2 sharding: tables: course: actual-data-nodes: ds$->{1..2}.course_$->{1..2} #指定course表里面主鍵生成策略 key-generator: column: cid # SNOWFLAKE 雪花算法生成的id type: SNOWFLAKE # 指定表分片策略, 約定cid值偶數添加到course_1表中,奇數添加到course_2表中 table-strategy: inline: sharding-column: cid algorithm-expression: course_$->{cid % 2 + 1} #指定庫分片策略 # 1. userid為偶數的數據添加到edu_db_1數據庫中 # 2. 奇數數據添加到edu_db_2數據庫中 database-strategy: inline: sharding-column: user_id algorithm-expression: ds$->{user_id % 2 + 1} # 配置user_db數據庫里面t_user 專庫專表 t_user: actual-data-nodes: ds$->{3}.t_user #指定course表里面主鍵生成策略 key-generator: column: user_id # SNOWFLAKE 雪花算法生成的id type: SNOWFLAKE # 指定表分片策略, 約定cid值偶數添加到course_1表中,奇數添加到course_2表中 table-strategy: inline: sharding-column: user_id algorithm-expression: t_user #指定庫分片策略 # 1. userid為偶數的數據添加到edu_db_1數據庫中 # 2. 奇數數據添加到edu_db_2數據庫中 # default-database-strategy: # inline: # sharding-column: user_id # algorithm-expression: ds$->{user_id % 2 + 1} # 打開SQL輸出日志 props: sql: show: true main: allow-bean-definition-overriding: true
-
編寫測試代碼
@Test public void addUserDb() { User user = new User(); user.setUsername("lin"); user.setUstatus("a"); userMapper.insert(user); } @Test public void findUserDb() { QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.eq("user_id", 580532807156105217L); User user = userMapper.selectOne(wrapper); System.out.println("-------------------------->" + user); }
查詢:
Sharding-JDBC操作公共表
什么是公共表?
- 存儲固定數據的表,表數據很少發生變化,查詢時經常要進行關聯。
- 在每個數據庫中都創建出相同結構公共表。
- 操作公共表時,同時操作添加了公共表的數據庫中的公共表,添加記錄時,同時添加,刪除時,同時刪除。
實現
-
在多個數據庫中創建相同結構公共表。前面我們有三個庫,現在我們在這三個庫中創建表。
建表SQL
CREATE TABLE t_udict( dictid BIGINT(20) PRIMARY KEY, ustatus VARCHAR(100) NOT NULL, uvalue VARCHAR(100) NOT NULL )
-
在application.yml中配置
# 配置分片策略 spring: shardingsphere: sharding: broadcast-tables: t_udict tables: t_udict: key-generator: column: dictid type: SNOWFLAKE
-
測試代碼
-
創建實體類和mapper
@Data @TableName(value = "u_udict") public class Udict { private Long dictid; private String ustatus; private String uvalue; }
@Mapper public interface UdictMapper extends BaseMapper<Udict> { }
-
測試代碼
@Test public void addUserDb() { User user = new User(); user.setUsername("lin"); user.setUstatus("a"); userMapper.insert(user); }
@Test void deleteDict() { QueryWrapper<Udict> wrapper = new QueryWrapper<>(); wrapper.eq("dictid", 580819000607375361L); udictMapper.delete(wrapper); }
-
Sharding-JDBC實現讀寫分離
什么是讀寫分離
為了確保數據庫產品的穩定性,很多數據庫擁有雙擊熱備功能。也就是,第一台數據庫服務器,是對外提供增刪改業務的生產服務器;第二胎數據庫服務器,主要進行讀的操作。
原理:讓主數據庫(master)處理事務性增、改、刪操作,而從數據庫(slave)處理select查詢操作
![]()
讀寫分離原理
主從復制:當主服務器有寫入(insert/update/delete)語句時候,從服務器自動獲取。
讀寫分離:insert/update/delete語句操作一台服務器,select操作另一個服務器

Sharding-JDBC讀寫分離則是根據SQL語義的分析,將讀操作和寫操作分別路由至主庫與從庫,它提供透明化讀寫分離,讓使用方法盡量像使用一個數據庫一樣使用主從數據庫集群。
主從復制配置
Sharding-JDBC操作主從
-
在配置文件中配置主從分離
# 配置分片策略 spring: shardingsphere: datasource: #配置數據源名字 names: ds1,ds2,ds3,s0 # 配置數據源具體內容 ds1: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://192.168.245.131:3306/edu_db_1 username: root password: 123456 # 第二個數據源 ds2: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://192.168.245.131:3306/edu_db_2 username: root password: 123456 # 第三個數據源 ds3: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://192.168.245.131:3306/user_db username: root password: 123456 # 從服務器 s0: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://192.168.245.131:3307/user_db username: root password: 123456 # 指定數據庫分布情況,配置表在哪個數據庫里面,表名稱都是什么 # m1 m2 course_1 course_2 sharding: tables: course: actual-data-nodes: ds$->{1..2}.course_$->{1..2} #指定course表里面主鍵生成策略 key-generator: column: cid # SNOWFLAKE 雪花算法生成的id type: SNOWFLAKE # 指定表分片策略, 約定cid值偶數添加到course_1表中,奇數添加到course_2表中 table-strategy: inline: sharding-column: cid algorithm-expression: course_$->{cid % 2 + 1} #指定庫分片策略 # 1. userid為偶數的數據添加到edu_db_1數據庫中 # 2. 奇數數據添加到edu_db_2數據庫中 database-strategy: inline: sharding-column: user_id algorithm-expression: ds$->{user_id % 2 + 1} # 配置user_db數據庫里面t_user 專庫專表 t_user: #actual-data-nodes: ds$->{3}.t_user actual-data-nodes: ds0.t_user #指定course表里面主鍵生成策略 key-generator: column: user_id # SNOWFLAKE 雪花算法生成的id type: SNOWFLAKE # 指定表分片策略, 約定cid值偶數添加到course_1表中,奇數添加到course_2表中 table-strategy: inline: sharding-column: user_id algorithm-expression: t_user t_udict: key-generator: column: dictid type: SNOWFLAKE #配置公共表 broadcast-tables: t_udict # 從服務器相關配置 master-slave-rules: ds3: master-data-source-name: ds3 slave-data-source-names: s0 #指定庫分片策略 # 1. userid為偶數的數據添加到edu_db_1數據庫中 # 2. 奇數數據添加到edu_db_2數據庫中 # default-database-strategy: # inline: # sharding-column: user_id # algorithm-expression: ds$->{user_id % 2 + 1} # 打開SQL輸出日志 props: sql: show: true main: allow-bean-definition-overriding: true
-
編寫測試代碼
@Test public void addUserDb() { User user = new User(); user.setUsername("lucymary"); user.setUstatus("a"); userMapper.insert(user); }
@Test public void findUserDb() { QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.eq("user_id", 465508031619137537L); User user = userMapper.selectOne(wrapper); System.out.println("-------------------------->" + user); }
Sharding-JDBC實現范圍查詢
配置yaml
spring:
shardingsphere:
sharding:
tables:
course:
table-strategy:
standard:
sharding-column: cid
range-algorithm-class-name: com.atguigu.shardingjdbcdemo.algorithm.RangeTableShardingAlgorithm
precise-algorithm-class-name: com.atguigu.shardingjdbcdemo.algorithm.PreciseTableShardingAlgorithm
算法實現
public class PreciseDSShardingAlgorithm implements PreciseShardingAlgorithm<Long> {
@Override
public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Long> shardingValue) {
String logicTableName = shardingValue.getLogicTableName();
String cid = shardingValue.getColumnName();
Long cidValue = shardingValue.getValue();
//實現ds$->{cid%2+1}
BigInteger bigIntegerB = BigInteger.valueOf(cidValue);
BigInteger resB = (bigIntegerB.mod(new BigInteger("2"))).add(new BigInteger("1"));
String key = "ds" + resB;
if (availableTargetNames.contains(key)) {
return key;
}
throw new UnsupportedOperationException("route" + key + " is not supported,please check your config");
}
}
public class RangeDSShardingAlgorithm implements RangeShardingAlgorithm<Long> {
@Override
public Collection<String> doSharding(Collection<String> collection, RangeShardingValue<Long> rangeShardingValue) {
//select * from course where cid between 200 and 300
//300
Long upperValue = rangeShardingValue.getValueRange().upperEndpoint();
//200
Long lowerValue = rangeShardingValue.getValueRange().lowerEndpoint();
String logicTableName = rangeShardingValue.getLogicTableName();
//ds$->{cid%2+1} ds1,ds2
return Arrays.asList("ds1", logicTableName + "ds2");
}
}
上面這種方式,只能實現in
操作,不能實現between
操作,因為上面只實現了分表的邏輯,沒有實現分庫的邏輯,分庫還是使用的inline的方式,所以,我們要實現以下分庫。
分庫邏輯
-
yaml
spring: shardingsphere: sharding: tables: course: table-strategy: standard: sharding-column: cid range-algorithm-class-name: com.atguigu.shardingjdbcdemo.algorithm.RangeTableShardingAlgorithm precise-algorithm-class-name: com.atguigu.shardingjdbcdemo.algorithm.PreciseTableShardingAlgorithm database-strategy: standard: sharding-column: cid range-algorithm-class-name: com.atguigu.shardingjdbcdemo.algorithm.RangeDSShardingAlgorithm precise-algorithm-class-name: com.atguigu.shardingjdbcdemo.algorithm.PreciseDSShardingAlgorithm
-
算法實現
public class PreciseDSShardingAlgorithm implements PreciseShardingAlgorithm<Long> { @Override public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Long> shardingValue) { String logicTableName = shardingValue.getLogicTableName(); String cid = shardingValue.getColumnName(); Long cidValue = shardingValue.getValue(); //實現ds$->{cid%2+1} BigInteger bigIntegerB = BigInteger.valueOf(cidValue); BigInteger resB = (bigIntegerB.mod(new BigInteger("2"))).add(new BigInteger("1")); String key = logicTableName + resB; if (availableTargetNames.contains(key)) { return key; } throw new UnsupportedOperationException("route" + key + " is not supported,please check your config"); } }
public class RangeDSShardingAlgorithm implements RangeShardingAlgorithm<Long> { @Override public Collection<String> doSharding(Collection<String> collection, RangeShardingValue<Long> rangeShardingValue) { //select * from course where cid between 200 and 300 //300 Long upperValue = rangeShardingValue.getValueRange().upperEndpoint(); //200 Long lowerValue = rangeShardingValue.getValueRange().lowerEndpoint(); String logicTableName = rangeShardingValue.getLogicTableName(); //ds$->{cid%2+1} ds1,ds2 return Arrays.asList("ds1", "ds2"); } }
Sharding-Proxy
簡介
定位為透明化的數據庫代理端,提供封裝了數據庫二進制協議的服務端版本,用於完成對異構語言的支持。 目前先提供MySQL/PostgreSQL版本,它可以使用任何兼容MySQL/PostgreSQL協議的訪問客戶端(如:MySQL Command Client, MySQL Workbench, Navicat等)操作數據,對DBA更加友好。
- 向應用程序完全透明,可直接當做MySQL/PostgreSQL使用。
- 適用於任何兼容MySQL/PostgreSQL協議的的客戶端。
![]()
Sharding-Proxy是一個獨立的應用,使用時需要安裝服務,進行分庫分表或者讀寫分離配置,然后啟動就行。
安裝下載
- 下載
點擊binary后,會跳轉到apache官網,然后點擊下面的地址就可以下載了
-
安裝
下載好之后,解壓,到bin目錄中啟動
start.bat
/start.sh
文件即可
Sharding-Proxy分表
-
進入到conf文件夾中,修改
server.yaml
文件# # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ###################################################################################################### # # If you want to configure orchestration, authorization and proxy properties, please refer to this file. # ###################################################################################################### # #orchestration: # name: orchestration_ds # overwrite: true # registry: # type: zookeeper # serverLists: localhost:2181 # namespace: orchestration # authentication: users: # 賬戶 root: # 密碼 password: 123456 sharding: password: sharding # 庫名 authorizedSchemas: sharding_db props: max.connections.size.per.query: 1 acceptor.size: 16 # The default value is available processors count * 2. executor.size: 16 # Infinite by default. proxy.frontend.flush.threshold: 128 # The default value is 128. # LOCAL: Proxy will run with LOCAL transaction. # XA: Proxy will run with XA transaction. # BASE: Proxy will run with B.A.S.E transaction. proxy.transaction.type: LOCAL proxy.opentracing.enabled: false query.with.cipher.column: true sql.show: false
-
修改config-sharding.yaml,這個文件主要是配置分庫分表操作的。
如果要連接mysql,需要把驅動復制到lib文件夾中
配置分庫分表規則:
schemaName: sharding_db dataSources: ds_0: url: jdbc:mysql://192.168.245.131:3306/edu_1?serverTimezone=UTC&useSSL=false username: root password: 123456 connectionTimeoutMilliseconds: 30000 idleTimeoutMilliseconds: 60000 maxLifetimeMilliseconds: 1800000 maxPoolSize: 50 shardingRule: tables: t_order: actualDataNodes: ds_${0}.t_order_${0..1} tableStrategy: inline: shardingColumn: order_id algorithmExpression: t_order_${order_id % 2} keyGenerator: type: SNOWFLAKE column: order_id bindingTables: - t_order defaultDatabaseStrategy: inline: shardingColumn: user_id algorithmExpression: ds_${0} defaultTableStrategy: none:
-
啟動Sharding-Proxy服務
雙擊
start.bat
啟動,出現下面的字樣表示成功啟動Sharding-Proxy默認端口號是3307,可以以命令行的方式啟動:在cmd命令行窗口中輸入start.bat 3308就可以以3308端口啟動
-
通過Sharding-Proxy啟動端口進行連接。
打開cmd窗口,連接Sharding-Proxy,連接方式和連接mysql一樣
mysql -u root -p 123456 -h127.0.0.1 -P3307 # 這種方式連接的是Sharding-Proxy,不是連接的mysql
-
建表SQL
USE sharding_db; CREATE TABLE IF NOT EXISTS ds_0.t_order ( `order_id` BIGINT PRIMARY KEY, `user_id` INT NOT NULL, `status` VARCHAR ( 50 ) ); INSERT INTO t_order ( `order_id`, `user_id`, `status` ) VALUES ( 11, 1, 'jack' );
-
做完以上操作后,可以在edu_1中看到,有兩個表
可以看到在t_order_1中有一下數據
再插入一條數據
在t_order_0中可以看到
-
這樣我們的分表操作就配置完成了。
Sharding-Proxy分庫
- 創建兩個數據庫
-
在配置文件中完成相應配置
schemaName: sharding_db dataSources: ds_0: url: jdbc:mysql://192.168.245.131:3306/edu_db_1?serverTimezone=UTC&useSSL=false username: root password: 123456 connectionTimeoutMilliseconds: 30000 idleTimeoutMilliseconds: 60000 maxLifetimeMilliseconds: 1800000 maxPoolSize: 50 ds_1: url: jdbc:mysql://192.168.245.131:3306/edu_db_2?serverTimezone=UTC&useSSL=false username: root password: 123456 connectionTimeoutMilliseconds: 30000 idleTimeoutMilliseconds: 60000 maxLifetimeMilliseconds: 1800000 maxPoolSize: 50 shardingRule: # 分表策略 tables: t_order: actualDataNodes: ds_${0..1}.t_order_${1..2} tableStrategy: inline: shardingColumn: order_id algorithmExpression: t_order_${order_id % 2 + 1} keyGenerator: type: SNOWFLAKE column: order_id bindingTables: - t_order # 分庫策略 defaultDatabaseStrategy: inline: shardingColumn: user_id algorithmExpression: ds_${user_id % 2} defaultTableStrategy: none:
-
啟動Sharding-Proxy服務
-
創建表
create table if not exists ds_0.t_order(order_id bigint not null,user_id int not null,status varchar(50),primary key(order_id)); insert into t_order(order_id,user_id,status) values(1,1,"init");
可以看到,在edu_db_2中的t_order_2中有數據
Sharding-Proxy讀寫分離
-
創建三個數據庫
-
修改config-master_slave.yaml
schemaName: master_slave_db dataSources: master_ds: url: jdbc:mysql://192.168.245.131:3306/demo_ds_master?serverTimezone=UTC&useSSL=false username: root password: 123456 connectionTimeoutMilliseconds: 30000 idleTimeoutMilliseconds: 60000 maxLifetimeMilliseconds: 1800000 maxPoolSize: 50 slave_ds_0: url: jdbc:mysql://192.168.245.131:3306/demo_ds_slave_0?serverTimezone=UTC&useSSL=false username: root password: 123456 connectionTimeoutMilliseconds: 30000 idleTimeoutMilliseconds: 60000 maxLifetimeMilliseconds: 1800000 maxPoolSize: 50 slave_ds_1: url: jdbc:mysql://192.168.245.131:3306/demo_ds_slave_1?serverTimezone=UTC&useSSL=false username: root password: 123456 connectionTimeoutMilliseconds: 30000 idleTimeoutMilliseconds: 60000 maxLifetimeMilliseconds: 1800000 maxPoolSize: 50 masterSlaveRule: name: ms_ds masterDataSourceName: master_ds slaveDataSourceNames: - slave_ds_0 - slave_ds_1
-
啟動sharding-Proxy服務
Sharding-JDBC原理
內核剖析
數據分片就是把一個邏輯SQL轉成多個實際SQL去執行。
SQL 解析
分為詞法解析和語法解析。 先通過詞法解析器將 SQL 拆分為一個個不可再分的單詞。再使用語法解析器對 SQL 進行理解,並最終提煉出解析上下文。 解析上下文包括表、選擇項、排序項、分組項、聚合函數、分頁信息、查詢條件以及可能需要修改的占位符的標記。
執行器優化
合並和優化分片條件,如 OR 等。
SQL 路由
根據解析上下文匹配用戶配置的分片策略,並生成路由路徑。目前支持分片路由和廣播路由。
SQL 改寫
將 SQL 改寫為在真實數據庫中可以正確執行的語句。SQL 改寫分為正確性改寫和優化改寫。
SQL 執行
通過多線程執行器異步執行。
結果歸並
將多個執行結果集歸並以便於通過統一的 JDBC 接口輸出。結果歸並包括流式歸並、內存歸並和使用裝飾者模式的追加歸並這幾種方式。
解析引擎
解析過程分為詞法解析和語法解析。詞法解析用於將SQL拆解為不可再分的原子符號,稱為Token。
並根據不同數據庫方言鎖提供的字典,將其歸類為關鍵字、表達式、字面量和操作符。再使用語法
解析器將SQL轉換為抽象語法樹(簡稱AST,Abstract syntax Tree)。
例如對下面一條SQL語句:
select id,name from t_user where status='active' and age>18;
會被解析成下面這樣一顆樹:

為了便於理解,抽閑語法樹中的關鍵字的Token用綠色表示,變量的Token用紅色表示,灰色表示需要進一步拆解。通過對抽象語法樹的遍歷,可以標記出所有可能需要改寫的位置。SQL的一次解析過程是不可逆的,所有token按SQL原本的順序依次進行解析,性能很高。並且在解析過程中,需要考慮各種數據庫SQL方言的異同,提供不同的解析模板。
其中,SQL解析是整個分庫分表產品的核心,其性能和兼容性是最重要的衡量指標、ShardingSphere在1.4.x之前采用的是性能較快的Druid作為SQL解析器。1.5.x版本后,采用自研的SQL解析器,針對分庫分表場景,采取對SQL辦理解的方式,提高SQL解析的性能和兼容性。然后從3.0.x版本后,開始使用ANTLR作為SQL解析引擎。這是個開源的SQL解析引擎,ShardingSphere在使用ANTLR
時,還增加了一些AST的緩存功能。整堆ANLTR4的特性,官網建議盡量采用PreparedStatement的預編譯方式來提高SQL執行的性能。
SQL解析整體結構:

路由引擎
根據解析上下文匹配數據庫和表分片策略,生成路由 路徑。
ShardingSphere的分片策略主要分為單片路由(分片鍵的操作符是=
)、多片路由(分片鍵的操作符是IN
)和范圍路由(分片鍵的操作符是Between
)。不攜帶分片鍵的SQl則是廣播路由。
分片策略通常可以由數據庫內置也可有用戶配置。內置的分片策略大致可以分為尾數取模、哈希、范圍、標簽、時間等。由用戶配置的分片策略則更加靈活,可以根據使用需求定制分片策略。

改寫引擎
用戶只需要面向邏輯庫和邏輯表來寫SQL,最終由ShardingSphere的改寫引擎將SQL改寫為在真實數據庫中可以正確執行的語句。SQL改寫分為正確性改寫和優化改寫。

執行引擎
ShardingSphere並不是簡單的將改寫完的SQl提交到數據庫執行。執行引擎的目標是自動化的平衡資源控制和執行效率。
例如他的連接模式分為內存限制模式(MEMORY_STRICTLY)和連接限制模式(CONNECTION_STRICTLY)。內存限制模式只關注一個數據庫連接的處理數量,通常一張真實表一個數據庫連接。而連接限制模式則關注數據庫連接的數量,較大的查詢會進行串行操作。

歸並引擎
將從各個數據節點獲取的多數據結果集,組合成為一個結果集並正確的返回至請求客戶端,稱為結果歸並。
其中,流式歸並是指一條一條數據的方式進行歸並,而內存歸並是將所有結果集都查詢到內存中,進行統一歸並。
