后端开发: 数据库分库分表sharding-jdbc


demo源码 https://github.com/wzjwhut/spring-jdbc-sharding

原理和开源库

原理: 通过解析sql语句, 根据分库分表的规则,替换掉表名和库名
文档资料 https://shardingsphere.apache.org/document/current/en/manual/sharding-jdbc/usage/sharding/
对应的SQL解析器为SQLParsingEngine, 有兴趣可以细看一下

使用方法

准备工具

安装mysql, 准备好需要的数据库和表
https://github.com/wzjwhut/spring-jdbc-sharding/blob/master/src/main/resources/config.sql

maven依赖

        <dependency>
            <groupId>com.dangdang</groupId>
            <artifactId>sharding-jdbc-core</artifactId>
            <version>1.5.4.1</version>
        </dependency>

配置DataSource

使用jdbc-sharding的data source将spring的data source包裹起来


import com.dangdang.ddframe.rdb.sharding.api.ShardingDataSourceFactory;
import com.dangdang.ddframe.rdb.sharding.api.rule.DataSourceRule;
import com.dangdang.ddframe.rdb.sharding.api.rule.ShardingRule;
import com.dangdang.ddframe.rdb.sharding.api.rule.TableRule;
import com.dangdang.ddframe.rdb.sharding.api.strategy.database.DatabaseShardingStrategy;
import com.dangdang.ddframe.rdb.sharding.api.strategy.table.TableShardingStrategy;
import com.dangdang.ddframe.rdb.sharding.keygen.DefaultKeyGenerator;
import com.dangdang.ddframe.rdb.sharding.keygen.KeyGenerator;
import com.mysql.jdbc.Driver;
import com.zaxxer.hikari.HikariDataSource;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;


@Configuration
public class DataSourceConfiguration {
    private final static Logger log = LogManager.getLogger(DataSourceConfiguration.class);

    @Bean
    public DataSource getDataSource() throws SQLException {
        return buildDataSource();
    }

    private DataSource buildDataSource() throws SQLException {
        // 设置分库映射
        Map<String, DataSource> dataSourceMap = new HashMap<>(2);
        //分库列表
        dataSourceMap.put("database0", createDataSource("database0"));
        dataSourceMap.put("database1", createDataSource("database1"));
        //默认库
        DataSourceRule dataSourceRule = new DataSourceRule(dataSourceMap, "database0");

        TableRule orderTableRule = TableRule.builder("person")
                .actualTables(Arrays.asList("person_0", "person_1"))
                .dataSourceRule(dataSourceRule)
                .build();

        //分库分表策略
        ShardingRule shardingRule = ShardingRule.builder()
                .dataSourceRule(dataSourceRule)
                .tableRules(Arrays.asList(orderTableRule))
                .databaseShardingStrategy(new DatabaseShardingStrategy("id", new ModuleDatabaseShardingAlgorithm()))
                .tableShardingStrategy(new TableShardingStrategy("id", new ModuleTableShardingAlgorithm())).build();
        DataSource dataSource = ShardingDataSourceFactory.createDataSource(shardingRule);
        return dataSource;
    }

    private static DataSource createDataSource(final String dataSourceName) {
        log.info("create data source: {}", dataSourceName);
        HikariDataSource result = new HikariDataSource();
        result.setJdbcUrl(String.format("jdbc:mysql://localhost:3306/%s", dataSourceName));
        result.setDriverClassName(Driver.class.getName());
        result.setUsername("root");
        result.setPassword("123456");
        return result;
    }

//    @Bean
//    public KeyGenerator keyGenerator() {
//        return new DefaultKeyGenerator();
//    }
}

配置分库

import com.dangdang.ddframe.rdb.sharding.api.ShardingValue;
import com.dangdang.ddframe.rdb.sharding.api.strategy.database.MultipleKeysDatabaseShardingAlgorithm;
import com.dangdang.ddframe.rdb.sharding.api.strategy.database.SingleKeyDatabaseShardingAlgorithm;
import com.google.common.collect.Range;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.Collection;
import java.util.LinkedHashSet;


/**
 * 分库算法
 * 多键分片 {@link com.dangdang.ddframe.rdb.sharding.api.strategy.database.MultipleKeysDatabaseShardingAlgorithm}
 */
public class ModuleDatabaseShardingAlgorithm implements SingleKeyDatabaseShardingAlgorithm<Long> {
    private final static Logger log = LogManager.getLogger(ModuleDatabaseShardingAlgorithm.class);

    //sql出现等于条件时,执行
    @Override
    public String doEqualSharding(Collection<String> dbNames, ShardingValue<Long> shardingValue) {
        //数据库列表,本例为[database0, database1]
        log.info("[doEqualSharding] availableTargetNames: {}", dbNames);

        //分库字段的值,本例为person的id值
        log.info("[doEqualSharding] shardingValue.getValue(): {}", shardingValue.getValue());

        //分库策略的类型. 本例为SINGLE
        log.info("[doEqualSharding] shardingValue.getType(): {}", shardingValue.getType());

        //分库字段的名称, 本例为id
        log.info("[doEqualSharding] shardingValue.getColumnName(): {}", shardingValue.getColumnName());

        //表的逻辑名称, 本例为person
        log.info("[doEqualSharding] shardingValue.getLogicTableName(): {}", shardingValue.getLogicTableName());

        //根据字段的值,选出一个数据库
        //临时写法
        for (String dbName : dbNames) {
            if (dbName.endsWith(String.valueOf(shardingValue.getValue() % 2 ))) {
                return dbName;
            }
        }
        throw new IllegalArgumentException();
    }

    //sql的出现的in条件时,执行
    @Override
    public Collection<String> doInSharding(Collection<String> dbNames, ShardingValue<Long> shardingValue) {
        log.info("[doInSharding] values: {}", shardingValue.getValues());
        Collection<String> result = new LinkedHashSet<>(dbNames.size());
        for (Long value : shardingValue.getValues()) {
            for (String dbName : dbNames) {
                if (dbName.endsWith(value % 2 + "")) {
                    result.add(dbName);
                }
            }
        }
        return result;
    }

    //sql的出现between条件时,执行
    @Override
    public Collection<String> doBetweenSharding(Collection<String> dbNames,
                                                ShardingValue<Long> shardingValue) {
        log.info("[doBetweenSharding] values: {}", shardingValue.getValueRange());
        Collection<String> result = new LinkedHashSet<>(dbNames.size());
        Range<Long> range = shardingValue.getValueRange();
        for (Long i = range.lowerEndpoint(); i <= range.upperEndpoint(); i++) {
            for (String dbName : dbNames) {
                if (dbName.endsWith(i % 2 + "")) {
                    result.add(dbName);
                }
            }
        }
        return result;
    }
}

配置分表

和分库的算法几乎一样。

import com.dangdang.ddframe.rdb.sharding.api.ShardingValue;
import com.dangdang.ddframe.rdb.sharding.api.strategy.table.SingleKeyTableShardingAlgorithm;
import com.google.common.collect.Range;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.Collection;
import java.util.LinkedHashSet;

public final class ModuleTableShardingAlgorithm implements SingleKeyTableShardingAlgorithm<Long> {
    private final static Logger log = LogManager.getLogger(ModuleTableShardingAlgorithm.class);

    @Override
    public String doEqualSharding(final Collection<String> tableNames, final ShardingValue<Long> shardingValue) {
        log.info("doEqualSharding, tables: {}", tableNames);
        log.info("[doEqualSharding] shardingValue.getValue(): {}", shardingValue.getValue());
        log.info("[doEqualSharding] shardingValue.getType(): {}", shardingValue.getType());
        log.info("[doEqualSharding] shardingValue.getColumnName(): {}", shardingValue.getColumnName());
        log.info("[doEqualSharding] shardingValue.getLogicTableName(): {}", shardingValue.getLogicTableName());

        //临时写法
        for (String tableName : tableNames) {
            log.info("[doEqualSharding] availableTargetNames: {}", tableName);
            if (tableName.endsWith(shardingValue.getValue() % 2 + "")) {
                return tableName;
            }
        }
        throw new IllegalArgumentException();
    }

    @Override
    public Collection<String> doInSharding(final Collection<String> tableNames, final ShardingValue<Long> shardingValue) {
        log.info("doInSharding");
        Collection<String> result = new LinkedHashSet<>(tableNames.size());
        for (Long value : shardingValue.getValues()) {
            for (String tableName : tableNames) {
                if (tableName.endsWith(value % 2 + "")) {
                    result.add(tableName);
                }
            }
        }
        return result;
    }

    @Override
    public Collection<String> doBetweenSharding(final Collection<String> tableNames, final ShardingValue<Long> shardingValue) {
        log.info("doBetweenSharding");
        Collection<String> result = new LinkedHashSet<>(tableNames.size());
        Range<Long> range = shardingValue.getValueRange();
        for (Long i = range.lowerEndpoint(); i <= range.upperEndpoint(); i++) {
            for (String tableName : tableNames) {
                if (tableName.endsWith(i % 2 + "")) {
                    result.add(tableName);
                }
            }
        }
        return result;
    }
}

完整的源代码

demo源码 https://github.com/wzjwhut/spring-jdbc-sharding

猜你喜欢

转载自blog.csdn.net/wzj_whut/article/details/87564783