分库分表-分片算法运用

news2024/11/18 16:23:13

分库分表-分片算法运用

ShardingSphere 分片算法

用于将数据分片的算法,支持 =、>=、<=、>、<、BETWEEN 和 IN 进行分片。分片算法可由开发者自行 实现,也可使用 Apache ShardingSphere 内置的分片算法语法糖,灵活度非常高。原理简单来说就是在解析sql后分析对应的分片键和分库键,如果存在,则采用对应算法进行路由,改写sql,合并结果。分片算法一般包括如下算法:

自动化分片算法

分片算法语法糖,用于便捷的托管所有数据节点,使用者无需关注真实表的物理分布。包括取模、哈希、 范围、时间等常用分片算法的实现。自动化分片在实际情况中使用比较少,功能较为单一。

标准分片算法(InlineShardingStrategy)

1.用于处理使用单一键作为分片键的 =、IN、BETWEEN AND、>、<、>=、<= 进行分片的场景。

这里使用inline的语法,将userId 和2取模,得到分库的数据库是demo0还是demo1,同理,将pay_parent中id主键取模,得到对应的分表。

sharding:
  binding-tables: pay_parent,pay_parent_item
  broadcast-tables: t_address
  default-database-strategy:
    ## 实际数据库分库路由键和路由算法
    inline:
      algorithm-expression: demo$->{user_id % 2}
      sharding-column: user_id
  tables:
    pay_parent:
      ## 实际数据库分库数量和分表数量
      actual-data-nodes: demo$->{0..1}.pay_parent_$->{0..1}
      ## 表主键策略
      key-generator:
        column: id
        props:
          worker:
            id: 123
        type: SNOWFLAKE
      table-strategy:
        ## 实际数据库分表路由键和路由算法
        inline:
          algorithm-expression: pay_parent_$->{id % 2}
          sharding-column: id

2.标准分片策略(StandardShardingStrategy)

这里使用两张表,分别两个库和两个表来存储。数据库是demo0还是demo1,分表情况如下。

配置如下,User_id作为库和表的路由键,分别实现了分库分表的精准路由和范围路由算法。这两个算法作用区别是,如果解析的sql带有切确的路由字段值,“=”、“IN”之类运算符相对较多的场景。,这里是user_id,那么会使用精准路由算法去路由具体的库和表;如果是带有范围sql例如使用“>”、“<”、“BETWEEN ... AND ...”之类运算符相对较多的场景。这种语法,那么会走范围路由算法。

sharding:
  binding-tables: pay_parent,pay_parent_item
  broadcast-tables: t_address
  default-database-strategy:
    ## 实际数据库分库路由键和路由算法
    standard:
      precise-algorithm-class-name: com.ilearning.common.datasource.shardingJdbc.DatabasePreciseShardingAlgorithm
      range-algorithm-class-name: com.ilearning.common.datasource.shardingJdbc.DatabasePreciseRangeShardingAlgorithm
      sharding-column: user_id
  tables:
    pay_parent:
      ## 实际数据库分库数量和分表数量
      actual-data-nodes: demo$->{0..1}.pay_parent_$->{0..1}
      ## 表主键策略
      key-generator:
        column: id
        props:
          worker:
            id: 123
        type: SNOWFLAKE
      table-strategy:
        ## 实际数据库分表路由键和路由算法
        standard:
          precise-algorithm-class-name: com.ilearning.common.datasource.shardingJdbc.MyPreciseTableShardingAlgorithm
          range-algorithm-class-name: com.ilearning.common.datasource.shardingJdbc.MyPreciseTableRangeShardingAlgorithm
          sharding-column: user_id
    pay_parent_item:
      actual-data-nodes: demo$->{0..1}.pay_parent_item_$->{0..1}
      key-generator:
        column: id
        props:
          worker:
            id: 123
        type: SNOWFLAKE
      table-strategy:
        standard:
          precise-algorithm-class-name: com.ilearning.common.datasource.shardingJdbc.MyPreciseTableShardingAlgorithm
          range-algorithm-class-name: com.ilearning.common.datasource.shardingJdbc.MyPreciseTableRangeShardingAlgorithm
          sharding-column: user_id

分表路由算法实现,值得注意的是availableTargetNames指需要路由的表,shardingValue指路由的键,路由的逻辑表具体值发起的主表,例如联接查询l left join r where l.user_id = xx,左表一直为逻辑表,具体的路由表需要到availableTargetNames获取。

@Component
public class MyPreciseTableShardingAlgorithm  implements PreciseShardingAlgorithm<Integer> {
​
    @Override
    public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Integer> shardingValue) {
        Integer curValue = shardingValue.getValue();
        int tableKey = curValue % 2;
        for (String targetTable : availableTargetNames) {
            if (targetTable.contains(Integer.toString(tableKey))){
                return targetTable;
            }
        }
        throw new RuntimeException("bot find tableKey");
    }
}
@Component
public class MyPreciseTableRangeShardingAlgorithm implements RangeShardingAlgorithm<Long> {
​
    @Override
    public Collection<String> doSharding(Collection<String> collection, RangeShardingValue<Long> rangeShardingValue) {
        return Arrays.asList(rangeShardingValue.getLogicTableName()+ "_0", rangeShardingValue.getLogicTableName()+ "_1");
    }
}
​

2.复杂分片策略(ComplexShardingStrategy)

它支持多分片键的复杂分片策略,配置参数:complex.sharding-columns 分片键(多个),如下面的连两个user_id,update_time两列;complex.algorithm-class-name 分片算法实现类,具体实现的算法样例如下,实现起来可根据字段的实际值返回路由表。值得注意的下,ComplexShardingStrategy解析的sql带有切确的路由字段值,“=”、“IN”之类运算符相对较多的场景。没有适配范围查询的算法,算是一个较大的缺点。

sharding:
  binding-tables: pay_parent,pay_parent_item
  broadcast-tables: t_address
  default-database-strategy:
    ## 实际数据库分库路由键和路由算法
    standard:
      precise-algorithm-class-name: com.ilearning.common.datasource.shardingJdbc.DatabasePreciseShardingAlgorithm
      range-algorithm-class-name: com.ilearning.common.datasource.shardingJdbc.DatabasePreciseRangeShardingAlgorithm
      sharding-column: user_id
  tables:
    pay_parent:
      ## 实际数据库分库数量和分表数量
      actual-data-nodes: demo$->{0..1}.pay_parent_$->{0..1}
      ## 表主键策略
      key-generator:
        column: id
        props:
          worker:
            id: 123
        type: SNOWFLAKE
      table-strategy:
        ## 实际数据库分表路由键和路由算法
        complex:
          algorithm-class-name: com.ilearning.common.datasource.shardingJdbc.MyComplexTableShardingAlgorithm
          sharding-columns: user_id,update_time
    pay_parent_item:
      actual-data-nodes: demo$->{0..1}.pay_parent_item_$->{0..1}
      key-generator:
        column: id
        props:
          worker:
            id: 123
        type: SNOWFLAKE
      table-strategy:
        standard:
          algorithm-class-name: com.ilearning.common.datasource.shardingJdbc.MyComplexTableShardingAlgorithm
          sharding-columns: user_id,update_time

@Component
public class MyComplexTableShardingAlgorithm implements ComplexKeysShardingAlgorithm<Comparable<?>> {
​
    @Override
    public Collection<String> doSharding(Collection<String> availableTargetNames, ComplexKeysShardingValue<Comparable<?>> shardingValue) {
        Collection<Comparable<?>> cidRange = shardingValue.getColumnNameAndShardingValuesMap().get("update_time");
        Collection<Comparable<?>> userIdCol = shardingValue.getColumnNameAndShardingValuesMap().get("user_id");
​
        List<String> res = new ArrayList<>();
​
        for(Object userId: userIdCol){
​
            //course_{userID%2+1}
        }
​
        return Collections.singleton("pay_parent_0");
    }
​
}

3.定制连表查询策略(HintShardingStrategy)

不需要分片键的强制分片策略。这个分片策略,简单来理解就是说,他的分片键不再跟SQL语句相关联,而是用程序另行指定。对于一些复杂的情况,例如select count(*) from (select userid from t_user where userid in (1,3,5,7,9))这样的SQL语句,就没法通过SQL语句来指定一个分片键。这个时候就可以通过程序,给他另行执行一个分片键,例如在按userid奇偶分片的策略下,可以指定1作为分片键,然后自行指定他的分片策略。

-- 不支持的复杂sql场景
-- 不支持UNION 
SELECT * FROM t_order1 UNION SELECT * FROM t_order2 INSERT INTO tbl_name (col1, col2, …) SELECT col1, col2, … FROM tbl_name WHERE col3 = ? 
-- 不支持多层子查询 
SELECT COUNT(*) FROM (SELECT * FROM t_order o WHERE o.id IN (SELECT id FROM t_order WHERE status = ?)) 
-- 不支持函数计算。ShardingSphere只能通过SQL字面提取用于分片的值 
SELECT * FROM t_order WHERE to_date(create_time, 'yyyy-mm-dd') = '2019-01-01';

    • 配置参数:hint.algorithm-class-name 分片算法实现类。

    • 实现方式:

      • algorithmClassName指向一个实现了org.apache.shardingsphere.api.sharding.hint.HintShardingAlgorithm接口的java类名。
    • 在这个算法类中,同样是需要分片键的。而分片键的指定是通过HintManager.addDatabaseShardingValue方法(分库)和HintManager.addTableShardingValue(分表)来指定。

    • 使用时要注意,这个分片键是线程隔离的,只在当前线程有效,所以通常建议使用之后立即关闭,或者用try资源方式打开。

  • 而Hint分片策略并没有完全按照SQL解析树来构建分片策略,是绕开了SQL解析的,所有对某些比较复杂的语句,Hint分片策略性能有可能会比较好。

    和其他3种不同的是,Hint 需要指定分片表 的数据库分片算法 + 表分片算法 ,才能走hint的路由策略

public boolean isRoutingByHint(TableRule tableRule) {
    return this.getDatabaseShardingStrategy(tableRule) instanceof HintShardingStrategy && this.getTableShardingStrategy(tableRule) instanceof HintShardingStrategy;
}

yaml的配置如下

tables:
  pay_parent:
    ## 实际数据库分库数量和分表数量
    actual-data-nodes: demo$->{0..1}.pay_parent_$->{0..1}
    ## 表主键策略
    key-generator:
      column: id
      props:
        worker:
          id: 123
      type: SNOWFLAKE
    table-strategy:
      ## 实际数据库分表路由键和路由算法
      hint:
        algorithm-class-name: com.ilearning.common.datasource.shardingJdbc.MyHintTableShardingAlgorithm
    database-strategy:
      ## 实际数据库分表路由键和路由算法
      hint:
        algorithm-class-name: com.ilearning.common.datasource.shardingJdbc.MyHintDatabaseShardingAlgorithm
  pay_parent_item:
    actual-data-nodes: demo$->{0..1}.pay_parent_item_$->{0..1}
    key-generator:
      column: id
      props:
        worker:
          id: 123
      type: SNOWFLAKE
    table-strategy:
      hint:
        algorithm-class-name: com.ilearning.common.datasource.shardingJdbc.MyHintTableShardingAlgorithm
    database-strategy:
      ## 实际数据库分表路由键和路由算法
      hint:
        algorithm-class-name: com.ilearning.common.datasource.shardingJdbc.MyHintDatabaseShardingAlgorithm

实际使用和路由算法如下,将userId的值作为两张表和库的路由键,路由算法比较简单,和2取模路由,实际情况中可能是更复杂的路由的算法,例如有userId+订单号一起路由的。

@Component
public class MyHintTableShardingAlgorithm implements HintShardingAlgorithm<Comparable<?>> {

    @Override
    public Collection<String> doSharding(Collection<String> availableTargetNames, HintShardingValue<Comparable<?>> shardingValue) {
        String key = shardingValue.getLogicTableName() + "_" + shardingValue.getValues().toArray()[0];
        Integer curValue = (Integer) shardingValue.getValues().toArray()[0];
        int databaseKey = curValue % 2;
        return Arrays.asList(shardingValue.getLogicTableName() + "_" + + databaseKey);
    }
}

 public CommonResult<PageResult<ParentRespVO>> getParentDetailPage(@Valid ParentPageReqVO pageVO) {
        // Hint分片策略必须要使用 HintManager工具类
        HintManager hintManager = HintManager.getInstance();
        hintManager.addDatabaseShardingValue("pay_parent", pageVO.getUserId());
        hintManager.addTableShardingValue("pay_parent",pageVO.getUserId());
        hintManager.addTableShardingValue("pay_parent_item",pageVO.getUserId());

        //在读写分离数据库中,Hint 可以强制读主库(主从复制是存在一定延时,但在业务场景中,可能更需要保证数据的实时性)
        //hintManager.setMasterRouteOnly();

        PageResult<ParentDO2> pageResult = parentService.getParentPageDetail(pageVO);
        hintManager.close();
        return success(ParentConvert.INSTANCE.convertPage2(pageResult));
    }

总结

在实施分库分表时,根据业务主体,选择合适路由字段和合适的分片算法是非常关键的,决定了业务的拓展性。这里有几个情况思考。上述将的四种路由算法各有特点,标准分片策略具有精准和范围两种sql类型,但是绑定的字段只有一个。复杂分片策略适用于多字段,但是对于范围查询还没有找到一个好的解决方案。定制连表查询策略灵活度最高,可以基于非表字段且适用于不同的sql,缺点是和业务耦合在一起,嵌入了业务代码。至于选择何种算法,这与分表的路由键、实际的业务场景有关系,尽量做到灵活度高,准确的,匹配业务的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/22217.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

uploads_labs前9题

upload-labs详解1.代码使用的函数详解2.uploads_labs1.Pass-012.Pass-023.Pass-034.Pass-045.Pass-056.Pass-067.Pass-078.Pass-089.Pass-091.代码使用的函数详解 2.uploads_labs 1.Pass-01 function checkFile() {var file document.getElementsByName(upload_file)[0].va…

一年前端面试打怪升级之路

Promise是什么? Promise 是异步编程的一种解决方案&#xff1a;从语法上讲&#xff0c;promise是一个对象&#xff0c;从它可以获取异步操作的消息&#xff1b;从本意上讲&#xff0c;它是承诺&#xff0c;承诺它过一段时间会给你一个结果。promise有三种状态&#xff1a; pe…

基于android平台下的猎聘校园系统的设计与实现

目 录 摘 要 I Abstract II 第1章 绪论 1 1.1选题背景及意义 1 1.2研究现状 1 1.3 研究内容 3 第2章 系统开发环境及相关技术 4 2.1开发环境 4 2.2 Java SDK 4 2.3 Eclipse 5 2.4 ADT 6 2.5 Android SDK 6 3.6 Android开发平台搭建 10 第3章 系统分析 12 3.1 研究目标 12 3.2 需…

矿井水深度除总氮有什么好工艺?硝酸根离子交换柱

煤矿矿井水是指在采煤过程中&#xff0c;所有渗入井下采掘空间的水&#xff0c;矿井水的排放是煤炭工业具有行业特点的污染源之一&#xff0c;量大面广&#xff0c;我国煤炭开发每年矿井的涌水量为20多亿立方米&#xff0c;其特性取决于成煤的地质环境和煤系地层的矿物化学成分…

智慧城市可视化(一)

✍️ 作者简介: 前端博主&#xff1b;CSDN博客专家&#xff1b;51CTO特邀专家博主&#xff0c;全网粉丝2W左右 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多web可视化介绍】 &#x1f947;联系作者&#xff1a;【私信&#xff1a;技术交流 | 信息分享 | 商务合作…

新Token经济模型: 解决当下GameFi困境的「新范式」

介绍&#xff1a; 当你每天打开 StepN&#xff0c;奔跑在回本的道路上&#xff0c;却发现&#xff0c;原来路可以越跑越长&#xff0c;回本成了可望而不可即的幻想&#xff0c;你是否感到些许无奈&#xff1f; 当你充满激情&#xff0c;怀揣致富的梦想&#xff0c;去寻找下一…

【Lua基础 第3章】变量、赋值语句、索引、lua中的循环、循环控制语句

文章目录&#x1f4a8;更多相关知识&#x1f447;一、变量二、赋值语句&#x1f31f;特点2演示&#x1f31f;特点3.1演示&#x1f31f;特点3.2演示三、索引四、lua的循环&#x1f538;while循环&#x1f31f;代码演示&#x1f538;for循环&#x1f31f;代码演示&#x1f538;re…

flink1.13(一)

1. 初识Flink 1.1 Flink是什么 Apache Flink 是一个框架和分布式处理引擎, 用于对无界和有界数据流进行状态计算. Flink框架处理流程如下图所示: Flink的应用场景: 1.2 为什么选择Flink 批处理和流处理流数据更真实地反应了我们的生活方式我们的目标 低延迟高吞吐结果的准…

【第九章】vim程序编辑器

文章目录vi与vimvi的使用范例按键说明一般指令模式可用的按钮说明&#xff1a;光标移动、复制贴上、搜寻取代等一般指令模式切换到编辑模式的可用的按钮说明一般指令模式切换到命令行界面的可用按钮说明vim的暂存盘、救援回复与打开时的警告讯息vim的额外功能区块选择&#xff…

AOP编程

什么是面向切面编程AOP? 在软件业&#xff0c;AOP为Aspect Oriented Programming的缩写&#xff0c;意为&#xff1a;面向切面编程&#xff0c;通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续&#xff0c;是软件开发中的一个热点&#xff…

【个人硬件开源分享】基于ESP32的能量机关主控受控模块,伏虎单手摇杆重力感应加摇杆加LCD屏幕分为左手和右手,基于PD5048A的I2C总线扩展级联器

提示&#xff1a;文章看完后&#xff0c;记得点赞&#xff01; 文章目录前言基于ESP32的能量机关主控受控模块伏虎单手摇杆I2C总线级联器MPU6050陀螺仪WS2812灯珠总结前言 这些是我业余时间做的一些电子小作品&#xff0c;包括基于ESP32的能量机关主控受控模块&#xff0c;伏虎…

在这个技术浮躁的时代也要注重架构的本质,多位大佬联袂推荐小团队构建大网站,让你少走弯路,甚至实现速成,成为优秀架构师!

作为一个技术人&#xff0c;架构师是每个人追寻的目标&#xff0c;也是个人价值和成就的一个重要标志。 那架构师是怎样炼成的呢&#xff1f;或者说&#xff0c;怎样才算是一个优秀的架构师呢&#xff1f; 我们需要几年甚至十几年的一线技术工作经验&#xff0c;我们需要正好遇…

isdigit isdecimal isnumeric 区别

一、代码测试 num "1" #unicode num.isdigit() # True num.isdecimal() # True num.isnumeric() # Truenum "1" # 全角 num.isdigit() # True num.isdecimal() # True num.isnumeric() # Truenum b"1" # byte num.isdigit() # True n…

还在手动电源模块测试吗?ATECLOUD智能云测试解放双手

江苏某生产电源的企业联系到纳米软件Namisoft&#xff0c;了解到他们目前依然使用的是全人工模式进行电源模块参数的测试&#xff0c;经常会因为手动记录数据而导致的一些数据出错&#xff0c;测试的效率也是非常低。通过网络了解到Namisoft的ATECLOUD智能云测试平台&#xff0…

Eruke是什么?怎么搭建?

Eruke是什么&#xff1f;怎么搭建?认识Eureka构成单机构建项目搭建注册中心生产者服务发现自我保护什么是自我保护&#xff1f;为什么会有自我保护&#xff1f;关闭结果演示停更认识Eureka 它是一个基于REST的服务&#xff0c;是实现服务发现与注册的组件。 构成 Eureka采用…

【愚公系列】2022年11月 uniapp专题-运行uniapp的多种方式

文章目录前言一、运行到web平台二、运行到安卓模拟器三、运行到微信开发者工具前言 uniapp本身就是一次开发多端部署&#xff0c;uniapp可以发布成各种应用包括以下几种 web版本 一、运行到web平台 点击工具栏的运行 -> 运行到浏览器 -> 选择浏览器&#xff0c;即可体…

BIOMOD2模型、MaxEnt模型物种分布模拟,生物多样性生境模拟,论文写作

目录 ①基于R语言BIOMOD2模型的物种分布模拟实践技术应用 ②基于R语言、MaxEnt模型融合技术的物种分布模拟、参数优化方法、结果分析制图与论文写作 ③基于MAXENT模型的生物多样性生境模拟与保护优先区甄选、自然保护区布局优化评估 ①基于R语言BIOMOD2模型的物种分布模拟实…

分享Markdown编写文档的技巧

Markdown 是一种轻量级标记语言&#xff0c;使用易读易写的纯文本格式编写文档&#xff0c;然后转换成有效的 XHTML&#xff08;或者HTML&#xff09;文档。Markdown有许多好用简单的用法&#xff0c;下面分享Markdown用来编写文档的技巧。 注&#xff1a;下面通过添加\反斜杠来…

Get To The Point: Summarization with Pointer-Generator Networks

论文题目&#xff1a;《Get To The Point: Summarization with Pointer-Generator Networks》 主要内容&#xff1a;seq2seq&#xff0c;文本摘要 《切中要害&#xff1a;指针生成器网络概述》 摘要 神经序列到序列模型为抽象文本摘要提供了一种可行的新方法&#xff08;这意…

TCP链接异常: SYN_RECV

1. 异常数据包分析&#xff1a; 从数据包分析来看应该是网关这边出问题了&#xff0c;应该是网关的服务程序在1217上出问题了&#xff0c; &#xff08;注意左右量变的数据包的一个syn的Seq都是1358143899&#xff09; 从重发2开始网关这边就一直认为它没有收到client回复给它…