Skip to content

Latest commit

 

History

History
221 lines (211 loc) · 14.9 KB

Sharding-Sharding-Proxy-Overview-Quickstart.md

File metadata and controls

221 lines (211 loc) · 14.9 KB

分库分表框架系列:


项目情况

ShardingSphere前身是当当网开源的Sharding-JDBC,2018年京东选择将它作为云化数据库中间件(对标阿里云的DRDS),加大投入力度,合作成立社区,更名为ShardingSphere,加入Apache孵化项目。目前包括:

  • Sharding-JDBC:客户端分库分表组件;
  • Sharding-Proxy:实现MySQL协议,面向客户端语言无关,本身采用Sharding-JDBC与后端数据库通讯,总体架构方案上与Mycat、DRDS类似;
  • Sharding-Sidecar:开发中尚未发布,类似Service Mesh的Sidecar,实现Database Mesh能力;
  • Sharding-UI:提供界面管理功能,通过Zookeeper动态管理Sharding-Proxy的配置;

Sharding-Proxy相关概念

  • 分片相关,参考核心概念-SQL核心概念-分片
    • 分库:将数据分片存入不同的数据库schema;
    • 分表:将数据分片存入同一个数据库schema下不同的表中,例如t_order_0、``t_order_1`;

      注意概念区别:Sharding-Proxy中分库分表都能用于Sharding水平拆分,分库将分片存入不同数据库,分表将分片存入同一个库下不同名称的表中。
      在解决逻辑分片与物理存储对应关系方面,DRDS采用在同一个MySQL实例下创建多个schema,每个schema作为dataNode对应一个逻辑分片。数据量或访问压力增加时,增加MySQL实例(服务器),以schema为单位迁移数据重新平衡。这种方案不涉及分片规则调整,以schema为单位的数据迁移方案很多。

    • 数据源:指分片规则中的dataSources配置,与数据库schema对应;
    • 数据节点:数据分片的最小单元,数据源和表组成,例如:ds_0.t_order_0, ds_0.t_order_1
    • 绑定表:父子关系表,分片字段和规则一样,关联数据落在同一个分片上;
    • 广播表:每个数据源中都存在的表,表结构和数据完全一样;
  • 路由:参考内核剖析-路由引擎
    • 直接路由:客户端通过hint指定分片键值。分片规则必须为Hint方式,且不分表;
    • 笛卡尔路由:指关联查询无法确定分片(非绑定表),必须按2个表数据节点的笛卡尔积组合执行;
    • 广播路由:不能确定分片的单表查询:
      • 全库表路由:在所有数据节点上执行,例如DDL、不能确定分片的DML等;
      • 全库路由:路由到每个数据库schema,例如schema层级的库设置SET命令、事务控制语句等;
      • 全实例路由:路由到每个数据库实例,例如授权语句等;
    • 单播路由:只需要路由到任意一个数据节点,例如desc t_order语句;
    • 阻断路由:不发送到后端数据库执行,例如use db_order,因为Sharding-Proxy给客户端展示的是逻辑库,后端数据库上可能根本不存在这个schema;
  • SQL改写:参考内核剖析-改写引擎,例如:
    • 补列:客户端select avg(price),proxy在各分片上执行select sum(price) as sum_price, count(price) as count_price,结果集汇总后计算avg(price)
    • 批量拆分:INSERT INTO t_order(order_id, xxx) VALUES(1, 'xxx'), (2, 'xxx'), ...;,必须拆分后发给分片节点;
    • 分页改写:limit 180, 20改写为limit 0, 200,内存中合并排序后再取分页数据;
  • 连接模式:参考内核剖析-执行引擎,因为有分表,同一个schema下可能有多个分表存在,为了平衡数据库连接资源而设计的解决方案:
    • 连接限制模式:对同一个数据节点下的分表,只使用一个连接,串行在各分表上执行;对不同数据节点仍采用多线程执行。这种模式在结果集合并时,不能采用基于游标的流式合并,只能将每个分片数据全部读取到内存,再进行合并;
    • 内存限制模式:不限制连接数,同一个数据节点中有多个分表,使用多线程、多链接并行执行。处理结果集合并时,可以采用基于游标的流式合并,节约内存开销;
  • 归并引擎:参考内核剖析-归并引擎,处理结果集合并。
    • 流式合并:前提是各分片返回的数据是有序的,使用游标,每次next取数即可完成合并操作,避免将全部数据加载到内存后再合并。
  • 分布式事务:参考分布式事务,支持XA事务、Saga柔性事务、Seata柔性事务(阿里2019开源的分布式事务框架);

演示方案说明

使用my-demo项目作为演示,演示环境和详细方案参考MyCat分库分表概览


部署Sharding-Proxy

使用Sharding-Proxy 4.0.0-RC3

  1. 下载解压。

    注意:Windows环境用WinRAR解压会将文件名较长的截断,导致启动时无法加载相关jar,报找不到Java Main Class错误。需要使用tar解压。

  2. 下载mysql-connector-java-5.1.47.tar.gz,将mysql-connector-java-5.1.47.jar拷贝到lib目录。

    使用MySQL Connector/J 8.0以上版本会报错,改回官方使用的版本。

  3. 配置分库分表规则。实现MyCat分库分表概览同等效果的规则配置如下(详细配置文件参考docs/sharding-proxy-conf):
    • server.yaml
      authentication:
        users: # 定义逻辑库用户密码
          root: # 用户名:root,可访问所有逻辑库
            password: 123
          mydemo: # 用户名:mydemo
            password: mydemo
            authorizedSchemas: db_user, db_order # 只能访问指定的逻辑库
      props:
        acceptor.size: 16  # 接收客户端请求的工作线程数,默认CPU核数*2
        proxy.transaction.type: LOCAL
        sql.show: true
    • config-user.yaml
      schemaName: db_user # 逻辑库名称
      dataSources: #数据源配置,可配置多个
        ds_0:
          url: jdbc:mysql://127.0.0.1:3306/mydemo-dn1?characterEncoding=utf8&useTimezone=true&serverTimezone=Asia/Shanghai&useSSL=false
          username: root
          password: 1234
          connectionTimeoutMilliseconds: 3000 #连接超时毫秒数
          idleTimeoutMilliseconds: 60000 #空闲连接回收超时毫秒数
          maxLifetimeMilliseconds: 1800000 #连接最大存活时间毫秒数
          maxPoolSize: 3
        ds_1: # 配置同上,省略
      shardingRule:
        tables:
          usr_user:
            actualDataNodes: ds_${0..1}.usr_user
            databaseStrategy:
              inline: 
                shardingColumn: user_id
                algorithmExpression: ds_${user_id % 2} # 简单使用inline表达式分片
            keyGenerator:
              type: SNOWFLAKE
              column: user_id
              props: 
                worker.id: 1
                max.tolerate.time.difference.milliseconds: 600000 # 允许的系统时钟回拨10分钟
          usr_user_account:
            actualDataNodes: ds_${0..1}.usr_user_account
            databaseStrategy:
              inline: 
                shardingColumn: account_hash
                algorithmExpression: ds_${account_hash % 2}
          undo_log:
            actualDataNodes: ds_0.undo_log
      defaultDataSourceName: ds_0 # 必须设置,否则使用Seata时会报错
      defaultDatabaseStrategy:
        none:
      defaultTableStrategy:
        none:
    • config-order.yaml
      schemaName: db_order
      dataSources: #数据源配置,可配置多个
        ds_0:
          url: jdbc:mysql://127.0.0.1:3306/mydemo-dn1?characterEncoding=utf8&useTimezone=true&serverTimezone=Asia/Shanghai&useSSL=false
          username: root
          password: 1234
          connectionTimeoutMilliseconds: 3000 #连接超时毫秒数
          idleTimeoutMilliseconds: 60000 #空闲连接回收超时毫秒数
          maxLifetimeMilliseconds: 1800000 #连接最大存活时间毫秒数
          maxPoolSize: 3
        # ds_1, ds_2, ds_3 配置同上,省略
      shardingRule:
        tables:
          ord_order:
            actualDataNodes: ds_${0..3}.ord_order
            databaseStrategy:
              inline: 
                shardingColumn: order_id
                algorithmExpression: ds_${order_id % 4}
          ord_order_item:
            actualDataNodes: ds_${0..3}.ord_order_item
            databaseStrategy:
              inline: 
                shardingColumn: order_id
                algorithmExpression: ds_${order_id % 4}
            keyGenerator:
              type: SNOWFLAKE
              column: order_item_id
              props: 
                worker.id: 1
                max.tolerate.time.difference.milliseconds: 600000 # 允许的系统时钟回拨10分钟
          ord_user_order:
            actualDataNodes: ds_${0..3}.ord_user_order
            databaseStrategy:
              inline: 
                shardingColumn: user_id
                algorithmExpression: ds_${user_id % 17 % 4} # SNOWFLAKE生成的user_id按4取模会导致数据分布不平衡,所以先按17取模
          undo_log:
            actualDataNodes: ds_0.undo_log
        bindingTables:
          - ord_order,ord_order_item
      defaultDataSourceName: ds_0 # 必须设置,否则使用Seata时会报错
      defaultDatabaseStrategy:
        none:
      defaultTableStrategy:
        none:
  4. 启动Sharding-Proxy
    bin\start.bat [port]
    未指定端口号,默认3307

启动后即可用mysql客户端连接3307端口,对逻辑库进行操作验证。


使用Sharding-Proxy

my-demo项目中使用Sharding-Proxy,涉及到下面几处调整:

  1. mysql-connector-java版本改为5.1.47,在parent pom.xml中。
    降低版本后,JDBC Driver类不同了,老版本使用com.mysql.jdbc.Driver,在parent pom中定义了maaven属性。

    目前Sharding-Proxy客户端和服务端都不能使用MySQL Connect/J 8.0以上版本,否则会报错。

  2. 使用Sharding-Proxy的JDBC连接参数,包括端口号、账号密码、数据库名称等,相关属性都定义在parent pom中了。

my-demo项目配置了maven profile来启用Sharding-Proxy,为package.sh指定-sharding-proxy选项打包即可:package.sh -sharding-proxy

使用限制

参考:JDBC不支持项SQL支持情况分页性能。代码中使用的SQL,最基本的CRUD应该没问题,涉及到子查询、表达式等,必须先进行测试,例如:

select m, count(m) as cnt from (select (user_id % 3) as m from usr_user) t group by m;

Sharding-Proxy和Mycat执行结果分别如下(因为Sharding-Proxy对子查询支持很弱造成):

Sharding-Proxy         MyCat
+------+------+    +------+-----+
| m    | cnt  |    | m    | cnt |
+------+------+    +------+-----+
|    0 |    7 |    |    0 |  13 |
|    1 |   12 |    |    1 |  19 |
|    2 |    3 |    |    2 |  12 |
|    0 |    6 |    +------+-----+
|    1 |    7 |
|    2 |    9 |
+------+------+
发现的问题
  • 与Seata集成时,Sharding-Proxy必须设置默认数据源defaultDataSourceName
    Seata生成回滚日志时需要获取数据库元数据信息,会使用SHOW FULL COLUMNS FROM usr_user LIKE '%'Sharding-Proxy没有设置默认数据源时会报错。
    defaultDataSourceName: ds_0
  • 与Seata集成,只能通过MySQL的GENERATED_KEY获取keyGenerator生成的值,不能使用last_insert_id()函数。
    MyBatis中的使用方法:
    @Insert("insert into usr_user (nickname, mobile, email, created_at) values (#{nickname}, #{mobile}, #{email}, #{createdAt})")
    //用 Options 代替 SelectKey
    @Options(useGeneratedKeys=true, keyProperty="userId", keyColumn="user_id")
    int createUser(User user);
  • 支持的MySQL Connector/J版本较低,MySQL官方建议针对MySQL Server 5.5, 5.6, 5.7, 8.0都使用MySQL Connector/J 8.0版本;
  • 从演示效果看,Mycat性能比Sharding-Proxy快1倍以上;
  • 初步映像Mycat在功能细节上比目前的Sharding-Proxy要好些;