我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Sharding-Jdbc 自定义复合分片的实现(分库分表)

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Sharding-Jdbc 自定义复合分片的实现(分库分表)

Sharding-JDBC中的分片策略有两个维度,分别是:

  • 数据源分片策略(DatabaseShardingStrategy)
  • 表分片策略(TableShardingStrategy)

其中,数据源分片策略表示:数据路由到的物理目标数据源,表分片策略表示数据被路由到的目标表。

特别的,表分片策略是依赖于数据源分片策略的,也就是说要先分库再分表,当然也可以只分表。

Sharding-JDBC的数据分片策略

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

以下内容引用自官方文档。官方文档

首先介绍四种分片算法。

通过分片算法将数据分片,支持通过=、BETWEEN和IN分片。
分片算法需要应用方开发者自行实现,可实现的灵活度非常高。

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

分片键

用于分片的数据库字段,是将数据库(表)水平拆分的关键字段。例:将订单表中的订单主键的尾数取模分片,则订单主键为分片字段。 SQL中如果无分片字段,将执行全路由,性能较差。 除了对单分片字段的支持,ShardingSphere也支持根据多个字段进行分片。

分片算法

通过分片算法将数据分片,支持通过=BETWEENIN分片。分片算法需要应用方开发者自行实现,可实现的灵活度非常高。

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

精确分片算法

对应PreciseShardingAlgorithm,用于处理使用单一键作为分片键的=与IN进行分片的场景。需要配合StandardShardingStrategy使用。

范围分片算法

对应RangeShardingAlgorithm,用于处理使用单一键作为分片键的BETWEEN AND进行分片的场景。需要配合StandardShardingStrategy使用。

复合分片算法

对应ComplexKeysShardingAlgorithm,用于处理使用多键作为分片键进行分片的场景,包含多个分片键的逻辑较复杂,需要应用开发者自行处理其中的复杂度。需要配合ComplexShardingStrategy使用。

Hint分片算法

对应HintShardingAlgorithm,用于处理使用Hint行分片的场景。需要配合HintShardingStrategy使用。

分片策略

包含分片键和分片算法,由于分片算法的独立性,将其独立抽离。真正可用于分片操作的是分片键 + 分片算法,也就是分片策略。目前提供5种分片策略。

标准分片策略

对应StandardShardingStrategy。提供对SQL语句中的=, IN和BETWEEN AND的分片操作支持。StandardShardingStrategy只支持单分片键,提供PreciseShardingAlgorithm和RangeShardingAlgorithm两个分片算法。PreciseShardingAlgorithm是必选的,用于处理=和IN的分片。RangeShardingAlgorithm是可选的,用于处理BETWEEN AND分片,如果不配置RangeShardingAlgorithm,SQL中的BETWEEN AND将按照全库路由处理。

复合分片策略

对应ComplexShardingStrategy。复合分片策略。提供对SQL语句中的=, IN和BETWEEN AND的分片操作支持。ComplexShardingStrategy支持多分片键,由于多分片键之间的关系复杂,因此并未进行过多的封装,而是直接将分片键值组合以及分片操作符透传至分片算法,完全由应用开发者实现,提供最大的灵活度。

行表达式分片策略

对应InlineShardingStrategy。使用Groovy的表达式,提供对SQL语句中的=和IN的分片操作支持,只支持单分片键。对于简单的分片算法,可以通过简单的配置使用,从而避免繁琐的Java代码开发,如:t_user_$->{u_id % 8}表示t_user表根据u_id模8,而分成8张表,表名称为t_user_0t_user_7

Hint分片策略

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

不分片策略

对应NoneShardingStrategy。不分片的策略。

SQL Hint

对于分片字段非SQL决定,而由其他外置条件决定的场景,可使用SQL Hint灵活的注入分片字段。例:内部系统,按照员工登录主键分库,而数据库中并无此字段。SQL Hint支持通过Java API和SQL注释(待实现)两种方式使用。

实战–自定义复合分片策略

由于目的为贴近实战,因此着重讲解如何实现复杂分片策略,即实现ComplexShardingStrategy接口定制生产可用的分片策略。

AdminIdShardingAlgorithm 复合分片算法代码如下:


import com.google.common.collect.Range;
import io.shardingjdbc.core.api.algorithm.sharding.ListShardingValue;
import io.shardingjdbc.core.api.algorithm.sharding.PreciseShardingValue;
import io.shardingjdbc.core.api.algorithm.sharding.RangeShardingValue;
import io.shardingjdbc.core.api.algorithm.sharding.ShardingValue;
import io.shardingjdbc.core.api.algorithm.sharding.complex.ComplexKeysShardingAlgorithm;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
 
import java.util.*;
 

public class AdminIdShardingAlgorithm implements ComplexKeysShardingAlgorithm {
 
    private Logger logger = Logger.getLogger(getClass());
 
    @Override
    public Collection<String> doSharding(Collection<String> availableTargetNames, Collection<ShardingValue> shardingValues) {
        Collection<String> routTables = new HashSet<String>();
        if (shardingValues != null) {
            for (ShardingValue shardingValue : shardingValues) {
 
                // eq in 条件
                if (shardingValue instanceof ListShardingValue) {
                    ListShardingValue listShardingValue = (ListShardingValue) shardingValue;
                    Collection<Comparable> values = listShardingValue.getValues();
                    if (values != null) {
                        Iterator<Comparable> it = values.iterator();
                        while (it.hasNext()) {
                            Comparable value = it.next();
                            String routTable = getRoutTable(shardingValue.getLogicTableName(), value);
                            if (StringUtils.isNotBlank(routTable)) {
                                routTables.add(routTable);
                            }
                        }
                    }
 
                    // eq 条件
                } else if (shardingValue instanceof PreciseShardingValue) {
                    PreciseShardingValue preciseShardingValue = (PreciseShardingValue) shardingValue;
 
                    Comparable value = preciseShardingValue.getValue();
                    String routTable = getRoutTable(shardingValue.getLogicTableName(), value);
                    if (StringUtils.isNotBlank(routTable)) {
                        routTables.add(routTable);
                    }
                    // between 条件
                } else if (shardingValue instanceof RangeShardingValue) {
                    RangeShardingValue rangeShardingValue = (RangeShardingValue) shardingValue;
                    Range<Comparable> valueRange = rangeShardingValue.getValueRange();
                    Comparable lowerEnd = valueRange.lowerEndpoint();
                    Comparable upperEnd = valueRange.upperEndpoint();
 
                    Collection<String> tables = getRoutTables(shardingValue.getLogicTableName(), lowerEnd, upperEnd);
                    if (tables != null && tables.size() > 0) {
                        routTables.addAll(tables);
                    }
                }
 
                if (routTables != null && routTables.size() > 0) {
                    return routTables;
                }
            }
        }
 
        throw new UnsupportedOperationException();

    }
 
    private String getRoutTable(String logicTable, Comparable keyValue) {
        Map<String, List<KeyShardingRange>> keyRangeMap = KeyShardingRangeConfig.getKeyRangeMap();
 
        List<KeyShardingRange> keyShardingRanges = keyRangeMap.get(KeyShardingRangeConfig.SHARDING_ID_KEY);
 
        if (keyValue != null && keyShardingRanges != null) {
            if (keyValue instanceof Integer) {
                keyValue = Long.valueOf(((Integer) keyValue).intValue());
            }
            for (KeyShardingRange range : keyShardingRanges) {
                if (keyValue.compareTo(range.getMin()) >= 0 && keyValue.compareTo(range.getMax()) <= 0) {
                    return logicTable + range.getTableKey();
                }
            }
        }
        return null;
    }
    private Collection<String> getRoutTables(String logicTable, Comparable lowerEnd, Comparable upperEnd) {
        Map<String, List<KeyShardingRange>> keyRangeMap = KeyShardingRangeConfig.getKeyRangeMap();
 
        List<KeyShardingRange> keyShardingRanges = keyRangeMap.get(KeyShardingRangeConfig.SHARDING_CONTENT_ID_KEY);
        Set<String> routTables = new HashSet<String>();
        if (lowerEnd != null && upperEnd != null && keyShardingRanges != null) {
            if (lowerEnd instanceof Integer) {
                lowerEnd = Long.valueOf(((Integer) lowerEnd).intValue());
            }
 
            if (upperEnd instanceof Integer) {
                upperEnd = Long.valueOf(((Integer) upperEnd).intValue());
            }
            boolean start = false;
            for (KeyShardingRange range : keyShardingRanges) {
                if (lowerEnd.compareTo(range.getMin()) >= 0 && lowerEnd.compareTo(range.getMax()) <= 0) {
                    start = true;
                }
                if (start) {
                    routTables.add(logicTable + range.getTableKey());
                }
                if (upperEnd.compareTo(range.getMin()) >= 0 && upperEnd.compareTo(range.getMax()) <= 0) {
                    break;
                }
            }
        }
        return routTables;
    }
}

范围 map 如下:


import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
 

public class KeyShardingRangeConfig {
 
    private static Map<String, List<KeyShardingRange>> keyRangeMap = new LinkedHashMap<String, List<KeyShardingRange>>();
 
    public static final String SHARDING_ORDER_ID_KEY = "id";
 
    public static final String SHARDING_USER_ID_KEY = "adminId";
 
    public static final String SHARDING_DATE_KEY = "createTime";
 
    static {
        List<KeyShardingRange> idRanges = new ArrayList<KeyShardingRange>();
        idRanges.add(new KeyShardingRange(0, "_0", 0L, 4000000L));
        idRanges.add(new KeyShardingRange(1, "_1", 4000001L, 8000000L));
        idRanges.add(new KeyShardingRange(2, "_2", 8000001L, 12000000L));
        idRanges.add(new KeyShardingRange(3, "_3", 12000001L, 16000000L));
        idRanges.add(new KeyShardingRange(4, "_4", 16000001L, 2000000L));
        keyRangeMap.put(SHARDING_ID_KEY, idRanges);
 
        List<KeyShardingRange> contentIdRanges = new ArrayList<KeyShardingRange>();
        contentIdRanges.add(new KeyShardingRange(0, "_0", 0L, 4000000L));
        contentIdRanges.add(new KeyShardingRange(1, "_1", 4000001L, 8000000L));
        contentIdRanges.add(new KeyShardingRange(2, "_2", 8000001L, 12000000L));
        contentIdRanges.add(new KeyShardingRange(3, "_3", 12000001L, 16000000L));
        contentIdRanges.add(new KeyShardingRange(4, "_4", 16000001L, 2000000L));
        keyRangeMap.put(SHARDING_CONTENT_ID_KEY, contentIdRanges);
 
        List<KeyShardingRange> timeRanges = new ArrayList<KeyShardingRange>();
        timeRanges.add(new KeyShardingRange("_0", 20170701L, 20171231L));
        timeRanges.add(new KeyShardingRange("_1", 20180101L, 20180630L));
        timeRanges.add(new KeyShardingRange("_2", 20180701L, 20181231L));
        timeRanges.add(new KeyShardingRange("_3", 20190101L, 20190630L));
        timeRanges.add(new KeyShardingRange("_4", 20190701L, 20191231L));
        keyRangeMap.put(SHARDING_DATE_KEY, timeRanges);
    }
 
    public static Map<String, List<KeyShardingRange>> getKeyRangeMap() {
        return keyRangeMap;
    }
}

核心逻辑解析

梳理一下逻辑,首先介绍一下该方法的入参

参数名                                         解释

availableTargetNames     有效的物理数据源,即配置文件中的 t_order_0,t_order_1,t_order_2,t_order_3

shardingValues             分片属性,如:{“columnName”:”order_id”,”logicTableName”:”t_order”,”values”:[“UD020003011903261545436593200002”]} ,包含:分片列名,逻辑表名,当前列的具体分片值

该方法返回值为

参数名                                                 解释

Collection<String>      分片结果,可以是目标数据源,也可以是目标数据表,此处为数据源

接着回来看业务逻辑,伪代码如下

首先打印了一下数据源集合 availableTargetNames 以及 分片属性 shardingValues的值,执行测试用例后,日志输出为:


availableTargetNames:["t_order_0","t_order_1","t_order_2","t_order_3"],
shardingValues:[{"columnName":"user_id","logicTableName":"t_order","values":["UD020003011903261545436593200002"]},
                {"columnName":"order_id","logicTableName":"t_order","values":["OD000000011903261545475143200001"]}]

从日志可以看出,我们可以在该路由方法中取到配置时的物理数据源列表,以及在运行时获取本次执行时的路由属性及其值

完整的逻辑流程如下:

  • 定义一个集合用于放置最终匹配好的路由数据源,接着对shardingValues进行遍历,目的为至少命中一个路由键
  • 遍历shardingValues循环体中,打印了当前循环的shardingValue,即实际的分片键的数值,如:订单号、用户id等。通过getIndex方法,获取该分片键值中包含的物理数据源索引
  • 接着遍历数据源列表availableTargetNames,截取当前循环对应availableTargetName的索引值,(eg: ds0则取0,ds1则取1…以此类推)将该配置的物理数据源索引与 第2步 中解析到的数据源路由索引进行比较,两者相等则表名我们期望将该数据路由到该匹配到的数据源。
  • 执行这个过程,直到匹配到一个路由键则停止循环,之所以这么做是因为我们是复合分片,至少要匹配到一个路由规则,才能停止循环,最终将路由到的物理数据源(ds0/ds1/ds2/ds3)通过add方法添加到事先定义好的集合中并返回给框架。
  • 逻辑结束。

小结

本文中,基本完成了Sharding-JDBC中复合分片路由算法的自定义实现,并经过测试验证符合预期,该实现方案在生产上已经经历过考验。定义分片路由策略的核心还是要熟悉ComplexKeysShardingAlgorithm,对如何解析 doSharding(CollectionavailableTargetNames, CollectionshardingValues)的参数有明确的认识,最简单的方法就是实际打印一下参数,相信会让你更加直观的感受到作者优良的接口设计能力,站在巨人的肩膀上我们能看到更远。

到此这篇关于Sharding-Jdbc 自定义复合分片的实现的文章就介绍到这了,更多相关Sharding-Jdbc 自定义复合分片内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Sharding-Jdbc 自定义复合分片的实现(分库分表)

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Sharding-Jdbc自定义复合分片的实现方法

这篇文章主要讲解了“Sharding-Jdbc自定义复合分片的实现方法”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Sharding-Jdbc自定义复合分片的实现方法”吧!目录Shardin
2023-06-20

SpringBoot整合sharding-jdbc实现自定义分库分表的方法是什么

这篇文章主要讲解了“SpringBoot整合sharding-jdbc实现自定义分库分表的方法是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“SpringBoot整合sharding-j
2023-06-25

SpringBoot怎么整合sharding-jdbc实现分库分表与读写分离

本篇内容主要讲解“SpringBoot怎么整合sharding-jdbc实现分库分表与读写分离”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“SpringBoot怎么整合sharding-jdbc
2023-06-25

SpringBoot2如何整合Sharding-Jdbc中间件实现数据分库分表

小编给大家分享一下SpringBoot2如何整合Sharding-Jdbc中间件实现数据分库分表,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!一、水平分割1、水平分库1)、概念:以字段为依据,按照一定策略,将一个库中的数据
2023-06-02

Mysql数据分片技术(二)——轻量分库分表框架Sharding-jdbc实例

1. 初识shardingJdbc2. sharding-jdbc四种配置方式3. YAML配置方式及mysql环境准备4. sharding-jdbc分库分表实例测试5. 轻量的sharding-jdbc在我目前工作项目的集成方案1. 初识shardingJ

	Mysql数据分片技术(二)——轻量分库分表框架Sharding-jdbc实例
2016-04-27

如何使用sharding-jdbc实现水平分库+水平分表

这篇文章给大家分享的是有关如何使用sharding-jdbc实现水平分库+水平分表的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。分库分表策略:将id为偶数的存入到库1中,奇数存入到库2中,在每个库中,再根据学生的
2023-06-22

Sharding-JDBC自动实现MySQL读写分离的示例代码怎么编写

Sharding-JDBC自动实现MySQL读写分离的示例代码怎么编写,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。一、ShardingSphere和Shard
2023-06-25

android中自定义View之复合控件的示例分析

这篇文章给大家分享的是有关android中自定义View之复合控件的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。复合控件可以很好地创建出具有重用功能的控件集合。很多的APP都有一些共通的UI界面,为了统
2023-06-15

DEDECMS实现自定义表单(模型)分步提交实现思路

一般的企业会遇到各类用户调查,我此前也用DEDE的自定义表单和模型,完成了一些诸如报名等系统的开发。 现在遇到一个用户需求,即,要求将一个表单(或者模型,以下统称表单)分成多步提交实现,以解决部分问卷内容过多,以减轻用户因内容繁杂产生DoS
2022-06-12

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录