`
dwj147258
  • 浏览: 194676 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

shrding-jdbc分库分表再解

阅读更多

阅读本指南前,请先阅读快速入门。本文档使用更复杂的场景进一步介绍分库分表能力

1.数据库环境

CREATE TABLE IF NOT EXISTS t_order_x (
  order_id INTNOTNULL,
  user_id  INTNOTNULL,
  PRIMARY KEY (order_id)
);
CREATE TABLE IF NOT EXISTS t_order_item_x (
  item_id  INTNOTNULL,
  order_id INTNOTNULL,
  user_id  INTNOTNULL,
  PRIMARY KEY (item_id)
);

2.逻辑表与实际表映射关系

  • 均匀分布

db0
  ├── t_order_0 
  └── t_order_1 
db1
  ├── t_order_0 
  └── t_order_1

表规则可以使用默认的配置

 ShardTableRule orderTableRule = ShardTableRule.builder("t_order").actualTables(Arrays.asList("t_order_0", "t_order_1")).dataSourceRule(dataSourceRule).build();
  • 自定义分布

db0
  ├── t_order_0 
  └── t_order_1 
db1
  ├── t_order_2
  ├── t_order_3
  └── t_order_4

表规则可以指定每张表在数据源中的分布情况

ShardTableRule orderTableRule = ShardTableRule .builder("t_order").actualTables(Arrays.asList("db0.t_order_0", "db0.t_order_1", "db1.t_order_2", "db1.t_order_3", "db1.t_order_4")).dataSourceRule(dataSourceRule).build();

本教程采用的数据分布例子

db0
  ├── t_order_0               user_id为偶数   order_id为偶数
  ├── t_order_1               user_id为偶数   order_id为奇数
  ├── t_order_item_0          user_id为偶数   order_id为偶数
  └── t_order_item_1          user_id为偶数   order_id为奇数
db1
  ├── t_order_0               user_id为奇数   order_id为偶数
  ├── t_order_1               user_id为奇数   order_id为奇数
  ├── t_order_item_0          user_id为奇数   order_id为偶数
  └── t_order_item_1          user_id为奇数   order_id为奇数

3.逻辑表与实际表

     配置分库分表的目的是将原有一张表的数据分散到不同库不同表中,且不改变原有SQL语句的情况下来使用这一张表。那么从一张表到多张的映射关系需要使用逻辑表与实际表这两种概念。下面通过一个例子来解释一下。假设在使用PreparedStatement访问数据库,SQL如下:

select * from t_order where user_id = ? and order_id = ?;

当user_id=0且order=0时,Sharding-JDBC将会将SQL语句转换为如下形式:

select * from db0.t_order_0 where user_id = ? and order_id = ?;

其中原始SQL中的t_order就是 逻辑表,而转换后的db0.t_order_0就是 实际表

4.规则配置

    以上分库分表的形式Dbcluster是通过规则配置来进行的描述的,下面讲通过几个小节来描述规则的详细配置

 ShardRule shardingRule = new ShardRule(dataSourceRule, Arrays.asList(orderTableRule, orderItemTableRule),
                new DatabaseShardingStrategy("user_id", new ModuloDatabaseShardingAlgorithm()),
                new TableShardingStrategy("order_id", new ModuloTableShardingAlgorithm()));

5.数据源配置

首先我们来构造DataSourceRule对象,它是来描述数据源的分布规则的

ShardDbRule dataSourceRule = new ShardDbRule(dataSourceMap);

这里构造器需要一个入参:数据源名称与真实数据源之间的映射关系,这个关系的构造方法如下

Map<String, DataSource> dataSourceMap = new HashMap<String, DataSource>(2);
dataSourceMap.put("ds_0", createDataSource("ds_0"));
dataSourceMap.put("ds_1", createDataSource("ds_1"));

真实的数据源可以使用任意一种数据库连接池,这里使用DBCP来举例

privatestatic DataSource createDataSource(final String dataSourceName){
    BasicDataSource result = new BasicDataSource();
    result.setDriverClassName(com.mysql.jdbc.Driver.class.getName());
    result.setUrl(String.format("jdbc:mysql://localhost:3306/%s", dataSourceName));
    result.setUsername("root");
    result.setPassword("");    return result;
}

6.策略配置

  • 分库策略与分表策略

StrategyClass.900.png

Dbcluster认为对于分片策略存有两种维度 - 数据源分片策略DatabaseShardingStrategy:数据被分配的目标数据源 ,表分片策略TableShardingStrategy:数据被分配的目标表,该目标表存在与该数据的目标数据源内。故表分片策略是依赖与数据源分片策略的结果的 这里注意的是两种策略的API完全相同,以下针对策略API的讲解将适用于这两种策略

  • 全局默认策略与特定表策略

策略是作用在特定的表规则上的,数据源策略与表策略与特定表相关

ShardTableRule orderTableRule = ShardTableRule.builder("t_order")
         .actualTables(Arrays.asList("t_order_0", "t_order_1")
         .dataSourceRule(dataSourceRule)
         .databaseShardingStrategy(new DatabaseShardingStrategy("user_id", new ModuloDatabaseShardingAlgorithm()))
         .tableShardingStrategy(new TableShardingStrategy("order_id", new ModuloTableShardingAlgorithm())))
         .build();

如果分片规则中的所有表或大部分表的分片策略相同,可以使用默认策略来简化配置。

//使用了默认策略配置
  ShardTableRule orderTableRule = ShardTableRule .builder("t_order")
          .actualTables(Arrays.asList("t_order_0", "t_order_1"))
          .dataSourceRule(dataSourceRule)
          .build();
  ShardTableRule orderItemTableRule = ShardTableRule .builder("t_order_item")
            .actualTables(Arrays.asList("t_order_item_0", "t_order_item_1"))
            .dataSourceRule(dataSourceRule)
            .build();
  ShardRule shardingRule = ew ShardRule(dataSourceRule, Arrays.asList(orderTableRule, orderItemTableRule),
                new DatabaseShardingStrategy("user_id", new ModuloDatabaseShardingAlgorithm()),
                new TableShardingStrategy("order_id", new ModuloTableShardingAlgorithm()));
  • 分片键

分片键是分片策略的第一个参数。分片键表示的是SQL语句中WHERE中的条件列。分片键可以配置多个

单分片键

new TableShardingStrategy("order_id", new SingleKeyShardingAlgorithm()))

多分片键

new TableShardingStrategy(Arrays.asList("order_id", "order_type", "order_date"), new MultiKeyShardingAlgorithm()))
  • 分片算法

分片算法接口类图关系如下:

AlgorithmClass.900.png

  • 分区算法

分区算法在数据源策略执行之前运行,对数据源列表进行匹配过滤处理。接口方法定义如下:

public interface PartitionAlgorithm {
/**
* 对所有可用的数据源列表进行过滤处理
* @param availableTargetNames 所有可用的数据源列表
* @param logicTable sql操作对应的逻辑表名
* @return
*/
public Collection<String> databasePartition(Collection<String> availableTargetNames,String logicTable);
}

7.绑定表

绑定表代表一组表,这组表的逻辑表与实际表之间的映射关系是相同的。比如t_order与t_order_item就是这样一组绑定表关系,它们的分库与分表策略是完全相同的,那么可以使用它们的表规则将它们配置成绑定表

new BindingTableRule(Arrays.asList(orderTableRule, orderItemTableRule))

那么在进行SQL路由时,如果SQL为

SELECT i.* FROM t_order o JOIN t_order_item i ON o.order_id=i.order_id WHERE o.user_id=? AND o.order_id=?

其中t_order在FROM的最左侧,Sharding-JDBC将会以它作为整个绑定表的主表。所有路由计算将会只使用主表的策略,那么t_order_item表的分片计算将会使用t_order的条件。故绑定表之间的分区键要完全相同。

8.分片算法详解

单分片键算法与多分片键算法这两种算法从名字上就可以知道前者是针对只有一个分片键,后者是针对有多个分片键的。单分片键算法是多分片键算法的一种简便形式,所以完全可以使用多分片算法去替代单分片键算法。下面两种形式是等价的

new TableShardingStrategy("order_id", new SingleKeyShardingAlgorithm()))
new TableShardingStrategy(Arrays.asList("order_id"), new MultiKeyShardingAlgorithm()))

同时在算法内部,doSharding等方法的shardingValue入参根据使用算法类型不同而不同 单分片键算法,单分片键接口定义如下:

public interface SingleKeyShardingAlgorithm<T extends Comparable<?>> extends ShardingAlgorithm {
    /**
     * 根据分片值和SQL的=运算符计算分片结果名称集合.
     * 
     * @param availableTargetNames 所有的可用目标名称集合, 一般是数据源或表名称
     * @param shardingValue 分片值
     * @return 分片后指向的目标名称, 一般是数据源或表名称
     */
    String doEqualSharding(Collection<String> availableTargetNames, ShardingValue<T> shardingValue);
    /**
     * 根据分片值和SQL的IN运算符计算分片结果名称集合.
     * 
     * @param availableTargetNames 所有的可用目标名称集合, 一般是数据源或表名称
     * @param shardingValue 分片值
     * @return 分片后指向的目标名称集合, 一般是数据源或表名称
     */
    Collection<String> doInSharding(Collection<String> availableTargetNames, ShardingValue<T> shardingValue);
    /**
     * 根据分片值和SQL的BETWEEN运算符计算分片结果名称集合.
     * 
     * @param availableTargetNames 所有的可用目标名称集合, 一般是数据源或表名称
     * @param shardingValue 分片值
     * @return 分片后指向的目标名称集合, 一般是数据源或表名称
     */
    Collection<String> doBetweenSharding(Collection<String> availableTargetNames, ShardingValue<T> shardingValue);
}

多分片键接口定义如下:

public interface MultipleKeysShardingAlgorithm extends ShardingAlgorithm {
    /**
     * 根据分片值计算分片结果名称集合.
     * 
     * @param availableTargetNames 所有的可用目标名称集合, 一般是数据源或表名称
     * @param shardingValues 分片值集合
     * @return 分片后指向的目标名称集合, 一般是数据源或表名称
     */
    Collection<String> doSharding(Collection<String> availableTargetNames, Collection<ShardingValue<?>> shardingValues);
}

根据数据源策略与表策略、单分片与多分片,这两种组合,一共产生了4种可供实现的分片算法的接口

  • 单分片键数据源分片算法SingleKeyDatabaseShardingAlgorithm
  • 单分片表分片算法SingleKeyTableShardingAlgorithm
  • 多分片键数据源分片算法MultipleKeyDatabaseShardingAlgorithm
  • 多分片表分片算法MultipleKeyTableShardingAlgorithm

单分片键算法详解

单分片键算法需要实现三个方法,下面以”单分片键数据源分片算法“举例

@Override
public String doEqualSharding(final Collection<String> availableTargetNames, final ShardingValue<Integer> shardingValue)
@Override
public Collection<String> doInSharding(final Collection<String> availableTargetNames, final ShardingValue<Integer> shardingValue)
@Override
public Collection<String> doBetweenSharding(final Collection<String> availableTargetNames, final ShardingValue<Integer> shardingValue)

这三种算法作用如下 - doEqualSharding在WHERE使用=作为条件分片键。算法中使用shardingValue.getValue()获取等=后的值 - doInSharding在WHERE使用IN作为条件分片键。算法中使用shardingValue.getValues()获取IN后的值 - doBetweenSharding在WHERE使用BETWEEN作为条件分片键。算法中使用shardingValue.getValueRange()获取BETWEEN后的值

下面是一个余2的算法的例子,当分片键的值除以2余数就是实际表的结尾。注意注释中提供了一些算法生成SQL的结果,参数tableNames集合中有两个参数t_order_0和t_order_1

public class SingleKeyModuloDatabaseShardingAlgorithm implements SingleKeyDatabaseShardingAlgorithm<Integer> {
    public String doEqualSharding(final Collection<String> availableTargetNames, final ShardingValue<Integer> shardingValue) {
        for (String each : availableTargetNames) {
            if (each.endsWith(shardingValue.getValue() % 2 + "")) {
                return each;
            }
        }
        throw new UnsupportedOperationException();
    }
    public Collection<String> doInSharding(final Collection<String> availableTargetNames, final ShardingValue<Integer> shardingValue) {
        Collection<String> result = new LinkedHashSet<String>(availableTargetNames.size());
        Collection<Integer> values = shardingValue.getValues();
        for (Integer value : values) {
            for (String dataSourceName : availableTargetNames) {
                if (dataSourceName.endsWith(value % 2 + "")) {
                    result.add(dataSourceName);
                }
            }
        }
        return result;
    }
    public Collection<String> doBetweenSharding(final Collection<String> availableTargetNames, final ShardingValue<Integer> shardingValue) {
        Collection<String> result = new LinkedHashSet<String>(availableTargetNames.size());
        Range<Integer> range = shardingValue.getValueRange();
        for (Integer i = range.lowerEndpoint(); i <= range.upperEndpoint(); i++) {
            for (String each : availableTargetNames) {
                if (each.endsWith(i % 2 + "")) {
                    result.add(each);
                }
            }
        }
        return result;
    }
}

多分片键算法详解

多分片键试用于使用场景比较复杂,为了能提供更高的灵活性,故只提供实现一个方法。

@Override
public Collection<String> doSharding(final Collection<String> availableTargetNames, final Collection<ShardingValue<?>> shardingValues)

算法实现的时候根据shardingValue.getType()来获取条件是=,IN或者BETWEEN。然后根据业务进行灵活的实现。

如果表的数据分布如下

db0
  ├── t_order_00               user_id以a偶数   order_id为偶数
  ├── t_order_01               user_id以a偶数   order_id为奇数
  ├── t_order_10               user_id以b奇数   order_id为偶数
  └── t_order_11               user_id以b奇数   order_id为奇数

算法实现如下:

public final class MultipleKeysModuloTableShardingAlgorithm implements MultipleKeysTableShardingAlgorithm {
    @Override
    public Collection<String> doSharding(final Collection<String> availableTargetNames, final Collection<ShardingValue<?>> shardingValues) {
        Set<Integer> orderIdValueSet = getShardingValue(shardingValues, "order_id");
        Set<Integer> userIdValueSet = getShardingValue(shardingValues, "user_id");
        List<String> result = new ArrayList<>();
        /*
        userIdValueSet[10,11] + orderIdValueSet[101,102] => valueResult[[10,101],[10,102],[11,101],[11,102]]
         */
        Set<List<Integer>> valueResult = Sets.cartesianProduct(userIdValueSet, orderIdValueSet);
        for (List<Integer> value : valueResult) {
            String suffix = Joiner.on("").join(value.get(0) % 2, value.get(1) % 2);
            for (String tableName : availableTargetNames) {
                if (tableName.endsWith(suffix)) {
                    result.add(tableName);
                }
            }
        }
        return result;
    }
    private Set<Integer> getShardingValue(final Collection<ShardingValue<?>> shardingValues, final String shardingKey) {
        Set<Integer> valueSet = new HashSet<>();
        ShardingValue<Integer> shardingValue = null;
        for (ShardingValue<?> each : shardingValues) {
            if (each.getColumnName().equals(shardingKey)) {
                shardingValue = (ShardingValue<Integer>) each;
                break;
            }
        }
        if (null == shardingValue) {
            return valueSet;
        }
        switch (shardingValue.getType()) {
            case SINGLE:
                valueSet.add(shardingValue.getValue());
                break;
            case LIST:
                valueSet.addAll(shardingValue.getValues());
                break;
            case RANGE:
                for (Integer i = shardingValue.getValueRange().lowerEndpoint(); i <= shardingValue.getValueRange().upperEndpoint(); i++) {
                    valueSet.add(i);
                }
                break;
            default:
                throw new UnsupportedOperationException();
        }
        return valueSet;
    }
}

9.使用方式

  • 数据源方式

构建DbclusterDataSource实例

 DbclusterDataSource shardingDataSource= new DbclusterDataSource(new AppRule(shardingRule));

使用DbclusterDataSource的例子:

 DbclusterDataSource shardingDataSource= new DbclusterDataSource(new AppRule(shardingRule));
 String sql = "SELECT i.* FROM t_order o JOIN t_order_item i ON o.order_id=i.order_id WHERE o.user_id=? AND o.order_id=?";       
 Connection conn = dataSource.getConnection();
 PreparedStatement preparedStatement = conn.prepareStatement(sql);
 preparedStatement.setInt(1, 10);
 preparedStatement.setInt(2, 1001);
 ResultSet rs = preparedStatement.executeQuery();            
 while (rs.next()) {
      System.out.println(rs.getInt(1));
      System.out.println(rs.getInt(2));
      System.out.println(rs.getInt(3));
 }

该数据源与普通数据源完全相同,你可以通过上例的API形式来使用,也可以将其配置在Spring,Hibernate、Mybatis等框架中使用。

  • 驱动方式

DbclusterDriver是java.sql.Driver接口的实现类,可以通过原生JDBC驱动方式进行数据库操作,示例如下:

 String sql = "SELECT i.* FROM t_order o JOIN t_order_item i ON o.order_id=i.order_id WHERE o.user_id=? AND o.order_id=?";       
 Class.forName("org.tinygroup.dbcluster.jdbc.DbclusterDriver");
 Connection connection = DriverManager.getConnection("jdbc:dbcluster://drivertest");
 PreparedStatement preparedStatement = conn.prepareStatement(sql);
 preparedStatement.setInt(1, 10);
 preparedStatement.setInt(2, 1001);
 ResultSet rs = preparedStatement.executeQuery();            
 while (rs.next()) {
      System.out.println(rs.getInt(1));
      System.out.println(rs.getInt(2));
      System.out.println(rs.getInt(3));
 }

原文地址:http://www.tinygroup.org/docs/1560036131262789918

分享到:
评论

相关推荐

    sharding-jdbc按月分表样例2

    7. **实战解析**:在“sharding-jdbc-demo”中,我们可以通过查看源码理解如何配置Sharding-JDBC、定义分片策略、编写业务代码来调用分片API以及测试分片功能是否正常工作。 通过学习这个示例,开发者可以了解到...

    4-Sharding-JDBC分库分表.pdf

    分库分表的概念、垂直拆分与水平拆分、分片策略和分片算法是Sharding-JDBC中核心的知识点,下面将对这些概念进行详细的解释。 ### 分库分表概念 分库分表是为了解决随着业务数据量的不断增长,单库单表模式面临的...

    shareding-jdbc分库分表

    Sharding-JDBC 是当当网开源的适用于微服务的分布式数据访问基础类库,完整的实现了分库分表,读写分离和分布式主键功能,并初步实现了柔性事务。从 2016 年开源至今,在经历了整体架构的数次精炼以及稳定性打磨后,...

    sharding-jdbc按月分表样例

    【标题】"sharding-jdbc按月分表样例"是一个关于使用Sharding-JDBC进行数据库分片的示例项目,旨在展示如何根据月份动态地将数据分散到不同的表中,以实现数据的水平扩展和负载均衡。Sharding-JDBC是阿里巴巴开源的...

    sharding-jdbc分库分表

    它作为JDBC的一个增强工具,可以在现有的Java应用中透明地添加分布式数据库处理能力,实现了数据的分库分表以及读写分离。 1. **分库分表原理**: 分库分表是解决大数据量下数据库性能瓶颈的有效手段。Sharding-...

    集成sharding-jdbc实现分库分表.zip

    Sharding-JDBC作为阿里巴巴开源的一款轻量级数据库中间件,它提供了一种无侵入的分库分表解决方案,非常适合于单体项目的数据库扩展。在这个"集成sharding-jdbc实现分库分表.zip"的压缩包中,我们可以深入学习如何将...

    sharding + mybatis-plus 分库分表

    当Mybatis-Plus与Sharding-JDBC结合使用时,可以在分库分表的环境中实现便捷的数据操作。 在实际应用中,我们首先需要在配置文件中定义分片策略,例如可以设置基于用户ID的哈希分片,使得相同的用户ID会被路由到同...

    springboot + mybatis+mysql+shareding-jdbc分库分表实-shareding.zip

    总之,"springboot + mybatis+mysql+shareding-jdbc分库分表实"项目旨在展示如何利用SpringBoot、MyBatis和Sharding-JDBC在MySQL上实现数据库的分库分表,提高系统的并发处理能力和数据存储能力。通过理解和实践这个...

    shards-jdbc分库分表实例

    shards-jdbc分库分表实例 dataSources: ds0: !!org.apache.commons.dbcp.BasicDataSource driverClassName: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/ds0 username: root password: ...

    Sharding-JDBC从入门到精通-深入Sharding-JDBC分库分表-学习笔记教程与源码.zip

    Sharding-JDBC, 它定位为轻量级 java 框架,在 Java 的 JDBC 层提供的额外服务。它使用客户端直连数据库,以 jar 包形式提供服务,无需额外部署和依赖,可理解为增强版的 JDBC 驱动,完全兼容 JDBC 和各种 ORM 框架。...

    基于yml 配置方式 ,实现springBoot+sharding-jdbc+mybatis-plus 实现分库分表,读写分离,以及全局表,子表的配置

    1、基于yml 配置方式 ,实现springBoot+sharding-jdbc+mybatis-plus 实现分库分表,读写分离,以及全局表,子表的配置。 2、实现mybatis-plus 整合到springboot 详细使用请看 测试用例

    sharding-jdbc之——分库分表实例完整源码

    标题"sharding-jdbc之——分库分表实例完整源码"指出了本主题的核心,即`Sharding-JDBC`在实现数据库分库分表中的应用。Sharding-JDBC是阿里巴巴开源的轻量级Java框架,它可以在不修改现有数据库的情况下,对数据库...

    spring+mybatis+sharding-jdbc 1.3.1实现分库分表案例(可直接运行)

    本案例基于Spring、MyBatis和Sharding-JDBC 1.3.1版本,提供了一个可以直接运行的分库分表实现,帮助开发者快速理解和实践这一技术。 首先,我们要理解什么是分库分表。分库是指将一个大型数据库拆分为多个小型...

    Sharding-JDBC教程:Spring Boot整合Sharding-JDBC实现分库分表+读写分离.docx

    Sharding-JDBC教程:Spring Boot整合Sharding-JDBC实现分库分表+读写分离 Sharding-JDBC是阿里巴巴开源的关系型数据库中间件,提供了数据库分库分表、读写分离、数据库路由等功能。本教程将指导读者使用Sharding-...

    当当开源sharding-jdbc-轻量级数据库分库分表中间件

    ### 当当开源Sharding-JDBC:轻量级数据库分库分表中间件 #### 概述 当当网近期开源了一款名为Sharding-JDBC的轻量级数据库分库分表中间件。作为一款高性能、易用性高的数据库水平分片框架,Sharding-JDBC在设计上...

    17、ShardingJDBC分库分表实战指南-ev.rar

    17、ShardingJDBC分库分表实战指南_ev.rar17、ShardingJDBC分库分表实战指南_ev.rar17、ShardingJDBC分库分表实战指南_ev.rar17、ShardingJDBC分库分表实战指南_ev.rar17、ShardingJDBC分库分表实战指南_ev.rar17、...

    Sharding-JDBC使用案例-分库分表

    Sharding-JDBC是由阿里巴巴开源的轻量级Java框架,它定位为数据库中间件,提供了一种无需修改现有应用代码就能实现数据库分库分表的能力。Sharding-JDBC完全基于JDBC标准,可以理解为一个增强版的数据库驱动,它工作...

    SpringBoot+Sharding-JDBC分库分表实战

    《SpringBoot+Sharding-JDBC分库分表实战》是一个深度探讨如何在Java环境中利用SpringBoot框架和Sharding-JDBC库实现数据库分库分表的实践教程。本教程旨在帮助开发者掌握在高并发、大数据量场景下,如何有效提升...

    cloud-shard-jdbc:SpringCloud实现基于Shard-jdbc分库分表模式下,数据库扩容解决办法

    阅读标签 【】【】【】【】【】 【】【】【】【】【】 【】【】【 】 参考文章 序号 文章标题 01 02 微服务基础:Ribbon和Feign组件,实现请求负载均衡 ...基于SpringCloud实现Shard-Jdbc的分库分表模式,数据库扩容方

Global site tag (gtag.js) - Google Analytics