Shardingsphere公式ウェブサイト:https://shardingsphere.apache.org/
シナリオ: テーブル内のデータが大きすぎる場合、テーブルを複数のテーブルに分割する必要がある場合があります。ここでは、テーブル分割機能は ShardingSphere を通じて実装されていますが、データベースでは実装されていません。
シャーディングスフィア
テーブルシャーディングに shardingsphere をすぐに使用する
1. データベース設計
このテーブル分割操作をデモンストレーションするために、ここで myqxin というライブラリを構築しました。基本テーブルとして sx_text_a があり、これを 3 つのテーブルに分割して格納し、サブテーブルとして sx_test_a0、sx_test_a1、sx_test_a2 があり、テーブル構造は同じですが、テーブル名が異なります。
図に示すように:
sx_test_a1 と sx_test_a2 は上記と同じなので、スクリーンショットは表示されません。
2. Springboot プロジェクトは shardingsphere を統合します
次の場合のプロジェクト構造:
これは、主にテーブル分割操作をデモンストレーションするための単純な Springboot プロジェクトです。
- プロジェクトの依存関係
<?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>
<groupId>com.myqxin</groupId>
<artifactId>itcast-sharding-server</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<!-- 父工程 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<!--SpringMVC-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- mybatis-plus begin-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.0</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.4.0</version>
</dependency>
<!--MySQL连接包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.13</version>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
<scope>provided</scope>
</dependency>
<!--工具包-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.2</version>
</dependency>
<!-- 本次演示必须的依赖 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>4.0.0-RC1</version>
</dependency>
</dependencies>
</project>
それらのほとんどはプロジェクトに必要な基本的な依存関係であり、最後の 2 つの依存関係はこのデモンストレーションに必要です。
- application.properties 構成ファイル
server.port=9018
# 数据源 主库
spring.shardingsphere.datasource.names=master
spring.shardingsphere.datasource.master.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.master.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.master.url=jdbc:mysql://localhost:3306/myqxin?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
spring.shardingsphere.datasource.master.username=root
spring.shardingsphere.datasource.master.password=root
# 数据分表规则
# 指定所需分的表
spring.shardingsphere.sharding.tables.sx_test_a.actual-data-nodes=master.sx_test_a$->{
0..2}
# 指定字段
spring.shardingsphere.sharding.tables.sx_test_a.table-strategy.inline.sharding-column=indexes
# 分表规则为该字段值除以3取模
spring.shardingsphere.sharding.tables.sx_test_a.table-strategy.inline.algorithm-expression=sx_test_a$->{
indexes % 3}
# 打印SQL
spring.shardingsphere.props.sql.show=true
spring.main.allow-bean-definition-overriding=true
ここでは、単一のデータベースに対してテーブル分割操作を実行するだけなので、複数のデータ ソースは必要ありません。データ接続情報については説明しません。
次のことを説明してください。
spring.shardingsphere.sharding.tables.sx_test_a.actual-data-nodes=master.sx_test_a$->{0..2}
# 指定字段
spring.shardingsphere.sharding.tables.sx_test_a.table-strategy.inline.sharding-column=indexes
# 分表规则为该字段值除以3取模
spring.shardingsphere.sharding.tables.sx_test_a.table-strategy.inline.algorithm-expression=sx_test_a$->{indexes % 3}
sx_test_a:这个是基础表,这没什么好说的
master.sx_test_a$->{
0..2}:
master这个主数据源节点,这个很好理解。
sx_test_a基础表也不说了。
$->{
0..2}如下图介绍(看完下图,说一下我理解配置与数据库之间的关系:因为$->{
0..2}是区间,由此它的内容加上前面的sx_test_a就组合成了sx_test_a0、sx_test_a1、sx_test_a2。这与我们库里面所分的三个表是一致,这样他们就能通过这个匹配上(根据inline.algorithm-expression=sx_test_a$->{
indexes % 3}规则),将数据插入到匹配对应的表)
indexes:这个理解为你库里面的某个字段
$->{
indexes % 3}如下图介绍(看完下图,说一下我的理解,我们可以把上面的actual-data-nodes=master.sx_test_a$->{
0..2}这个看成一个整体,就是我们总共要插入的表,而$->{
indexes % 3}这个拿着indexes字段的值跟3取模,得到的结果就是我们从整体操作的表中选择指定的一表进行数据的插入)
- SxTestAController.java
package com.myqxin.controller;
import com.myqxin.pojo.SxTestA;
import com.myqxin.service.SxTestAService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
/**
* @author: myqxin
* @Desc:
* @create: 2021-12-21 09:22
**/
@RestController
@RequestMapping("/sxa")
public class SxTestAController {
@Autowired
private SxTestAService sxTestAService;
@GetMapping("/savetest")
public String saveTest(){
ArrayList<SxTestA> sxTestAS = new ArrayList<>();
sxTestAS.add(new SxTestA(1,"number1",1,"2020","no","1234.3"));
sxTestAS.add(new SxTestA(2,"number2",2,"2020","no","14.3"));
sxTestAS.add(new SxTestA(3,"number3",3,"2020","no","5434.3"));
sxTestAS.add(new SxTestA(4,"number4",4,"2020","no","334.3"));
sxTestAS.add(new SxTestA(5,"number5",5,"2020","no","134.3"));
sxTestAService.saveBatch(sxTestAS);
return "myqxin";
}
@GetMapping("/list")
public ResponseEntity<List<SxTestA>> list(){
return ResponseEntity.ok(sxTestAService.list());
}
}
3. テストをリクエストする
- リクエストの送信: localhost:9018/sxa/savetest
コンソールから SQL print ステートメントを確認できます。図に示すように、内容が多すぎて一部のみがインターセプトされています。
データベースの効果を見てみましょう。
- sx_test_a0
- sx_test_a1
- sx_test_a2
これでサブテーブルへのデータの挿入は完了です。
詳細な説明:
4. 挿入したすべてのデータを取得します
- リクエストの送信: localhost:9018/sxa/list
リクエストの結果は次の図に示すようになります。
テーブルを分割しましたが、すべてのテーブルをクエリするときにテーブルを集約するのに依然として役立つことがわかります。
テーブル分割ルールをカスタマイズして、日付と月に基づいてテーブル分割を実装する
プロジェクトはまだこのプロジェクトのままですが、構成ファイルにいくつかの変更を加える必要があります。 ルール: sx_test_a 基本テーブルでは、data_time フィールドの時間を使用してテーブルを月に分割します。つまり、myqxin ライブラリには、テーブルが多すぎるため、sx_test_a2021_1 から sx_test_a2021_12 までの合計 12 テーブルです。はい、4 か月分のデータだけを取得し、4 つのテーブルだけを作成します。テーブルの作成時に、ストアド プロシージャを通じてテーブルを動的に生成できます。
- 表は次のとおりです。
1. application.properties ファイル
server.port=9018
# 数据源 主库
spring.shardingsphere.datasource.names=master
spring.shardingsphere.datasource.master.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.master.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.master.url=jdbc:mysql://localhost:3306/myqxin?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
spring.shardingsphere.datasource.master.username=root
spring.shardingsphere.datasource.master.password=root
# 指定所需分的表
spring.shardingsphere.sharding.tables.sx_test_a.actual-data-nodes=master.sx_test_a$->{2021}_$->{1..4}
# 指定字段
spring.shardingsphere.sharding.tables.sx_test_a.table-strategy.standard.sharding-column=date_time
# 自定义分表规则
spring.shardingsphere.sharding.tables.sx_test_a.table-strategy.standard.precise-algorithm-class-name=com.myqxin.utils.TableShardingAlgor
# 打印SQL
spring.shardingsphere.props.sql.show=true
spring.main.allow-bean-definition-overriding=true
2. TableShardingAlgor.java カスタム実装クラス
package com.myqxin.utils;
import lombok.extern.slf4j.Slf4j;
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingValue;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.logging.SimpleFormatter;
/**
* @author: myqxin
* @Desc:
* @create: 2021-12-22 09:41
**/
@Slf4j
public class TableShardingAlgor implements PreciseShardingAlgorithm<String> {
@Override
public String doSharding(Collection<String> collection, PreciseShardingValue<String> preciseShardingValue) {
// 真实节点
collection.stream().forEach((item) -> {
log.info("actual node table:{}",item);
});
// 精确分片
log.info("column value:{}",preciseShardingValue.getValue());
// 获取基础表名
String tb_name = preciseShardingValue.getLogicTableName();
// 根据当前日期来分表
String format = null;
try {
// 这里的类型是根据你实现PreciseShardingAlgorithm的类型String
String date = preciseShardingValue.getValue();
// 我这里需要进行一些处理(不需要的可以忽略)
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-M");
Date parse = sdf.parse(date);
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy_M");
format = sdf1.format(parse);
} catch (ParseException e) {
e.printStackTrace();
}
// 选择表 基础表+分表名称
tb_name = tb_name + format;
System.out.println("tb_name: "+tb_name);
// 进行匹配
for (String each : collection) {
System.out.println("sx_text_a: "+each);
if (each.equals(tb_name)){
// 匹配成功开始对这个表进行数据的插入
return each;
}
}
throw new IllegalArgumentException();
}
}
- SxTestAController.java
package com.myqxin.controller;
import com.myqxin.pojo.SxTestA;
import com.myqxin.service.SxTestAService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
/**
* @author: myqxin
* @Desc:
* @create: 2021-12-21 09:22
**/
@RestController
@RequestMapping("/sxa")
public class SxTestAController {
@Autowired
private SxTestAService sxTestAService;
@GetMapping(value = "/readDirectory")
public String readDirectory(){
sxTestAService.readDirectory();
return "myqxin";
}
}
- SxTestAServiceImpl.java
package com.myqxin.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.myqxin.mapper.SxTestAMapper;
import com.myqxin.pojo.SxTestA;
import com.myqxin.service.SxTestAService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* @author: myqxin
* @Desc:
* @create: 2021-12-21 09:21
**/
@Service
public class SxTestAServiceImpl extends ServiceImpl<SxTestAMapper, SxTestA> implements SxTestAService {
@Autowired(required = false)
private SxTestAMapper sxTestAMapper;
@Override
public void readDirectory() {
String path = "C:\\Users\\Administrator\\Desktop\\pams";
File file = new File(path);
if (!file.isDirectory()) {
System.err.println(file.getPath() + ": " + file.getName());
} else if (file.isDirectory()) {
String[] list = file.list();
for (int i = 0; i < list.length; i++) {
ArrayList<SxTestA> sxMonitors = new ArrayList<>();
File file1 = new File(path + "\\" + list[i]);
try {
String s = FileUtils.readFileToString(new File(file1.getPath()), "utf-8");
String[] lonList = s.split("\r|\n|\\s+");
for (int i1 = 0; i1 < lonList.length; i1++) {
SxTestA sxMonitor = new SxTestA();
String[] split = file1.getName().split("\\_|\\.");
sxMonitor.setNumber(file1.getName());
sxMonitor.setIndexes(i1);
String taskTime = split[1];
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd'T'HH");
Date date = sdf.parse(taskTime);
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String sr = df.format(date);
sxMonitor.setDateTime(sr);
sxMonitor.setMonitorName(split[0]);
sxMonitor.setMonitorValue(lonList[i1]);
sxMonitors.add(sxMonitor);
}
System.err.println(sxMonitors.size()); //13345
// TODO 数据批处理
saveBatch(sxMonitors);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
@Override
public List<SxTestA> selectAll() {
QueryWrapper<SxTestA> wrapper = new QueryWrapper<>();
//wrapper.eq("date_time",timeStamp);
return list();
}
}
データは自分で作成したので、作成したテーブルを基にシミュレーション用のデータを作成することができます。
3. テストをリクエストする
- リクエストの送信: localhost:9018/sxa/readDirectory
データベースの効果は次のとおりです。
sx_test_a2021_1
sx_test_a2021_2
sx_test_a2021_3
ここでは 3 つだけ示しますが、ここでの効果は達成されています。
よくある質問と解決策
- エラー メッセージ 1:
その理由は、テーブルシャーディングに shardingsphere フレームワークを使用する場合、
spring.main.allow-bean-definition-overriding=true が構成ファイルに追加されないためです。
spring.main.allow-bean-definition-overriding=true
これは設定ファイルに追加することで解決できます
- エラー メッセージ 2:
原因は、現在使用している mysql のバージョンがインポートに依存するバージョンと一致していないためですが、mysql8.0.27 バージョンをインストールしたので、一致するように変更するだけです。
したがって、導入されたデータベース接続の依存関係は 8.0.27 です。
<!--MySQL连接包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.27</version>
</dependency>
今回デモしているプロジェクトはバージョン 8.0.13 を使用しているため、導入される依存関係も 8.0.13 になりますが、実際に使用するバージョンに合わせて修正してください。
データベーステーブルを動的に作成する
今回は基本テーブル上に一つずつ作成しましたが、これでは実際のビジネスニーズを満たせません。テーブル作成操作をプログラムに渡し、必要なテーブルを動的に生成する必要があります(このテーブル作成プログラムを実行するスケジュールされたタスク)。整理しました。クリックして表示します。データベース テーブルを動的に作成します。