关于shardingsphere:Apache-ShardingSphere-500-内核优化及升级指南

162次阅读

共计 22882 个字符,预计需要花费 58 分钟才能阅读完成。

通过近两年工夫的优化和打磨,Apache ShardingSphere 5.0.0 GA 版终于在本月正式公布,相比于 4.1.1 GA 版,5.0.0 GA 版在内核层面进行了大量的优化。首先,基于可插拔架构对内核进行了全面革新,内核中的各个性能能够任意组合并叠加应用。其次,为了晋升 SQL 分布式查问能力,5.0.0 GA 版打造了全新的 Federation 执行引擎,来满足用户简单的业务场景。此外,5.0.0 GA 版在内核性能 API 层面也进行了大量优化,旨在升高用户应用这些性能的老本。 本文将为大家具体解读 5.0.0 GA 版中的这些重大内核优化,并将比照两个 GA 版本中存在的差别,以典型的数据分片、读写拆散和加解密整合应用的场景为例,帮忙用户更好地了解这些优化并实现版本升级。

作者介绍

端正强

SphereEx 高级中间件开发工程师,Apache ShardingSphere Committer。

2018 年开始接触 Apache ShardingSphere 中间件,曾主导公司外部海量数据的分库分表,有着丰盛的实践经验;酷爱开源,乐于分享,目前专一于 Apache ShardingSphere 内核模块开发。


可拔插架构内核

Apache ShardingSphere 5.0.0 GA 版提出了全新的 Database Plus 理念,指标是构建异构数据库下层规范和生态,为用户提供精准化和差异化的能力。Database Plus 具备连贯、增量、可插拔的特点,具体来说,Apache ShardingSphere 可能连贯不同的异构数据库,基于异构数据库的根底服务,提供数据分片、数据加解密及分布式事务等增量性能。另外,通过可插拔平台,Apache ShardingSphere 提供的增量性能可能有限扩大,用户也能够依据需要灵便进行扩大。Database Plus 理念的呈现,使得 ShardingSphere 真正意义上从一个分库分表中间件变质成为一套弱小的分布式数据库生态系统。通过践行 Database Plus 理念,基于可插拔平台提供的扩大点,Apache ShardingSphere 内核也进行了全面地可插拔化革新。下图展现了全新的可插拔架构内核:

Apache ShardingSphere 内核流程中的元数据加载、SQL 解析、SQL 路由、SQL 改写、SQL 执行和后果归并,都提供了丰盛的扩大点,基于这些扩大点,Apache ShardingSphere 默认实现了数据分片、读写拆散、加解密、影子库压测及高可用等性能。

依照扩大点是基于技术还是基于性能实现,咱们能够将扩大点划分为性能扩大点和技术扩大点。Apache ShardingSphere 内核流程中,SQL 解析引擎及 SQL 执行引擎的扩大点属于技术扩大点,而元数据加载、SQL 路由引擎、SQL 改写引擎及后果归并引擎的扩大点属于性能扩大点。

SQL 解析引擎扩大点,次要包含 SQL 语法树解析及 SQL 语法树遍历两个扩大点。Apache ShardingSphere 的 SQL 解析引擎,基于这两个扩大点,默认反对了 MySQL、PostgreSQL、Oracle、SQLServer、openGauss 和 SQL92 等数据库方言的解析和遍历。用户也能够基于这两个扩大点,实现 Apache ShardingSphere SQL 解析引擎暂不反对的数据库方言,以及开发诸如 SQL 审计这样的新性能。

SQL 执行引擎扩大点依照不同的执行形式来提供扩大,目前 Apache ShardingSphere SQL 执行引擎曾经提供了单线程执行引擎和多线程执行引擎。单线程执行引擎次要用于解决蕴含事务的语句执行,多线程执行引擎则实用于不蕴含事务的场景,用于晋升 SQL 执行的性能。将来,Apache ShardingSphere 将基于执行引擎扩大点,提供诸如 MPP 执行引擎在内的更多执行引擎,满足分布式场景下 SQL 执行的要求。

基于性能扩大点,Apache ShardingSphere 提供了数据分片、读写拆散、加解密、影子库压测及高可用等性能,这些性能依据各自需要,实现了全副或者局部性能扩大点,并且在性能外部,又通过细化性能级扩大点提供了诸如分片策略、分布式 ID 生成及负载平衡算法等外部扩大点。上面是 Apache ShardingSphere 内核性能实现的扩大点:

  • 数据分片: 实现了元数据加载、SQL 路由、SQL 改写和后果归并的全副性能扩大点,在数据分片性能外部,又提供了分片算法、分布式 ID 等扩大点;
  • 读写拆散: 实现了 SQL 路由的性能扩大点,性能外部提供了负载平衡算法扩大点;
  • 加解密: 实现了元数据加载、SQL 改写和后果归并的扩大点,外部提供了加解密算法扩大点;
  • 影子库压测: 实现了 SQL 路由的扩大点,在影子库压测性能外部,提供了影子算法扩大点;
  • 高可用: 实现了 SQL 路由的扩大点。

基于这些扩大点,Apache ShardingSphere 性能的可扩大空间十分大,像多租户和 SQL 审计等性能,都能够通过扩大点无缝地集成到 Apache ShardingSphere 生态中 。此外,用户也能够依据本人的业务需要,基于扩大点实现定制化性能开发,疾速地搭建出一套分布式数据库系统。对于可插拔架构扩大点的具体阐明,能够参考 官网开发者手册:
https://shardingsphere.apache…

综合比照来看,5.0.0 GA 版可插拔架构内核和 4.1.1 GA 版内核次要的差别如下:

版本 4.1.1 GA 5.0.0 GA
定位 分库分表中间件 分布式数据库生态系统
性能 提供根底性能 提供基础设施和最佳实际
耦合 耦合较大,存在性能依赖 互相隔离,互无感知
组合应用 固定的组合形式,必须以数据分片为根底,叠加读写拆散和加解密等性能 性能自由组合,数据分片、读写拆散、影子库压测、加解密和高可用等性能能够任意叠加组合

首先,从我的项目定位上来看,5.0.0 GA 版借助可插拔架构实现了从分库分表中间件到分布式数据库生态系统的转变,各个性能都能够通过可插拔架构融入到分布式数据库生态系统中。其次,从我的项目性能上来看,4.1.1 GA 版只提供一些根底性能,而 5.0.0 GA 版则更加侧重于提供基础设施,以及一些性能的最佳实际,用户齐全能够舍弃这些性能,基于内核基础设施开发定制化性能。从性能耦合的角度来看,5.0.0 GA 版的内核性能,做到了互相隔离,互无感知,这样能够最大水平地保障内核的稳定性。最初,从性能组合应用的角度来看,5.0.0 GA 版实现了性能的层级统一,数据分片、读写拆散、影子库压测、加解密和高可用等性能,能够依照用户的需要任意组合。而在 4.1.1 GA 版中,用户在组合应用这些性能时,必须以数据分片为核心,再叠加应用其余性能。

通过这些比照能够看出,5.0.0 GA 版可插拔内核进行了全方位地加强,用户能够像搭积木一样对性能进行叠加组合,从而满足更多业务需要。然而,可插拔架构的调整也导致了内核性能的应用形式呈现了很大的变动,在文章的后续内容中,咱们会通过实例来具体介绍在 5.0.0 GA 版中如何组合应用这些性能。

Federation 执行引擎

Federation 执行引擎是 5.0.0 GA 版内核的又一大亮点性能,指标是反对那些在 4.1.1 GA 版中无奈执行的分布式查问语句,例如:跨数据库实例的关联查问及子查问。Federation 执行引擎的呈现,使得业务研发人员不用再关怀 SQL 的应用范畴,可能专一于业务性能开发,缩小了业务层面的性能限度。


上图展现了 Federation 执行引擎的解决流程,总体上来看,依然是遵循着 SQL 解析、SQL 路由、SQL 改写、SQL 执行这几个步骤,惟一的区别是 Federation 执行引擎额定引入了 SQL 优化,对分布式查问语句进行 RBO(Rule Based Optimizer)和 CBO(Cost Based Optimizer)优化,从而失去代价最小的执行打算。在 SQL 路由阶段,路由引擎会依据 SQL 语句是否跨多个数据库实例,来决定 SQL 是否通过 Federation 执行引擎来执行。

Federation 执行引擎目前处于疾速开发中,依然须要大量的优化,还是一个实验性的性能,因而默认是敞开的,如果想要体验 Federation 执行引擎,能够通过配置 sql-federation-enabled: true 来开启该性能。

Federation 执行引擎次要用来反对跨多个数据库实例的关联查问和子查问,以及局部内核不反对的聚合查问。上面咱们通过具体的场景,来理解下 Federation 执行引擎反对的语句。

  • 跨库关联查问: 当关联查问中的多个表散布在不同的数据库实例上时,由 Federation 执行引擎提供反对。

例如,在上面的数据分片配置中,t_ordert_order_item 表是多数据节点的分片表,并且未配置绑定表规定,t_usert_user_role 则是散布在不同的数据库实例上的单表。

rules:
- !SHARDING
  tables:
    t_order:
      actualDataNodes: ds_${0..1}.t_order_${0..1}
      tableStrategy:
        standard:
          shardingColumn: order_id
          shardingAlgorithmName: t_order_inline
    t_order_item:
      actualDataNodes: ds_${0..1}.t_order_item_${0..1}
      tableStrategy:
        standard:
          shardingColumn: order_id
          shardingAlgorithmName: t_order_item_inline

因为跨多个数据库实例,上面这些罕用的 SQL,会应用 Federation 执行引擎进行关联查问。

SELECT * FROM t_order o INNER JOIN t_order_item i ON o.order_id = i.order_id WHERE o.order_id = 1;
SELECT * FROM t_order o INNER JOIN t_user u ON o.user_id = u.user_id WHERE o.user_id = 1;
SELECT * FROM t_order o LEFT JOIN t_user_role r ON o.user_id = r.user_id WHERE o.user_id = 1;
SELECT * FROM t_order_item i LEFT JOIN t_user u ON i.user_id = u.user_id WHERE i.user_id = 1;
SELECT * FROM t_order_item i RIGHT JOIN t_user_role r ON i.user_id = r.user_id WHERE i.user_id = 1;
SELECT * FROM t_user u RIGHT JOIN t_user_role r ON u.user_id = r.user_id WHERE u.user_id = 1;
  • 子查问: Apache ShardingSphere 的 Simple Push Down 引擎可能反对分片条件统一的子查问,以及路由到单个分片的子查问。对于子查问和外层查问未同时指定分片键,或分片键的值不统一的场景,须要由 Federation 执行引擎来提供反对。

上面展现了一些由 Federation 执行引擎反对的子查问场景:

SELECT * FROM (SELECT * FROM t_order) o;
SELECT * FROM (SELECT * FROM t_order) o WHERE o.order_id = 1;
SELECT * FROM (SELECT * FROM t_order WHERE order_id = 1) o;
SELECT * FROM (SELECT * FROM t_order WHERE order_id = 1) o WHERE o.order_id = 2;
  • 聚合查问: 对于 Apache ShardingSphere Simple Push Down 引擎暂不反对的一些聚合查问,咱们也同样通过 Federation 执行引擎提供了反对。
SELECT user_id, SUM(order_id) FROM t_order GROUP BY user_id HAVING SUM(order_id) > 10;
SELECT (SELECT MAX(user_id) FROM t_order) a, order_id FROM t_order;
SELECT COUNT(DISTINCT user_id), SUM(order_id) FROM t_order;

Federation 执行引擎的呈现,使得 Apache ShardingSphere 分布式查问能力失去明显增强,将来 Apache ShardingSphere 将继续优化,无效升高 Federation 执行引擎的内存占用,一直晋升分布式查问的能力。对于 Federation 执行引擎反对语句的具体清单,可参考官网文档中的 实验性反对的 SQL

https://shardingsphere.apache…

内核性能 API 调整

为了升高用户应用内核性能的老本,5.0.0 GA 版在 API 层面也进行了大量的优化。首先,针对社区反馈较多的数据分片 API 过于简单、难以了解的问题,通过社区充沛探讨之后,在 5.0.0 GA 版中提供了全新的数据分片 API。同时,随着 Apache ShardingSphere 我的项目定位的变动——由传统数据库中间件变质为分布式数据库生态系统,实现透明化的数据分片性能也变得越发重要。因而,5.0.0 GA 版提供了自动化的分片策略,用户无需关怀分库分表的细节,通过指定分片数即可实现主动分片。此外,因为可插拔架构的提出,以及影子库压测等性能的进一步加强,内核性能 API 都进行了相应的优化调整。上面咱们将会从不同性能的角度,为大家具体介绍 5.0.0 GA 版 API 层面的调整。

数据分片 API 调整

在 4.x 版中,社区常常反馈数据分片的 API 过于简单,难以了解。上面是 4.1.1 GA 版中的数据分片配置,分片策略蕴含了 standardcomplexinlinehintnone 5 种策略,不同的分片策略之间参数也大不相同,导致普通用户很难了解和应用。

shardingRule:
  tables: 
    t_order:  
      databaseStrategy: 
        standard:  
          shardingColumn: order_id
          preciseAlgorithmClassName: xxx
          rangeAlgorithmClassName: xxx
        complex:  
          shardingColumns: year, month
          algorithmClassName: xxx
        hint:
          algorithmClassName: xxx
        inline:  
          shardingColumn: order_id
          algorithmExpression: ds_${order_id % 2}
        none:
      tableStrategy:
        ...

5.0.0 GA 版对分片 API 中的分片策略进行了简化,首先去除了原有的 inline 策略,只保留了 standardcomplexhintnone这四个分片策略,同时将分片算法从分片策略中抽取进去,放到 shardingAlgorithms 属性下进行独自配置,分片策略中通过指定 shardingAlgorithmName 属性进行援用即可。

rules:
- !SHARDING
  tables: 
    t_order: 
      databaseStrategy: 
        standard: 
          shardingColumn: order_id
          shardingAlgorithmName: database_inline   
        complex: 
          shardingColumns: year, month
          shardingAlgorithmName: database_complex
        hint: 
          shardingAlgorithmName: database_hint
        none:
      tableStrategy:
        ...

  shardingAlgorithms:
    database_inline:
      type: INLINE
      props:
        algorithm-expression: ds_${order_id % 2}
    database_complex:
      type: CLASS_BASED
      props:
        strategy: COMPLEX
        algorithmClassName: xxx
    database_hint:
      type: CLASS_BASED
      props:
        strategy: HINT
        algorithmClassName: xxx

下面是依据 4.1.1 GA 版分片配置批改后的配置,能够看出新的分片 API 更加简洁清晰。同时为了缩小用户的配置量,Apache ShardingSphere 提供了泛滥内置分片算法供用户抉择,用户也能够通过 CLASS_BASED 分片算法进行自定义。更多对于内置分片算法的内容,能够参考官网文档 内置算法 - 分片算法

https://shardingsphere.apache…

除了优化数据分片 API 之外,为了可能实现透明化数据分片,5.0.0 GA 版还提供了自动化的分片策略。上面展现了自动化分片策略配置和手动申明分片策略配置的差别:

rules:
- !SHARDING
  autoTables:
    # 主动分片策略
    t_order:
      actualDataSources: ds_0, ds_1
      shardingStrategy:
        standard:
          shardingColumn: order_id
          shardingAlgorithmName: auto_mod
      keyGenerateStrategy:
        column: order_id
        keyGeneratorName: snowflake
  shardingAlgorithms:
    auto_mod:
      type: MOD
      props:
        sharding-count: 4

  tables:
    # 手动申明分片策略
    t_order: 
      actualDataNodes: ds_${0..1}.t_order_${0..1}
      tableStrategy:
        standard:
          shardingColumn: order_id
          shardingAlgorithmName: table_inline
      dataBaseStrategy:
        standard:
          shardingColumn: user_id
          shardingAlgorithmName: database_inline

自动化分片策略,须要配置在 autoTables 属性下,用户只须要指定数据存储的数据源,同时通过主动分片算法指定分片数即可,不再须要通过 actualDataNodes 来手动申明数据分布,也无需专门设置分库策略和分表策略,Apache ShardingSphere 将主动实现数据分片治理。

此外,5.0.0 GA 版删除了数据分片 API 中的 defaultDataSourceName 配置。在 5.0.0 GA 版中,Apache ShardingSphere 定位为分布式数据库生态系统,用户能够像应用传统数据库一样,间接应用 Apache ShardingSphere 提供的服务,因而用户无需感知底层的数据库存储。Apache ShardingSphere 通过内置的 SingleTableRule 来治理数据分片之外的单表,帮忙用户实现单表的主动加载和路由。

5.0.0 GA 版为了进一步简化用户配置,同时配合数据分片 API 中的 defaultDatabaseStrategydefaultTableStrategy 分片策略,减少了 defaultShardingColumn 配置,作为默认的分片键。当多个表分片键雷同时,用户能够不配置 shardingColumn,应用默认的 defaultShardingColumn 配置。上面的分片配置中,t_order 表的分片策略都会应用默认的 defaultShardingColumn 配置。

rules:
- !SHARDING
  tables:
    t_order: 
      actualDataNodes: ds_${0..1}.t_order_${0..1}
      tableStrategy: 
        standard:
          shardingAlgorithmName: table_inline
  defaultShardingColumn: order_id
  defaultDatabaseStrategy:
    standard:
      shardingAlgorithmName: database_inline
  defaultTableStrategy:
    none:

读写拆散 API 调整

读写拆散 API 的基本功能,在 5.0.0 GA 版变动不大,只是由 MasterSlave 调整为 ReadWriteSplitting,其余用法基本相同。上面是 4.1.1 GA 版和 5.0.0 GA 版读写拆散 API 的比照。

# 4.1.1 GA 读写拆散 API
masterSlaveRule:
  name: ms_ds
  masterDataSourceName: master_ds
  slaveDataSourceNames:
    - slave_ds_0
    - slave_ds_1

# 5.0.0 GA 读写拆散 API
rules:
- !READWRITE_SPLITTING
  dataSources:
    pr_ds:
      writeDataSourceName: write_ds
      readDataSourceNames:
        - read_ds_0
        - read_ds_1

此外,在 5.0.0 GA 版中,基于可插拔架构开发了高可用性能,读写拆散能够配合高可用性能,提供可能主动切换主从的高可用版读写拆散,欢送大家关注高可用性能后续的官网文档及技术分享。

加解密 API 调整

5.0.0 GA 版对于加解密 API 进行了小幅度优化,减少了 table 级别的 queryWithCipherColumn 属性,不便用户可能对加解密字段的明文、密文切换进行表级别的管制,其余配置和 4.1.1 GA 版根本保持一致。

rules:
- !ENCRYPT
  encryptors:
    aes_encryptor:
      type: AES
      props:
        aes-key-value: 123456abc
    md5_encryptor:
      type: MD5
  tables:
    t_encrypt:
      columns:
        user_id:
          plainColumn: user_plain
          cipherColumn: user_cipher
          encryptorName: aes_encryptor
        order_id:
          cipherColumn: order_cipher
          encryptorName: md5_encryptor
      queryWithCipherColumn: true
  queryWithCipherColumn: false

影子库压测 API 调整

影子库压测 API,在 5.0.0 GA 版中进行了全面调整,首先删除了影子库中的逻辑列,并减少了功能强大的影子库匹配算法,用来帮忙用户实现更加灵便的路由管制。上面是 4.1.1 GA 版影子库压测的 API,总体上性能较为简单,依据逻辑列对应的值判断是否开启影子库压测。

shadowRule:
  column: shadow
  shadowMappings:
    ds: shadow_ds

5.0.0 GA 版中影子库压测 API 则更加弱小,用户能够通过 enable 属性,管制是否开启影子库压测,同时能够依照表的维度,细粒度管制须要进行影子库压测的生产表,并反对多种不同的匹配算法,例如:列值匹配算法、列正则表达式匹配算法以及 SQL 正文匹配算法。

rules:
- !SHADOW
  enable: true
  dataSources:
    shadowDataSource:
      sourceDataSourceName: ds
      shadowDataSourceName: shadow_ds
  tables:
    t_order:
      dataSourceNames:
        - shadowDataSource
      shadowAlgorithmNames:
        - user-id-insert-match-algorithm
        - simple-hint-algorithm
  shadowAlgorithms:
    user-id-insert-match-algorithm:
      type: COLUMN_REGEX_MATCH
      props:
        operation: insert
        column: user_id
        regex: "[1]"
    simple-hint-algorithm:
      type: SIMPLE_NOTE
      props:
        shadow: true
        foo: bar

在后续的技术分享文章中,咱们会对影子库压测性能进行具体介绍,此处就不开展阐明,更多影子库匹配算法能够参考官网文档 影子算法: https://shardingsphere.apache…

5.0.0 GA 降级指南

后面别离从可插拔内核架构、Federation 执行引擎以及内核性能 API 调整三个方面,具体地介绍了 5.0.0 GA 版内核的重大优化。面对两个版本存在的泛滥差别,大家最关怀的莫过于如何从 4.1.1 GA 降级到 5.0.0 GA 版本? 上面咱们将基于数据分片、读写拆散和加解密整合应用这样一个典型的场景,具体介绍下降级 5.0.0 GA 版本须要留神哪些问题。

在 4.1.1 GA 中,组合应用多个性能时,必须以数据分片为根底,而后叠加读写拆散和加解密,因而 4.1.1 GA 版中的配置通常如下:

shardingRule:
  tables:
    t_order:
      actualDataNodes: ms_ds_${0..1}.t_order_${0..1}
      tableStrategy:
        inline:
          shardingColumn: order_id
          algorithmExpression: t_order_${order_id % 2}
    t_order_item:
      actualDataNodes: ms_ds_${0..1}.t_order_item_${0..1}
      tableStrategy:
        inline:
          shardingColumn: order_id
          algorithmExpression: t_order_item_${order_id % 2}
  bindingTables:
    - t_order,t_order_item
  broadcastTables:
    - t_config
  defaultDataSourceName: ds_0
  defaultDatabaseStrategy:
    inline:
      shardingColumn: user_id
      algorithmExpression: ms_ds_${user_id % 2}
  defaultTableStrategy:
    none:

  masterSlaveRules:
    ms_ds_0:
      masterDataSourceName: ds_0
      slaveDataSourceNames:
        - ds_0_slave_0
        - ds_0_slave_1
      loadBalanceAlgorithmType: ROUND_ROBIN
    ms_ds_1:
      masterDataSourceName: ds_1
      slaveDataSourceNames:
        - ds_1_slave_0
        - ds_1_slave_1
      loadBalanceAlgorithmType: ROUND_ROBIN

  encryptRule:
    encryptors:
      aes_encryptor:
        type: aes
        props:
          aes.key.value: 123456abc
    tables:
      t_order:
        columns:
          content:
            plainColumn: content_plain
            cipherColumn: content_cipher
            encryptor: aes_encryptor
      t_user:
        columns:
          telephone:
            plainColumn: telephone_plain
            cipherColumn: telephone_cipher
            encryptor: aes_encryptor

从下面的配置文件中能够看出,t_ordert_order_item配置了分片规定,并且 t_order 表的 content 字段同时设置了加解密规定,应用 AES 算法进行加解密。t_user则是未分片的一般表,telephone字段也配置了加解密规定。另外须要留神的是,读写拆散规定和加解密规定都是以属性的模式,配置在分片规定中,这也是 4.1.1 GA 中性能依赖的具体体现,其余性能都必须以数据分片为根底。

配置实现之后,咱们启动 4.1.1 GA 版 Proxy 接入端,对 t_ordert_order_itemt_user表进行初始化。初始化语句执行的后果如下:

CREATE TABLE t_order(order_id INT(11) PRIMARY KEY, user_id INT(11), content VARCHAR(100));
# Logic SQL: CREATE TABLE t_order(order_id INT(11) PRIMARY KEY, user_id INT(11), content VARCHAR(100))
# Actual SQL: ds_0 ::: CREATE TABLE t_order_0(order_id INT(11) PRIMARY KEY, user_id INT(11), content VARCHAR(100))
# Actual SQL: ds_0 ::: CREATE TABLE t_order_1(order_id INT(11) PRIMARY KEY, user_id INT(11), content VARCHAR(100))
# Actual SQL: ds_1 ::: CREATE TABLE t_order_0(order_id INT(11) PRIMARY KEY, user_id INT(11), content VARCHAR(100))
# Actual SQL: ds_1 ::: CREATE TABLE t_order_1(order_id INT(11) PRIMARY KEY, user_id INT(11), content VARCHAR(100))

CREATE TABLE t_order_item(item_id INT(11) PRIMARY KEY, order_id INT(11), user_id INT(11), content VARCHAR(100));
# Logic SQL: CREATE TABLE t_order_item(item_id INT(11) PRIMARY KEY, order_id INT(11), user_id INT(11), content VARCHAR(100))
# Actual SQL: ds_0 ::: CREATE TABLE t_order_item_0(item_id INT(11) PRIMARY KEY, order_id INT(11), user_id INT(11), content VARCHAR(100))
# Actual SQL: ds_0 ::: CREATE TABLE t_order_item_1(item_id INT(11) PRIMARY KEY, order_id INT(11), user_id INT(11), content VARCHAR(100))
# Actual SQL: ds_1 ::: CREATE TABLE t_order_item_0(item_id INT(11) PRIMARY KEY, order_id INT(11), user_id INT(11), content VARCHAR(100))
# Actual SQL: ds_1 ::: CREATE TABLE t_order_item_1(item_id INT(11) PRIMARY KEY, order_id INT(11), user_id INT(11), content VARCHAR(100))

CREATE TABLE t_user(user_id INT(11) PRIMARY KEY, telephone VARCHAR(100));
# Logic SQL: CREATE TABLE t_user(user_id INT(11) PRIMARY KEY, telephone VARCHAR(100))
# Actual SQL: ds_0 ::: CREATE TABLE t_user(user_id INT(11) PRIMARY KEY, telephone VARCHAR(100))

t_order表分片性能路由改写失常,但加解密性能对应的改写没有可能反对,因为 4.1.1 GA 版本不反对加解密场景下 DDL 语句的改写,因而,须要用户在底层数据库上提前创立好对应的加解密表,DDL 语句反对加解密改写在 5.0.0 GA 版曾经完满反对,缩小了用户不必要的操作。

t_order_item 表因为不波及加解密,路由改写的后果失常。t_user 表同样存在加解密 DDL 语句改写的问题,并且 t_user 表被路由到了 ds_0 数据源,这是因为咱们在分片规定中配置了 defaultDataSourceName: ds_0,所以对于非分片表,都会应用这个规定进行路由。

对于 t_order 表和 t_user 表,咱们通过如下 SQL 在路由后果对应的底层数据库上,手动创立加解密表。

# ds_0 创立 t_order_0、t_order_1 和 t_user
CREATE TABLE t_order_0(order_id INT(11) PRIMARY KEY, user_id INT(11), content_plain VARCHAR(100), content_cipher VARCHAR(100))
CREATE TABLE t_order_1(order_id INT(11) PRIMARY KEY, user_id INT(11), content_plain VARCHAR(100), content_cipher VARCHAR(100))
CREATE TABLE t_user(user_id INT(11) PRIMARY KEY, telephone_plain VARCHAR(100), telephone_cipher VARCHAR(100))

# ds_1 创立 t_order_0 和 t_order_1
CREATE TABLE t_order_0(order_id INT(11) PRIMARY KEY, user_id INT(11), content_plain VARCHAR(100), content_cipher VARCHAR(100))
CREATE TABLE t_order_1(order_id INT(11) PRIMARY KEY, user_id INT(11), content_plain VARCHAR(100), content_cipher VARCHAR(100))

咱们重启 Proxy 并向 t_ordert_order_itemt_user 表增加数据。t_ordert_order_item 表在插入数据过程中,会依据分片键及配置的分片策略,路由到对应的数据节点。t_user 表则依据 defaultDataSourceName 配置路由到 ds_0数据源。

INSERT INTO t_order(order_id, user_id, content) VALUES(1, 1, 'TEST11'), (2, 2, 'TEST22'), (3, 3, 'TEST33');
# Logic SQL: INSERT INTO t_order(order_id, user_id, content) VALUES(1, 1, 'TEST11'), (2, 2, 'TEST22'), (3, 3, 'TEST33')
# Actual SQL: ds_0 ::: INSERT INTO t_order_0(order_id, user_id, content_cipher, content_plain) VALUES(2, 2, 'mzIhTs2MD3dI4fqCc5nF/Q==', 'TEST22')
# Actual SQL: ds_1 ::: INSERT INTO t_order_1(order_id, user_id, content_cipher, content_plain) VALUES(1, 1, '3qpLpG5z6AWjRX2sRKjW2g==', 'TEST11'), (3, 3, 'oVkQieUbS3l/85axrf5img==', 'TEST33')

INSERT INTO t_order_item(item_id, order_id, user_id, content) VALUES(1, 1, 1, 'TEST11'), (2, 2, 2, 'TEST22'), (3, 3, 3, 'TEST33');
# Logic SQL: INSERT INTO t_order_item(item_id, order_id, user_id, content) VALUES(1, 1, 1, 'TEST11'), (2, 2, 2, 'TEST22'), (3, 3, 3, 'TEST33')
# Actual SQL: ds_0 ::: INSERT INTO t_order_item_0(item_id, order_id, user_id, content) VALUES(2, 2, 2, 'TEST22')
# Actual SQL: ds_1 ::: INSERT INTO t_order_item_1(item_id, order_id, user_id, content) VALUES(1, 1, 1, 'TEST11'), (3, 3, 3, 'TEST33')

INSERT INTO t_user(user_id, telephone) VALUES(1, '11111111111'), (2, '22222222222'), (3, '33333333333');
# Logic SQL: INSERT INTO t_user(user_id, telephone) VALUES(1, '11111111111'), (2, '22222222222'), (3, '33333333333')
# Actual SQL: ds_0 ::: INSERT INTO t_user(user_id, telephone_cipher, telephone_plain) VALUES(1, 'jFZBCI7G9ggRktThmMlClQ==', '11111111111'), (2, 'lWrg5gaes8eptaQkUM2wtA==', '22222222222'), (3, 'jeCwC7gXus4/1OflXeGW/w==', '33333333333')

而后再执行几个简略的查问语句,看下读写拆散是否失效。依据日志能够看出,t_ordert_order_item 表,进行了加解密改写,也正确地路由到了从库。而 t_user 表依然路由到 ds_0 数据源上执行,规定中配置的读写拆散规定没有起到作用。这是因为在 4.1.1 GA 版中,读写拆散和加解密都是基于分片性能进行整合,这种计划人造限度了分片之外性能的配合应用。

SELECT * FROM t_order WHERE user_id = 1 AND order_id = 1;
# Logic SQL: SELECT * FROM t_order WHERE user_id = 1 AND order_id = 1
# Actual SQL: ds_1_slave_0 ::: SELECT order_id, user_id, content_plain, content_cipher FROM t_order_1 WHERE user_id = 1 AND order_id = 1

SELECT * FROM t_order_item WHERE user_id = 1 AND order_id = 1;
# Logic SQL: SELECT * FROM t_order_item WHERE user_id = 1 AND order_id = 1
# Actual SQL: ds_1_slave_1 ::: SELECT * FROM t_order_item_1 WHERE user_id = 1 AND order_id = 1

SELECT * FROM t_user WHERE user_id = 1;
# Logic SQL: SELECT * FROM t_user WHERE user_id = 1
# Actual SQL: ds_0 ::: SELECT user_id, telephone_plain, telephone_cipher FROM t_user WHERE user_id = 1

5.0.0 GA 版基于可插拔架构,对内核进行了全面地降级,内核中的各个性能都能够任意组合应用。同时,5.0.0 GA 版删除了须要用户额定配置的 defaultDataSourceName,默认通过 SingleTableRule 实现单表的元数据加载及路由。 上面咱们来看看雷同的性能,在 5.0.0 GA 版中是如何配置和应用的,具体配置如下:

rules:
- !SHARDING
  tables:
    t_order:
      actualDataNodes: ms_ds_${0..1}.t_order_${0..1}
      tableStrategy:
        standard:
          shardingColumn: order_id
          shardingAlgorithmName: t_order_inline
    t_order_item:
      actualDataNodes: ms_ds_${0..1}.t_order_item_${0..1}
      tableStrategy:
        standard:
          shardingColumn: order_id
          shardingAlgorithmName: t_order_item_inline
  bindingTables:
    - t_order,t_order_item
  broadcastTables:
    - t_config
  defaultDatabaseStrategy:
    standard:
      shardingColumn: user_id
      shardingAlgorithmName: database_inline
  defaultTableStrategy:
    none:

  shardingAlgorithms:
    database_inline:
      type: INLINE
      props:
        algorithm-expression: ms_ds_${user_id % 2}
    t_order_inline:
      type: INLINE
      props:
        algorithm-expression: t_order_${order_id % 2}
    t_order_item_inline:
      type: INLINE
      props:
        algorithm-expression: t_order_item_${order_id % 2}

- !READWRITE_SPLITTING
  dataSources:
    ms_ds_0:
      writeDataSourceName: ds_0
      readDataSourceNames:
        - ds_0_slave_0
        - ds_0_slave_1
      loadBalancerName: ROUND_ROBIN
    ms_ds_1:
      writeDataSourceName: ds_1
      readDataSourceNames:
        - ds_1_slave_0
        - ds_1_slave_1
      loadBalancerName: ROUND_ROBIN

- !ENCRYPT
  encryptors:
    aes_encryptor:
      type: AES
      props:
        aes-key-value: 123456abc
  tables:
    t_order:
      columns:
        content:
          plainColumn: content_plain
          cipherColumn: content_cipher
          encryptor: aes_encryptor
    t_user:
      columns:
        telephone:
          plainColumn: telephone_plain
          cipherColumn: telephone_cipher
          encryptor: aes_encryptor

首先,从配置上来看,5.0.0 GA 版和 4.1.1 GA 版 最大的区别在于不同性能之间的关系,它们是一个平级关系,不存在 4.1.1 GA 中的性能依赖,每个性能都能够通过可插拔的形式灵便加载和卸载。其次,这些性能在整合应用时,应用相似于管道的传递形式, 例如:读写拆散规定基于两组主从关系,聚合出两个逻辑数据源,别离是 ms_ds_0ms_ds_1。数据分片规定基于读写拆散聚合出的逻辑数据源,配置数据分片规定,从而又聚合出逻辑表 t_order。加解密性能则关注于列和值的改写,面向数据分片性能聚合出的逻辑表,配置加解密规定。读写拆散、数据分片和加解密性能层层传递,通过装璜模式,一直对性能进行减少。

为了比照 4.1.1 GA 版性能,咱们执行同样的初始化语句、插入语句和查问语句对 5.0.0 GA 版进行测试。

CREATE TABLE t_order(order_id INT(11) PRIMARY KEY, user_id INT(11), content VARCHAR(100));
# Logic SQL: CREATE TABLE t_order(order_id INT(11) PRIMARY KEY, user_id INT(11), content VARCHAR(100))
# Actual SQL: ds_1 ::: CREATE TABLE t_order_0(order_id INT(11) PRIMARY KEY, user_id INT(11), content_cipher VARCHAR(100), content_plain VARCHAR(100))
# Actual SQL: ds_1 ::: CREATE TABLE t_order_1(order_id INT(11) PRIMARY KEY, user_id INT(11), content_cipher VARCHAR(100), content_plain VARCHAR(100))
# Actual SQL: ds_0 ::: CREATE TABLE t_order_0(order_id INT(11) PRIMARY KEY, user_id INT(11), content_cipher VARCHAR(100), content_plain VARCHAR(100))
# Actual SQL: ds_0 ::: CREATE TABLE t_order_1(order_id INT(11) PRIMARY KEY, user_id INT(11), content_cipher VARCHAR(100), content_plain VARCHAR(100))

CREATE TABLE t_order_item(item_id INT(11) PRIMARY KEY, order_id INT(11), user_id INT(11), content VARCHAR(100));
# Logic SQL: CREATE TABLE t_order_item(item_id INT(11) PRIMARY KEY, order_id INT(11), user_id INT(11), content VARCHAR(100))
# Actual SQL: ds_1 ::: CREATE TABLE t_order_item_0(item_id INT(11) PRIMARY KEY, order_id INT(11), user_id INT(11), content VARCHAR(100))
# Actual SQL: ds_1 ::: CREATE TABLE t_order_item_1(item_id INT(11) PRIMARY KEY, order_id INT(11), user_id INT(11), content VARCHAR(100))
# Actual SQL: ds_0 ::: CREATE TABLE t_order_item_0(item_id INT(11) PRIMARY KEY, order_id INT(11), user_id INT(11), content VARCHAR(100))
# Actual SQL: ds_0 ::: CREATE TABLE t_order_item_1(item_id INT(11) PRIMARY KEY, order_id INT(11), user_id INT(11), content VARCHAR(100))

CREATE TABLE t_user(user_id INT(11) PRIMARY KEY, telephone VARCHAR(100));
# Logic SQL: CREATE TABLE t_user(user_id INT(11) PRIMARY KEY, telephone VARCHAR(100))
# Actual SQL: ds_1 ::: CREATE TABLE t_user(user_id INT(11) PRIMARY KEY, telephone_cipher VARCHAR(100), telephone_plain VARCHAR(100))

在 5.0.0 GA 版中,减少了对加解密 DDL 语句改写的反对,因而在创立 t_order 过程中,不论是数据分片、读写拆散还是加解密,路由和改写都可能失常执行。t_user 表从日志来看,被路由到 ds_1 数据源执行,在 5.0.0 GA 版中,t_user 属于单表,无需用户配置数据源,在执行建表语句时,会随机抉择一个数据源进行路由。对于单表,咱们须要保障它在逻辑库中惟一,从而保障路由后果的准确性。

INSERT INTO t_order(order_id, user_id, content) VALUES(1, 1, 'TEST11'), (2, 2, 'TEST22'), (3, 3, 'TEST33');
# Logic SQL: INSERT INTO t_order(order_id, user_id, content) VALUES(1, 1, 'TEST11'), (2, 2, 'TEST22'), (3, 3, 'TEST33')
# Actual SQL: ds_1 ::: INSERT INTO t_order_1(order_id, user_id, content_cipher, content_plain) VALUES(1, 1, '3qpLpG5z6AWjRX2sRKjW2g==', 'TEST11'), (3, 3, 'oVkQieUbS3l/85axrf5img==', 'TEST33')
# Actual SQL: ds_0 ::: INSERT INTO t_order_0(order_id, user_id, content_cipher, content_plain) VALUES(2, 2, 'mzIhTs2MD3dI4fqCc5nF/Q==', 'TEST22')

INSERT INTO t_order_item(item_id, order_id, user_id, content) VALUES(1, 1, 1, 'TEST11'), (2, 2, 2, 'TEST22'), (3, 3, 3, 'TEST33');
# Logic SQL: INSERT INTO t_order_item(item_id, order_id, user_id, content) VALUES(1, 1, 1, 'TEST11'), (2, 2, 2, 'TEST22'), (3, 3, 3, 'TEST33')
# Actual SQL: ds_1 ::: INSERT INTO t_order_item_1(item_id, order_id, user_id, content) VALUES(1, 1, 1, 'TEST11'), (3, 3, 3, 'TEST33')
# Actual SQL: ds_0 ::: INSERT INTO t_order_item_0(item_id, order_id, user_id, content) VALUES(2, 2, 2, 'TEST22')

INSERT INTO t_user(user_id, telephone) VALUES(1, '11111111111'), (2, '22222222222'), (3, '33333333333');
# Logic SQL: INSERT INTO t_user(user_id, telephone) VALUES(1, '11111111111'), (2, '22222222222'), (3, '33333333333')
# Actual SQL: ds_1 ::: INSERT INTO t_user(user_id, telephone_cipher, telephone_plain) VALUES(1, 'jFZBCI7G9ggRktThmMlClQ==', '11111111111'), (2, 'lWrg5gaes8eptaQkUM2wtA==', '22222222222'), (3, 'jeCwC7gXus4/1OflXeGW/w==', '33333333333')

在对 t_user表执行数据插入时,会依据元数据中存储的信息来进行主动路由,因为前一个步骤中 t_user 路由到了 ds_1 数据源,因而其余语句会依据 t_user: ds_1 这样的元数据进行路由解决。

SELECT * FROM t_order WHERE user_id = 1 AND order_id = 1;
# Logic SQL: SELECT * FROM t_order WHERE user_id = 1 AND order_id = 1
# Actual SQL: ds_1_slave_0 ::: SELECT `t_order_1`.`order_id`, `t_order_1`.`user_id`, `t_order_1`.`content_cipher` AS `content` FROM t_order_1 WHERE user_id = 1 AND order_id = 1

SELECT * FROM t_order_item WHERE user_id = 1 AND order_id = 1;
# Logic SQL: SELECT * FROM t_order_item WHERE user_id = 1 AND order_id = 1
# Actual SQL: ds_1_slave_1 ::: SELECT * FROM t_order_item_1 WHERE user_id = 1 AND order_id = 1

SELECT * FROM t_user WHERE user_id = 1;
# Logic SQL: SELECT * FROM t_user WHERE user_id = 1
# Actual SQL: ds_1_slave_0 ::: SELECT `t_user`.`user_id`, `t_user`.`telephone_cipher` AS `telephone` FROM t_user WHERE user_id = 1

在执行查问语句时,咱们能够发现,t_user表被路由到了 ds_1_slave_0 数据源,实现了单表的读写拆散。在 5.0.0 GA 版中,Apache ShardingSphere 内核通过元数据加载,外部保护了单表的数据分布信息,并充分考虑了不同性能组合应用的场景,使得单表也可能完满反对。

5.0.0 GA 版中还有很多新性能,降级指南中的案例只是筛选了两个 GA 版本中都可能反对的一些性能进行比照,冀望可能帮忙大家了解新性能,并顺利地实现性能降级。如果大家对可插拔架构、Federation 执行引擎或者其余的新性能感兴趣,欢送参考官网文档进行测试应用。

结语

历经两年工夫的打磨,Apache ShardingSphere 以全新的姿势展现在大家背后,可插拔架构内核为所有的开发者提供了有限的可能性,将来,咱们将基于可插拔架构内核,一直开辟新的性能,丰盛 Apache ShardingSphere 生态系统。 Federation 执行引擎则关上了分布式查问的大门,后续咱们将专一于内存及性能的优化,为大家提供更牢靠、更高效的分布式查问能力。最初,也欢送大家可能踊跃地参加进来,独特推动 Apache ShardingSphere 的倒退。

参考文档

1️⃣ Apache ShardingSphere Release Note: https://github.com/apache/sha…

2️⃣ Brand new sharding configuration API of Release 5.x: https://github.com/apache/sha…

3️⃣ Automatic Sharding Strategies for Databases and Tables: https://github.com/apache/sha…

4️⃣ 从中间件到分布式数据库生态,ShardingSphere 5.x 革新变旧

5️⃣ ShardingSphere X openGauss,将会产生怎么的化学反应

6️⃣ 奉献指南: https://shardingsphere.apache…

7️⃣ 中文社区: https://community.sphere-ex.com/

欢送增加社区经理微信(ss_assistant_1),回复“交换群”进群和泛滥 ShardingSphere 爱好者一起交换探讨!

正文完
 0