ShardingSphere 分庫分表實戰(zhàn)

一. 簡介 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.  `}`

四. 測試

啟動項目
打開瀏覽器 分別訪問:

  1. http://localhost:8080/insert?id=1&name=lhd&age=12

  2. http://localhost:8080/insert?id=2&name=lhd&age=13

  3. http://localhost:8080/insert?id=3&name=lhd&age=14

  4. http://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)載博客

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容