環境-版本
Springboot:2.2.5.RELEASE
com.alibaba:druid-spring-boot-starter:1.1.21
io.shardingsphere:sharding-jdbc-spring-boot-starter:3.1.0
基礎環境
數據庫權限:
用戶 | tp數據庫(查) | tp數據庫(增) | tp1數據庫(查) | tp1數據庫(增) | tp2數據庫(查) | tp2數據庫(增) |
---|---|---|---|---|---|---|
tp | √ | √ | × | × | × | × |
tp1 | × | × | × | × | × | √ |
tp2 | × | × | × | × | × | √ |
數據庫表結構:
tp、tp1、tp2數據下的test_user、test_user0、test_user1結構均一樣。
項目結構預覽
基於shardingsphere,無論是實現的分庫分表還是讀寫分離,對業務代碼均沒有侵入,因此這裏先把基礎的代碼列出
application.yml
spring:
profiles:
#[單庫單表]
#active: single
#[讀寫分離]
active: masterslave
#[分庫分表]
#active: strategy
#[讀寫分離+分庫分表]
#active: masterslavestrategy
main:
allow-bean-definition-overriding: true
server:
servlet:
context-path: /tp
port: 8806
mybatis:
mapper-locations: classpath:mapping/*Mapper.xml
type-aliases-package: cn.com.test.sharding.model
log4j:
rootLogger: debug,stdout
config: classpath:log4j2.xml
實體TestUser.java
package cn.com.test.sharding.model;
import com.alibaba.fastjson.JSON;
import java.util.Date;
public class TestUser {
public Integer id;
public String db;
public String userId;
public Integer sex;
public String remark;
public Date modifyDate;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getDb() {
return db;
}
public void setDb(String db) {
this.db = db;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public Integer getSex() {
return sex;
}
public void setSex(Integer sex) {
this.sex = sex;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
public Date getModifyDate() {
return modifyDate;
}
public void setModifyDate(Date modifyDate) {
this.modifyDate = modifyDate;
}
@Override
public String toString() {
return JSON.toJSONString(this);
}
}
Dao操作接口TestUserMapper.java
package cn.com.test.sharding.dao;
import cn.com.test.sharding.model.TestUser;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface TestUserMapper {
void insert(TestUser user);
List<TestUser> list();
List<TestUser> listByremark(String remark);
List<TestUser> listByremarkLimit2(String remark);
}
SQL操作 TestUserMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.com.test.sharding.dao.TestUserMapper">
<resultMap id="baseResultMap" type="cn.com.test.sharding.model.TestUser">
<id column="id" jdbcType="INTEGER" property="id"/>
<id column="db" jdbcType="VARCHAR" property="db"/>
<result column="user_id" jdbcType="VARCHAR" property="userId"/>
<result column="sex" jdbcType="INTEGER" property="sex"/>
<result column="remark" jdbcType="VARCHAR" property="remark"/>
<result column="modify_time" jdbcType="TIMESTAMP" property="modifyDate"/>
</resultMap>
<sql id="base_column_list">
id,db,user_id,sex,remark,modify_time
</sql>
<insert id="insert" parameterType="cn.com.test.sharding.model.TestUser" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
insert into test_user (
<include refid="base_column_list"/>
)
value (
#{id,jdbcType=INTEGER},#{db,jdbcType=VARCHAR},#{userId,jdbcType=VARCHAR},#{sex,jdbcType=INTEGER},#{remark,jdbcType=VARCHAR},current_timestamp
);
</insert>
<select id="list" resultMap="baseResultMap">
select
<include refid="base_column_list"/>
from test_user
where 1=1
</select>
<select id="listByremark" resultMap="baseResultMap" parameterType="string">
select
<include refid="base_column_list"/>
from test_user
where remark like #{remark,jdbcType=VARCHAR}
</select>
<select id="listByremarkLimit2" resultMap="baseResultMap" parameterType="string">
select
<include refid="base_column_list"/>
from test_user
where remark like #{remark,jdbcType=VARCHAR}
limit 2
</select>
</mapper>
操作層 TestController.xml
package cn.com.test.sharding.controller;
import cn.com.test.sharding.dao.TestUserMapper;
import cn.com.test.sharding.model.TestUser;
import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.UUID;
@RestController
@RequestMapping("/test")
public class TestController {
static Logger logger = LoggerFactory.getLogger(TestController.class);
@Autowired
TestUserMapper testUserMapper;
@GetMapping("/user")
public String user(TestUser user){
logger.info("請求:"+user);
user.setUserId(UUID.randomUUID().toString());
testUserMapper.insert(user);
logger.info(user.toString());
List<TestUser> userList = testUserMapper.list();
logger.info("all查詢結果:"+userList.size()+"\t"+JSON.toJSONString(userList));
List<TestUser> userList1 = testUserMapper.listByremark(user.getRemark()+"%");
logger.info("remark查詢結果:"+userList1.size()+"\t"+JSON.toJSONString(userList1));
List<TestUser> userList2 = testUserMapper.listByremarkLimit2(user.getRemark()+"%");
logger.info("remark查詢(限定查詢2條)結果:"+userList2.size()+"\t"+JSON.toJSONString(userList2));
return JSON.toJSONString(userList);
}
}
常規用法
常規用法,既不分庫也不分表;讀寫在一個庫上進行
application-single.yml配置:
# 不讀寫分離也不分表分庫
spring:
servlet:
multipart:
max-file-size: 1MB
max-request-size: 2MB
datasource:
username: tp
password: tp@123
url: jdbc:mysql://192.168.1.16:3308/tp?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
initialSize: 2
minIdle: 2
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUALco
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
druid.filters: stat,wall
maxPoolPreparedStatementPerConnectionSize: 2
pom.xml引入
<?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 https://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.2.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.test</groupId>
<artifactId>sprintboot-sharding</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>sharding-demon</name>
<description>Spring-boot sharding</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
<exclusions>
<exclusion>
<artifactId>spring-boot-starter-logging</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.21</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
<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>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.9</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<!--fork : 如果沒有該項配置,肯定devtools不會起作用,即應用不會restart -->
<fork>true</fork>
<excludeDevtools>true</excludeDevtools>
<excludes>
<exclude>
<artifactId>spring-boot-starter-tomcat</artifactId>
<groupId>org.springframework.boot</groupId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
常規情況
測試url:localhost:8806/tp/test/user?sex=0&remark=DD&db=0
分析:
這種情況比較容易理解,數據庫操作對象清晰可見,數據庫:tp,表:test_user
測試url:http://localhost:8806/tp/test/user?sex=0&remark=DD&db=0
代碼執行結果:
數據庫:
讀寫分離用法
讀寫在不同的數據庫上
採用策略:在tp上寫,在tp1上讀 表:test_user
預期結果:tp上數據正常寫入,tp1讀取到測試數據
application-masterslave.yml配置:
sharding:
jdbc:
datasource:
names: master,slave
master:
username: tp
password: tp@123
url: jdbc:mysql://192.168.1.16:3308/tp?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
slave:
username: tp1
password: tp1@123
url: jdbc:mysql://192.168.1.16:3308/tp1?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
config:
# 主從庫配置 操作表名不會跟上數字(test_user)
masterslave:
# 負載均衡策略(load-balance-algorithm-class-name: 指定自定義策略,實現MasterSlaveLoadBalanceAlgorithm且提供無參構造)
load-balance-algorithm-type: random
master-data-source-name: master
slave-data-source-names: slave
name: demo1
# 讀寫分離的實驗結果:讀庫沒有數據,所以數據可以正常寫入,但是讀出結果爲空
執行測試前:
tp:
tp1:
執行結果:
tp:
tp1
分庫分表用法
相對與讀寫分離而言,pom無需變化
無論讀寫,根據db決策使用的數據庫,根據sex%2決策使用的表
採用策略:數據庫:tp{sex % 2}
預期結果:{db=0,sex=1}寫入tp.test_user1
{db=0,sex=2}寫入tp.test_user0
{db=1,sex=3}寫入tp1.test_user1
{db=1,sex=4}寫入tp1.test_user0
application-strategy.yml
spring:
main:
allow-bean-definition-overriding: true
sharding:
jdbc:
datasource:
names: ds0,ds1
ds0:
username: tp
password: tp@123
url: jdbc:mysql://192.168.1.16:3308/tp?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
ds1:
username: tp1
password: tp1@123
url: jdbc:mysql://192.168.1.16:3308/tp1?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
config:
sharding:
props:
sql.show: true
#分表分庫策略 操作帶數字的表和數據源ds[0,1].test_user[0,1]
tables:
test_user:
# 自增主鍵,這裏聲明後mapper.xml中返回主鍵不再可用
# key-generator-column-name: id
# 數據節點,均勻分佈 單一數據源去除數據庫後的表達式即可 eg: ds$.test_user${0..1}
actual-data-nodes: ds${0..1}.test_user${0..1}
#分庫策略
database-strategy:
#行表達式
inline:
#列名稱,多個列以逗號分隔
sharding-column: db
#直接參數裏指定了數據源
algorithm-expression: ds${db}
#分表策略
table-strategy:
#行表達式
inline:
#分片列
sharding-column: sex
#分片表達式 對sex列取模
algorithm-expression: test_user${sex % 2}
#自定義複雜分片
# complex:
# sharding-columns: #分片列名稱,多個列以逗號分隔
# algorithm-class-name: #複合分片算法類名稱。實現ComplexKeysShardingAlgorithm並提供無參數的構造器
#分庫分表,根據db決策寫入的數據庫,根據sex決策寫入的表,讀取時應爲所有來源之和
{db=0,sex=1}寫入tp.test_user1
測試url:localhost:8806/tp/test/user?sex=1&remark=DD&db=0
清空tp、tp1下的test_user0、test_user1
執行結果
tp.test_user1
{db=0,sex=2}寫入tp.test_user0
測試url:localhost:8806/tp/test/user?sex=2&remark=DD&db=0
執行結果(彙總上一種的情況進行查詢,所以查詢結果應該爲2條):
tp.test_user0
{db=1,sex=3}寫入tp1.test_user1
測試url:localhost:8806/tp/test/user?sex=3&remark=DD&db=1
執行結果(彙總上一種的情況進行查詢,所以查詢結果應該爲3條):
tp1.test_user1
{db=1,sex=4}寫入tp1.test_user0
測試url:localhost:8806/tp/test/user?sex=4&remark=DD&db=1
執行結果(彙總上一種的情況進行查詢,所以查詢結果應該爲4條):
簡單整理下對應關係~
條件db | 條件sex | 數據源 | 操作數據庫 | 操作表 |
---|---|---|---|---|
0 | 1 | ds0 | tp | test_user1 |
0 | 2 | ds0 | tp | test_user0 |
1 | 3 | ds1 | tp1 | test_user1 |
1 | 4 | ds1 | tp1 | test_user0 |
讀寫分離+分庫分表用法
相對與讀寫分離、分庫分表而言,pom無需變化
讀slave庫,根據db決策查詢的數據庫,根據sex%2決策查詢的表
寫master庫,根據db決策寫入的數據庫,根據sex%2決策寫入的表
採用策略:數據庫:tp{sex % 2}
增加tp2並僅對tp2賦予對tp2庫所有表的查詢權限
預期結果:{db=0,sex=5}寫入tp.test_user1 讀取tp2兩次
{db=0,sex=6}寫入tp.test_user0 讀取tp2兩次
{db=1,sex=7}寫入tp1.test_user1 讀取tp2兩次
{db=1,sex=8}寫入tp1.test_user0 讀取tp2兩次
注意:db被拆分爲兩個表,所以讀取和寫入均在兩個表之間決策和聚集
application-masterslavestrategy
sharding:
jdbc:
datasource:
names: master0,master1,master0-slave,master1-slave
master0:
username: tp
password: tp@123
url: jdbc:mysql://192.168.1.16:3308/tp?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
master1:
username: tp1
password: tp1@123
url: jdbc:mysql://192.168.1.16:3308/tp1?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
master0-slave:
username: tp2
password: tp2@123
url: jdbc:mysql://192.168.1.16:3308/tp2?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
master1-slave:
username: tp2
password: tp2@123
url: jdbc:mysql://192.168.1.16:3308/tp2?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
config:
sharding:
tables:
test_user:
# 自增主鍵,這裏聲明後mapper.xml中返回主鍵不再可用
# key-generator-column-name: id
# 數據節點,均勻分佈 單一數據源去除數據庫後的表達式即可 eg: ds$.test_user${0..1}
actual-data-nodes: ds${0..1}.test_user${0..1}
#分庫策略
database-strategy:
#行表達式
inline:
#列名稱,多個列以逗號分隔
sharding-column: db
#直接參數裏指定了數據源
algorithm-expression: ds${db}
#分表策略
table-strategy:
#行表達式
inline:
#分片列
sharding-column: sex
#分片表達式 對sex列取模
algorithm-expression: test_user${sex % 2}
master-slave-rules:
ds0:
master-data-source-name: master0
slave-data-source-names: master0-slave
ds1:
master-data-source-name: master1
slave-data-source-names: master1-slave
測試過程省略,執行結果如下:
tp.test_user0
tp.test_user1
tp1.test_user0
tp1.test_user1
tp2.test_user0
![在這裏插入圖片描述](https://img-blog.csdnimg.cn/20200408215603959.png
tp2.test_user1