一. 項目需求java
在以前我作項目的時候,數據量比較大,單表千萬級別的,須要分庫分表,因而在網上搜索這方面的開源框架,最多見的就是mycat,sharding-sphere,最終我選擇後者,用它來作分庫分表比較容易上手。node
二. 簡介sharding-spheremysql
官網地址: https://shardingsphere.apache.org/web
三. 項目實戰算法
本項目基於 Spring Boot 2.1.5 使用sharding-sphere + Mybatis-Plus 實現分庫分表spring
1. pom.xml引入依賴sql
<?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> <parent> <groupId> org.springframework.boot </groupId> <artifactId> spring-boot-starter-parent </artifactId> <version> 2.1.5.RELEASE </version> <relativePath/> </parent> <groupId> com.xd </groupId> <artifactId> spring-boot-sharding-table </artifactId> <version> 0.0.1-SNAPSHOT </version> <name> spring-boot-sharding-table </name> <description> 基於 Spring Boot 2.1.5 使用sharding-sphere + Mybatis-Plus 實現分庫分表 </description> <properties> <java.version> 1.8 </java.version> </properties> <dependencies> <dependency> <groupId> org.springframework.boot </groupId> <artifactId> spring-boot-starter-web </artifactId> </dependency> <dependency> <groupId> org.springframework.boot </groupId> <artifactId> spring-boot-starter-test </artifactId> <scope> test </scope> </dependency> <!--mysql--> <dependency> <groupId> mysql </groupId> <artifactId> mysql-connector-java </artifactId> <scope> runtime </scope> </dependency> <!--Mybatis-Plus--> <dependency> <groupId> com.baomidou </groupId> <artifactId> mybatis-plus-boot-starter </artifactId> <version> 3.1.1 </version> </dependency> <!--shardingsphere start--> <!-- for spring boot --> <dependency> <groupId> io.shardingsphere </groupId> <artifactId> sharding-jdbc-spring-boot-starter </artifactId> <version> 3.1.0 </version> </dependency> <!-- for spring namespace --> <dependency> <groupId> io.shardingsphere </groupId> <artifactId> sharding-jdbc-spring-namespace </artifactId> <version> 3.1.0 </version> </dependency> <!--shardingsphere end--> <!--lombok--> <dependency> <groupId> org.projectlombok </groupId> <artifactId> lombok </artifactId> <version> 1.18.8 </version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId> org.springframework.boot </groupId> <artifactId> spring-boot-maven-plugin </artifactId> </plugin> </plugins> </build> </project>
2. 建立數據庫和表數據庫
ds0 ├── user_0 └── user_1 ds1 ├── user_0 └── user_1
既然是分庫分表 庫結構與表結構必定是一致的 數據庫: ds0express
CREATE DATABASE IF NOT EXISTS `ds0` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci */ ; USE `ds0` ; SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0 ; -- ---------------------------- -- Table structure for user_0 -- ---------------------------- DROP TABLE IF EXISTS `user_0` ; CREATE TABLE `user_0` ( `id` int ( 11 ) NOT NULL, `name` varchar( 255 ) DEFAULT NULL, `age` int ( 11 ) DEFAULT NULL, PRIMARY KEY ( `id` ) ) ENGINE= InnoDB DEFAULT CHARSET=utf8mb4; -- ---------------------------- -- Table structure for user_1 -- ---------------------------- DROP TABLE IF EXISTS `user_1` ; CREATE TABLE `user_1` ( `id` int ( 11 ) NOT NULL, `name` varchar( 255 ) DEFAULT NULL, `age` int ( 11 ) DEFAULT NULL, PRIMARY KEY ( `id` ) ) ENGINE= InnoDB DEFAULT CHARSET=utf8mb4; SET FOREIGN_KEY_CHECKS = 1 ;
數據庫: ds1apache
CREATE DATABASE IF NOT EXISTS `ds1` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci */ ; USE `ds1` ; SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0 ; -- ---------------------------- -- Table structure for user_0 -- ---------------------------- DROP TABLE IF EXISTS `user_0` ; CREATE TABLE `user_0` ( `id` int ( 11 ) NOT NULL, `name` varchar( 255 ) DEFAULT NULL, `age` int ( 11 ) DEFAULT NULL, PRIMARY KEY ( `id` ) ) ENGINE= InnoDB DEFAULT CHARSET=utf8mb4; -- ---------------------------- -- Table structure for user_1 -- ---------------------------- DROP TABLE IF EXISTS `user_1` ; CREATE TABLE `user_1` ( `id` int ( 11 ) NOT NULL, `name` varchar( 255 ) DEFAULT NULL, `age` int ( 11 ) DEFAULT NULL, PRIMARY KEY ( `id` ) ) ENGINE= InnoDB DEFAULT CHARSET=utf8mb4; SET FOREIGN_KEY_CHECKS = 1 ;
3. application.properties (重點)
# 數據源 ds0,ds1 sharding.jdbc.datasource.names=ds0,ds1 # 第一個數據庫 sharding.jdbc.datasource.ds0.type=com.zaxxer.hikari. HikariDataSource sharding.jdbc.datasource.ds0.driver- class -name=com.mysql.jdbc. Driver sharding.jdbc.datasource.ds0.jdbc-url=jdbc:mysql: //localhost:3306/ds0?characterEncoding=utf-8 sharding.jdbc.datasource.ds0.username=root sharding.jdbc.datasource.ds0.password=root # 第二個數據庫 sharding.jdbc.datasource.ds1.type=com.zaxxer.hikari. HikariDataSource sharding.jdbc.datasource.ds1.driver- class -name=com.mysql.jdbc. Driver sharding.jdbc.datasource.ds1.jdbc-url=jdbc:mysql: //localhost:3306/ds1?characterEncoding=utf-8 sharding.jdbc.datasource.ds1.username=root sharding.jdbc.datasource.ds1.password=root # 水平拆分的數據庫(表) 配置分庫 + 分表策略 行表達式分片策略 # 分庫策略 sharding.jdbc.config.sharding. default -database-strategy. inline .sharding-column=id sharding.jdbc.config.sharding. default -database-strategy. inline .algorithm-expression=ds$->{id % 2 } # 分表策略 其中user爲邏輯表 分表主要取決於age行 sharding.jdbc.config.sharding.tables.user.actual-data-nodes=ds$->{ 0. . 1 }.user_$->{ 0. . 1 } sharding.jdbc.config.sharding.tables.user.table-strategy. inline .sharding-column=age # 分片算法表達式 sharding.jdbc.config.sharding.tables.user.table-strategy. inline .algorithm-expression=user_$->{age % 2 } # 主鍵 UUID 18位數 若是是分佈式還要進行一個設置 防止主鍵重複 #sharding.jdbc.config.sharding.tables.user.key-generator-column-name=id # 打印執行的數據庫以及語句 sharding.jdbc.config.props..sql.show= true spring.main.allow-bean-definition-overriding= true
我此次使用配置文件方式實現分庫以及分表
以上配置說明:
邏輯表 user
水平拆分的數據庫(表)的相同邏輯和數據結構表的總稱。例:用戶數據根據主鍵尾數拆分爲2張表,分別是user0到user1,他們的邏輯表名爲user。
真實表
在分片的數據庫中真實存在的物理表。即上個示例中的user0到user1
分片算法:
Hint分片算法
對應HintShardingAlgorithm,用於處理使用Hint行分片的場景。須要配合HintShardingStrategy使用。
分片策略:
行表達式分片策略 對應InlineShardingStrategy。使用Groovy的表達式,提供對SQL語句中的=和IN的分片操做支持,只支持單分片鍵。對於簡單的分片算法,能夠經過簡單的配置使用,從而避免繁瑣的Java代碼開發,如: user$->{id % 2} 表示user表根據id模2,而分紅2張表,表名稱爲user0到user_1。
自增主鍵生成策略
經過在客戶端生成自增主鍵替換以數據庫原生自增主鍵的方式,作到分佈式主鍵無重複。採用UUID.randomUUID()的方式產生分佈式主鍵。或者 SNOWFLAKE
4. 實體類
package com.xd.springbootshardingtable.entity; import com.baomidou.mybatisplus.annotation. TableName ; import com.baomidou.mybatisplus.extension.activerecord. Model ; import groovy.transform. EqualsAndHashCode ; import lombok. Data ; import lombok.experimental. Accessors ; /** * @Classname User * @Description 用戶實體類 * @Author 李號東 lihaodongmail@163.com * @Date 2019-05-26 17:24 * @Version 1.0 */ @Data @EqualsAndHashCode (callSuper = true ) @Accessors (chain = true ) @TableName ( "user" ) public class User extends Model < User > { /** * 主鍵Id */ private int id; /** * 名稱 */ private String name; /** * 年齡 */ private int age; }
5. dao層
package com.xd.springbootshardingtable.mapper; import com.baomidou.mybatisplus.core.mapper. BaseMapper ; import com.xd.springbootshardingtable.entity. User ; /** * user dao層 * @author lihaodong */ public interface UserMapper extends BaseMapper < User > { }
6. service層以及實現類
UserService
package com.xd.springbootshardingtable.service; import com.baomidou.mybatisplus.extension.service. IService ; import com.xd.springbootshardingtable.entity. User ; import java.util. List ; /** * @Classname UserService * @Description 用戶服務類 * @Author 李號東 lihaodongmail@163.com * @Date 2019-05-26 17:31 * @Version 1.0 */ public interface UserService extends IService < User > { /** * 保存用戶信息 * @param entity * @return */ @Override boolean save( User entity); /** * 查詢所有用戶信息 * @return */ List < User > getUserList(); }
UserServiceImpl
package com.xd.springbootshardingtable.service. Impl ; import com.baomidou.mybatisplus.core.toolkit. Wrappers ; import com.baomidou.mybatisplus.extension.service.impl. ServiceImpl ; import com.xd.springbootshardingtable.entity. User ; import com.xd.springbootshardingtable.mapper. UserMapper ; import com.xd.springbootshardingtable.service. UserService ; import org.springframework.stereotype. Service ; import java.util. List ; /** * @Classname UserServiceImpl * @Description 用戶服務實現類 * @Author 李號東 lihaodongmail@163.com * @Date 2019-05-26 17:32 * @Version 1.0 */ @Service public class UserServiceImpl extends ServiceImpl < UserMapper , User > implements UserService { @Override public boolean save( User entity) { return super .save(entity); } @Override public List < User > getUserList() { return baseMapper.selectList( Wrappers .< User >lambdaQuery()); } }
7. 測試控制類
package com.xd.springbootshardingtable.controller; import com.xd.springbootshardingtable.entity. User ; import com.xd.springbootshardingtable.service. UserService ; import org.springframework.beans.factory.annotation. Autowired ; import org.springframework.web.bind.annotation. GetMapping ; import org.springframework.web.bind.annotation. RestController ; import java.util. List ; /** * @Classname UserController * @Description 用戶測試控制類 * @Author 李號東 lihaodongmail@163.com * @Date 2019-05-26 17:36 * @Version 1.0 */ @RestController public class UserController { @Autowired private UserService userService; @GetMapping ( "/select" ) public List < User > select () { return userService.getUserList(); } @GetMapping ( "/insert" ) public Boolean insert( User user) { return userService.save(user); } }
四. 測試
啓動項目
打開瀏覽器 分別訪問:
http: //localhost:8080/insert?id=1&name=lhd&age=12 http: //localhost:8080/insert?id=2&name=lhd&age=13 http: //localhost:8080/insert?id=3&name=lhd&age=14 http: //localhost:8080/insert?id=4&name=lhd&age=15
則執行插數據 而後查看控制檯日誌:
根據分片算法和分片策略 不一樣的id以及age取模落入不一樣的庫表 達到了分庫分表的結果
有的人說 查詢的話 該怎麼作呢 其實也幫咱們作好了 打開瀏覽器 訪問:
http://localhost:8080/select
控制檯打印:
分別從ds0數據庫兩張表和ds1兩張表查詢結果 而後彙總結果返回
以前有朋友問我單表數據量達千萬,想作水平分割,不分庫,也能夠的吧?
是徹底能夠的 只要修改配置文件的配置便可 很是靈活
經過代碼你們也能夠看到,個人業務層代碼和平時單表操做是同樣的,只須要引入sh配置和邏輯表保持現有的不便便可,使用無侵入咱們的代碼 能夠在原有的基礎上改動便可 能夠說是很是方便
若是想學習Java工程化、高性能及分佈式、深刻淺出。微服務、Spring,MyBatis,Netty源碼分析的朋友能夠加個人Java高級交流:787707172,羣裏有阿里大牛直播講解技術,以及Java大型互聯網技術的視頻免費分享給你們。