学习sharding-jdbc的分库分表功能,以分库,分表,分库分表三种方式来实现,此文章只展示分表,为简洁其余两种分篇写
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</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>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.20</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>4.0.0-RC1</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
</dependencies>
spring:
main:
allow-bean-definition-overriding: true # 需要配置否则加载数据源报错 是否允许定义重名的bean对象覆盖原有的bean
profiles:
active: table
mybatis-plus:
mapperLocations: classpath*:mapper/*Mapper.xml
# 数据源命名
spring.shardingsphere.datasource.names=book2021
# datasource1
spring.shardingsphere.datasource.book2021.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.book2021.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.book2021.url=jdbc:mysql://192.168.31.212:3306/book2021?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8&allowMultiQueries=true
spring.shardingsphere.datasource.book2021.username=root
spring.shardingsphere.datasource.book2021.password=123
#库名.表名
spring.shardingsphere.sharding.tables.book.actual-data-nodes=book2021.book_info_20210${1..9},book2021.book_info_20211${0..2}
# 数据表分片字段
spring.shardingsphere.sharding.tables.book.table-strategy.standard.sharding-column=create_time
# 数据表分表策略
spring.shardingsphere.sharding.tables.book.table-strategy.standard.preciseAlgorithmClassName=cn.xwl.xshardingjdbc.config.table.PreciseTableShardingAlgorithm
spring.shardingsphere.sharding.tables.book.table-strategy.standard.rangeAlgorithmClassName=cn.xwl.xshardingjdbc.config.table.RangeTableShardingAlgorithm
# 打印sql
spring.shardingsphere.props.sql.show=true
package cn.xwl.xshardingjdbc.config.table;
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingValue;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Collection;
/**
* 表精确分片算法
*/
public class PreciseTableShardingAlgorithm implements PreciseShardingAlgorithm<Timestamp> {
/**
* 表精确分片算法
*
* @param availableTargetNames 所有配置的表列表,这里代表所匹配到库的所有表
* @param shardingValue 分片值
* @return 所匹配表的结果
*/
@Override
public String doSharding(Collection<String> availableTargetNames,
PreciseShardingValue<Timestamp> shardingValue) {
// 分片键值
Timestamp value = shardingValue.getValue();
if (value == null) {
throw new UnsupportedOperationException("分表出现异常:{分片键值为空}");
}
LocalDateTime localDateTime = value.toLocalDateTime();
//根据键值匹配对应的表
String yearMonth = localDateTime.format(DateTimeFormatter.ofPattern("yyyyMM"));
for (String availableTargetName : availableTargetNames) {
if (availableTargetName.endsWith(yearMonth)) {
return availableTargetName;
}
}
throw new UnsupportedOperationException();
}
}
package cn.xwl.xshardingjdbc.config.table;
import com.google.common.collect.Range;
import org.apache.shardingsphere.api.sharding.standard.RangeShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.standard.RangeShardingValue;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Stream;
/**
* 表范围分片算法
*
*/
public class RangeTableShardingAlgorithm implements RangeShardingAlgorithm<Timestamp> {
/**
* 表范围分片算法
*/
@Override
public Collection<String> doSharding(Collection<String> availableTargetNames,
RangeShardingValue<Timestamp> rangeShardingValue) {
Range<Timestamp> range = rangeShardingValue.getValueRange();
LocalDateTime minTime = range.lowerEndpoint().toLocalDateTime();
LocalDateTime maxTime = range.upperEndpoint().toLocalDateTime();
return getMonthBetween(minTime, maxTime, availableTargetNames);
}
/**
* 计算有效的库表名
*/
public static Collection<String> getMonthBetween(LocalDateTime minTime, LocalDateTime maxTime,
Collection<String> availableTargetNames) {
Collection<String> result = new ArrayList<>();
LocalDate startDate = minTime.toLocalDate();
LocalDate endDate = maxTime.toLocalDate();
long distance = ChronoUnit.MONTHS.between(startDate, endDate);
Stream.iterate(startDate, d -> d.plusMonths(1)).limit(distance + 1).forEach(f -> {
availableTargetNames.forEach(availableTargetName -> {
if (availableTargetName.endsWith(f.format(DateTimeFormatter.ofPattern("yyyyMM")))) {
result.add(availableTargetName);
}
});
});
return result;
}
}
package cn.xwl.xshardingjdbc.model;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import lombok.ToString;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
@ToString
public class Book {
@TableId(type = IdType.ID_WORKER) //id生成策略 唯一id
private Long id;
private String name;
private BigDecimal money;
private LocalDateTime createTime;
}
package cn.xwl.xshardingjdbc.model;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface BookMapper extends BaseMapper<Book> {
}
package cn.xwl.xshardingjdbc;
import cn.xwl.xshardingjdbc.model.Book;
import cn.xwl.xshardingjdbc.model.BookMapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
@SpringBootTest
class TestDatabase {
@Resource
public BookMapper bookMapper;
//插入 这里采用的是= 所以采用的是精准分片策略
@Test
void insert() {
Book book = new Book();
book.setName("王大毛");
LocalDateTime dateTime=LocalDateTime.of(2021,12,1,12,59,50);
book.setCreateTime(dateTime);
book.setMoney(new BigDecimal("888.88"));
bookMapper.insert(book);
}
//查 这里采用了between所以会触发区间分片策略
@Test
void select() {
LocalDateTime start=LocalDateTime.of(2021,12,5,12,59,50);
LocalDateTime end=LocalDateTime.of(2022,12,5,12,59,50);
List<Book> books = bookMapper.selectList(new LambdaQueryWrapper<Book>().between(Book::getCreateTime,start,end));
books.forEach(x -> {
System.out.println(x.toString());
});
}
//改
@Test
void update() {
Book book=new Book();
book.setId(1693868063067258882L);
book.setName("李大帅");
bookMapper.updateById(book);
}
//删
@Test
void delete() {
bookMapper.delete(new LambdaQueryWrapper<Book>()
.eq(Book::getId,1693868063067258882L));
}
}