Sharding-JDBC + SpringBoot
一.垂直分表
垂直分表介绍
- 也就是“大表拆小表”,基于列字段进行的
- 拆分原则一般是表中的字段较多,将不常用的或者数据较大,长度较长的拆分到“扩展表 如text类型字段
- 访问频次低、字段大的商品描述信息单独存放在一张表中,访问频次较高的商品基本信息单独放在一张表中
垂直拆分原则
5. 把不常用的字段单独放在一张表
6. 把text,blob等大字段拆分出来放在附表中
7. 业务经常组合查询的列放在一张表中
二、按照水平分表的方式创建数据库表
约定规则,如果添加商品id是偶数把数据加入product_order_0,如果是偶数把数据加入product_order_1
三、编写代码
创建product_order实体类
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("product_order")
public class ProductOrderDO {
@TableId(value = "id", type = IdType.ASSIGN_ID)
private Long id;
private String outTradeNo;
private String state;
private Date createTime;
private Double payAmount;
private String nickname;
private Long userId;
}
创建product_order
//数据库实体类
public interface ProductOrderMapper extends BaseMapper<ProductOrderDO> {
}
启动类配置扫描Mapper包
@org.springframework.boot.autoconfigure.SpringBootApplication
@MapperScan("xiao.su.mapper")
public class SpringBootApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootApplication.class);
}
}
四、application.properties配置Sharding-JDBC
#端口号
server.port=8080
#服务名
spring.application.name=xiao_su
#log打印
logging.level.root=INFO
# 打印执行的数据库以及语句
spring.shardingsphere.props.sql.show=true
# 数据源 db0
spring.shardingsphere.datasource.names=ds0
# 第一个数据库
#连接池
spring.shardingsphere.datasource.ds0.type=com.zaxxer.hikari.HikariDataSource
#mysql驱动
spring.shardingsphere.datasource.ds0.driver-class-name=com.mysql.cj.jdbc.Driver
#连接地址
spring.shardingsphere.datasource.ds0.jdbc-url=jdbc:mysql://127.0.0.1:3306/xdclass_shop_order_0?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
spring.shardingsphere.datasource.ds0.username=root
spring.shardingsphere.datasource.ds0.password=root
# 指定product_order表的数据分布情况,配置数据节点,行表达式标识符使用 ${...} 或 $->{...},但前者与 Spring 本身的文件占位符冲突,所以在 Spring 环境中建议使用 $->{...}
#spring.shardingsphere.sharding.tables.product_order product_order是逻辑表名 actual-data-nodes设置实际节点格式
spring.shardingsphere.sharding.tables.product_order.actual-data-nodes=ds0.product_order_$->{0..1}
#设置雪花算法 也可以使用mybatis-puls生成策略
#spring.shardingsphere.sharding.tables.product_order.key-generator.props.worker.id=1
#id生成策略
#spring.shardingsphere.sharding.tables.product_order.key-generator.column=id
#spring.shardingsphere.sharding.tables.product_order.key-generator.type=SNOWFLAKE
# 指定product_order表的分片策略,分片策略包括【分片键和分片算法】
spring.shardingsphere.sharding.tables.product_order.table-strategy.inline.sharding-column=user_id
#用user_id进行%于2如果是偶数的话就进入product_order_0表,否则进入product_order_1表
spring.shardingsphere.sharding.tables.product_order.table-strategy.inline.algorithm-expression=product_order_$->{user_id % 2}
五、编写测试代码
@RunWith(SpringRunner.class) //底层用junit SpringJUnit4ClassRunner
@SpringBootTest(classes = SpringBootApplication.class)
@Slf4j
public class DbTest {
@Autowired
private ProductOrderMapper productOrderMapper;
@Test
public void testSaveProductOrder(){
for(int i=0;i<10;i++){
ProductOrderDO productOrder = new ProductOrderDO();
productOrder.setCreateTime(new Date());
productOrder.setNickname("小苏="+i);
productOrder.setOutTradeNo(UUID.randomUUID().toString().substring(0,32));
productOrder.setPayAmount(100.00);
productOrder.setState("PAY");
productOrder.setUserId(Long.valueOf(i+""));
productOrderMapper.insert(productOrder);
}
}
}
启动报错
解决方案:
将此配置在application.properties中
spring.main.allow-bean-definition-overriding=true
运行控制台解析,有分片处理
没分片处理会产生多条没有的sql,从而产生笛卡儿积效应
六、Sharding-Jdbc广播表介绍和配置
- 什么是广播表
- 指所有的分片数据源中都存在的表,表结构和表中的数据在每个数据库中均完全一致
- 适用于数据量不大且需要与海量数据的表进行关联查询的场景
- 例如:字典表、配置表
- 注意点
- 分库分表中间件,对应的数据库字段,不能是sql的关键字,否则容易出问题,且报错不明显
配置文件
spring.shardingsphere.sharding.broadcast-tables=表名
#key的字段
spring.shardingsphere.sharding.tables.ad_config.key-generator.column=id
#key的生成策略
spring.shardingsphere.sharding.tables.ad_config.key-generator.type=SNOWFLAKE
七、Sharding-Jdbc水平分库
#分库和分表策略
spring.shardingsphere.sharding.tables.product_order.actual-data-nodes=ds$->{0..1}.product_order_$->{0..1}
# 分库分片健
spring.shardingsphere.sharding.tables.product_order.database-strategy.standard.sharding-column=user_id
# 分库分片算法
spring.shardingsphere.sharding.tables.product_order.database-strategy.standard.precise-algorithm-class-name=ds$->{user_id%2}
八、Sharding-Jdbc核心流程+多种分片策略实战
- Sharding-JDBC做的事情
- 长:SQL解析 -> SQL优化 -> SQL路由 -> SQL改写 -> SQL执行 -> 结果归并 ->返回结果
- 短:解析->路由->改写->执行->结果归并
精准分片算法
public class CustomDBPreciseShardingAlgorithm implements PreciseShardingAlgorithm<Long> {
/**
*
* @param dataSourceNames 数据源集合
* 在分库时值为所有分片库的集合 databaseNames
* 分表时为对应分片库中所有分片表的集合 tablesNames
*
* @param shardingValue 分片属性,包括
* logicTableName 为逻辑表,
* columnName 分片健(字段),
* value 为从 SQL 中解析出的分片健的值
* @return
*/
@Override
public String doSharding(Collection<String> dataSourceNames, PreciseShardingValue<Long> shardingValue) {
for (String databaseName : dataSourceNames) {
String value = shardingValue.getValue() % dataSourceNames.size() + "";
//value是0,则进入0库表,1则进入1库表
if (databaseName.endsWith(value)) {
return databaseName;
}
}
throw new IllegalArgumentException();
}
}
Random random = new Random();
for(int i=0;i<5;i++){
ProductOrder productOrder = new ProductOrder();
productOrder.setCreateTime(new Date());
productOrder.setNickname("000二当家i="+i);
productOrder.setOutTradeNo(UUID.randomUUID().toString().substring(0,32));
productOrder.setPayAmount(100.00);
productOrder.setState("PAY");
int value = random.nextInt(100);
productOrder.setUserId(Long.valueOf(value));
productOrderMapper.insert(productOrder);
}
配置
#指定全部数据节点,水平分库分表
spring.shardingsphere.sharding.tables.product_order.actual-data-nodes=ds$->{0..1}.product_order_$->{0..1}
# 分库分片健
spring.shardingsphere.sharding.tables.product_order.database-strategy.standard.sharding-column=user_id
# 分库分片算法
spring.shardingsphere.sharding.tables.product_order.database-strategy.standard.precise-algorithm-class-name=net.xiaosu.strategy.CustomDBPreciseShardingAlgorithm
范围分片算法
- RangeShardingAlgorithm 范围分片
- 用于处理BETWEEN AND语法,没配置的话会报错 Cannot find range sharding strategy in sharding rule
- 主要是会根据 SQL中给出的分片健值范围值处理分库、分表逻辑
编码
public class CustomRangeShardingAlgorithm implements RangeShardingAlgorithm<Long> {
@Override
public Collection<String> doSharding(Collection<String> dataSourceNames, RangeShardingValue<Long> rangeShardingValue) {
Set<String> result = new LinkedHashSet<>();
// between 起始值
Long lower = rangeShardingValue.getValueRange().lowerEndpoint();
// between 结束值
Long upper = rangeShardingValue.getValueRange().upperEndpoint();
// 循环范围计算分库逻辑
for (long i = lower; i <= upper; i++) {
for (String databaseName : dataSourceNames) {
if (databaseName.endsWith(i % dataSourceNames.size() + "")) {
result.add(databaseName);
}
}
}
return result;
}
}
配置
# 分库分片健
spring.shardingsphere.sharding.tables.product_order.database-strategy.standard.sharding-column=user_id
#精准水平分表下,增加一个范围分片
spring.shardingsphere.sharding.tables.product_order.table-strategy.standard.range-algorithm-class-name=net.xiaosu.strategy.CustomRangeShardingAlgorithm
复合分片算法
public class CustomComplexKeysShardingAlgorithm implements ComplexKeysShardingAlgorithm<Long> {
/**
* @param dataSourceNames 数据源集合
* 在分库时值为所有分片库的集合 databaseNames:比如表:product_order_0/product_order_1、库ds0/ds1 等
* 分表时为对应分片库中所有分片表的集合 tablesNames
* @param shardingValue 分片属性,包括
* logicTableName 为逻辑表,
* columnNameAndShardingValuesMap 存储多个分片健,包括key-value
* @return
*/
@Override
public Collection<String> doSharding(Collection<String> dataSourceNames, ComplexKeysShardingValue<Long> complexKeysShardingValue) {
// 得到每个分片健对应的值
Collection<Long> orderIdValues = this.getShardingValue(complexKeysShardingValue, "id");
Collection<Long> userIdValues = this.getShardingValue(complexKeysShardingValue, "user_id");
List<String> shardingSuffix = new ArrayList<>();
// 对两个分片健取模的方式
for (Long userId : userIdValues) {
for (Long orderId : orderIdValues) {
String suffix = userId % 2 + "_" + orderId % 2;
for (String databaseName : dataSourceNames) {
if (databaseName.endsWith(suffix)) {
shardingSuffix.add(databaseName);
}
}
}
}
return shardingSuffix;
}
/**
* shardingValue 分片属性,包括
* logicTableName 为逻辑表,
* columnNameAndShardingValuesMap 存储多个分片健 包括key-value
* key:分片key,id和user_id
* value:分片value,66和99
*
* @return shardingValues 集合
*/
private Collection<Long> getShardingValue(ComplexKeysShardingValue<Long> shardingValues, final String key) {
Collection<Long> valueSet = new ArrayList<>();
Map<String, Collection<Long>> columnNameAndShardingValuesMap = shardingValues.getColumnNameAndShardingValuesMap();
if (columnNameAndShardingValuesMap.containsKey(key)) {
valueSet.addAll(columnNameAndShardingValuesMap.get(key));
}
return valueSet;
}
}
配置
## 复合分片算法,order_id,user_id 同时作为分片健
spring.shardingsphere.sharding.tables.product_order.table-strategy.complex.sharding-columns=user_id,id
spring.shardingsphere.sharding.tables.product_order.table-strategy.complex.algorithm-class-name=net.xiaosu.strategy.CustomComplexKeysShardingAlgorithm
版权声明:本文为weixin_50230433原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。