ShardingJDBC 分庫(kù)分表詳解

一、ShardingSphere概述

1.1、ShardingSphere概述

Apache ShardingSphere 是一套開(kāi)源的分布式數(shù)據(jù)庫(kù)解決方案組成的生態(tài)圈,它由 JDBC、Proxy 和 Sidecar(規(guī)劃中)這 3 款既能夠獨(dú)立部署,又支持混合部署配合使用的產(chǎn)品組成。 它們均提供標(biāo)準(zhǔn)化的數(shù)據(jù)水平擴(kuò)展、分布式事務(wù)和分布式治理等功能,可適用于如 Java 同構(gòu)、異構(gòu)語(yǔ)言、云原生等各種多樣化的應(yīng)用場(chǎng)景。

Apache ShardingSphere 旨在充分合理地在分布式的場(chǎng)景下利用關(guān)系型數(shù)據(jù)庫(kù)的計(jì)算和存儲(chǔ)能力,而并非實(shí)現(xiàn)一個(gè)全新的關(guān)系型數(shù)據(jù)庫(kù)。 關(guān)系型數(shù)據(jù)庫(kù)當(dāng)今依然占有巨大市場(chǎng)份額,是企業(yè)核心系統(tǒng)的基石,未來(lái)也難于撼動(dòng),我們更加注重在原有基礎(chǔ)上提供增量,而非顛覆。

Apache ShardingSphere 5.x 版本開(kāi)始致力于可插拔架構(gòu),項(xiàng)目的功能組件能夠靈活的以可插拔的方式進(jìn)行擴(kuò)展。 目前,數(shù)據(jù)分片、讀寫(xiě)分離、數(shù)據(jù)加密、影子庫(kù)壓測(cè)等功能,以及 MySQL、PostgreSQL、SQLServer、Oracle 等 SQL 與協(xié)議的支持,均通過(guò)插件的方式織入項(xiàng)目。 開(kāi)發(fā)者能夠像使用積木一樣定制屬于自己的獨(dú)特系統(tǒng)。Apache ShardingSphere 目前已提供數(shù)十個(gè) SPI 作為系統(tǒng)的擴(kuò)展點(diǎn),仍在不斷增加中。

ShardingSphere 已于2020年4月16日成為 Apache 軟件基金會(huì)的頂級(jí)項(xiàng)目。

主要來(lái)說(shuō)就以下三點(diǎn):

  • 一套開(kāi)源的分布式數(shù)據(jù)庫(kù)中間件解決方案
  • 有三個(gè)產(chǎn)品:主要使用到的是Sharding-JDBC 和 Sharding-Proxy
  • 定位為關(guān)系型數(shù)據(jù)庫(kù)中間件,合理在分布式環(huán)境下使用關(guān)系型數(shù)據(jù)庫(kù)操作

1.2、ShardingSphere-JDBC概述

定位為輕量級(jí) Java 框架,和spring、mybatis一樣,在 Java 的 JDBC 層提供的額外服務(wù)。 它使用客戶端直連數(shù)據(jù)庫(kù),以 jar 包形式提供服務(wù),無(wú)需額外部署和依賴,可理解為增強(qiáng)版的 JDBC 驅(qū)動(dòng),完全兼容 JDBC 和各種 ORM 框架。

  • 適用于任何基于 JDBC 的 ORM 框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template 或直接使用 JDBC。
  • 支持任何第三方的數(shù)據(jù)庫(kù)連接池,如:DBCP, C3P0, BoneCP, Druid, HikariCP 等。
  • 支持任意實(shí)現(xiàn) JDBC 規(guī)范的數(shù)據(jù)庫(kù),目前支持 MySQL,Oracle,SQLServer,PostgreSQL 以及任何遵循 SQL92 標(biāo)準(zhǔn)的數(shù)據(jù)庫(kù)。

1.3、ShardingSphere-Proxy概述

定位為透明化的數(shù)據(jù)庫(kù)代理端,提供封裝了數(shù)據(jù)庫(kù)二進(jìn)制協(xié)議的服務(wù)端版本,用于完成對(duì)異構(gòu)語(yǔ)言的支持。 目前提供 MySQL 和 PostgreSQL 版本,它可以使用任何兼容 MySQL/PostgreSQL 協(xié)議的訪問(wèn)客戶端(如:MySQL Command Client, MySQL、Workbench, Navicat 等)操作數(shù)據(jù),對(duì) DBA 更加友好。

  • 向應(yīng)用程序完全透明,可直接當(dāng)做 MySQL/PostgreSQL 使用。
  • 適用于任何兼容 MySQL/PostgreSQL 協(xié)議的的客戶端。

1.4、ShardingSphere-Sidecar概述

定位為Kubernetes的云原生數(shù)據(jù)庫(kù)代理,以Sidecar的形式代理所有對(duì)數(shù)據(jù)庫(kù)的訪問(wèn)。 通過(guò)無(wú)中心、零侵入的方案提供與數(shù)據(jù)庫(kù)交互的的嚙合層,即Database Mesh,又可稱數(shù)據(jù)網(wǎng)格。

Database Mesh的關(guān)注重點(diǎn)在于如何將分布式的數(shù)據(jù)訪問(wèn)應(yīng)用與數(shù)據(jù)庫(kù)有機(jī)串聯(lián)起來(lái),它更加關(guān)注的是交互,是將雜亂無(wú)章的應(yīng)用與數(shù)據(jù)庫(kù)之間的交互有效的梳理。使用Database Mesh,訪問(wèn)數(shù)據(jù)庫(kù)的應(yīng)用和數(shù)據(jù)庫(kù)終將形成一個(gè)巨大的網(wǎng)格體系,應(yīng)用和數(shù)據(jù)庫(kù)只需在網(wǎng)格體系中對(duì)號(hào)入座即可,它們都是被嚙合層所治理的對(duì)象。

1.5、三個(gè)組件的對(duì)比

1.6、混合架構(gòu)

Sharding-JDBC采用無(wú)中心化架構(gòu),適用于Java開(kāi)發(fā)的高性能的輕量級(jí)OLTP應(yīng)用;Sharding-Proxy提供靜態(tài)入口以及異構(gòu)語(yǔ)言的支持,適用于OLAP應(yīng)用以及對(duì)分片數(shù)據(jù)庫(kù)進(jìn)行管理和運(yùn)維的場(chǎng)景。

ShardingSphere是多接入端共同組成的生態(tài)圈。 通過(guò)混合使用Sharding-JDBC和Sharding-Proxy,并采用同一注冊(cè)中心統(tǒng)一配置分片策略,能夠靈活的搭建適用于各種場(chǎng)景的應(yīng)用系統(tǒng),架構(gòu)師可以更加自由的調(diào)整適合于當(dāng)前業(yè)務(wù)的最佳系統(tǒng)架構(gòu)。

1.7、分庫(kù)分表概述

存在問(wèn)題:數(shù)據(jù)庫(kù)數(shù)據(jù)量不可控的,隨著時(shí)間和業(yè)務(wù)發(fā)展,造成表里面數(shù)據(jù)越來(lái)越多,如果再去對(duì)數(shù)據(jù)庫(kù)表 curd 操作時(shí)候,造成性能問(wèn)題。

解決方案:

  • 方案 1:從硬件上
  • 方案 2:分庫(kù)分表

分庫(kù)分表有兩種方式:垂直切分和水平切分

  • 1、垂直切分:垂直分表和垂直分庫(kù)

    • 垂直分表:操作數(shù)據(jù)庫(kù)中某張表,把這張表中一部分字段數(shù)據(jù)存到一張新表里面,再把這張表另一部分字段數(shù)據(jù)存到另外一張表里面。
    • 垂直分庫(kù):把單一數(shù)據(jù)庫(kù)按照業(yè)務(wù)進(jìn)行劃分,專庫(kù)專表
  • 2、水平切分:水平分表和水平分庫(kù)

  • 可以將數(shù)據(jù)的水平切分理解為是按照數(shù)據(jù)行的切分,就是將表中的某些行切分到一個(gè)數(shù)據(jù)庫(kù),而另外的某些行又切分到其他的數(shù)據(jù)庫(kù)中,這也就是對(duì)應(yīng)的分表和分庫(kù)

1.8、分庫(kù)分表應(yīng)用和存在的問(wèn)題

應(yīng)用

  • 在數(shù)據(jù)庫(kù)設(shè)計(jì)時(shí)候考慮垂直分庫(kù)和垂直分表
  • 隨著數(shù)據(jù)庫(kù)數(shù)據(jù)量增加,不要馬上考慮做水平切分,首先考慮緩存處理,讀寫(xiě)分離,使用索引等等方式,如果這些方式不能根本解決問(wèn)題了,再考慮做水平分庫(kù)和水平分表

分庫(kù)分表問(wèn)題

  • 跨節(jié)點(diǎn)連接查詢問(wèn)題(分頁(yè)、排序)
  • 多數(shù)據(jù)源管理問(wèn)題

二、ShardingSphere-JDBC概述

ShardingSphere–JDBC操作流程,而使用他它的主要目的是為了幫我們簡(jiǎn)化對(duì)分庫(kù)分表之后數(shù)據(jù)相關(guān)操作

核心概念

2.1、邏輯表

水平拆分的數(shù)據(jù)庫(kù)(表)的相同邏輯和數(shù)據(jù)結(jié)構(gòu)表的總稱。例:訂單數(shù)據(jù)根據(jù)主鍵尾數(shù)拆分為10張表,分別是t_order_0到t_order_9,他們的邏輯表名為t_order。

2.2、真實(shí)表

在分片的數(shù)據(jù)庫(kù)中真實(shí)存在的物理表。即上個(gè)示例中的t_order_0到t_order_9。

2.3、數(shù)據(jù)節(jié)點(diǎn)

數(shù)據(jù)分片的最小單元。由數(shù)據(jù)源名稱和數(shù)據(jù)表組成,例:ds_0.t_order_0。

2.4、綁定表

# 設(shè)置product_info和product_descript為綁定表
spring.shardingsphere.sharding.binding-tables[0]=product_info,product_descript

指分片規(guī)則一致的主表和子表。例如:t_order表和t_order_item表,均按照order_id分片,則此兩張表互為綁定表關(guān)系。綁定表之間的多表關(guān)聯(lián)查詢不會(huì)出現(xiàn)笛卡爾積關(guān)聯(lián),關(guān)聯(lián)查詢效率將大大提升。舉例說(shuō)明,如果SQL為:

SELECT i.* FROM t_order o JOIN t_order_item i ON o.order_id=i.order_id WHERE o.order_id in (10, 11);

在不配置綁定表關(guān)系時(shí),假設(shè)分片鍵order_id將數(shù)值10路由至第0片,將數(shù)值11路由至第1片,那么路由后的SQL應(yīng)該為4條,它們呈現(xiàn)為笛卡爾積:

SELECT i.* FROM t_order_0 o JOIN t_order_item_0 i ON o.order_id=i.order_id WHERE o.order_id in (10, 11);

SELECT i.* FROM t_order_0 o JOIN t_order_item_1 i ON o.order_id=i.order_id WHERE o.order_id in (10, 11);

SELECT i.* FROM t_order_1 o JOIN t_order_item_0 i ON o.order_id=i.order_id WHERE o.order_id in (10, 11);

SELECT i.* FROM t_order_1 o JOIN t_order_item_1 i ON o.order_id=i.order_id WHERE o.order_id in (10, 11);

在配置綁定表關(guān)系后,路由的SQL應(yīng)該為2條:

SELECT i.* FROM t_order_0 o JOIN t_order_item_0 i ON o.order_id=i.order_id WHERE o.order_id in (10, 11);

SELECT i.* FROM t_order_1 o JOIN t_order_item_1 i ON o.order_id=i.order_id WHERE o.order_id in (10, 11);

其中t_order在FROM的最左側(cè),ShardingSphere將會(huì)以它作為整個(gè)綁定表的主表。 所有路由計(jì)算將會(huì)只使用主表的策略,那么t_order_item表的分片計(jì)算將會(huì)使用t_order的條件。故綁定表之間的分區(qū)鍵要完全相同。

2.5、廣播表

指所有的分片數(shù)據(jù)源中都存在的表,表結(jié)構(gòu)和表中的數(shù)據(jù)在每個(gè)數(shù)據(jù)庫(kù)中均完全一致。適用于數(shù)據(jù)量不大且需要與海量數(shù)據(jù)的表進(jìn)行關(guān)聯(lián)查詢的場(chǎng)景,例如:字典表。

# 指定region表為公共表,每次更新操作會(huì)發(fā)送至所有數(shù)據(jù)源
spring.shardingsphere.sharding.broadcast-tables=region

2.6、分片鍵

用于分片的數(shù)據(jù)庫(kù)字段,是將數(shù)據(jù)庫(kù)(表)水平拆分的關(guān)鍵字段。例:將訂單表中的訂單主鍵的尾數(shù)取模分片,則訂單主鍵為分片字段。 SQL中如果無(wú)分片字段,將執(zhí)行全路由,性能較差。 除了對(duì)單分片字段的支持,ShardingSphere也支持根據(jù)多個(gè)字段進(jìn)行分片。

2.7、分片算法

通過(guò)分片算法將數(shù)據(jù)分片,支持通過(guò)=、>=、<=、>、<、BETWEEN和IN分片。分片算法需要應(yīng)用方開(kāi)發(fā)者自行實(shí)現(xiàn),可實(shí)現(xiàn)的靈活度非常高。

目前提供4種分片算法。由于分片算法和業(yè)務(wù)實(shí)現(xiàn)緊密相關(guān),因此并未提供內(nèi)置分片算法,而是通過(guò)分片策略將各種場(chǎng)景提煉出來(lái),提供更高層級(jí)的抽象,并提供接口讓?xiě)?yīng)用開(kāi)發(fā)者自行實(shí)現(xiàn)分片算法。

  • 精確分片算法
    對(duì)應(yīng)PreciseShardingAlgorithm,用于處理使用單一鍵作為分片鍵的=與IN進(jìn)行分片的場(chǎng)景。需要配合StandardShardingStrategy使用。

  • 范圍分片算法
    對(duì)應(yīng)RangeShardingAlgorithm,用于處理使用單一鍵作為分片鍵的BETWEEN AND、>、<、>=、<=進(jìn)行分片的場(chǎng)景。需要配合StandardShardingStrategy使用。

  • 復(fù)合分片算法
    對(duì)應(yīng)ComplexKeysShardingAlgorithm,用于處理使用多鍵作為分片鍵進(jìn)行分片的場(chǎng)景,包含多個(gè)分片鍵的邏輯較復(fù)雜,需要應(yīng)用開(kāi)發(fā)者自行處理其中的復(fù)雜度。需要配合ComplexShardingStrategy使用。

  • Hint分片算法
    對(duì)應(yīng)HintShardingAlgorithm,用于處理使用Hint行分片的場(chǎng)景。需要配合HintShardingStrategy使用。

2.8、分片策略

包含分片鍵和分片算法,由于分片算法的獨(dú)立性,將其獨(dú)立抽離。真正可用于分片操作的是分片鍵 + 分片算法,也就是分片策略。目前提供5種分片策略。

  • 標(biāo)準(zhǔn)分片策略
    對(duì)應(yīng)StandardShardingStrategy。提供對(duì)SQL語(yǔ)句中的=, >, <, >=, <=, IN和BETWEEN AND的分片操作支持。StandardShardingStrategy只支持單分片鍵,提供PreciseShardingAlgorithm和RangeShardingAlgorithm兩個(gè)分片算法。PreciseShardingAlgorithm是必選的,用于處理=和IN的分片。RangeShardingAlgorithm是可選的,用于處理BETWEEN AND, >, <, >=, <=分片,如果不配置RangeShardingAlgorithm,SQL中的BETWEEN AND將按照全庫(kù)路由處理。

  • 復(fù)合分片策略
    對(duì)應(yīng)ComplexShardingStrategy。復(fù)合分片策略。提供對(duì)SQL語(yǔ)句中的=, >, <, >=, <=, IN和BETWEEN AND的分片操作支持。ComplexShardingStrategy支持多分片鍵,由于多分片鍵之間的關(guān)系復(fù)雜,因此并未進(jìn)行過(guò)多的封裝,而是直接將分片鍵值組合以及分片操作符透?jìng)髦练制惴?,完全由?yīng)用開(kāi)發(fā)者實(shí)現(xiàn),提供最大的靈活度。

  • 行表達(dá)式分片策略
    對(duì)應(yīng)InlineShardingStrategy。使用Groovy的表達(dá)式,提供對(duì)SQL語(yǔ)句中的=和IN的分片操作支持,只支持單分片鍵。對(duì)于簡(jiǎn)單的分片算法,可以通過(guò)簡(jiǎn)單的配置使用,從而避免繁瑣的Java代碼開(kāi)發(fā),如: t_user_$->{u_id % 8} 表示t_user表根據(jù)u_id模8,而分成8張表,表名稱為t_user_0到t_user_7。

  • Hint分片策略
    對(duì)應(yīng)HintShardingStrategy。通過(guò)Hint指定分片值而非從SQL中提取分片值的方式進(jìn)行分片的策略。

  • 不分片策略
    對(duì)應(yīng)NoneShardingStrategy。不分片的策略。

三、ShardingSphere-JDBC使用

項(xiàng)目構(gòu)建:使用springboot進(jìn)行集成ShardingSphere-JDBC,使用idea進(jìn)行初始化一個(gè)springboot的項(xiàng)目,之后我們導(dǎo)入關(guān)于數(shù)據(jù)庫(kù)和sharding-JDBC的相關(guān)依賴。

<!-- 連接池依賴 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.22</version>
</dependency>

<!-- 數(shù)據(jù)庫(kù)依賴 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

<!-- sharding-jdbc依賴 -->
<dependency>
    <groupId>org.apache.shardingsphere</groupId>
    <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
    <version>4.0.1</version>
</dependency>

<!-- mybatis 依賴 -->
<dependency>
    <groupId>tk.mybatis</groupId>
    <artifactId>mapper-spring-boot-starter</artifactId>
    <version>2.1.5</version>
</dependency>

<!-- lombok依賴 -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>

3.1、水平分表

約定規(guī)則:如果添加用戶id是奇數(shù)把數(shù)據(jù)添加user1,如果偶數(shù)添加到user2。首先我們創(chuàng)建對(duì)應(yīng)的兩張表

reate table user1(
    id BIGINT(20) PRIMARY KEY,
    name VARCHAR(50) NOT NULL,
    user_id BIGINT(20) NOT NULL
)

create table user2(
    id BIGINT(20) PRIMARY KEY,
    name VARCHAR(50) NOT NULL,
    user_id BIGINT(20) NOT NULL
)

先加上實(shí)體類、以及mapper直接繼承BaseMapper進(jìn)行后續(xù)操作,在啟動(dòng)類上加入注解 @MapperScan(mapper文件目錄) 用來(lái)掃描mapper。

// 實(shí)體類
@Data
public class User {
    private Long id;
    private String name;
    private int userId;
}

import tk.mybatis.mapper.common.Mapper;

public interface UserMapper extends Mapper<User> {
}


// 啟動(dòng)類
@MapperScan("com.yibo.shardingcase.mapper")

mybatis配置

mybatis.type-aliases-package: com.yibo.shardingcase.domain.entity
mybatis.mapper-locations: classpath:mapper/*.xml
mapper.identity: MYSQL
mapper.not-empty: false

而后需要對(duì)ShardingJdbc分表進(jìn)行相關(guān)的配置:官網(wǎng)配置地址

# 配置真實(shí)數(shù)據(jù)源(給數(shù)據(jù)源取一個(gè)名字)
spring.shardingsphere.datasource.names=ds

# 配置第 1 個(gè)數(shù)據(jù)源(對(duì)應(yīng)自己的數(shù)據(jù)庫(kù))
spring.shardingsphere.datasource.ds.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.ds.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.ds.url=jdbc:mysql://localhost:3306/springboot?serverTimezone=GMT%2B8
spring.shardingsphere.datasource.ds.username=root
spring.shardingsphere.datasource.ds.password=root

# 分表規(guī)則 表名+1,2
spring.shardingsphere.sharding.tables.user.actual-data-nodes=ds.user$->{1..2}

# 指定主鍵生成策略 主鍵id通過(guò)雪花算法生成
spring.shardingsphere.sharding.tables.user.key-generator.column=id
spring.shardingsphere.sharding.tables.user.key-generator.type=SNOWFLAKE

# 指定分片策略 根據(jù)生成的id進(jìn)行分表
spring.shardingsphere.sharding.tables.user.table-strategy.inline.sharding-column=id
spring.shardingsphere.sharding.tables.user.table-strategy.inline.algorithm-expression=user$->{id % 2 +1}

# 打印執(zhí)行的sql語(yǔ)句日志
spring.shardingsphere.props.sql.show=true

# 防止bean已被使用
spring.main.allow-bean-definition-overriding=true

3.2、水平分庫(kù)分表

約定規(guī)則:如果添加用戶id是奇數(shù)把數(shù)據(jù)添加user1,如果偶數(shù)添加到user2。這里還是用上面的表結(jié)構(gòu),但是在這里我們將創(chuàng)建兩個(gè)庫(kù),springboot1和springboot2兩個(gè)庫(kù),當(dāng)userid為奇數(shù)加入springboot1這個(gè)庫(kù)當(dāng)中,偶數(shù)加入到springboot2這個(gè)庫(kù)當(dāng)中。首先將兩個(gè)庫(kù)以及庫(kù)里面的表創(chuàng)建出來(lái)。

在這里修改配置文件,加入第二個(gè)數(shù)據(jù)源,以及分庫(kù)的策略。其余Java代碼不變,同樣的直接執(zhí)行test進(jìn)行測(cè)試,查看對(duì)應(yīng)的數(shù)據(jù)庫(kù)的表數(shù)據(jù)進(jìn)行驗(yàn)證。

# 配置真實(shí)數(shù)據(jù)源(給數(shù)據(jù)源取一個(gè)名字)
spring.shardingsphere.datasource.names=ds1,ds2

# 配置第 1 個(gè)數(shù)據(jù)源(對(duì)應(yīng)自己的數(shù)據(jù)庫(kù))
spring.shardingsphere.datasource.ds1.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.ds1.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds1.url=jdbc:mysql://localhost:3306/springboot1?serverTimezone=GMT%2B8
spring.shardingsphere.datasource.ds1.username=root
spring.shardingsphere.datasource.ds1.password=root

# 配置第 2 個(gè)數(shù)據(jù)源(對(duì)應(yīng)自己的數(shù)據(jù)庫(kù))
spring.shardingsphere.datasource.ds2.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.ds2.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds2.url=jdbc:mysql://localhost:3306/springboot2?serverTimezone=GMT%2B8
spring.shardingsphere.datasource.ds2.username=root
spring.shardingsphere.datasource.ds2.password=root

# 指定庫(kù)表的分布規(guī)則
spring.shardingsphere.sharding.tables.user.actual-data-nodes=ds$->{1..2}.user$->{1..2}

# 指定主鍵生成策略 主鍵id通過(guò)雪花算法生成
spring.shardingsphere.sharding.tables.user.key-generator.column=id
spring.shardingsphere.sharding.tables.user.key-generator.type=SNOWFLAKE

# 指定分片策略 根據(jù)生成的id進(jìn)行分表
spring.shardingsphere.sharding.tables.user.table-strategy.inline.sharding-column=id
spring.shardingsphere.sharding.tables.user.table-strategy.inline.algorithm-expression=user$->{id % 2 +1}

# 指定庫(kù)的分片策略
spring.shardingsphere.sharding.tables.user.database-strategy.inline.sharding-column=user_id
spring.shardingsphere.sharding.tables.user.database-strategy.inline.algorithm-expression=ds$->{user_id % 2 +1}

# 打印執(zhí)行的sql語(yǔ)句日志
spring.shardingsphere.props.sql.show=true

# 防止bean已被使用
spring.main.allow-bean-definition-overriding=true

3.3、垂直分庫(kù)分表

加入新庫(kù)和新表

CREATE DATABASE detail;

CREATE TABLE user_detail(
    user_id BIGINT(20) PRIMARY KEY,
    age VARCHAR(50) NOT NULL,
    sex VARCHAR(2) NOT NULL
);

修改配置文件:

# 配置真實(shí)數(shù)據(jù)源(給數(shù)據(jù)源取一個(gè)名字)
spring.shardingsphere.datasource.names=ds1,ds2,ds3

# 配置第 1 個(gè)數(shù)據(jù)源(對(duì)應(yīng)自己的數(shù)據(jù)庫(kù))
spring.shardingsphere.datasource.ds1.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.ds1.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds1.url=jdbc:mysql://localgost:3306/springboot1?serverTimezone=GMT%2B8
spring.shardingsphere.datasource.ds1.username=root
spring.shardingsphere.datasource.ds1.password=root

# 配置第 2 個(gè)數(shù)據(jù)源(對(duì)應(yīng)自己的數(shù)據(jù)庫(kù))
spring.shardingsphere.datasource.ds2.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.ds2.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds2.url=jdbc:mysql://localhost:3306/springboot2?serverTimezone=GMT%2B8
spring.shardingsphere.datasource.ds2.username=root
spring.shardingsphere.datasource.ds2.password=root

# 配置第 3 個(gè)數(shù)據(jù)源(對(duì)應(yīng)自己的數(shù)據(jù)庫(kù))
spring.shardingsphere.datasource.ds3.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.ds3.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds3.url=jdbc:mysql://localhost:3306/detail?serverTimezone=GMT%2B8
spring.shardingsphere.datasource.ds3.username=root
spring.shardingsphere.datasource.ds3.password=root

# 指定庫(kù)表的分布規(guī)則
spring.shardingsphere.sharding.tables.user_detail.actual-data-nodes=ds3.user_detail

# 指定主鍵生成策略 主鍵id通過(guò)雪花算法生成
spring.shardingsphere.sharding.tables.user_detail.key-generator.column=user_id
spring.shardingsphere.sharding.tables.user_detail.key-generator.type=SNOWFLAKE

# 指定分片策略 根據(jù)生成的id進(jìn)行分表
spring.shardingsphere.sharding.tables.user_detail.table-strategy.inline.sharding-column=user_id
spring.shardingsphere.sharding.tables.user_detail.table-strategy.inline.algorithm-expression=user_detail

# 打印執(zhí)行的sql語(yǔ)句日志
spring.shardingsphere.props.sql.show=true

# 防止bean已被使用
spring.main.allow-bean-definition-overriding=true

3.4、公共表操作

在進(jìn)行分庫(kù)分表之后,多個(gè)數(shù)據(jù)表的數(shù)據(jù)會(huì)存在公共使用的表,在這里shardingjdbc也提供了對(duì)公共表的操作,在多個(gè)庫(kù)當(dāng)中的相同表,再每對(duì)其中一個(gè)公共表進(jìn)行操作之后,另外庫(kù)里面的公共表也會(huì)隨之進(jìn)行該變。在前面有使用到了三個(gè)庫(kù),直接在這三個(gè)庫(kù)當(dāng)中都加入一個(gè)相同表結(jié)構(gòu)的表,作為一個(gè)公共表。

create table common(
    common_id BIGINT(20) PRIMARY KEY,
    common_name VARCHAR(50) NOT NULL,
    common_detail VARCHAR(20) NOT NULL
)

而后在對(duì)于前面的配置文件進(jìn)行修改,只需要加上對(duì)公共表的配置進(jìn)行即可:

# 配置公共表
spring.shardingsphere.sharding.broadcast-tables=common
spring.shardingsphere.sharding.tables.common.key-generator.column=common_id
spring.shardingsphere.sharding.tables.common.key-generator.type=SNOWFLAKE

之后加上對(duì)應(yīng)的實(shí)體類和mapper進(jìn)行測(cè)試:直接跑一遍執(zhí)行,程序跑完之后,直接在這三個(gè)庫(kù)當(dāng)中的common表進(jìn)行查看數(shù)據(jù)。

3.5、讀寫(xiě)分離與主從復(fù)制

為了確保數(shù)據(jù)庫(kù)產(chǎn)品的穩(wěn)定性,很多數(shù)據(jù)庫(kù)擁有雙機(jī)熱備功能。也就是,第一臺(tái)數(shù)據(jù)庫(kù)服務(wù)器,是對(duì)外提供增刪改業(yè)務(wù)的生產(chǎn)服務(wù)器;第二臺(tái)數(shù)據(jù)庫(kù)服務(wù)器,主要進(jìn)行讀的操作。原理∶讓主數(shù)據(jù)庫(kù)( master )處理事務(wù)性增、改、刪操作,而從數(shù)據(jù)庫(kù)( slave )處理SELECT查詢操作。

在進(jìn)行搭建數(shù)據(jù)庫(kù)主從復(fù)制,首先我們需要準(zhǔn)備兩個(gè)數(shù)據(jù)庫(kù)服務(wù),這里以就按windows上的mysql服務(wù)為例,只需要將第一次安裝的mysql服務(wù)復(fù)制一份出來(lái),修改對(duì)應(yīng)的my.ini配置文件,將端口、安裝位置,數(shù)據(jù)存儲(chǔ)目錄進(jìn)行相對(duì)應(yīng)的修改即可。

[mysql]
# 設(shè)置mysql客戶端默認(rèn)字符集
default-character-set=utf8 
[mysqld]
#設(shè)置3307端口
port = 3307
# 設(shè)置mysql的安裝目錄
basedir=F:\mysql\mysql-8.0.18-winx64-slave
# 允許最大連接數(shù)
max_connections=200
# 服務(wù)端使用的字符集默認(rèn)為8比特編碼的latin1字符集
character-set-server=utf8
# 創(chuàng)建新表時(shí)將使用的默認(rèn)存儲(chǔ)引擎
default-storage-engine=INNODB
# 設(shè)置mysql數(shù)據(jù)庫(kù)的數(shù)據(jù)的存放目錄
datadir=F:\mysql\mysql-8.0.18-winx64-slave\data
default-time_zone = '+8:00'

修改完成之后,進(jìn)入到復(fù)制后的bin目錄當(dāng)中打開(kāi)cmd窗口,將這個(gè)服務(wù)進(jìn)行安裝,使用以下命令:

mysqld install mysqlslave --defaults-file="F:\mysql\mysql-8.0.18-winx64-slave\my.ini"
# 安裝失敗報(bào)錯(cuò),使用管理員打開(kāi)cmd安裝即可
Install/Remove of the Service Denied

之后我們需要對(duì)兩個(gè)mysql服務(wù)進(jìn)行設(shè)置主服務(wù)器和從服務(wù)器。首先在主服務(wù)器上加上配置:

[mysqld]
server-id = 1        # 節(jié)點(diǎn)ID,確保唯一 一般設(shè)置為IP
binlog-do-db=springboot  # 復(fù)制過(guò)濾:需要備份的數(shù)據(jù)庫(kù),輸出binlog
# log config
log-bin = mysql-bin        #開(kāi)啟mysql的binlog日志功能 可以隨便取,最好有含義
sync_binlog = 1            #控制數(shù)據(jù)庫(kù)的binlog刷到磁盤(pán)上去 , 0 不控制,性能最好,1每次事物提交都會(huì)刷到日志文件中,性能最差,最安全
binlog_format = mixed      #binlog日志格式,mysql默認(rèn)采用statement,建議使用mixed
expire_logs_days = 7       #binlog過(guò)期清理時(shí)間 二進(jìn)制日志自動(dòng)刪除/過(guò)期的天數(shù)。默認(rèn)值為0,表示不自動(dòng)刪除
max_binlog_size = 100m     #binlog每個(gè)日志文件大小
binlog_cache_size = 4m     #binlog緩存大小  為每個(gè)session 分配的內(nèi)存,在事務(wù)過(guò)程中用來(lái)存儲(chǔ)二進(jìn)制日志的緩存
max_binlog_cache_size= 512m   #最大binlog緩存大
binlog-ignore-db=mysql     #不需要備份的數(shù)據(jù)庫(kù)不生成日志文件的數(shù)據(jù)庫(kù),多個(gè)忽略數(shù)據(jù)庫(kù)可以用逗號(hào)拼接,或者 復(fù)制這句話,寫(xiě)多行
## 如:1062錯(cuò)誤是指一些主鍵重復(fù),1032錯(cuò)誤是因?yàn)橹鲝臄?shù)據(jù)庫(kù)數(shù)據(jù)不一致
## 跳過(guò)主從復(fù)制中遇到的所有錯(cuò)誤或指定類型的錯(cuò)誤,避免slave端復(fù)制中斷。
#slave-skip-errors = all #跳過(guò)從庫(kù)錯(cuò)誤
slave-skip-errors = all 
auto-increment-offset = 1     # 自增值的偏移量
auto-increment-increment = 1  # 自增值的自增量

而后在從服務(wù)器上同樣的加上配置:

[mysqld]
server-id = 2
log-bin=mysql-bin
relay-log=mysql-relay-bin
replicate-wild-ignore-table=mysql.%
replicate-wild-ignore-table=test.%
replicate-wild-ignore-table=information_schema.%

修改配置之后,將服務(wù)進(jìn)行重啟,同樣,可以創(chuàng)建另外的用戶進(jìn)行測(cè)試。或者直接拿root用戶進(jìn)行測(cè)試。

GRANT REPLICATION SLAVE ON *.* TO 'db_sync'@'%' IDENTIFIED BY 'db_sync';
#刷新權(quán)限
FLUSH PRIVILEGES;

先進(jìn)入到主服務(wù)器當(dāng)中查看主服務(wù)器的狀態(tài):

mysql> SHOW MASTER STATUS;

+------------------+----------+--------------+------------------+-------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000001 |      537 | springboot   | mysql            |                   |
+------------------+----------+--------------+------------------+-------------------+

而后進(jìn)入到從服務(wù)器當(dāng)中,

# 先停止同步
STOP SLAVE;

# 修改從庫(kù)指向到主庫(kù),使用上一步記錄的文件名以及位點(diǎn),對(duì)應(yīng)前面主服務(wù)的狀態(tài)數(shù)據(jù)
CHANGE MASTER TO
MASTER_HOST = 'localhost',
MASTER_USER = 'root',
MASTER_PASSWORD = 'root',
MASTER_LOG_FILE = 'mysql-bin.000001',
MASTER_LOG_POS = 155;

# 啟動(dòng)同步
START SLAVE;

# 查看Slave_IO_Runing和Slave_SQL_Runing字段值都為Yes,表示同步配置成功。
SHOW SLAVE STATUS;

# 存在問(wèn)題 slave_io_running的值為NO,繼續(xù)看查出來(lái)的表數(shù)據(jù),發(fā)現(xiàn)在后面有一個(gè)單元格有錯(cuò)誤的log信息,可以查看log信息:
# Fatal error: The slave I/O thread stops because master and slave have equal MySQL server UUIDs; these UUIDs must be different for replication to work.
# 因?yàn)橹鞣?wù)器和從服務(wù)器具有相同的MySQL服務(wù)器UUID;這些UUID必須不同,復(fù)制才能工作。
# 在前面進(jìn)行復(fù)制的時(shí)候,是全量復(fù)制過(guò)來(lái)的,而UUID又沒(méi)有進(jìn)行修改,直接修改mysql的存放數(shù)據(jù)目錄的auto.cnf文件,將UUID隨便給個(gè)值,再重啟從服務(wù)器即可。

最后進(jìn)行驗(yàn)證,直接再主服務(wù)器當(dāng)中對(duì)對(duì)應(yīng)的庫(kù)的數(shù)據(jù)進(jìn)行修改或者新增等等操作,看從服務(wù)器當(dāng)中的數(shù)據(jù)會(huì)不會(huì)進(jìn)行相對(duì)應(yīng)的改變即可。

代碼實(shí)現(xiàn),修改配置文件:對(duì)主服務(wù)器和從服務(wù)器進(jìn)行相關(guān)配置

# 配置真實(shí)數(shù)據(jù)源(給數(shù)據(jù)源取一個(gè)名字)
spring.shardingsphere.datasource.names=ds1,s1

# 主服務(wù)器
spring.shardingsphere.datasource.ds1.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.ds1.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds1.url=jdbc:mysql://localhost:3306/springboot?serverTimezone=GMT%2B8
spring.shardingsphere.datasource.ds1.username=root
spring.shardingsphere.datasource.ds1.password=root

# 從服務(wù)器
spring.shardingsphere.datasource.s1.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.s1.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.s1.url=jdbc:mysql://localhost:3307/springboot?serverTimezone=GMT%2B8
spring.shardingsphere.datasource.s1.username=root
spring.shardingsphere.datasource.s1.password=root

# 主從關(guān)系
spring.shardingsphere.sharding.master-slave-rules.ds1.master-data-source-name=ds1
spring.shardingsphere.sharding.master-slave-rules.ds1.slave-data-source-names=s1

spring.shardingsphere.sharding.tables.user1.actual-data-nodes=ds1.user1

# 打印執(zhí)行的sql語(yǔ)句日志
spring.shardingsphere.props.sql.show=true

# 防止bean已被使用
spring.main.allow-bean-definition-overriding=true

最后添加代碼進(jìn)行測(cè)試,加入一個(gè)插入和查詢的方法,查看日志,插入操作的是主服務(wù)器,而查詢數(shù)據(jù)是操作的從服務(wù)器。這樣也就完成了讀寫(xiě)分離和主從復(fù)制。

參考:
https://shardingsphere.apache.org/document/current/cn/overview/

https://blog.csdn.net/qq_44973159/article/details/120524562

https://blog.csdn.net/qq_43843037/article/details/112741161

https://blog.csdn.net/Kiven_ch/article/details/119087048

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

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

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