主頁 > 後端開發 > Sharding-Jdbc 實作讀寫分離 + 分庫分表,寫得太好了!

Sharding-Jdbc 實作讀寫分離 + 分庫分表,寫得太好了!

2021-11-10 06:21:00 後端開發

1、概覽

ShardingSphere-Jdbc定位為輕量級Java框架,在Java的Jdbc層提供的額外服務,它使用客戶端直連資料庫,以jar包形式提供服務,可理解為增強版的Jdbc驅動,完全兼容Jdbc和各種ORM框架

2、MySQL主從復制

1)、docker配置mysql主從復制

1)創建主服務器所需目錄

mkdir -p /usr/local/mysqlData/master/cnf
mkdir -p /usr/local/mysqlData/master/data

2)定義主服務器組態檔

vim /usr/local/mysqlData/master/cnf/mysql.cnf
[mysqld]
## 設定server_id,注意要唯一
server-id=1
## 開啟binlog
log-bin=mysql-bin
## binlog快取
binlog_cache_size=1M
## binlog格式(mixed、statement、row,默認格式是statement)
binlog_format=mixed

3)創建并啟動mysql主服務

docker run -itd -p 3306:3306 --name master -v /usr/local/mysqlData/master/cnf:/etc/mysql/conf.d -v /usr/local/mysqlData/master/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7

4)添加復制master資料的用戶reader,供從服務器使用

[root@aliyun /]# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                               NAMES
6af1df686fff        mysql:5.7           "docker-entrypoint..."   5 seconds ago       Up 4 seconds        0.0.0.0:3306->3306/tcp, 33060/tcp   master
[root@aliyun /]# docker exec -it master /bin/bash
root@41d795785db1:/# mysql -u root -p123456

mysql> GRANT REPLICATION SLAVE ON *.* to 'reader'@'%' identified by 'reader';
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.00 sec)

5)創建從服務器所需目錄,編輯組態檔

mkdir /usr/local/mysqlData/slave/cnf -p
mkdir /usr/local/mysqlData/slave/cnf -p
vim /usr/local/mysqlData/slave/cnf/mysql.cnf
[mysqld]
## 設定server_id,注意要唯一
server-id=2
## 開啟binlog,以備Slave作為其它Slave的Master時使用
log-bin=mysql-slave-bin
## relay_log配置中繼日志
relay_log=edu-mysql-relay-bin
## 如果需要同步函式或者存盤程序
log_bin_trust_function_creators=true
## binlog快取
binlog_cache_size=1M
## binlog格式(mixed、statement、row,默認格式是statement)
binlog_format=mixed
## 跳過主從復制中遇到的所有錯誤或指定型別的錯誤,避免slave端復制中斷
## 如:1062錯誤是指一些主鍵重復,1032錯誤是因為主從資料庫資料不一致
slave_skip_errors=1062

6)創建并運行mysql從服務器

docker run -itd -p 3307:3306 --name slaver -v /usr/local/mysqlData/slave/cnf:/etc/mysql/conf.d -v /usr/local/mysqlData/slave/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7

7)在從服務器上配置連接主服務器的資訊

首先主服務器上查看master_log_filemaster_log_pos兩個引數,然后切換到從服務器上進行主服務器的連接資訊的設定

主服務上執行:

root@6af1df686fff:/# mysql -u root -p123456

mysql> show master status;
+------------------+----------+--------------+------------------+-------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000003 |      591 |              |                  |                   |
+------------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)

docker查看主服務器容器的ip地址

[root@aliyun /]# docker inspect --format='{{.NetworkSettings.IPAddress}}' master
172.17.0.2

從服務器上執行:

[root@aliyun /]# docker exec -it slaver /bin/bash
root@fe8b6fc2f1ca:/# mysql -u root -p123456  

mysql> change master to master_host='172.17.0.2',master_user='reader',master_password='reader',master_log_file='mysql-bin.000003',master_log_pos=591;

8)從服務器啟動I/O 執行緒和SQL執行緒

mysql> start slave;
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> show slave status\G
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 172.17.0.2
                  Master_User: reader
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: mysql-bin.000003
          Read_Master_Log_Pos: 591
               Relay_Log_File: edu-mysql-relay-bin.000002
                Relay_Log_Pos: 320
        Relay_Master_Log_File: mysql-bin.000003
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes

Slave_IO_Running: Yes,Slave_SQL_Running: Yes即表示啟動成功

2)、binlog和redo log回顧

1)redo log(重做日志)

InnoDB首先將redo log放入到redo log buffer,然后按一定頻率將其重繪到redo log file

下列三種情況下會將redo log buffer重繪到redo log file:

  • Master Thread每一秒將redo log buffer重繪到redo log file
  • 每個事務提交時會將redo log buffer重繪到redo log file
  • 當redo log緩沖池剩余空間小于1/2時,會將redo log buffer重繪到redo log file

MySQL里常說的WAL技術,全稱是Write Ahead Log,即當事務提交時,先寫redo log,再修改頁,也就是說,當有一條記錄需要更新的時候,InnoDB會先把記錄寫到redo log里面,并更新Buffer Pool的page,這個時候更新操作就算完成了

Buffer Pool是物理頁的快取,對InnoDB的任何修改操作都會首先在Buffer Pool的page上進行,然后這樣的頁將被標記為臟頁并被放到專門的Flush List上,后續將由專門的刷臟執行緒階段性的將這些頁面寫入磁盤

InnoDB的redo log是固定大小的,比如可以配置為一組4個檔案,每個檔案的大小是1GB,回圈使用,從頭開始寫,寫到末尾就又回到開頭回圈寫(順序寫,節省了隨機寫磁盤的IO消耗)

img

Write Pos是當前記錄的位置,一邊寫一邊后移,寫到第3號檔案末尾后就回到0號檔案開頭,Check Point是當前要擦除的位置,也是往后推移并且回圈的,擦除記錄前要把記錄更新到資料檔案

Write Pos和Check Point之間空著的部分,可以用來記錄新的操作,如果Write Pos追上Check Point,這時候不能再執行新的更新,需要停下來擦掉一些記錄,把Check Point推進一下

當資料庫發生宕機時,資料庫不需要重做所有的日志,因為Check Point之前的頁都已經重繪回磁盤,只需對Check Point后的redo log進行恢復,從而縮短了恢復的時間

當緩沖池不夠用時,根據LRU演算法會溢位最近最少使用的頁,若此頁為臟頁,那么需要強制執行Check Point,將臟頁重繪回磁盤

2)binlog(歸檔日志)

MySQL整體來看就有兩塊:一塊是Server層,主要做的是MySQL功能層面的事情;還有一塊是引擎層,負責存盤相關的具體事宜,redo log是InnoDB引擎特有的日志,而Server層也有自己的日志,稱為binlog

binlog記錄了對MySQL資料庫執行更改的所有操作,不包括SELECT和SHOW這類操作,主要作用是用于資料庫的主從復制及資料的增量恢復

使用mysqldump備份時,只是對一段時間的資料進行全備,但是如果備份后突然發現資料庫服務器故障,這個時候就要用到binlog的日志了

binlog格式有三種:STATEMENT,ROW,MIXED

  • STATEMENT模式:binlog里面記錄的就是SQL陳述句的原文,優點是并不需要記錄每一行的資料變化,減少了binlog日志量,節約IO,提高性能,缺點是在某些情況下會導致master-slave中的資料不一致
  • ROW模式:不記錄每條SQL陳述句的背景關系資訊,僅需記錄哪條資料被修改了,修改成什么樣了,解決了STATEMENT模式下出現master-slave中的資料不一致,缺點是會產生大量的日志,尤其是alter table的時候會讓日志暴漲
  • MIXED模式:以上兩種模式的混合使用,一般的復制使用STATEMENT模式保存binlog,對于STATEMENT模式無法復制的操作使用ROW模式保存binlog,MySQL會根據執行的SQL陳述句選擇日志保存方式

3)redo log和binlog日志的不同

  • redo log是InnoDB引擎特有的;binlog是MySQL的Server層實作的,所有引擎都可以使用
  • redo log是物理日志,記錄的是在某個資料也上做了什么修改;binlog是邏輯日志,記錄的是這個陳述句的原始邏輯,比如給ID=2這一行的c欄位加1
  • redo log是回圈寫的,空間固定會用完;binlog是可以追加寫入的,binlog檔案寫到一定大小后會切換到下一個,并不會覆寫以前的日志

4)兩階段提交

create table T(ID int primary key, c int);
update T set c=c+1 where ID=2;

執行器和InnoDB引擎在執行這個update陳述句時的內部流程:

  • 執行器先找到引擎取ID=2這一行,ID是主鍵,引擎直接用樹搜索找到這一行,如果ID=2這一行所在的資料也本來就在記憶體中,就直接回傳給執行器;否則,需要先從磁盤讀入記憶體,然后再回傳
  • 執行器拿到引擎給的行資料,把這個值加上1,得到新的一行資料,再呼叫引擎介面寫入這行新資料
  • 引擎將這行新資料更新到記憶體中,同時將這個更新操作記錄到redo log里面,此時redo log處于prepare狀態,然后告知執行器執行完成了,隨時可以提交事務
  • 執行器生成這個操作的binlog,并把binlog寫入磁盤
  • 執行器呼叫引擎的提交事務介面,引擎把剛剛寫入的redo log改成提交狀態,更新完成

update陳述句的執行流程圖如下,圖中淺色框表示在InnoDB內部執行的,深色框表示是在執行器中執行的

將redo log的寫入拆成了兩個步驟:prepare和commit,這就是兩階段提交

3)、MySQL主從復制原理

從庫B和主庫A之間維持了一個長連接,主庫A內部有一個執行緒,專門用于服務從庫B的這個長連接,一個事務日志同步的完整程序如下:

  • 在從庫B上通過change master命令,設定主庫A的IP、埠、用戶名、密碼,以及要從哪個位置開始請求binlog,這個位置包含檔案名和日志偏移量
  • 在從庫B上執行start slave命令,這時從庫會啟動兩個執行緒,就是圖中的I/O執行緒和SQL執行緒,其中I/O執行緒負責與主庫建立連接
  • 主庫A校驗完用戶名、密碼后,開始按照從庫B傳過來的位置,從本地讀取binlog,發給B
  • 從庫B拿到binlog后,寫到本地檔案,稱為中繼日志
  • SQL執行緒讀取中繼日志,決議出日志里的命令,并執行

由于多執行緒復制方案的引入,SQL執行緒演化成了多個執行緒

主從復制不是完全實時地進行同步,而是異步實時,這中間存在主從服務之間的執行延時,如果主服務器的壓力很大,則可能導致主從服務器延時較大

3、Sharding-Jdbc實作讀寫分離

Spring Boot 基礎就不介紹了,推薦下這個實戰教程:
https://www.javastack.cn/categories/Spring-Boot/

1)、新建Springboot工程,引入相關依賴

<dependencies>
    <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.4</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
        <version>1.1.21</version>
    </dependency>
    <dependency>
        <groupId>org.apache.shardingsphere</groupId>
        <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
        <version>4.0.0-RC1</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

2)、application.properties組態檔

spring.main.allow-bean-definition-overriding=true
#顯示sql
spring.shardingsphere.props.sql.show=true

#配置資料源
spring.shardingsphere.datasource.names=ds1,ds2,ds3

#master-ds1資料庫連接資訊
spring.shardingsphere.datasource.ds1.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.ds1.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.ds1.url=jdbc:mysql://47.101.58.187:3306/sharding-jdbc-db?useUnicode=true&useSSL=false&serverTimezone=Asia/Shanghai
spring.shardingsphere.datasource.ds1.username=root
spring.shardingsphere.datasource.ds1.password=123456
spring.shardingsphere.datasource.ds1.maxPoolSize=100
spring.shardingsphere.datasource.ds1.minPoolSize=5

#slave-ds2資料庫連接資訊
spring.shardingsphere.datasource.ds2.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.ds2.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.ds2.url=jdbc:mysql://47.101.58.187:3307/sharding-jdbc-db?useUnicode=true&useSSL=false&serverTimezone=Asia/Shanghai
spring.shardingsphere.datasource.ds2.username=root
spring.shardingsphere.datasource.ds2.password=123456
spring.shardingsphere.datasource.ds2.maxPoolSize=100
spring.shardingsphere.datasource.ds2.minPoolSize=5

#slave-ds3資料庫連接資訊
spring.shardingsphere.datasource.ds3.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.ds3.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.ds3.url=jdbc:mysql://47.101.58.187:3307/sharding-jdbc-db?useUnicode=true&useSSL=false&serverTimezone=Asia/Shanghai
spring.shardingsphere.datasource.ds3.username=root
spring.shardingsphere.datasource.ds3.password=123456
spring.shardingsphere.datasource.ds.maxPoolSize=100
spring.shardingsphere.datasource.ds3.minPoolSize=5

#配置默認資料源ds1 默認資料源,主要用于寫
spring.shardingsphere.sharding.default-data-source-name=ds1
#配置主從名稱
spring.shardingsphere.masterslave.name=ms
#置主庫master,負責資料的寫入
spring.shardingsphere.masterslave.master-data-source-name=ds1
#配置從庫slave節點
spring.shardingsphere.masterslave.slave-data-source-names=ds2,ds3
#配置slave節點的負載均衡均衡策略,采用輪詢機制
spring.shardingsphere.masterslave.load-balance-algorithm-type=round_robin

#整合mybatis的配置
mybatis.type-aliases-package=com.ppdai.shardingjdbc.entity

3)、創建t_user表

CREATE TABLE `t_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `nickname` varchar(100) DEFAULT NULL,
  `password` varchar(100) DEFAULT NULL,
  `sex` int(11) DEFAULT NULL,
  `birthday` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4;

4)、定義Controller、Mapper、Entity

@Data
public class User {
    private Integer id;

    private String nickname;

    private String password;

    private Integer sex;

    private String birthday;
}
@RestController
@RequestMapping("/api/user")
public class UserController {
    @Autowired
    private UserMapper userMapper;

    @PostMapping("/save")
    public String addUser() {
        User user = new User();
        user.setNickname("zhangsan" + new Random().nextInt());
        user.setPassword("123456");
        user.setSex(1);
        user.setBirthday("1997-12-03");
        userMapper.addUser(user);
        return "success";
    }

    @GetMapping("/findUsers")
    public List<User> findUsers() {
        return userMapper.findUsers();
    }
}
public interface UserMapper {

    @Insert("insert into t_user(nickname,password,sex,birthday) values(#{nickname},#{password},#{sex},#{birthday})")
    void addUser(User user);

    @Select("select * from t_user")
    List<User> findUsers();
}

5)、驗證

啟動日志中三個資料源初始化成功:

呼叫http://localhost:8080/api/user/save一直進入到ds1主節點

呼叫http://localhost:8080/api/user/findUsers一直進入到ds2、ds3節點,并且輪詢進入

4、MySQL分庫分表原理

1)、分庫分表

水平拆分:同一個表的資料拆到不同的庫不同的表中,可以根據時間、地區或某個業務鍵維度,也可以通過hash進行拆分,最后通過路由訪問到具體的資料,拆分后的每個表結構保持一致

垂直拆分:就是把一個有很多欄位的表給拆分成多個表,或者是多個庫上去,每個庫表的結構都不一樣,每個庫表都包含部分欄位,一般來說,可以根據業務維度進行拆分,如訂單表可以拆分為訂單、訂單支持、訂單地址、訂單商品、訂單擴展等表;也可以,根據資料冷熱程度拆分,20%的熱點欄位拆到一個表,80%的冷欄位拆到另外一個表

2)、不停機分庫分表資料遷移

一般資料庫的拆分也是有一個程序的,一開始是單表,后面慢慢拆成多表,那么我們就看下如何平滑的從MySQL單表過度到MySQL的分庫分表架構

  • 利用MySQL+Canal做增量資料同步,利用分庫分表中間件,將資料路由到對應的新表中
  • 利用分庫分表中間件,全量資料匯入到對應的新表中
  • 通過單表資料和分庫分表資料兩兩比較,更新不匹配的資料到新表中
  • 資料穩定后,將單表的配置切換到分庫分表配置上

5、Sharding-Jdbc實作分庫分表

1)、邏輯表

用戶資料根據訂單id%2拆分為2個表,分別是:t_order0和t_order1,他們的邏輯表名是:t_order

多資料源相同表:

#多資料源$->{0..N}.邏輯表名$->{0..N} 相同表
spring.shardingsphere.sharding.tables.t_order.actual-data-nodes=ds$->{0..1}.t_order$->{0..1}

多資料源不同表:

#多資料源$->{0..N}.邏輯表名$->{0..N} 不同表
spring.shardingsphere.sharding.tables.t_order.actual-data-nodes=ds0.t_order$->{0..1},ds1.t_order$->{2..4}

單庫分表:

#單資料源的配置方式
spring.shardingsphere.sharding.tables.t_order.actual-data-nodes=ds0.t_order$->{0..4}

全部手動指定:

spring.shardingsphere.sharding.tables.t_order.actual-data-nodes=ds0.t_order0,ds1.t_order0,ds0.t_order1,ds1.t_order1

2)、inline分片策略

spring.shardingsphere.sharding.tables.t_order.actual-data-nodes=ds$->{0..1}.t_order$->{0..1}
#資料源分片策略
spring.shardingsphere.sharding.tables.t_order.database-strategy.inline.sharding-column=user_id
#資料源分片演算法
spring.shardingsphere.sharding.tables.t_order.database-strategy.inline.algorithm-expression=ds$->{user_id%2}
#表分片策略
spring.shardingsphere.sharding.tables.t_order.table-strategy.inline.sharding-column=order_id
#表分片演算法
spring.shardingsphere.sharding.tables.t_order.table-strategy.inline.algorithm-expression=t_order$->{order_id%2}

上面的配置通過user_id%2來決定具體資料源,通過order_id%2來決定具體表

insert into t_order(user_id,order_id) values(2,3),user_id%2 = 0使用資料源ds0,order_id%2 = 1使用t_order1,insert陳述句最終操作的是資料源ds0的t_order1表,

3)、分布式主鍵配置

Sharding-Jdbc可以配置分布式主鍵生成策略,默認使用雪花演算法(snowflake),生成64bit的長整型資料,也支持UUID的方式

#主鍵的列名
spring.shardingsphere.sharding.tables.t_order.key-generator.column=id
#主鍵生成策略
spring.shardingsphere.sharding.tables.t_order.key-generator.type=SNOWFLAKE

4)、inline分片策略實作分庫分表

需求:

對1000w的用戶資料進行分庫分表,對用戶表的資料進行分表和分庫的操作,根據年齡奇數存盤在t_user1,偶數t_user0,同時性別奇數存盤在ds1,偶數ds0

表結構:

CREATE TABLE `t_user0` (
  `id` bigint(20) DEFAULT NULL,
  `nickname` varchar(200) DEFAULT NULL,
  `password` varchar(200) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  `sex` int(11) DEFAULT NULL,
  `birthday` varchar(100) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

CREATE TABLE `t_user1` (
  `id` bigint(20) DEFAULT NULL,
  `nickname` varchar(200) DEFAULT NULL,
  `password` varchar(200) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  `sex` int(11) DEFAULT NULL,
  `birthday` varchar(100) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

兩個資料庫中都包含t_user0和t_user1兩張表

application.properties:

spring.main.allow-bean-definition-overriding=true
#顯示sql
spring.shardingsphere.props.sql.show=true

#配置資料源
spring.shardingsphere.datasource.names=ds0,ds1

#ds0資料庫連接資訊
spring.shardingsphere.datasource.ds0.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.ds0.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.ds0.url=jdbc:mysql://47.101.58.187:3306/t_user_db0?useUnicode=true&useSSL=false&serverTimezone=Asia/Shanghai
spring.shardingsphere.datasource.ds0.username=root
spring.shardingsphere.datasource.ds0.password=123456
spring.shardingsphere.datasource.ds0.maxPoolSize=100
spring.shardingsphere.datasource.ds0.minPoolSize=5

#ds1資料庫連接資訊
spring.shardingsphere.datasource.ds1.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.ds1.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.ds1.url=jdbc:mysql://47.101.58.187:3306/t_user_db1?useUnicode=true&useSSL=false&serverTimezone=Asia/Shanghai
spring.shardingsphere.datasource.ds1.username=root
spring.shardingsphere.datasource.ds1.password=123456
spring.shardingsphere.datasource.ds1.maxPoolSize=100
spring.shardingsphere.datasource.ds1.minPoolSize=5

#整合mybatis的配置
mybatis.type-aliases-package=com.ppdai.shardingjdbc.entity

spring.shardingsphere.sharding.tables.t_user.actual-data-nodes=ds$->{0..1}.t_user$->{0..1}
#資料源分片策略
spring.shardingsphere.sharding.tables.t_user.database-strategy.inline.sharding-column=sex
#資料源分片演算法
spring.shardingsphere.sharding.tables.t_user.database-strategy.inline.algorithm-expression=ds$->{sex%2}
#表分片策略
spring.shardingsphere.sharding.tables.t_user.table-strategy.inline.sharding-column=age
#表分片演算法
spring.shardingsphere.sharding.tables.t_user.table-strategy.inline.algorithm-expression=t_user$->{age%2}
#主鍵的列名
spring.shardingsphere.sharding.tables.t_user.key-generator.column=id
spring.shardingsphere.sharding.tables.t_user.key-generator.type=SNOWFLAKE

Spring Boot 基礎就不介紹了,推薦下這個實戰教程:
https://www.javastack.cn/categories/Spring-Boot/

測驗類:

@SpringBootTest
class ShardingJdbcApplicationTests {

    @Autowired
    private UserMapper userMapper;

    /**
     * sex:奇數
     * age:奇數
     * ds1.t_user1
     */
    @Test
    public void test01() {
        User user = new User();
        user.setNickname("zhangsan" + new Random().nextInt());
        user.setPassword("123456");
        user.setAge(17);
        user.setSex(1);
        user.setBirthday("1997-12-03");
        userMapper.addUser(user);
    }

    /**
     * sex:奇數
     * age:偶數
     * ds1.t_user0
     */
    @Test
    public void test02() {
        User user = new User();
        user.setNickname("zhangsan" + new Random().nextInt());
        user.setPassword("123456");
        user.setAge(18);
        user.setSex(1);
        user.setBirthday("1997-12-03");
        userMapper.addUser(user);
    }

    /**
     * sex:偶數
     * age:奇數
     * ds0.t_user1
     */
    @Test
    public void test03() {
        User user = new User();
        user.setNickname("zhangsan" + new Random().nextInt());
        user.setPassword("123456");
        user.setAge(17);
        user.setSex(2);
        user.setBirthday("1997-12-03");
        userMapper.addUser(user);
    }

    /**
     * sex:偶數
     * age:偶數
     * ds0.t_user0
     */
    @Test
    public void test04() {
        User user = new User();
        user.setNickname("zhangsan" + new Random().nextInt());
        user.setPassword("123456");
        user.setAge(18);
        user.setSex(2);
        user.setBirthday("1997-12-03");
        userMapper.addUser(user);
    }
}

參考檔案:
https://shardingsphere.apache.org/document/current/cn/overview/
https://www.bilibili.com/video/BV1ei4y1K7dn

原文鏈接:https://blog.csdn.net/qq_40378034/article/details/115264837
著作權宣告:本文為CSDN博主「邋遢的流浪劍客」的原創文章,遵循CC 4.0 BY-SA著作權協議,轉載請附上原文出處鏈接及本宣告,

近期熱文推薦:

1.1,000+ 道 Java面試題及答案整理(2021最新版)

2.別在再滿屏的 if/ else 了,試試策略模式,真香!!

3.臥槽!Java 中的 xx ≠ null 是什么新語法?

4.Spring Boot 2.5 重磅發布,黑暗模式太炸了!

5.《Java開發手冊(嵩山版)》最新發布,速速下載!

覺得不錯,別忘了隨手點贊+轉發哦!

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/354426.html

標籤:Java

上一篇:《我是廖志偉》

下一篇:Java8-JVM記憶體區域劃分白話解讀

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more