sharding-jdbc mysql分库分表的代码实现

发布时间 2023-06-29 23:34:06作者: 乐之者v

一、依赖包

<dependency>
    <groupId>io.shardingsphere</groupId>
    <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
    <version>3.0.0.M4</version>
</dependency>

注意,sharding-jdbc 不同版本的差异较大,如果引入 其他版本,有可能不兼容。

二、mysql 建表

CREATE TABLE tb_shard_test
(
    id          INT         NOT NULL AUTO_INCREMENT COMMENT '主键,自增id',
    order_id    VARCHAR(25) NOT NULL UNIQUE COMMENT '订单号,唯一',
    pay_status  INT UNSIGNED DEFAULT 0 COMMENT '10:未支付,20:支付成功,30:支付失败, 40:已下单,50:申请退款,60:退款成功,70:退款失败 ',
    user_id     BIGINT(20)  NOT NULL COMMENT '用户id',
    total_price DECIMAL(25, 2)   DEFAULT 0.00 COMMENT '交易金额',
    result      TEXT COMMENT '结果',
    order_desc  VARCHAR(128)     DEFAULT '' COMMENT '订单描述',
    order_date  DATE             DEFAULT NULL COMMENT '订单日期',
    create_time DATETIME         DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间,默认当前时间',
    update_time DATETIME         DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间,更新时默认当前时间',
    is_delete   TINYINT(1)       DEFAULT 0 COMMENT '是否删除,0表示否,1表示是',
    PRIMARY KEY (id),
    INDEX idx_order (order_id)
) ENGINE = INNODB
  DEFAULT CHARSET = utf8
  AUTO_INCREMENT = 1 COMMENT ='示例表';

建立多个分表:

CREATE TABLE tb_shard_test_0 LIKE tb_shard_test;
CREATE TABLE tb_shard_test_1 LIKE tb_shard_test;
CREATE TABLE tb_shard_test_2 LIKE tb_shard_test;
CREATE TABLE tb_shard_test_3 LIKE tb_shard_test;
CREATE TABLE tb_shard_test_4 LIKE tb_shard_test;
CREATE TABLE tb_shard_test_5 LIKE tb_shard_test;
CREATE TABLE tb_shard_test_6 LIKE tb_shard_test;
CREATE TABLE tb_shard_test_7 LIKE tb_shard_test;

三、properties 配置文件

properties如下:

# 数据源命名。这个名称,后面会经常用到。
sharding.jdbc.datasource.names=ds
# default-data-source-name 默认的数据源,可以是 sharding.jdbc.datasource.names 配置的值
sharding.jdbc.config.sharding.default-data-source-name=ds
# tables后面是 表名 tb_shard_test,自行修改。 ds是前面配置的数据源名称。0..7 表示分为 8 张表。
sharding.jdbc.config.sharding.tables.tb_shard_test.actual-data-nodes=ds.tb_shard_test_$->{0..7}
sharding.jdbc.config.sharding.tables.tb_shard_test.table-strategy.standard.sharding-column=order_id
# 指定分表策略的类的路径。路径自行修改
sharding.jdbc.config.sharding.tables.tb_shard_test.table-strategy.standard.precise-algorithm-class-name=com.example.demo.config.MyShardingAlgorithm

sharding.jdbc.config.sharding.props.sql.show=true


# 数据库连接池,如果是 Druid,则使用  com.alibaba.druid.pool.DruidDataSource
# 注意 sharding.jdbc.datasource. 后面的值,必须是 sharding.jdbc.datasource.names 配置的值。比如配置为 ds,那在使用时就用 ds。
sharding.jdbc.datasource.ds.type=com.zaxxer.hikari.HikariDataSource
sharding.jdbc.datasource.ds.driver-class-name=com.mysql.jdbc.Driver
#sharding.jdbc.datasource.ds.url=jdbc:mysql://mysql的ip:端口/库名?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true
sharding.jdbc.datasource.ds.jdbcUrl=jdbc:mysql://mysql的ip:端口/库名?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true
sharding.jdbc.datasource.ds.username=账号
sharding.jdbc.datasource.ds.password=密码
sharding.jdbc.datasource.ds.initialSize=0
sharding.jdbc.datasource.ds.maxActive=20
sharding.jdbc.datasource.ds.maxIdle=10
sharding.jdbc.datasource.ds.minIdle=5
sharding.jdbc.datasource.ds.maxWait=60000
sharding.jdbc.datasource.ds.timeBetweenEvictionRunsMillis=60000
sharding.jdbc.datasource.ds.minEvictableIdleTimeMillis=300000
sharding.jdbc.datasource.ds.validationQuery=SELECT 'x'
sharding.jdbc.datasource.ds.testWhileIdle=true
sharding.jdbc.datasource.ds.testOnBorrow=false
sharding.jdbc.datasource.ds.testOnReturn=false
#spring.datasource.connection-init-sqls=set names utf8mb4;
sharding.jdbc.datasource.ds.filters=stat

自定义分表策略:

注意,这个类要放在对应的路径。

比如 properties 文件中配置的是:

sharding.jdbc.config.sharding.tables.表名.table-strategy.standard.precise-algorithm-class-name=com.example.demo.config.MyShardingAlgorithm

那么类就要放在 com.example.demo.config.MyShardingAlgorithm 这个路径。

import io.shardingsphere.api.algorithm.sharding.PreciseShardingValue;
import io.shardingsphere.api.algorithm.sharding.standard.PreciseShardingAlgorithm;

import java.util.Collection;

/**
 *  自定义分表策略。根据 分表字段的hashCode 取对8进行取余。
 *  根据分表策略,返回表名。
 */
public class MyShardingAlgorithm implements PreciseShardingAlgorithm<String> {
    @Override
    public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<String> shardingValue) {
        String shardingStr = shardingValue.getValue().trim();
        for (String tableName : availableTargetNames) {
            if (tableName.endsWith(Math.abs(shardingStr.hashCode()) % 8 + "")) {
                return tableName;
            }
        }
        throw new IllegalArgumentException();
    }
}

Service层代码

此处用的是 Mybatis-Plus,相关讲解见:https://www.cnblogs.com/expiator/p/17125880.html

也可以直接用 mybatis 写sql。

@Service
public class ShardTestServiceImpl extends ServiceImpl<ShardTestMapper, ShardTestEntity> implements ShardTestService {

    /**
     * 根据 orderId 查询结果
     */
    public List<ShardTestEntity> getByOrderId(String orderId) {
        LambdaQueryWrapper<ShardTestEntity> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ShardTestEntity::getOrderId, orderId);
        return list(queryWrapper);

    }


}

测试代码:

由于是根据 orderId的 hashCode 对8取余进行分表。

orderId为 12345 时, 计算结果为 3,保存/查询就都在 tb_shard_test_3 表。

orderId为 1234 时, 计算结果为 2,保存/查询就都 tb_shard_test_2 表。

如下:

    @Test
    public void testSave() throws Exception {
        String orderId = "12345";
        long userId = 123L;
        //根据 hashCode 对8取余,可以知道数据存在哪一张分表上。
        int partition = Math.abs(orderId.hashCode()) % 8;
        System.out.println("partition:"+ partition);

        ShardTestEntity shardTestEntity = new ShardTestEntity();
        shardTestEntity.setOrderId(orderId);
        shardTestEntity.setUserId(userId);
        //保存
        shardTestService.save(shardTestEntity);

    }

 	@Test
    public void testGetByOrderId() throws Exception {
        String orderId = "1234";
        //根据 hashCode 对8取余,可以知道数据存在哪一张分表上。
        int partition = Math.abs(orderId.hashCode()) % 8;
        System.out.println("partition:"+ partition);

        List<ShardTestEntity> entities = shardTestService.getByOrderId(orderId);
        System.out.println(JSON.toJSONString(entities));
    }

报错:

  • sharding-jdbc 报错:
io.shardingsphere.core.exception.ShardingException: Cannot find data source in sharding rule, invalid actual data node is 

解决方法:

使用的数据源,必须 是 sharding.jdbc.datasource.names 配置的值。

比如 配置了:

sharding.jdbc.datasource.names=ds

那么 sharding.jdbc.config.sharding.tables.表名.actual-data-nodes 的配置,就只能是 ds.加表名,示例:

sharding.jdbc.config.sharding.tables.tb_shard_test.actual-data-nodes=ds.tb_shard_test_$->{0..7}
  • sharding-jdbc 报错:
java.lang.IllegalArgumentException: jdbcUrl is required with driverClassName.

添加配置:

# ds代替为 sharding.jdbc.datasource.names 对应的值。自行修改。
sharding.jdbc.datasource.ds.jdbcUrl=jdbc:mysql://mysql的ip:端口号/库名?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true

参考资料

https://www.cnblogs.com/badboy200800/p/9790395.html

https://mp.weixin.qq.com/s/nUtHelMW_az_9YT38dn8WQ