一. 簡介 sharding-sphere
官網(wǎng)地址: https://shardingsphere.apache.org/
ShardingSphere 是一套開源的分布式數(shù)據(jù)庫中間件解決方案組成的生態(tài)圈,它由 Sharding-JDBC、Sharding-Proxy 和 Sharding-Sidecar(計劃中)這 3 款相互獨立的產(chǎn)品組成。 他們均提供標(biāo)準(zhǔn)化的數(shù)據(jù)分片、分布式事務(wù)和數(shù)據(jù)庫治理功能,可適用于如 Java 同構(gòu)、異構(gòu)語言、容器、云原生等各種多樣化的應(yīng)用場景。
ShardingSphere 定位為關(guān)系型數(shù)據(jù)庫中間件,旨在充分合理地在分布式的場景下利用關(guān)系型數(shù)據(jù)庫的計算和存儲能力,而并非實現(xiàn)一個全新的關(guān)系型數(shù)據(jù)庫。 它與 NoSQL 和 NewSQL 是并存而非互斥的關(guān)系。NoSQL 和 NewSQL 作為新技術(shù)探索的前沿,放眼未來,擁抱變化,是非常值得推薦的。反之,也可以用另一種思路看待問題,放眼未來,關(guān)注不變的東西,進而抓住事物本質(zhì)。 關(guān)系型數(shù)據(jù)庫當(dāng)今依然占有巨大市場,是各個公司核心業(yè)務(wù)的基石,未來也難于撼動,我們目前階段更加關(guān)注在原有基礎(chǔ)上的增量,而非顛覆。

sharding-jdbc 定位為輕量級 Java 框架,在 Java 的 JDBC 層提供的額外服務(wù)。 它使用客戶端直連數(shù)據(jù)庫,以 jar 包形式提供服務(wù),無需額外部署和依賴,可理解為增強版的 JDBC 驅(qū)動,完全兼容 JDBC 和各種 ORM 框架。
適用于任何基于 Java 的 ORM 框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template 或直接使用 JDBC。 基于任何第三方的數(shù)據(jù)庫連接池,如:DBCP, C3P0, BoneCP, Druid, HikariCP 等。 支持任意實現(xiàn) JDBC 規(guī)范的數(shù)據(jù)庫。目前支持 MySQL,Oracle,SQLServer 和 PostgreSQL。

三. 項目實戰(zhàn)
本項目基于 Spring Boot 2.1.5 使用 sharding-sphere + Mybatis-Plus 實現(xiàn)分庫分表
1. pom.xml 引入依賴
1. `<?xml version="1.0" encoding="UTF-8"?>`
2. `<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"`
3. `xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">`
4. `<modelVersion>4.0.0</modelVersion>`
5. `<parent>`
6. `<groupId>org.springframework.boot</groupId>`
7. `<artifactId>spring-boot-starter-parent</artifactId>`
8. `<version>2.1.5.RELEASE</version>`
9. `<relativePath/>`
10. `</parent>`
11. `<groupId>com.xd</groupId>`
12. `<artifactId>spring-boot-sharding-table</artifactId>`
13. `<version>0.0.1-SNAPSHOT</version>`
14. `<name>spring-boot-sharding-table</name>`
15. `<description>基于 Spring Boot 2.1.5 使用sharding-sphere + Mybatis-Plus 實現(xiàn)分庫分表</description>`
17. `<properties>`
18. `<java.version>1.8</java.version>`
19. `</properties>`
21. `<dependencies>`
22. `<dependency>`
23. `<groupId>org.springframework.boot</groupId>`
24. `<artifactId>spring-boot-starter-web</artifactId>`
25. `</dependency>`
27. `<dependency>`
28. `<groupId>org.springframework.boot</groupId>`
29. `<artifactId>spring-boot-starter-test</artifactId>`
30. `<scope>test</scope>`
31. `</dependency>`
32. `<!--mysql-->`
33. `<dependency>`
34. `<groupId>mysql</groupId>`
35. `<artifactId>mysql-connector-java</artifactId>`
36. `<scope>runtime</scope>`
37. `</dependency>`
38. `<!--Mybatis-Plus-->`
39. `<dependency>`
40. `<groupId>com.baomidou</groupId>`
41. `<artifactId>mybatis-plus-boot-starter</artifactId>`
42. `<version>3.1.1</version>`
43. `</dependency>`
44. `<!--shardingsphere start-->`
45. `<!-- for spring boot -->`
46. `<dependency>`
47. `<groupId>io.shardingsphere</groupId>`
48. `<artifactId>sharding-jdbc-spring-boot-starter</artifactId>`
49. `<version>3.1.0</version>`
50. `</dependency>`
51. `<!-- for spring namespace -->`
52. `<dependency>`
53. `<groupId>io.shardingsphere</groupId>`
54. `<artifactId>sharding-jdbc-spring-namespace</artifactId>`
55. `<version>3.1.0</version>`
56. `</dependency>`
57. `<!--shardingsphere end-->`
58. `<!--lombok-->`
59. `<dependency>`
60. `<groupId>org.projectlombok</groupId>`
61. `<artifactId>lombok</artifactId>`
62. `<version>1.18.8</version>`
63. `</dependency>`
64. `</dependencies>`
66. `<build>`
67. `<plugins>`
68. `<plugin>`
69. `<groupId>org.springframework.boot</groupId>`
70. `<artifactId>spring-boot-maven-plugin</artifactId>`
71. `</plugin>`
72. `</plugins>`
73. `</build>`
75. `</project>`
2. 創(chuàng)建數(shù)據(jù)庫和表
1. `ds0`
2. `├── user_0`
3. `└── user_1`
4. `ds1`
5. `├── user_0`
6. `└── user_1`
既然是分庫分表 庫結(jié)構(gòu)與表結(jié)構(gòu)一定是一致的 數(shù)據(jù)庫: ds0
1. `CREATE DATABASE IF NOT EXISTS `ds0` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci */;`
2. `USE `ds0`;`
4. `SET NAMES utf8mb4;`
5. `SET FOREIGN_KEY_CHECKS = 0;`
7. `-- ----------------------------`
8. `-- Table structure for user_0`
9. `-- ----------------------------`
10. `DROP TABLE IF EXISTS `user_0`;`
11. `CREATE TABLE `user_0` (`
12. ``id` int(11) NOT NULL,`
13. ``name` varchar(255) DEFAULT NULL,`
14. ``age` int(11) DEFAULT NULL,`
15. `PRIMARY KEY (`id`)`
16. `) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;`
18. `-- ----------------------------`
19. `-- Table structure for user_1`
20. `-- ----------------------------`
21. `DROP TABLE IF EXISTS `user_1`;`
22. `CREATE TABLE `user_1` (`
23. ``id` int(11) NOT NULL,`
24. ``name` varchar(255) DEFAULT NULL,`
25. ``age` int(11) DEFAULT NULL,`
26. `PRIMARY KEY (`id`)`
27. `) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;`
29. `SET FOREIGN_KEY_CHECKS = 1;`
數(shù)據(jù)庫: ds1
1. `CREATE DATABASE IF NOT EXISTS `ds1` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci */;`
2. `USE `ds1`;`
4. `SET NAMES utf8mb4;`
5. `SET FOREIGN_KEY_CHECKS = 0;`
7. `-- ----------------------------`
8. `-- Table structure for user_0`
9. `-- ----------------------------`
10. `DROP TABLE IF EXISTS `user_0`;`
11. `CREATE TABLE `user_0` (`
12. ``id` int(11) NOT NULL,`
13. ``name` varchar(255) DEFAULT NULL,`
14. ``age` int(11) DEFAULT NULL,`
15. `PRIMARY KEY (`id`)`
16. `) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;`
18. `-- ----------------------------`
19. `-- Table structure for user_1`
20. `-- ----------------------------`
21. `DROP TABLE IF EXISTS `user_1`;`
22. `CREATE TABLE `user_1` (`
23. ``id` int(11) NOT NULL,`
24. ``name` varchar(255) DEFAULT NULL,`
25. ``age` int(11) DEFAULT NULL,`
26. `PRIMARY KEY (`id`)`
27. `) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;`
29. `SET FOREIGN_KEY_CHECKS = 1;`
3. application.properties (重點)
1. `# 數(shù)據(jù)源 ds0,ds1`
2. `sharding.jdbc.datasource.names=ds0,ds1`
3. `# 第一個數(shù)據(jù)庫`
4. `sharding.jdbc.datasource.ds0.type=com.zaxxer.hikari.HikariDataSource`
5. `sharding.jdbc.datasource.ds0.driver-class-name=com.mysql.jdbc.Driver`
6. `sharding.jdbc.datasource.ds0.jdbc-url=jdbc:mysql://localhost:3306/ds0?characterEncoding=utf-8`
7. `sharding.jdbc.datasource.ds0.username=root`
8. `sharding.jdbc.datasource.ds0.password=root`
10. `# 第二個數(shù)據(jù)庫`
11. `sharding.jdbc.datasource.ds1.type=com.zaxxer.hikari.HikariDataSource`
12. `sharding.jdbc.datasource.ds1.driver-class-name=com.mysql.jdbc.Driver`
13. `sharding.jdbc.datasource.ds1.jdbc-url=jdbc:mysql://localhost:3306/ds1?characterEncoding=utf-8`
14. `sharding.jdbc.datasource.ds1.username=root`
15. `sharding.jdbc.datasource.ds1.password=root`
17. `# 水平拆分的數(shù)據(jù)庫(表) 配置分庫 + 分表策略 行表達式分片策略`
18. `# 分庫策略`
19. `sharding.jdbc.config.sharding.default-database-strategy.inline.sharding-column=id`
20. `sharding.jdbc.config.sharding.default-database-strategy.inline.algorithm-expression=ds$->{id % 2}`
22. `# 分表策略 其中user為邏輯表 分表主要取決于age行`
23. `sharding.jdbc.config.sharding.tables.user.actual-data-nodes=ds$->{0..1}.user_$->{0..1}`
24. `sharding.jdbc.config.sharding.tables.user.table-strategy.inline.sharding-column=age`
25. `# 分片算法表達式`
26. `sharding.jdbc.config.sharding.tables.user.table-strategy.inline.algorithm-expression=user_$->{age % 2}`
28. `# 主鍵 UUID 18位數(shù) 如果是分布式還要進行一個設(shè)置 防止主鍵重復(fù)`
29. `#sharding.jdbc.config.sharding.tables.user.key-generator-column-name=id`
31. `# 打印執(zhí)行的數(shù)據(jù)庫以及語句`
32. `sharding.jdbc.config.props..sql.show=true`
33. `spring.main.allow-bean-definition-overriding=true`
我這次使用配置文件方式實現(xiàn)分庫以及分表
以上配置說明:
邏輯表 user
水平拆分的數(shù)據(jù)庫(表)的相同邏輯和數(shù)據(jù)結(jié)構(gòu)表的總稱。例:用戶數(shù)據(jù)根據(jù)主鍵尾數(shù)拆分為 2 張表,分別是 user0 到 user1,他們的邏輯表名為 user。
真實表
在分片的數(shù)據(jù)庫中真實存在的物理表。即上個示例中的 user0 到 user1
分片算法:
Hint 分片算法
對應(yīng) HintShardingAlgorithm,用于處理使用 Hint 行分片的場景。需要配合 HintShardingStrategy 使用。
分片策略:
行表達式分片策略 對應(yīng) InlineShardingStrategy。使用 Groovy 的表達式,提供對 SQL 語句中的 = 和 IN 的分片操作支持,只支持單分片鍵。對于簡單的分片算法,可以通過簡單的配置使用,從而避免繁瑣的 Java 代碼開發(fā),如: user$->{id % 2} 表示 user 表根據(jù) id 模 2,而分成 2 張表,表名稱為 user0 到 user_1。
自增主鍵生成策略
通過在客戶端生成自增主鍵替換以數(shù)據(jù)庫原生自增主鍵的方式,做到分布式主鍵無重復(fù)。 采用 UUID.randomUUID() 的方式產(chǎn)生分布式主鍵?;蛘?SNOWFLAKE
4. 實體類
1. `package com.xd.springbootshardingtable.entity;`
3. `import com.baomidou.mybatisplus.annotation.TableName;`
4. `import com.baomidou.mybatisplus.extension.activerecord.Model;`
5. `import groovy.transform.EqualsAndHashCode;`
6. `import lombok.Data;`
7. `import lombok.experimental.Accessors;`
9. `/**`
10. `* @Classname User`
11. `* @Description 用戶實體類`
12. `* @Author 李號東 lihaodongmail@163.com`
13. `* @Date 2019-05-26 17:24`
14. `* @Version 1.0`
15. `*/`
16. `@Data`
17. `@EqualsAndHashCode(callSuper = true)`
18. `@Accessors(chain = true)`
19. `@TableName("user")`
20. `public class User extends Model<User> {`
22. `/**`
23. `* 主鍵Id`
24. `*/`
25. `private int id;`
27. `/**`
28. `* 名稱`
29. `*/`
30. `private String name;`
32. `/**`
33. `* 年齡`
34. `*/`
35. `private int age;`
36. `}`
5. dao 層
1. `package com.xd.springbootshardingtable.mapper;`
3. `import com.baomidou.mybatisplus.core.mapper.BaseMapper;`
4. `import com.xd.springbootshardingtable.entity.User;`
6. `/**`
7. `* user dao層`
8. `* @author lihaodong`
9. `*/`
10. `public interface UserMapper extends BaseMapper<User> {`
12. `}`
6. service 層以及實現(xiàn)類
UserService
1. `package com.xd.springbootshardingtable.service;`
3. `import com.baomidou.mybatisplus.extension.service.IService;`
4. `import com.xd.springbootshardingtable.entity.User;`
6. `import java.util.List;`
8. `/**`
9. `* @Classname UserService`
10. `* @Description 用戶服務(wù)類`
11. `* @Author 李號東 lihaodongmail@163.com`
12. `* @Date 2019-05-26 17:31`
13. `* @Version 1.0`
14. `*/`
15. `public interface UserService extends IService<User> {`
17. `/**`
18. `* 保存用戶信息`
19. `* @param entity`
20. `* @return`
21. `*/`
22. `@Override`
23. `boolean save(User entity);`
25. `/**`
26. `* 查詢?nèi)坑脩粜畔
27. `* @return`
28. `*/`
29. `List<User> getUserList();`
30. `}`
UserServiceImpl
1. `package com.xd.springbootshardingtable.service.Impl;`
3. `import com.baomidou.mybatisplus.core.toolkit.Wrappers;`
4. `import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;`
5. `import com.xd.springbootshardingtable.entity.User;`
6. `import com.xd.springbootshardingtable.mapper.UserMapper;`
7. `import com.xd.springbootshardingtable.service.UserService;`
8. `import org.springframework.stereotype.Service;`
10. `import java.util.List;`
12. `/**`
13. `* @Classname UserServiceImpl`
14. `* @Description 用戶服務(wù)實現(xiàn)類`
15. `* @Author 李號東 lihaodongmail@163.com`
16. `* @Date 2019-05-26 17:32`
17. `* @Version 1.0`
18. `*/`
19. `@Service`
20. `public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {`
21. `@Override`
22. `public boolean save(User entity) {`
23. `return super.save(entity);`
24. `}`
26. `@Override`
27. `public List<User> getUserList() {`
28. `return baseMapper.selectList(Wrappers.<User>lambdaQuery());`
29. `}`
31. `}`
7. 測試控制類
1. `package com.xd.springbootshardingtable.controller;`
3. `import com.xd.springbootshardingtable.entity.User;`
4. `import com.xd.springbootshardingtable.service.UserService;`
5. `import org.springframework.beans.factory.annotation.Autowired;`
6. `import org.springframework.web.bind.annotation.GetMapping;`
7. `import org.springframework.web.bind.annotation.RestController;`
9. `import java.util.List;`
11. `/**`
12. `* @Classname UserController`
13. `* @Description 用戶測試控制類`
14. `* @Author 李號東 lihaodongmail@163.com`
15. `* @Date 2019-05-26 17:36`
16. `* @Version 1.0`
17. `*/`
18. `@RestController`
19. `public class UserController {`
21. `@Autowired`
22. `private UserService userService;`
24. `@GetMapping("/select")`
25. `public List<User> select() {`
26. `return userService.getUserList();`
27. `}`
29. `@GetMapping("/insert")`
30. `public Boolean insert(User user) {`
31. `return userService.save(user);`
32. `}`
34. `}`
四. 測試
啟動項目
打開瀏覽器 分別訪問:
http://localhost:8080/insert?id=1&name=lhd&age=12http://localhost:8080/insert?id=2&name=lhd&age=13http://localhost:8080/insert?id=3&name=lhd&age=14http://localhost:8080/insert?id=4&name=lhd&age=15
則執(zhí)行插數(shù)據(jù) 然后查看控制臺日志:


根據(jù)分片算法和分片策略 不同的 id 以及 age 取模落入不同的庫表 達到了分庫分表的結(jié)果
有的人說 查詢的話 該怎么做呢 其實也幫我們做好了 打開瀏覽器 訪問:
http://localhost:8080/select

控制臺打印:

分別從 ds0 數(shù)據(jù)庫兩張表和 ds1 兩張表查詢結(jié)果 然后匯總結(jié)果返回
之前有人問我單表數(shù)據(jù)量達千萬, 想做水平分割, 不分庫, 也可以的吧?
是完全可以的 只要修改配置文件的配置即可 非常靈活通過代碼大家也可以看到, 我的業(yè)務(wù)層代碼和平時單表操作是一樣的, 只需要引入 sh 配置和邏輯表保持現(xiàn)有的不便即可, 使用無侵入我們的代碼 可以在原有的基礎(chǔ)上改動即可 可以說是非常方便
以上為轉(zhuǎn)載博客