Sharding-JDBC + SpringBoot

一.垂直分表

垂直分表介绍

  1. 也就是“大表拆小表”,基于列字段进行的
  2. 拆分原则一般是表中的字段较多,将不常用的或者数据较大,长度较长的拆分到“扩展表 如text类型字段
  3. 访问频次低、字段大的商品描述信息单独存放在一张表中,访问频次较高的商品基本信息单独放在一张表中

垂直拆分原则
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广播表介绍和配置

  • 什么是广播表
    1. 指所有的分片数据源中都存在的表,表结构和表中的数据在每个数据库中均完全一致
    2. 适用于数据量不大且需要与海量数据的表进行关联查询的场景
    3. 例如:字典表、配置表
  • 注意点
    • 分库分表中间件,对应的数据库字段,不能是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 版权协议,转载请附上原文出处链接和本声明。
THE END
< <上一篇
下一篇>>