В этой статье рассказывается об использовании SpringBoot Dangdang Sharding-JDBC для сегментирования баз данных и таблиц.
1. О Sharding-JDBC
Я не буду много говорить о внедрении Sharding-JDBC. Раньше Sharding-JDBC был горизонтальной структурой расширения реляционной базы данных, разработанной Dangdang.com, которая была передана в дар Apache.Убейте верхнюю сферу.Apache.org/document/…
Адрес документа сферы шардинга:Убейте верхнюю сферу.Apache.org/document/….
В настоящее время кажется, что зависимости не могут быть загружены из репозитория Maven. Вам необходимо вручную загрузить исходный код для упаковки, поэтому в этой статье используются зависимости Dangdang.com.
2. Место действия этой статьи
2.1 База данных
Далее я представлю сценарий этой статьи.В этой статье создаются две базы данных database0 и database1 соответственно. Каждая из этих баз данных создала 2 таблицы данных, goods_0 и goods_1, как показано. Здесь синие представляют таблицы в базе данных0, а красные представляют таблицы в базе данных1. Стол с зелеными товарами — это виртуальный стол (изображения уродливые, а эстетика не очень, так что давайте обойдемся).
2.2 Подбиблиотека
Пример подбазы данных в этой статье относительно прост.Он оценивается по размеру поля goods_id в таблице базы данных.Если goods_id больше 20, используйте базу данных0, в противном случае используйте базу данных1.
2.3 Подтаблица
Разделить выборку относительно просто, о ней судят по четности значения поля goods_type в таблице базы данных, нечетное количество использует таблицу goods_1, а четное количество использует таблицу goods_0.
2.4 Поток кода
Процесс примерно такой: в приложении мы работаем с виртуальными таблицами товаров, но когда мы на самом деле работаем с базой данных, мы будем сопоставлять и работать в соответствии с правилами нашей подбазы данных и подтаблиц.
3. Реализация кода
В этой статье используются SpringBoot2.0.3, SpringData-JPA, пул подключения DRUID и JDBC Dangdang Sharding-JDBC.
3.1 построить таблицу SQL
SQL для создания таблицы и базы данных показан ниже.
CREATE DATABASE database0;
USE database0;
DROP TABLE IF EXISTS `goods_0`;
CREATE TABLE `goods_0` (
`goods_id` bigint(20) NOT NULL,
`goods_name` varchar(100) COLLATE utf8_bin NOT NULL,
`goods_type` bigint(20) DEFAULT NULL,
PRIMARY KEY (`goods_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
DROP TABLE IF EXISTS `goods_1`;
CREATE TABLE `goods_1` (
`goods_id` bigint(20) NOT NULL,
`goods_name` varchar(100) COLLATE utf8_bin NOT NULL,
`goods_type` bigint(20) DEFAULT NULL,
PRIMARY KEY (`goods_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
CREATE DATABASE database1;
USE database1;
DROP TABLE IF EXISTS `goods_0`;
CREATE TABLE `goods_0` (
`goods_id` bigint(20) NOT NULL,
`goods_name` varchar(100) COLLATE utf8_bin NOT NULL,
`goods_type` bigint(20) DEFAULT NULL,
PRIMARY KEY (`goods_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
DROP TABLE IF EXISTS `goods_1`;
CREATE TABLE `goods_1` (
`goods_id` bigint(20) NOT NULL,
`goods_name` varchar(100) COLLATE utf8_bin NOT NULL,
`goods_type` bigint(20) DEFAULT NULL,
PRIMARY KEY (`goods_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
3.2 Файлы зависимостей
Создайте новый проект, добавьте зависимость Dangdang sharding-jdbc-core и пул соединений druid, полный pom выглядит следующим образом.
<?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.0.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.dalaoyang</groupId>
<artifactId>springboot2_shardingjdbc_fkfb</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot2_shardingjdbc_fkfb</name>
<description>springboot2_shardingjdbc_fkfb</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.9</version>
</dependency>
<!-- sharding-jdbc -->
<dependency>
<groupId>com.dangdang</groupId>
<artifactId>sharding-jdbc-core</artifactId>
<version>1.5.4</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
3.3 Информация о конфигурации
Информация о двух базах данных и простая конфигурация JPA настраиваются в информации о конфигурации.
##Jpa配置
spring.jpa.database=mysql
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none
##数据库配置
##数据库database0地址
database0.url=jdbc:mysql://localhost:3306/database0?characterEncoding=utf8&useSSL=false
##数据库database0用户名
database0.username=root
##数据库database0密码
database0.password=root
##数据库database0驱动
database0.driverClassName=com.mysql.jdbc.Driver
##数据库database0名称
database0.databaseName=database0
##数据库database1地址
database1.url=jdbc:mysql://localhost:3306/database1?characterEncoding=utf8&useSSL=false
##数据库database1用户名
database1.username=root
##数据库database1密码
database1.password=root
##数据库database1驱动
database1.driverClassName=com.mysql.jdbc.Driver
##数据库database1名称
database1.databaseName=database1
3.4 Стартовый класс
Класс Startup добавил @enableautoconFiguration, чтобы вынуть автоматическую конфигурацию базы данных, используйте @enabletransactionManagement для начать транзакции и использовать аннотацию @enableConfigurationProperties, чтобы добавить объекты конфигурации. Полный код класса Startup показан ниже.
package com.dalaoyang;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@SpringBootApplication
@EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
@EnableTransactionManagement(proxyTargetClass = true)
@EnableConfigurationProperties
public class Springboot2ShardingjdbcFkfbApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot2ShardingjdbcFkfbApplication.class, args);
}
}
3.5 Класс сущностей и уровень операций базы данных
Здесь нечего сказать, это простая сущность и репозиторий, но метод between и метод in добавляются в репозиторий для тестирования, код выглядит следующим образом.
Класс сущности товаров.
package com.dalaoyang.entity;
import lombok.Data;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
/**
* @author yangyang
* @date 2019/1/29
*/
@Entity
@Table(name="goods")
@Data
public class Goods {
@Id
private Long goodsId;
private String goodsName;
private Long goodsType;
}
Класс GoodsRepository.
package com.dalaoyang.repository;
import com.dalaoyang.entity.Goods;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
/**
* @author yangyang
* @date 2019/1/29
*/
public interface GoodsRepository extends JpaRepository<Goods, Long> {
List<Goods> findAllByGoodsIdBetween(Long goodsId1,Long goodsId2);
List<Goods> findAllByGoodsIdIn(List<Long> goodsIds);
}
3.6 Конфигурация базы данных
В этой статье используются две сущности для получения информации из базы данных и создания источника данных, но можно использовать и другие методы. Сначала взгляните на код классов Database0Config и Database1Config.
Класс Database0Config.
package com.dalaoyang.database;
import com.alibaba.druid.pool.DruidDataSource;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;
/**
* @author yangyang
* @date 2019/1/30
*/
@Data
@ConfigurationProperties(prefix = "database0")
@Component
public class Database0Config {
private String url;
private String username;
private String password;
private String driverClassName;
private String databaseName;
public DataSource createDataSource() {
DruidDataSource result = new DruidDataSource();
result.setDriverClassName(getDriverClassName());
result.setUrl(getUrl());
result.setUsername(getUsername());
result.setPassword(getPassword());
return result;
}
}
Класс Database1Config.
package com.dalaoyang.database;
import com.alibaba.druid.pool.DruidDataSource;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;
/**
* @author yangyang
* @date 2019/1/30
*/
@Data
@ConfigurationProperties(prefix = "database1")
@Component
public class Database1Config {
private String url;
private String username;
private String password;
private String driverClassName;
private String databaseName;
public DataSource createDataSource() {
DruidDataSource result = new DruidDataSource();
result.setDriverClassName(getDriverClassName());
result.setUrl(getUrl());
result.setUsername(getUsername());
result.setPassword(getPassword());
return result;
}
}
Затем создайте новый DataSourceConfig, чтобы создать источник данных и использовать стратегию подтаблицы подбазы данных.Стратегия подтаблицы подбазы данных вызовет класс алгоритма подбазы данных и класс алгоритма подтаблицы.Код класса DataSourceConfig как следует.
package com.dalaoyang.database;
import com.dalaoyang.config.DatabaseShardingAlgorithm;
import com.dalaoyang.config.TableShardingAlgorithm;
import com.dangdang.ddframe.rdb.sharding.api.ShardingDataSourceFactory;
import com.dangdang.ddframe.rdb.sharding.api.rule.DataSourceRule;
import com.dangdang.ddframe.rdb.sharding.api.rule.ShardingRule;
import com.dangdang.ddframe.rdb.sharding.api.rule.TableRule;
import com.dangdang.ddframe.rdb.sharding.api.strategy.database.DatabaseShardingStrategy;
import com.dangdang.ddframe.rdb.sharding.api.strategy.table.TableShardingStrategy;
import com.dangdang.ddframe.rdb.sharding.keygen.DefaultKeyGenerator;
import com.dangdang.ddframe.rdb.sharding.keygen.KeyGenerator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
* @author yangyang
* @date 2019/1/29
*/
@Configuration
public class DataSourceConfig {
@Autowired
private Database0Config database0Config;
@Autowired
private Database1Config database1Config;
@Autowired
private DatabaseShardingAlgorithm databaseShardingAlgorithm;
@Autowired
private TableShardingAlgorithm tableShardingAlgorithm;
@Bean
public DataSource getDataSource() throws SQLException {
return buildDataSource();
}
private DataSource buildDataSource() throws SQLException {
//分库设置
Map<String, DataSource> dataSourceMap = new HashMap<>(2);
//添加两个数据库database0和database1
dataSourceMap.put(database0Config.getDatabaseName(), database0Config.createDataSource());
dataSourceMap.put(database1Config.getDatabaseName(), database1Config.createDataSource());
//设置默认数据库
DataSourceRule dataSourceRule = new DataSourceRule(dataSourceMap, database0Config.getDatabaseName());
//分表设置,大致思想就是将查询虚拟表Goods根据一定规则映射到真实表中去
TableRule orderTableRule = TableRule.builder("goods")
.actualTables(Arrays.asList("goods_0", "goods_1"))
.dataSourceRule(dataSourceRule)
.build();
//分库分表策略
ShardingRule shardingRule = ShardingRule.builder()
.dataSourceRule(dataSourceRule)
.tableRules(Arrays.asList(orderTableRule))
.databaseShardingStrategy(new DatabaseShardingStrategy("goods_id", databaseShardingAlgorithm))
.tableShardingStrategy(new TableShardingStrategy("goods_type", tableShardingAlgorithm)).build();
DataSource dataSource = ShardingDataSourceFactory.createDataSource(shardingRule);
return dataSource;
}
@Bean
public KeyGenerator keyGenerator() {
return new DefaultKeyGenerator();
}
}
3.7 Алгоритм для подтаблицы подбазы данных
Поскольку это всего лишь простой пример сегментирования и сегментирования, класс сегментирования реализует здесь класс SingleKeyDatabaseShardingAlgorithm и использует алгоритм сегментирования источника данных с одним ключом сегментирования.Необходимо переписать три метода, а именно:
- doEqualSharding: правило для == в SQL.
- doInSharding: правила in в SQL.
- doBetweenSharding: Между правилами в SQL.
Правило сегментирования базы данных в этой статье основано на использовании базы данных 0, если значение больше 20, и базы данных 1 для остальных, поэтому выполняется простое условие if, else Код класса алгоритма сегментирования базы данных DatabaseShardingAlgorithm выглядит следующим образом.
package com.dalaoyang.config;
import com.dalaoyang.database.Database0Config;
import com.dalaoyang.database.Database1Config;
import com.dangdang.ddframe.rdb.sharding.api.ShardingValue;
import com.dangdang.ddframe.rdb.sharding.api.strategy.database.SingleKeyDatabaseShardingAlgorithm;
import com.google.common.collect.Range;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Collection;
import java.util.LinkedHashSet;
/**
* 这里使用的都是单键分片策略
* 示例分库策略是:
* GoodsId<=20使用database0库
* 其余使用database1库
* @author yangyang
* @date 2019/1/30
*/
@Component
public class DatabaseShardingAlgorithm implements SingleKeyDatabaseShardingAlgorithm<Long> {
@Autowired
private Database0Config database0Config;
@Autowired
private Database1Config database1Config;
@Override
public String doEqualSharding(Collection<String> availableTargetNames, ShardingValue<Long> shardingValue) {
Long value = shardingValue.getValue();
if (value <= 20L) {
return database0Config.getDatabaseName();
} else {
return database1Config.getDatabaseName();
}
}
@Override
public Collection<String> doInSharding(Collection<String> availableTargetNames, ShardingValue<Long> shardingValue) {
Collection<String> result = new LinkedHashSet<>(availableTargetNames.size());
for (Long value : shardingValue.getValues()) {
if (value <= 20L) {
result.add(database0Config.getDatabaseName());
} else {
result.add(database1Config.getDatabaseName());
}
}
return result;
}
@Override
public Collection<String> doBetweenSharding(Collection<String> availableTargetNames,
ShardingValue<Long> shardingValue) {
Collection<String> result = new LinkedHashSet<>(availableTargetNames.size());
Range<Long> range = shardingValue.getValueRange();
for (Long value = range.lowerEndpoint(); value <= range.upperEndpoint(); value++) {
if (value <= 20L) {
result.add(database0Config.getDatabaseName());
} else {
result.add(database1Config.getDatabaseName());
}
}
return result;
}
}
Разделение таблиц похоже на сегментирование, за исключением того, что реализуемые классы отличаются.Реализован класс SingleKeyTableShardingAlgorithm.Стратегия использует четность значений для разделения таблицы, а класс алгоритма сегментирования таблицы TableShardingAlgorithm показан в списке кодов.
package com.dalaoyang.config;
import com.dangdang.ddframe.rdb.sharding.api.ShardingValue;
import com.dangdang.ddframe.rdb.sharding.api.strategy.table.SingleKeyTableShardingAlgorithm;
import com.google.common.collect.Range;
import org.springframework.stereotype.Component;
import java.util.Collection;
import java.util.LinkedHashSet;
/**
* 这里使用的都是单键分片策略
* 示例分表策略是:
* GoodsType为奇数使用goods_1表
* GoodsType为偶数使用goods_0表
* @author yangyang
* @date 2019/1/30
*/
@Component
public class TableShardingAlgorithm implements SingleKeyTableShardingAlgorithm<Long> {
@Override
public String doEqualSharding(final Collection<String> tableNames, final ShardingValue<Long> shardingValue) {
for (String each : tableNames) {
if (each.endsWith(shardingValue.getValue() % 2 + "")) {
return each;
}
}
throw new IllegalArgumentException();
}
@Override
public Collection<String> doInSharding(final Collection<String> tableNames, final ShardingValue<Long> shardingValue) {
Collection<String> result = new LinkedHashSet<>(tableNames.size());
for (Long value : shardingValue.getValues()) {
for (String tableName : tableNames) {
if (tableName.endsWith(value % 2 + "")) {
result.add(tableName);
}
}
}
return result;
}
@Override
public Collection<String> doBetweenSharding(final Collection<String> tableNames,
final ShardingValue<Long> shardingValue) {
Collection<String> result = new LinkedHashSet<>(tableNames.size());
Range<Long> range = shardingValue.getValueRange();
for (Long i = range.lowerEndpoint(); i <= range.upperEndpoint(); i++) {
for (String each : tableNames) {
if (each.endsWith(i % 2 + "")) {
result.add(each);
}
}
}
return result;
}
}
3.8 Controller
Далее создайте Контроллер для тестирования.Метод сохранения использует вставку 40 кусков данных.По нашим правилам в каждую библиотеку будет вставляться 20 кусков данных.При этом я также создал здесь три метода запроса,а именно запросить все , между запросом, в Query и удалить все методы. Код класса контроллера показан ниже.
package com.dalaoyang.controller;
import com.dalaoyang.entity.Goods;
import com.dalaoyang.repository.GoodsRepository;
import com.dangdang.ddframe.rdb.sharding.keygen.KeyGenerator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
/**
* @author yangyang
* @date 2019/1/29
*/
@RestController
public class GoodsController {
@Autowired
private KeyGenerator keyGenerator;
@Autowired
private GoodsRepository goodsRepository;
@GetMapping("save")
public String save(){
for(int i= 1 ; i <= 40 ; i ++){
Goods goods = new Goods();
goods.setGoodsId((long) i);
goods.setGoodsName( "shangpin" + i);
goods.setGoodsType((long) (i+1));
goodsRepository.save(goods);
}
return "success";
}
@GetMapping("select")
public String select(){
return goodsRepository.findAll().toString();
}
@GetMapping("delete")
public void delete(){
goodsRepository.deleteAll();
}
@GetMapping("query1")
public Object query1(){
return goodsRepository.findAllByGoodsIdBetween(10L, 30L);
}
@GetMapping("query2")
public Object query2(){
List<Long> goodsIds = new ArrayList<>();
goodsIds.add(10L);
goodsIds.add(15L);
goodsIds.add(20L);
goodsIds.add(25L);
return goodsRepository.findAllByGoodsIdIn(goodsIds);
}
}
4. Тест
Запустите приложение и получите доступ к нему в браузере или инструменте HTTP-запросов.http://localhost:8080/save, как показано, возвращает успех.
Затем проверьте метод запроса, посетитеhttp://localhost:8080/select, как показано на рисунке, видно, что вставка данных не вызывает проблем.
Затем посмотрите на базу данных, сначала посмотрите на базу данных0, как показано на рисунке, каждая таблица содержит десять фрагментов данных, как показано ниже.
Затем посмотрите на базу данных1, как показано ниже.
Из приведенных выше картинок видно, что подтаблица подбазы данных была вставлена в соответствии с нашей стратегией.Что касается других тестов, мы не будем их здесь приводить.И запрос, и удаление могут быть успешными.
5 исходный код
Адрес источника:git ee.com/Большой парень Ян/Да…