使用 Sharding-Jdbc 實現分庫分表、讀寫分離(未完待續)

先貼代碼, 回頭講解。未完待續java

規劃

實現分庫分表、讀寫分離。
準備兩張表:svc_order, svc_order_item mysql

結構以下,能夠想象成是未實施分庫分表以前的數據庫結構:算法

CREATE DATABASE IF NOT EXISTS DATABASE;
USE DATABASE;

CREATE TABLE IF NOT EXISTS `svc_order` (
  `order_id` bigint(64) NOT NULL AUTO_INCREMENT,
  `user_id` bigint(64) DEFAULT NULL,
  `order_name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS  `svc_order_item` (
  `order_item_id` bigint(64) NOT NULL AUTO_INCREMENT,
  `order_id` bigint(64) DEFAULT NULL,
  `order_item_name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`order_item_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

必定要對原有結構作到心中有數,重複表述一下:
咱們原有一個數據庫叫 database, 裏面有兩張表,分別是 svc_order 訂單表和 svc_order_item 訂單明細表,每一個表都有一個主鍵ID 、用戶ID(user_id) 和 名稱。spring

計劃:
svc_order 表按照 user_id 分庫,按照主鍵 order_id 分片
svc_order_item 表按照 user_id 分庫,按照主鍵 order_item_id 分片sql

原有庫分紅兩個庫,使用 user_id 對 2 取模,例如 user_id = 2 的存入 0 庫,user_id = 3 的存入 1 庫。
原有表分紅兩個表,svc_order 訂單表使用 order_id 對 2 取模,svc_order_item 表使用 order_item_id 對 2 取模,例如 order_id = 2 的存入 0 表,order_id = 3 的存入 1 表。數據庫

讀寫分離設置爲 1 個寫庫,2 個讀庫。api

爲了便於理解,咱們定義經常使用名詞:ide

  • 邏輯表:指的就是未實施分表以前的表名。例如 svc_order 就是邏輯表名。工具

  • 虛擬表、物理表:指的是分表後真實存在的表名。上面咱們說到存到 0 表、 1 表就是虛擬表,爲了便於理解,咱們把邏輯表名加在前面,好比 svc_order 的兩個虛表咱們能夠命名爲 svc_order_0svc_order_1測試

順便說明一下數據庫名,咱們原有 database, 因爲要分紅兩個庫,咱們能夠命名爲 database_0database_1,因爲,須要讀寫分離(1帶2),至關於又多了四個數據庫,至此,咱們的數據庫應該有 write_database_0,read_0_database_0,read_1_database_0,write_database_1,read_0_database_1,read_1_database_1

數據庫

init.sql

DROP DATABASE IF EXISTS write_database_0;
DROP DATABASE IF EXISTS read_0_database_0;
DROP DATABASE IF EXISTS read_1_database_0;
DROP DATABASE IF EXISTS write_database_1;
DROP DATABASE IF EXISTS read_0_database_1;
DROP DATABASE IF EXISTS read_1_database_1;

CREATE DATABASE IF NOT EXISTS write_database_0;
USE write_database_0;

CREATE TABLE IF NOT EXISTS `svc_order_0` (
`order_id` bigint(64) NOT NULL AUTO_INCREMENT,
`user_id` bigint(64) DEFAULT NULL,
`order_name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS  `svc_order_1` (
`order_id` bigint(64) NOT NULL AUTO_INCREMENT,
`user_id` bigint(64) DEFAULT NULL,
`order_name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS  `svc_order_item_0` (
`order_item_id` bigint(64) NOT NULL AUTO_INCREMENT,
`order_id` bigint(64) DEFAULT NULL,
`order_item_name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`order_item_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS  `svc_order_item_1` (
`order_item_id` bigint(64) NOT NULL AUTO_INCREMENT,
`order_id` bigint(64) DEFAULT NULL,
`order_item_name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`order_item_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE DATABASE IF NOT EXISTS read_1_database_0;
USE read_1_database_0;

CREATE TABLE IF NOT EXISTS `svc_order_0` (
`order_id` bigint(64) NOT NULL AUTO_INCREMENT,
`user_id` bigint(64) DEFAULT NULL,
`order_name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS  `svc_order_1` (
`order_id` bigint(64) NOT NULL AUTO_INCREMENT,
`user_id` bigint(64) DEFAULT NULL,
`order_name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS  `svc_order_item_0` (
`order_item_id` bigint(64) NOT NULL AUTO_INCREMENT,
`order_id` bigint(64) DEFAULT NULL,
`order_item_name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`order_item_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS  `svc_order_item_1` (
`order_item_id` bigint(64) NOT NULL AUTO_INCREMENT,
`order_id` bigint(64) DEFAULT NULL,
`order_item_name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`order_item_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE DATABASE IF NOT EXISTS read_0_database_0;
USE read_0_database_0;

CREATE TABLE IF NOT EXISTS `svc_order_0` (
`order_id` bigint(64) NOT NULL AUTO_INCREMENT,
`user_id` bigint(64) DEFAULT NULL,
`order_name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS  `svc_order_1` (
`order_id` bigint(64) NOT NULL AUTO_INCREMENT,
`user_id` bigint(64) DEFAULT NULL,
`order_name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS  `svc_order_item_0` (
`order_item_id` bigint(64) NOT NULL AUTO_INCREMENT,
`order_id` bigint(64) DEFAULT NULL,
`order_item_name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`order_item_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS  `svc_order_item_1` (
`order_item_id` bigint(64) NOT NULL AUTO_INCREMENT,
`order_id` bigint(64) DEFAULT NULL,
`order_item_name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`order_item_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE DATABASE IF NOT EXISTS write_database_1;
USE write_database_1;

CREATE TABLE IF NOT EXISTS `svc_order_0` (
`order_id` bigint(64) NOT NULL AUTO_INCREMENT,
`user_id` bigint(64) DEFAULT NULL,
`order_name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS  `svc_order_1` (
`order_id` bigint(64) NOT NULL AUTO_INCREMENT,
`user_id` bigint(64) DEFAULT NULL,
`order_name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS  `svc_order_item_0` (
`order_item_id` bigint(64) NOT NULL AUTO_INCREMENT,
`order_id` bigint(64) DEFAULT NULL,
`order_item_name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`order_item_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS  `svc_order_item_1` (
`order_item_id` bigint(64) NOT NULL AUTO_INCREMENT,
`order_id` bigint(64) DEFAULT NULL,
`order_item_name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`order_item_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE DATABASE IF NOT EXISTS read_0_database_1;
USE read_0_database_1;

CREATE TABLE IF NOT EXISTS `svc_order_0` (
`order_id` bigint(64) NOT NULL AUTO_INCREMENT,
`user_id` bigint(64) DEFAULT NULL,
`order_name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS  `svc_order_1` (
`order_id` bigint(64) NOT NULL AUTO_INCREMENT,
`user_id` bigint(64) DEFAULT NULL,
`order_name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS  `svc_order_item_0` (
`order_item_id` bigint(64) NOT NULL AUTO_INCREMENT,
`order_id` bigint(64) DEFAULT NULL,
`order_item_name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`order_item_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS  `svc_order_item_1` (
`order_item_id` bigint(64) NOT NULL AUTO_INCREMENT,
`order_id` bigint(64) DEFAULT NULL,
`order_item_name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`order_item_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE DATABASE IF NOT EXISTS read_1_database_1;
USE read_1_database_1;

CREATE TABLE IF NOT EXISTS `svc_order_0` (
`order_id` bigint(64) NOT NULL AUTO_INCREMENT,
`user_id` bigint(64) DEFAULT NULL,
`order_name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS  `svc_order_1` (
`order_id` bigint(64) NOT NULL AUTO_INCREMENT,
`user_id` bigint(64) DEFAULT NULL,
`order_name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS  `svc_order_item_0` (
`order_item_id` bigint(64) NOT NULL AUTO_INCREMENT,
`order_id` bigint(64) DEFAULT NULL,
`order_item_name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`order_item_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS  `svc_order_item_1` (
`order_item_id` bigint(64) NOT NULL AUTO_INCREMENT,
`order_id` bigint(64) DEFAULT NULL,
`order_item_name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`order_item_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

依賴

pom.xml

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.5</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>6.0.6</version>
</dependency>
<dependency>
    <groupId>com.dangdang</groupId>
    <artifactId>sharding-jdbc-core</artifactId>
    <version>1.5.4.1</version>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.16.18</version>
    <scope>provided</scope>
</dependency>

配置

ShardingJdbcConfig.java

package com.junbaor.sharding.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.dangdang.ddframe.rdb.sharding.api.MasterSlaveDataSourceFactory;
import com.dangdang.ddframe.rdb.sharding.api.ShardingDataSourceFactory;
import com.dangdang.ddframe.rdb.sharding.api.rule.BindingTableRule;
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.slave.MasterSlaveLoadBalanceStrategyType;
import com.dangdang.ddframe.rdb.sharding.api.strategy.table.TableShardingStrategy;
import com.dangdang.ddframe.rdb.sharding.config.ShardingPropertiesConstant;
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;
import java.util.Properties;


@Configuration
public class ShardingJdbcConfig {

    @Bean
    public DataSource buildDataSource() throws SQLException {
        /*讀寫分離 start */
        DataSource writeDatabase0 = createDataSource("write_database_0");
        Map<String, DataSource> slave0DataBase = new HashMap<>(2);
        slave0DataBase.put("read0_database_0", createDataSource("read0_database_0"));
        slave0DataBase.put("read1_database_0", createDataSource("read1_database_0"));
        DataSource database0 = MasterSlaveDataSourceFactory.createDataSource("write_database_0", "write_database_0", writeDatabase0, slave0DataBase, MasterSlaveLoadBalanceStrategyType.ROUND_ROBIN);

        DataSource writeDatabase1 = createDataSource("write_database_1");
        Map<String, DataSource> slave1DataBase = new HashMap<>(2);
        slave1DataBase.put("read0_database_1", createDataSource("read0_database_1"));
        slave1DataBase.put("read1_database_1", createDataSource("read1_database_1"));
        DataSource database1 = MasterSlaveDataSourceFactory.createDataSource("write_database_1", "write_database_1", writeDatabase1, slave1DataBase, MasterSlaveLoadBalanceStrategyType.ROUND_ROBIN);
        /*讀寫分離 end */

        Map<String, DataSource> dataSourceMap = new HashMap<>(2);
        dataSourceMap.put("database_0", database0);
        dataSourceMap.put("database_1", database1);

        /*分庫分表 start*/
        DataSourceRule dataSourceRule = new DataSourceRule(dataSourceMap, "database_0");

        TableRule orderTableRule = TableRule.builder("svc_order")
                .actualTables(Arrays.asList("svc_order_0", "svc_order_1"))
                .generateKeyColumn("order_id", KeyGenerate.class)
                .dataSourceRule(dataSourceRule)
                .build();

        TableRule orderItemTableRule = TableRule.builder("svc_order_item")
                .actualTables(Arrays.asList("svc_order_item_0", "svc_order_item_1"))
                .generateKeyColumn("order_item_id",KeyGenerate.class)
                .dataSourceRule(dataSourceRule)
                .build();

        BindingTableRule bindOrderAndOrderItem = new BindingTableRule(Arrays.asList(orderTableRule, orderItemTableRule));

        ShardingRule shardingRule = ShardingRule.builder()
                .dataSourceRule(dataSourceRule)
                .tableRules(Arrays.asList(orderTableRule, orderItemTableRule))
                .databaseShardingStrategy(new DatabaseShardingStrategy(Arrays.asList("user_id"), new DbSharding()))
                .tableShardingStrategy(new TableShardingStrategy(Arrays.asList("order_id"), new TableSharding()))
                .bindingTableRules(Arrays.asList(bindOrderAndOrderItem))
                .build();
        /*分庫分表 end*/

        Properties properties = new Properties();
        properties.setProperty(ShardingPropertiesConstant.SQL_SHOW.getKey(), "true");
        DataSource dataSource = ShardingDataSourceFactory.createDataSource(shardingRule, properties);

        return dataSource;
    }

    private static DataSource createDataSource(final String dataSourceName) {
        DruidDataSource result = new DruidDataSource();
        result.setUrl(String.format("jdbc:mysql://127.0.0.1:3306/%s?useUnicode=true&characterEncoding=utf-8&useSSL=false", dataSourceName));
        result.setUsername("root");
        result.setPassword("root");
        return result;
    }

}

分片工具

AppUtils.java

package com.junbaor.sharding.util;

import com.dangdang.ddframe.rdb.sharding.api.ShardingValue;

import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;

public class AppUtils {

    public static Collection<String> sharding(Collection<String> availableTargetNames, Collection<ShardingValue<?>> shardingValues) {
        long shardingvalue = 0;

        Iterator<ShardingValue<?>> iterator = shardingValues.iterator();
        if (iterator.hasNext()) {
            shardingvalue = ((Long) iterator.next().getValue()).intValue();
        }

        for (String tableName : availableTargetNames) {
            if (tableName.endsWith((shardingvalue % 2) + "")) {
                return Arrays.asList(tableName);
            }
        }
        return null;
    }
}

分庫算法

DbSharding.java

package com.junbaor.sharding.config;

import com.dangdang.ddframe.rdb.sharding.api.ShardingValue;
import com.dangdang.ddframe.rdb.sharding.api.strategy.database.MultipleKeysDatabaseShardingAlgorithm;
import com.junbaor.sharding.util.AppUtils;

import java.util.Collection;

/**
 * 利用多分片鍵接口實現單鍵分片算法
 */
public class DbSharding implements MultipleKeysDatabaseShardingAlgorithm {

    @Override
    public Collection<String> doSharding(Collection<String> availableTargetNames, Collection<ShardingValue<?>> shardingValues) {
        Collection<String> sharding = AppUtils.sharding(availableTargetNames, shardingValues);
        if (sharding != null) {
            return sharding;
        }
        return null;
    }
}

分表算法

TableSharding.java

package com.junbaor.sharding.config;

import com.dangdang.ddframe.rdb.sharding.api.ShardingValue;
import com.dangdang.ddframe.rdb.sharding.api.strategy.table.MultipleKeysTableShardingAlgorithm;
import com.junbaor.sharding.util.AppUtils;

import java.util.Collection;

/**
 * 利用多分片鍵接口實現單鍵分片算法
 */
public class TableSharding implements MultipleKeysTableShardingAlgorithm {

    @Override
    public Collection<String> doSharding(Collection<String> availableTargetNames, Collection<ShardingValue<?>> shardingValues) {
        Collection<String> sharding = AppUtils.sharding(availableTargetNames, shardingValues);
        if (sharding != null) {
            return sharding;
        }
        return null;
    }

}

主鍵生成器

KeyGenerate.java 僅供單機測試

package com.junbaor.sharding.config;

import com.dangdang.ddframe.rdb.sharding.keygen.KeyGenerator;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.atomic.AtomicLong;

@Slf4j
public class KeyGenerate implements KeyGenerator {
    
    private static AtomicLong atomicInteger = new AtomicLong(1);

    @Override
    public Number generateKey() {
        long nextId = atomicInteger.addAndGet(1);
        log.info("nextId:{}", nextId);
        return nextId;
    }
}
相關文章
相關標籤/搜索