1.分库分表基本概念

垂直分库:垂直分库就相当于应用中有两个主数据源,所有的业务表分布在两个数据库中,避免单库压力太大。

水平分库:水平的意思就是将一张表的数据放置不同的数据库中,数据库a有t_order,数据库b也有t_order表,两张表结构没有差异。记录依据逻辑分别插入不同数据库中。如订单id是奇数的写入数据库a的t_order表中,为偶数的写入数据库b中的t_order表中。这样做的实际意义不大,实际应用中更倾向于水平分表。

垂直分表:垂直分表是指将一个大表依据业务逻辑拆分出一部分字段,新建一张表,如t_product表拆分出一张t_product_detail表,两张表在同一个库中,所以在表结构设计过程中就可以确定的。

水平分表:水平分表同水平分库类似,但是在同一个库中的,如t_order表依据规则分为t_order_1,t_order_2,依据id奇偶性来区分。

广播表:当应用中设置了垂直分库后,一些表带着公共属性的,例如地址表t_address,有可能会在每个库中都被使用,如果仅仅在一个库中有这张表,将会非常不方便,这个表在每个库中都有而且数据实时更新,那就非常方便了,sharding-jdbc支持广播表,当在广播表中crud时候,会在所有库中更新广播表。

绑定表:当两张表都设置了水平分表后,如t_order_1,t_order_2和t_order_detail_1,t_order_detail_2后,要对t_order和t_order_detail进行left join的时候,就会分别对t_order_1 t_order_detail_1;t_order_2 t_order_detail_1,t_order_2 t_order_detail_2分别left join 查询,如果设置了关联表,那只需要进行t_order_1 t_order_detail_1和t_order_2 t_order_detail_2两次查询。

2.sharding-jdbc

sharding-sphere是由三大模块组成,我主要关注sharding-jdbc。目前已经贡献给Apache开源基金会了,官方文档也有详细的使用demo,sharding-jdbc设置主要有yml,java代码方式,我个人认为分库分表的这些配置基本上不会变化太大,我个人比较喜欢将这些配置都写在java代码上,这样做也会有缺点,当新增表时候就需要改代码,也可以设置默认的数据源来解决此问题,所以也希望能够java代码和配置文件同时生效,尝试过了,发现不能共存。

3.sharding-jdbc使用案例(详细使用demo,请参考官方demo https://github.com/sharding-s...

a.引入依赖(版本号为4.0.0-RC3)
<dependency>
    <groupId>org.apache.shardingsphere</groupId>
    <artifactId>sharding-jdbc-core</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.shardingsphere</groupId>
    <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.shardingsphere</groupId>
    <artifactId>sharding-core-api</artifactId>
</dependency>

b.配置数据源,数据库连接池使用的是hikari

c.sharding-jdbc核心配置类
package com.xiayu.config; //打包


import com.google.common.collect.Lists;
import com.xiayu.constants.DataSourceConstants;
import com.zaxxer.hikari.HikariDataSource;
import lombok.Setter;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.shardingsphere.api.config.masterslave.MasterSlaveRuleConfiguration;
import org.apache.shardingsphere.api.config.sharding.KeyGeneratorConfiguration;
import org.apache.shardingsphere.api.config.sharding.ShardingRuleConfiguration;
import org.apache.shardingsphere.api.config.sharding.TableRuleConfiguration;
import org.apache.shardingsphere.api.config.sharding.strategy.InlineShardingStrategyConfiguration;
import org.apache.shardingsphere.shardingjdbc.api.ShardingDataSourceFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

@Setter //必须,否则@ConfigurationProperties会注解失败
@Configuration
@ConfigurationProperties(prefix = DataSourceConstants.SHARDING_DATASOURCE_PREFIX) //指定前缀
@EnableConfigurationProperties //开启ConfigurationProperties
@MapperScan(basePackages = { DataSourceConstants.MAPPER_BASEPACKAGE }, sqlSessionFactoryRef = "shardingSqlSessionFactory") //指定mapper位置,指定sqlSessionFactoryRef
public class ShardingDatasourceConfig {

    private String url;

    private String username;

    private String password;

    public ShardingDatasourceConfig() {
    }

    private DataSource getDataSource(String datasourceName){
        HikariDataSource dataSource = new HikariDataSource();
        String jdbcUrl = String.format(url,datasourceName); //获取到真正的jdbcUrl
        dataSource.setJdbcUrl(jdbcUrl);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        dataSource.setDriverClassName(DataSourceConstants.DRIVER_CLASS_NAME);
        return dataSource;
    }

    @Bean("shardingDataSource")
    @Primary
    public DataSource shardingDataSource() throws SQLException {

        ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();
        shardingRuleConfig.getTableRuleConfigs().add(getUserTableRuleConfiguration());//表进行分库并进行分表
        shardingRuleConfig.getTableRuleConfigs().add(getUserDetailTableRuleConfiguration()); //表进行分库并进行分表
        shardingRuleConfig.getTableRuleConfigs().add(new TableRuleConfiguration("t_order","ds_1.t_order"));//垂直分库
        shardingRuleConfig.getTableRuleConfigs().add(new TableRuleConfiguration("t_business","ds_2.t_business"));//垂直分库
        shardingRuleConfig.getBindingTableGroups().add("t_user, t_user_detail"); //绑定表
        shardingRuleConfig.setMasterSlaveRuleConfigs(getMasterSlaveRuleConfigurations()); //主从设置 一主多从
        shardingRuleConfig.setDefaultDataSourceName("ds_1"); //设置默认的数据源
        shardingRuleConfig.getBroadcastTables().add("t_address"); //设置广播表
        DataSource dataSource = ShardingDataSourceFactory.createDataSource(getDataSourceMap(), shardingRuleConfig,new Properties());
        return dataSource;
    }

    private TableRuleConfiguration getUserTableRuleConfiguration(){
        TableRuleConfiguration userTableRuleConfiguration = new TableRuleConfiguration("t_user","ds_${1..2}.t_user_${1..2}");
        userTableRuleConfiguration.setKeyGeneratorConfig(new KeyGeneratorConfiguration("SNOWFLAKE", "id", getProperties()));
        userTableRuleConfiguration.setDatabaseShardingStrategyConfig(new InlineShardingStrategyConfiguration("id", "ds_${id % 2 + 1}"));
        userTableRuleConfiguration.setTableShardingStrategyConfig(new InlineShardingStrategyConfiguration("id", "t_user_${id % 2 + 1}"));
        return userTableRuleConfiguration;
    }

    private TableRuleConfiguration getUserDetailTableRuleConfiguration(){
        TableRuleConfiguration userDetailTableRuleConfiguration = new TableRuleConfiguration("t_user_detail","ds_${1..2}.t_user_detatil_${1..2}");
        userDetailTableRuleConfiguration.setKeyGeneratorConfig(new KeyGeneratorConfiguration("SNOWFLAKE", "id", getProperties()));
        userDetailTableRuleConfiguration.setDatabaseShardingStrategyConfig(new InlineShardingStrategyConfiguration("id", "ds_${id % 2 + 1}"));
        userDetailTableRuleConfiguration.setTableShardingStrategyConfig(new InlineShardingStrategyConfiguration("id", "t_user_detail_${id % 2 + 1}"));
        return userDetailTableRuleConfiguration;
    }

    private Properties getProperties() {
        Properties result = new Properties();
        result.setProperty("worker.id", "123");
        return result;
    }

    private Map<String,DataSource> getDataSourceMap(){
        Map<String,DataSource> dataSourceMap = new HashMap<>();
        DataSource primaryDatasourceOne = getDataSource("1");
        DataSource firstSlaveDatasourceOne = getDataSource("2");
        DataSource secondSlaveDatasourceOne = getDataSource("3");
        DataSource primaryDatasourceTwo = getDataSource("4");
        DataSource firstSlaveDatasourceTwo = getDataSource("5");
        DataSource secondSlaveDatasourceTwo = getDataSource("6");

        dataSourceMap.put("ds_master_1",primaryDatasourceOne);
        dataSourceMap.put("ds_slave_1_1",firstSlaveDatasourceOne);
        dataSourceMap.put("ds_slave_1_2",secondSlaveDatasourceOne);

        dataSourceMap.put("ds_master_2",primaryDatasourceTwo);
        dataSourceMap.put("ds_slave_2_1",firstSlaveDatasourceTwo);
        dataSourceMap.put("ds_slave_2_2",secondSlaveDatasourceTwo);
        return dataSourceMap;
    }

    private List<MasterSlaveRuleConfiguration> getMasterSlaveRuleConfigurations(){

        MasterSlaveRuleConfiguration masterSlaveRuleConfigOne = new MasterSlaveRuleConfiguration("ds_1", "ds_master_1", Arrays.asList("ds_slave_1_1", "ds_slave_1_2"));
        MasterSlaveRuleConfiguration masterSlaveRuleConfigTwo = new MasterSlaveRuleConfiguration("ds_2", "ds_master_2", Arrays.asList("ds_slave_2_1", "ds_slave_2_2"));

        return Lists.newArrayList(masterSlaveRuleConfigOne,masterSlaveRuleConfigTwo);
    }

    @Bean(name = "primaryTransactionManager")
    public DataSourceTransactionManager transactionManager(@Qualifier("shardingDataSource")DataSource shardingDataSource) throws SQLException{
        return new DataSourceTransactionManager(shardingDataSource);
    }

    @Bean(name = "shardingSqlSessionFactory")
    public SqlSessionFactory primarySqlSessionFactory(@Qualifier("shardingDataSource") DataSource primaryDataSource) throws Exception{
        final SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
        sessionFactoryBean.setDataSource(primaryDataSource);
        return sessionFactoryBean.getObject();
    }
}
配置的基本思路
主从配置
a.获取数据源,如果设置两主三从,那就要获取6个数据源。
b.将数据源封装成map对象,key为数据源名称(在MasterSlaveRuleConfiguration中指定是主还是从),value为dataSource
c.创建MasterSlaveRuleConfigration对象,配置数据源之间主从关系
MasterSlaveRuleConfiguration masterSlaveRuleConfigOne = new MasterSlaveRuleConfiguration("ds_1", "ds_master_1", Arrays.asList("ds_slave_1_1", "ds_slave_1_2"));
ds_1为配置的名称,ds_master_1为主数据源的名称,ds_slave_1_1,ds_slave_1_2为从数据源的名称
d.将MasterSlaveRuleConfiguration转换成list
e.在ShardingRuleConfiguration中设置masterSlaveRuleConfigs为d步骤中的list即可
shardingRuleConfig.setMasterSlaveRuleConfigs(getMasterSlaveRuleConfigurations()); //主从设置 一主多从
主从数据库之间数据同步交由数据库去实现

水平分表设置
TableRuleConfiguration userTableRuleConfiguration = new TableRuleConfiguration("t_user","ds_${1..2}.t_user_${1..2}"); //指定逻辑表与物理表
userTableRuleConfiguration.setKeyGeneratorConfig(new KeyGeneratorConfiguration("SNOWFLAKE", "id", getProperties())); //id生成使用雪花算法
userTableRuleConfiguration.setDatabaseShardingStrategyConfig(new InlineShardingStrategyConfiguration("id", "ds_${id % 2 + 1}")); //定义分库规则
userTableRuleConfiguration.setTableShardingStrategyConfig(new InlineShardingStrategyConfiguration("id", "t_user_${id % 2 + 1}")); //定义分表规则
返回userTableRuleConfiguration,在ShardingRuleConfiguration中tableRuleConfigs中添加该对象,如果还想水平分别的表,增加类似的配置即可。

绑定表配置
假如两张表都进行了水平分表,那么就可以设置绑定关系了
a.直接在ShardingRuleConfigurations 中设置两张逻辑表的绑定关系即可
shardingRuleConfig.getBindingTableGroups().add("t_user, t_user_detail"); //绑定表

广播表
shardingRuleConfig.getBroadcastTables().add("t_address"); //将地址表设置广播表

设置默认的数据源
当表设计一个库中,类似没有使用sharding-jdbc的效果,defaultDataSourceName,在ShardingRuleConfigurations中配置默认的数据源即可
shardingRuleConfig.setDefaultDataSourceName("ds_1"); //设置默认的数据源
ds_1代表MasterSlaveRuleConfiguration配置的名称。

ShardingRuleConfigurations中还有这些属性
ShardingStrategyConfiguration defaultDatabaseShardingStrategyConfig;
ShardingStrategyConfiguration defaultTableShardingStrategyConfig;
KeyGeneratorConfiguration defaultKeyGeneratorConfig;
EncryptRuleConfiguration encryptRuleConfig;

4.sharding-jdbc基本原理

sharding-jdbc基本原理
1.解析: 解析定义的sql语句
2.路由:依据定义的规则修改部分sql
3.改写: 拼接成可执行的sql
4.执行: 执行sql,获取到结果集
5.归并结果:将所有结果集合并,返回结果

未完待续...

03-05 22:29