学习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: database
mybatis-plus:
mapperLocations: classpath*:mapper/*Mapper.xml
# 数据源命名
spring.shardingsphere.datasource.names=book2021,book2022,book2023,book2024
# 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
# datasource2
spring.shardingsphere.datasource.book2022.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.book2022.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.book2022.url=jdbc:mysql://192.168.31.212:3307/book2022?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8&allowMultiQueries=true
spring.shardingsphere.datasource.book2022.username=root
spring.shardingsphere.datasource.book2022.password=123
# datasource3
spring.shardingsphere.datasource.book2023.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.book2023.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.book2023.url=jdbc:mysql://192.168.31.212:3308/book2023?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8&allowMultiQueries=true
spring.shardingsphere.datasource.book2023.username=root
spring.shardingsphere.datasource.book2023.password=123
# datasource4
spring.shardingsphere.datasource.book2024.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.book2024.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.book2024.url=jdbc:mysql://192.168.31.212:3309/book2024?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8&allowMultiQueries=true
spring.shardingsphere.datasource.book2024.username=root
spring.shardingsphere.datasource.book2024.password=123
#库名.表名 采用了4个数据库 可以用${}自动遍历的方式 也可以直接book2021 ,book2022....的方式直接写死
spring.shardingsphere.sharding.tables.book.actual-data-nodes=book202${1..4}.book_info_0
# 数据库分片字段 这里采用时间 根据时间进行分库
spring.shardingsphere.sharding.tables.book.database-strategy.standard.sharding-column=create_time
# 数据库分片策略 精准匹配如=
spring.shardingsphere.sharding.tables.book.database-strategy.standard.preciseAlgorithmClassName=cn.xwl.xshardingjdbc.config.database.PreciseDatabaseShardingAlgorithm
# 数据库分片策略 区间匹配如 in,between
spring.shardingsphere.sharding.tables.book.database-strategy.standard.rangeAlgorithmClassName=cn.xwl.xshardingjdbc.config.database.RangeDatabaseShardingAlgorithm
# 打印sql
spring.shardingsphere.props.sql.show=true
可能会报错的解决:
1.数据源命名要与下方配置的一致
2.可能会出现ssl错误把数据源后的参数ssl设为false
3.下面配置中的tables.book中的book命名要与库名或者表名有一定关联(具体是什么不知道,这边我的库都是book开头的,所以我以book成功了,但是用别的会报错提示找不到数据源)
package cn.xwl.xshardingjdbc.config.database;
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;
/**
* 库精确分片算法
* PreciseShardingAlgorithm是必选的,用于处理=和IN的分片
*/
public class PreciseDatabaseShardingAlgorithm implements PreciseShardingAlgorithm<Timestamp> {
/**
* 库精确分片算法
*
* @param availableTargetNames 所有配置的库列表
* @param shardingValue 分片值
* @return 所匹配库的结果
* springboot2.0+自动转换datetime为timestamp导致datetime无法转换为localdatetime,因此使用Timestamp接收datetime类型的数据库字段
*/
@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 year = localDateTime.format(DateTimeFormatter.ofPattern("yyyy"));
for (String availableTargetName : availableTargetNames) {
if (availableTargetName.endsWith(year)) {
return availableTargetName;
}
}
throw new UnsupportedOperationException();
}
}
package cn.xwl.xshardingjdbc.config.database;
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.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collection;
/**
* 库范围分片算法
* RangeShardingAlgorithm是可选的,用于处理BETWEEN AND分片,如果不配置RangeShardingAlgorithm,SQL中的BETWEEN AND将按照全库路由处理
* 如果需要使用RangeShardingAlgorithm,必须和PreciseShardingAlgorithm配套使用
*/
public class RangeDatabaseShardingAlgorithm implements RangeShardingAlgorithm<Timestamp> {
/**
* 库范围分片算法
*
* @param availableTargetNames 所有配置的库列表
* @param rangeShardingValue 分片值,也就是save_time_com的值,范围分片算法必须提供开始时间和结束时间
* @return 所匹配库的结果
*/
@Override
public Collection<String> doSharding(Collection<String> availableTargetNames,
RangeShardingValue<Timestamp> rangeShardingValue) {
ArrayList<String> result = new ArrayList<>();
Range<Timestamp> range = rangeShardingValue.getValueRange();
// 起始年和结束年
int startYear = range.lowerEndpoint().toLocalDateTime().getYear();
int endYear = range.upperEndpoint().toLocalDateTime().getYear();
return startYear == endYear
? theSameYear(startYear, availableTargetNames, result)
: differentYear(startYear, endYear, availableTargetNames, result);
}
/**
* 同一年,说明只需要一个库
*/
private Collection<String> theSameYear(int startTime, Collection<String> availableTargetNames,
ArrayList<String> result) {
for (String availableTargetName : availableTargetNames) {
if (availableTargetName.endsWith(String.valueOf(startTime)))
result.add(availableTargetName);
}
return result;
}
/**
* 跨年
*/
private Collection<String> differentYear(int startYear, int endYear, Collection<String> availableTargetNames,
ArrayList<String> result) {
for (String availableTargetName : availableTargetNames) {
for (int i = startYear; i <= endYear; i++) {
if (availableTargetName.endsWith(String.valueOf(i)))
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));
}
}