【分库分表】sharding-jdbc—分片策略

2023-11-20

 


一、分片策略

Sharding-JDBC认为对于分片策略存有两种维度:

  • 数据源分片策略(DatabaseShardingStrategy):数据被分配的目标数据源
  • 表分片策略(TableShardingStrategy):数据被分配的目标表

两种分片策略API完全相同,但是表分片策略是依赖于数据源分片策略的(即:先分库然后才有分表)

二、分片算法

Sharding分片策略继承自ShardingStrategy,提供了5种分片策略:

  

由于分片算法和业务实现紧密相关,因此Sharding-JDBC并未提供内置分片算法,而是通过分片策略将各种场景提炼出来,提供更高层级的抽象,并提供接口让应用开发者自行实现分片算法。

StandardShardingStrategy

标准分片策略。提供对SQL语句中的=, IN和BETWEEN AND的分片操作支持。

StandardShardingStrategy只支持单分片键,提供PreciseShardingAlgorithm和RangeShardingAlgorithm两个分片算法。

  • PreciseShardingAlgorithm是必选的,用于处理=和IN的分片。
  • RangeShardingAlgorithm是可选的,用于处理BETWEEN AND分片,如果不配置RangeShardingAlgorithm,SQL中的BETWEEN AND将按照全库路由处理。

ComplexShardingStrategy

复合分片策略。提供对SQL语句中的=, IN和BETWEEN AND的分片操作支持。

ComplexShardingStrategy支持多分片键,由于多分片键之间的关系复杂,因此Sharding-JDBC并未做过多的封装,而是直接将分片键值组合以及分片操作符交于算法接口,完全由应用开发者实现,提供最大的灵活度。

InlineShardingStrategy

Inline表达式分片策略。使用Groovy的Inline表达式,提供对SQL语句中的=和IN的分片操作支持。

InlineShardingStrategy只支持单分片键,对于简单的分片算法,可以通过简单的配置使用,从而避免繁琐的Java代码开发,如: tuser${user_id % 8} 表示t_user表按照user_id按8取模分成8个表,表名称为t_user_0到t_user_7。

HintShardingStrategy

通过Hint而非SQL解析的方式分片的策略。

NoneShardingStrategy

不分片的策略。

三、自定义分片算法

Sharding提供了以下4种算法接口:

  • PreciseShardingAlgorithm
  • RangeShardingAlgorithm
  • HintShardingAlgorithm
  • ComplexKeysShardingAlgorithm

可以自己实现自定义的分片算法,下面以t_order_items表为例自己实现分片算法:

标准分片策略(StandardShardingStrategy)

a、PreciseShardingAlgorithm实现:(Precise处理 = 和 in 的路由)

        // 配置order_item表规则...
        TableRuleConfiguration orderItemTableRuleConfig = new TableRuleConfiguration();
        orderItemTableRuleConfig.setLogicTable("t_order_items");
        orderItemTableRuleConfig.setActualDataNodes("db${0..2}.t_order_items_${0..1}");
        // 自定义的分片算法实现
        StandardShardingStrategyConfiguration standardStrategy = new StandardShardingStrategyConfiguration("order_id",MyPreciseShardingAlgorithm.class.getName());

        // 配置分库策略
        orderItemTableRuleConfig.setDatabaseShardingStrategyConfig(standardStrategy);

        // 配置分表策略
        orderItemTableRuleConfig.setTableShardingStrategyConfig(standardStrategy);

        shardingRuleConfig.getTableRuleConfigs().add(orderItemTableRuleConfig);

        // 获取数据源对象
        DataSource dataSource = null;
        try {
            dataSource = ShardingDataSourceFactory.createDataSource(dataSourceMap, shardingRuleConfig, new ConcurrentHashMap(), new Properties());
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return dataSource;

自定义的分片算法,先继承接口,打印参数:

@Slf4j
public class MyPreciseShardingAlgorithm implements PreciseShardingAlgorithm<Long> {
    @Override
    public String doSharding(Collection collection, PreciseShardingValue<Long> preciseShardingValue) {

        log.info("collection:" + JSON.toJSONString(collection) + ",preciseShardingValue:" + JSON.toJSONString(preciseShardingValue));
        return null;
    }
}

输出如下日志:(第一行路由是db,下一行是table)

2018-01-19 20:13:36,790 -2 collection:["db0","db1","db2"],preciseShardingValue:{"columnName":"order_id","logicTableName":"t_order_items","value":100}

……

2018-01-21 16:33:22,269 -2 collection:["t_order_items_0","t_order_items_1"],preciseShardingValue:{"columnName":"order_id","logicTableName":"t_order_items","value":100}

于是可以简单实现一个类似Inline配置的规则:

@Slf4j
public class MyPreciseShardingAlgorithm implements PreciseShardingAlgorithm<Long> {
    @Override
    public String doSharding(Collection<String> collection, PreciseShardingValue<Long> preciseShardingValue) {
        log.info("collection:" + JSON.toJSONString(collection) + ",preciseShardingValue:" + JSON.toJSONString(preciseShardingValue));
        for (String name : collection) {
            if (name.endsWith(preciseShardingValue.getValue() % collection.size() + "")) {
                log.info("return name:"+name);
                return name;
            }
        }
        return null;
    }
}

 IN 条件的处理示例:

==> Preparing: select id,order_id,unique_no,quantity,is_active,inserttime,updatetime from t_order_items where is_active = 1 AND order_id in ( ? , ? , ? ) 
==> Parameters: 100(Long), 101(Long), 102(Long)


//第一轮route筛选数据库(分片键路由规则):
09:55:09.634 logback-demo [http-nio-8082-exec-1] INFO s.j.d.d.MyPreciseShardingAlgorithm - collection:["db0","db1","db2"],preciseShardingValue:{"columnName":"order_id","logicTableName":"t_order_items","value":100}
09:55:13.758 logback-demo [http-nio-8082-exec-1] INFO s.j.d.d.MyPreciseShardingAlgorithm - return name:db1
09:55:17.767 logback-demo [http-nio-8082-exec-1] INFO s.j.d.d.MyPreciseShardingAlgorithm - collection:["db0","db1","db2"],preciseShardingValue:{"columnName":"order_id","logicTableName":"t_order_items","value":101}
09:55:21.361 logback-demo [http-nio-8082-exec-1] INFO s.j.d.d.MyPreciseShardingAlgorithm - return name:db2
09:55:23.127 logback-demo [http-nio-8082-exec-1] INFO s.j.d.d.MyPreciseShardingAlgorithm - collection:["db0","db1","db2"],preciseShardingValue:{"columnName":"order_id","logicTableName":"t_order_items","value":102}
09:55:24.190 logback-demo [http-nio-8082-exec-1] INFO s.j.d.d.MyPreciseShardingAlgorithm - return name:db0

//第二轮route按第一轮筛选到的db,逐个进行table的计算:
09:58:45.086 logback-demo [http-nio-8082-exec-1] INFO s.j.d.d.MyPreciseShardingAlgorithm - collection:["t_order_items_0","t_order_items_1"],preciseShardingValue:{"columnName":"order_id","logicTableName":"t_order_items","value":100}
09:58:46.725 logback-demo [http-nio-8082-exec-1] INFO s.j.d.d.MyPreciseShardingAlgorithm - return name:t_order_items_0
09:58:58.647 logback-demo [http-nio-8082-exec-1] INFO s.j.d.d.MyPreciseShardingAlgorithm - collection:["t_order_items_0","t_order_items_1"],preciseShardingValue:{"columnName":"order_id","logicTableName":"t_order_items","value":101}
09:59:02.197 logback-demo [http-nio-8082-exec-1] INFO s.j.d.d.MyPreciseShardingAlgorithm - return name:t_order_items_1
09:59:11.710 logback-demo [http-nio-8082-exec-1] INFO s.j.d.d.MyPreciseShardingAlgorithm - collection:["t_order_items_0","t_order_items_1"],preciseShardingValue:{"columnName":"order_id","logicTableName":"t_order_items","value":102}
09:59:12.604 logback-demo [http-nio-8082-exec-1] INFO s.j.d.d.MyPreciseShardingAlgorithm - return name:t_order_items_0
10:00:01.538 logback-demo [http-nio-8082-exec-1] INFO s.j.d.d.MyPreciseShardingAlgorithm - collection:["t_order_items_0","t_order_items_1"],preciseShardingValue:{"columnName":"order_id","logicTableName":"t_order_items","value":100}
10:00:01.538 logback-demo [http-nio-8082-exec-1] INFO s.j.d.d.MyPreciseShardingAlgorithm - return name:t_order_items_0
10:00:02.042 logback-demo [http-nio-8082-exec-1] INFO s.j.d.d.MyPreciseShardingAlgorithm - collection:["t_order_items_0","t_order_items_1"],preciseShardingValue:{"columnName":"order_id","logicTableName":"t_order_items","value":101}
10:00:02.042 logback-demo [http-nio-8082-exec-1] INFO s.j.d.d.MyPreciseShardingAlgorithm - return name:t_order_items_1
10:00:02.442 logback-demo [http-nio-8082-exec-1] INFO s.j.d.d.MyPreciseShardingAlgorithm - collection:["t_order_items_0","t_order_items_1"],preciseShardingValue:{"columnName":"order_id","logicTableName":"t_order_items","value":102}
10:00:02.442 logback-demo [http-nio-8082-exec-1] INFO s.j.d.d.MyPreciseShardingAlgorithm - return name:t_order_items_0
10:00:03.581 logback-demo [http-nio-8082-exec-1] INFO s.j.d.d.MyPreciseShardingAlgorithm - collection:["t_order_items_0","t_order_items_1"],preciseShardingValue:{"columnName":"order_id","logicTableName":"t_order_items","value":100}
10:00:03.581 logback-demo [http-nio-8082-exec-1] INFO s.j.d.d.MyPreciseShardingAlgorithm - return name:t_order_items_0
10:00:03.946 logback-demo [http-nio-8082-exec-1] INFO s.j.d.d.MyPreciseShardingAlgorithm - collection:["t_order_items_0","t_order_items_1"],preciseShardingValue:{"columnName":"order_id","logicTableName":"t_order_items","value":101}
10:00:03.946 logback-demo [http-nio-8082-exec-1] INFO s.j.d.d.MyPreciseShardingAlgorithm - return name:t_order_items_1
10:00:04.578 logback-demo [http-nio-8082-exec-1] INFO s.j.d.d.MyPreciseShardingAlgorithm - collection:["t_order_items_0","t_order_items_1"],preciseShardingValue:{"columnName":"order_id","logicTableName":"t_order_items","value":102}
10:00:04.578 logback-demo [http-nio-8082-exec-1] INFO s.j.d.d.MyPreciseShardingAlgorithm - return name:t_order_items_0

b、PreciseShardingAlgorithm + RangeShardingAlgorithm

  View Code

22:17:35.318 logback-demo [http-nio-8082-exec-6] INFO s.j.demo.controller.OrderController - selectByOrderIds ,startNo:100,endNo:101

路由输出log:

-- 第一轮计算db
22:16:51.732 logback-demo [http-nio-8082-exec-6] INFO s.j.d.d.MyRangeShardingAlgorithm - Range collection:["db0","db1","db2"],preciseShardingValue:{"columnName":"order_id","logicTableName":"t_order_items","valueRange":{"empty":false}}

-- 第二轮计算table

22:17:16.325 logback-demo [http-nio-8082-exec-6] INFO s.j.d.d.MyRangeShardingAlgorithm - Range collection:["t_order_items_0","t_order_items_1"],preciseShardingValue:{"columnName":"order_id","logicTableName":"t_order_items","valueRange":{"empty":false}}
22:17:32.771 logback-demo [http-nio-8082-exec-6] INFO s.j.d.d.MyRangeShardingAlgorithm - Range collection:["t_order_items_0","t_order_items_1"],preciseShardingValue:{"columnName":"order_id","logicTableName":"t_order_items","valueRange":{"empty":false}}

 

路由到[db0,db1]X[t_order_items_0,t_order_items_1]表。

ComplexShardingStrategy

分库分表配置:user_id单键分库 + 【user_id+order_id】组合键分表

    @Bean(name = "shardingComplexDataSource", destroyMethod = "close")
    @Qualifier("shardingComplexDataSource")
    public DataSource getComlpexShardingDataSource() {
        // 配置真实数据源
        Map<String, DataSource> dataSourceMap = new HashMap<>(3);

        List<String> dbNames = new ArrayList<>();
        dbNames.add("db0");
        dbNames.add("db1");
        dbNames.add("db2");

        for (String dbName : dbNames) {
            DruidDataSource dataSource = createDefaultDruidDataSource();
            dataSource.setDriverClassName("com.mysql.jdbc.Driver");
            dataSource.setUrl("jdbc:mysql://localhost:3306/" + dbName);
            dataSource.setUsername("root");
            dataSource.setPassword("root");
            dataSourceMap.put(dbName, dataSource);
        }

        TableRuleConfiguration orderTableRuleConfig = new TableRuleConfiguration();
        orderTableRuleConfig.setLogicTable("t_order");
        orderTableRuleConfig.setActualDataNodes("db${0..2}."+"t_order_${0..1}_${0..1}");

        /**分库采用单片键 user_id*/
        orderTableRuleConfig.setDatabaseShardingStrategyConfig(new StandardShardingStrategyConfiguration("user_id", MyPreciseShardingAlgorithm.class.getName()));
        /**分表采用双片键 user_id*/
        orderTableRuleConfig.setTableShardingStrategyConfig(new ComplexShardingStrategyConfiguration("user_id,order_id", MyComplexShardingAlgorithm.class.getName()));

        ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();
        shardingRuleConfig.getTableRuleConfigs().add(orderTableRuleConfig);

        // 获取数据源对象
        DataSource dataSource = null;
        try {
            dataSource = ShardingDataSourceFactory.createDataSource(dataSourceMap, shardingRuleConfig, new ConcurrentHashMap(), new Properties());
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return dataSource;
    }

实现ComplexKeysShardingAlgorithm算法:

@Slf4j
public class MyComplexShardingAlgorithm implements ComplexKeysShardingAlgorithm {
    @Override
    public Collection<String> doSharding(Collection<String> collection, Collection<ShardingValue> shardingValues) {
        log.info("collection:" + JSON.toJSONString(collection) + ",shardingValues:" + JSON.toJSONString(shardingValues));

        Collection<Long> orderIdValues = getShardingValue(shardingValues, "order_id");
        Collection<Long> userIdValues = getShardingValue(shardingValues, "user_id");
        List<String> shardingSuffix = new ArrayList<>();
        /**例如:根据user_id + order_id 双分片键来进行分表*/
        //Set<List<Integer>> valueResult = Sets.cartesianProduct(userIdValues, orderIdValues);
        for (Long userIdVal : userIdValues) {
            for (Long orderIdVal : orderIdValues) {
                String suffix = userIdVal % 2 + "_" + orderIdVal % 2;
                collection.forEach(x -> {
                    if (x.endsWith(suffix)) {
                        shardingSuffix.add(x);
                    }
                });
            }
        }

        return shardingSuffix;
    }

    private Collection<Long> getShardingValue(Collection<ShardingValue> shardingValues, final String key) {
        Collection<Long> valueSet = new ArrayList<>();
        Iterator<ShardingValue> iterator = shardingValues.iterator();
        while (iterator.hasNext()) {
            ShardingValue next = iterator.next();
            if (next instanceof ListShardingValue) {
                ListShardingValue value = (ListShardingValue) next;
                /**例如:根据user_id + order_id 双分片键来进行分表*/
                if (value.getColumnName().equals(key)) {
                    return value.getValues();
                }
            }
        }
        return valueSet;
    }
}

运行示例:

16:53:16.267 logback-demo [http-nio-8082-exec-1] INFO s.j.d.d.MyPreciseShardingAlgorithm - collection:["db0","db1","db2"],preciseShardingValue:{"columnName":"user_id","logicTableName":"t_order","value":123}
16:53:16.267 logback-demo [http-nio-8082-exec-1] INFO s.j.d.d.MyPreciseShardingAlgorithm - return name:db0


16:53:16.740 logback-demo [http-nio-8082-exec-1] INFO s.j.d.d.MyComplexShardingAlgorithm - collection:["t_order_0_0","t_order_0_1","t_order_1_0","t_order_1_1"],shardingValues:[{"columnName":"order_id","logicTableName":"t_order","values":[321]},{"columnName":"user_id","logicTableName":"t_order","values":[123]}]

四、级联绑定表

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

 ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();
 shardingRuleConfig.getTableRuleConfigs().add(getOrderTableRuleConfiguration());
 shardingRuleConfig.getTableRuleConfigs().add(getOrderItemTableRuleConfiguration());
 shardingRuleConfig.getBindingTableGroups().add("t_order, t_order_item");

那么在进行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的条件。故绑定表之间的分区键要完全相同。

 





https://www.cnblogs.com/mr-yang-localhost/p/8313360.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【分库分表】sharding-jdbc—分片策略 的相关文章

  • 如何通过 MySQL Workbench 或 CLI 或 MySQLWeb 数据库管理应用程序连接到 Pivotal Cloud Foundry (PCF) 上的 MySQL 服务?

    我有一个Spring Boot based REST部署在的应用程序Pivotal Cloud Foundry PCF 并且工作正常 但是这个 REST 应用程序到目前为止还没有任何数据库连接 因此 我决定安装MySQL服务于PCF从市场上
  • 如何获取 JDBC 中 UPDATE 查询影响的所有行?

    我有一项任务需要使用更新记录PreparedStatement 一旦记录被更新 我们知道更新查询返回计数 即受影响的行数 但是 我想要的不是计数 而是受更新查询影响的行作为响应 或者至少是受影响的行的 id 值列表 这是我的更新查询 UPD
  • IMAP 和 PHP - 从已发送文件夹和收件箱文件夹中获取所有电子邮件

    我正在尝试获取接收和发送的所有电子邮件 并使用 PHP 将其写入 mySQL 数据库 我使用的主机名是 hostname imap gmail com 993 imap ssl INBOX 它仅引用收件箱 并成功抓取收到的电子邮件 为了抓取
  • 将sql查询结果写入mysql中的文件

    我正在尝试使用 mysql 将查询结果写入文件 我在一些地方看到了有关 outfile 构造的一些信息 但似乎这只将文件写入正在运行 MySQL 的机器 在本例中是远程机器 即数据库不在我的本地机器上 或者 我还尝试运行查询并从 mysql
  • pyodbc 无法正确处理 unicode 数据

    我确实使用 pyodbc 成功连接了 MySQL 数据库 并且它可以很好地处理 ascii 编码的数据 但是当我打印使用 unicode utf8 编码的数据时 它引发了错误 UnicodeEncodeError ascii codec c
  • DBX 错误:驱动程序无法正确初始化

    我在跑步德尔福XE3 终极版 MySQL 数据库 这是我点击时收到的错误Test Connection 作为回应 我在 xampp 目录中找到了 libmysql 库 并将其复制到我的 System32 目录中 但这是行不通的 此消息指的是
  • 级联删除时触发调用

    我在 MySQL 中有表 A 它有一些对其他表 B C D 的级联删除的引用 当从 A 中删除某些内容时 我需要使用触发器 当我直接从 A 删除记录时 此触发器起作用 但它不适用于级联删除 是否存在任何版本的 MySQL 可以让我的触发器与
  • 通过 PDO 将双精度数插入 MySQL 时精度损失

    我遇到了这种非常烦人的行为 我想知道我是否做错了什么 或者这是否是故意的 如果是的话 为什么 每当我在 php 5 3 中有一个 double 类型的变量 并且想将其插入到数据库 MYSQL 5 0 的 double 类型字段中时 该值总是
  • 如何处理PDO异常[重复]

    这个问题在这里已经有答案了 我正在尝试与PDOphp 上的类 但我在找到处理错误的正确方法时遇到了一些麻烦 我编写了以下代码
  • 将我的 Laravel 连接到外部数据库

    如何将 Laravel 连接到外部数据库 示例 我的本地计算机上有一个 Laravel 它在 xampp 上运行 我希望它连接到云服务器数据库 打开 env文件并编辑它 只需设置正确的外部数据库凭据 DB CONNECTION mysql
  • SQL 查询结果为字符串(或变量)

    是否可以将SQL查询结果输出到一个字符串或变量中 我的php和mysql不好 假设我有数据库 agents 其中包含列 agent id agent fname agent lname agent dept 使用此查询 sql SELECT
  • 重用 PDO 语句 var 会使进程崩溃

    我重用一个变量来存储两个不同的 PDO mysql 语句 stmt dbh gt prepare SELECT stmt gt execute stmt dbh gt prepare UPDATE crash here Error in o
  • Unicode(希腊语)字符存储在数据库中,例如“??????”

    数据库中的希腊字符就像问号 我找不到解决办法 我使用 Java Swing 开发了一个应用程序 但是当我在 MySQL 中插入希腊字母时 就像问号一样 我将数据库排序规则更改为 utf8 并将列也更改为 utf8 我的项目编码设置为UTF
  • 如何获取所有mysql元组结果并转换为json

    我能够从表中获取单个数据 但是当我试图获取表上的所有数据时 我只得到一行 cnn execute sql rows cnn fetchall column t 0 for t in cnn description for row in ro
  • 在 PHP 中比较字符串的方式与 MySQL 相同

    我将 varchar 存储在 utf8 MySQL 表中并使用 utf8 general ci 排序规则 我在 varchar 上有一个唯一索引 我想在 PHP 中进行字符串比较 这相当于 MySQL 对索引所做的操作 一个具体的例子是 我
  • MySQL如何根据字段是否存在来插入新记录或更新字段?

    我正在尝试实现一个评级系统 在数据库表中保留以下两个字段 评级 当前评级 num rates 迄今为止提交的评分数量 UPDATE mytable SET rating rating num rates theRating num rate
  • Codeigniter $this->db->reconnect();用法

    I m not自动加载数据库 因为我的应用程序的大多数页面don t需要数据库处理 否则整个事情会变慢 我想要做的是 当数据库已经存在时 不要建立与数据库的新连接 而是使用它而不是打扰服务器数据库 那么我该如何实施 this gt db g
  • 根据 MySQL 列内容的长度创建索引?

    如何根据值的长度在 MySQL v 5 0 myisam 数据库引擎 中的列上创建索引 它是文本数据类型 最多 7000 个字符 我是否必须添加具有第一列长度的另一列 是的 因为 MySQL 不支持基于函数的索引 例如ADD INDEX m
  • 在 ASP.NET MVC 中使用 MySQL 的 AccountController

    在 Visual Studio 中创建默认的 ASP NET MVC 项目会设置一个可以在其中注册用户的基本项目 我将如何继续更改它以使用 MySQL 服务器而不是 SQLServer 现在可以使用了 安装最新的 Connector NET
  • 从多个表中选择 - 一对多关系

    我有这样的表 表产品 身份证 姓名 表格图像 产品 ID 网址 订单号 表价 产品 ID 组合 货币 价格 表数量 产品 ID 组合 数量 表 Product 与其他表是一对多关系 我需要查询表并得到类似这样的结果 伪数组 ProductI

随机推荐