行表达式,适用于单表无限分片,简化分库配置,理论上一个表可以无限向下
版本说明,写在前面的话
👀别像我一样没有注意,一步踩进5.0的坑中哦,这个真心是有点坑的,不能选择官方最新的内测5.0的版本,文档竟然和代码不匹配有想法的请直接看我的第一篇文章:Shardingsphere结合ES、Mysql MHA、Logstash实现全家桶
这篇文章就不写废话了,直接上干货,下载源码后,maven项目首先要做的事情,请自行处理,无法启动服务的话,和前辈请教一下,实在不行,运行官方提供的服务包吧。
服务包下载
源码运行
注意,我下面的代理服务全部基于4.1.1的源码进行,没有用服务包,需要用docker的可以按照官方自己做镜像,4.x版本的容器镜像,我没有在网上找到
可视化工具连接配置说明
必须按照如下配置进行访问,否则无法正常使用工具连接服务,会一直跳,no database select,请注意,后面不再说明
SQL分库分表概述
逻辑表 水平拆分的数据库(表)的相同逻辑和数据结构表的总称。例:订单数据根据主键尾数拆分为10张表,分别是 在分片的数据库中真实存在的物理表。即上个示例中的 数据分片的最小单元。由数据源名称和数据表组成,例: 指分片规则一致的主表和子表。例如: SELECT i.* FROM t_order o JOIN t_order_item i ON o.order_id=i.order_id WHERE o.order_id in (10, 11);
在不配置绑定表关系时,假设分片键 SELECT i.* FROM t_order_0 o JOIN t_order_item_0 i ON o.order_id=i.order_id WHERE o.order_id in (10, 11); SELECT i.* FROM t_order_0 o JOIN t_order_item_1 i ON o.order_id=i.order_id WHERE o.order_id in (10, 11); SELECT i.* FROM t_order_1 o JOIN t_order_item_0 i ON o.order_id=i.order_id WHERE o.order_id in (10, 11); SELECT i.* FROM t_order_1 o JOIN t_order_item_1 i ON o.order_id=i.order_id WHERE o.order_id in (10, 11); 在配置绑定表关系后,路由的SQL应该为2条: SELECT i.* FROM t_order_0 o JOIN t_order_item_0 i ON o.order_id=i.order_id WHERE o.order_id in (10, 11); SELECT i.* FROM t_order_1 o JOIN t_order_item_1 i ON o.order_id=i.order_id WHERE o.order_id in (10, 11); 其中 指所有的分片数据源中都存在的表,表结构和表中的数据在每个数据库中均完全一致。适用于数据量不大且需要与海量数据的表进行关联查询的场景,例如:字典表。 |
分片
分片键 用于分片的数据库字段,是将数据库(表)水平拆分的关键字段。例:将订单表中的订单主键的尾数取模分片,则订单主键为分片字段。 SQL中如果无分片字段,将执行全路由,性能较差。 除了对单分片字段的支持,ShardingSphere也支持根据多个字段进行分片。 分片算法通过分片算法将数据分片,支持通过 目前提供4种分片算法。由于分片算法和业务实现紧密相关,因此并未提供内置分片算法,而是通过分片策略将各种场景提炼出来,提供更高层级的抽象,并提供接口让应用开发者自行实现分片算法。
对应PreciseShardingAlgorithm,用于处理使用单一键作为分片键的=与IN进行分片的场景。需要配合StandardShardingStrategy使用。
对应RangeShardingAlgorithm,用于处理使用单一键作为分片键的BETWEEN AND、>、<、>=、<=进行分片的场景。需要配合StandardShardingStrategy使用。
对应ComplexKeysShardingAlgorithm,用于处理使用多键作为分片键进行分片的场景,包含多个分片键的逻辑较复杂,需要应用开发者自行处理其中的复杂度。需要配合ComplexShardingStrategy使用。
对应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代码开发,如:
对应HintShardingStrategy。通过Hint指定分片值而非从SQL中提取分片值的方式进行分片的策略。
对应NoneShardingStrategy。不分片的策略。 SQL Hint对于分片字段非SQL决定,而由其他外置条件决定的场景,可使用SQL Hint灵活的注入分片字段。例:内部系统,按照员工登录主键分库,而数据库中并无此字段。SQL Hint支持通过Java API和SQL注释(待实现)两种方式使用。 |
配置说明
分片规则 分片规则配置的总入口。包含数据源配置、表配置、绑定表配置以及读写分离配置等。 数据源配置真实数据源列表。 表配置逻辑表名称、数据节点与分表规则的配置。 数据节点配置用于配置逻辑表与真实表的映射关系。可分为均匀分布和自定义分布两种形式。
指数据表在每个数据源内呈现均匀分布的态势,例如: db0 ├── t_order0 └── t_order1 db1 ├── t_order0 └── t_order1 那么数据节点的配置如下: db0.t_order0, db0.t_order1, db1.t_order0, db1.t_order1
指数据表呈现有特定规则的分布,例如: db0 ├── t_order0 └── t_order1 db1 ├── t_order2 ├── t_order3 └── t_order4 那么数据节点的配置如下: db0.t_order0, db0.t_order1, db1.t_order2, db1.t_order3, db1.t_order4
对于分片策略存有数据源分片策略和表分片策略两种维度。
对应于DatabaseShardingStrategy。用于配置数据被分配的目标数据源。
对应于TableShardingStrategy。用于配置数据被分配的目标表,该目标表存在与该数据的目标数据源内。故表分片策略是依赖与数据源分片策略的结果的。 两种策略的API完全相同。 自增主键生成策略通过在客户端生成自增主键替换以数据库原生自增主键的方式,做到分布式主键无重复。 行表达式,适用于单表无限分片,简化分库配置,理论上一个表可以无限向下语法说明行表达式的使用非常直观,只需要在配置中使用
行表达式中如果出现连续多个 例如,以下行表达式: ${['online', 'offline']}_table${1..3}
最终会解析为: online_table1, online_table2, online_table3, offline_table1, offline_table2, offline_table3
对于均匀分布的数据节点,如果数据结构如下: db0 ├── t_order0 └── t_order1 db1 ├── t_order0 └── t_order1 用行表达式可以简化为: db${0..1}.t_order${0..1}
或者 db$->{0..1}.t_order$->{0..1}
对于自定义的数据节点,如果数据结构如下: db0 ├── t_order0 └── t_order1 db1 ├── t_order2 ├── t_order3 └── t_order4 用行表达式可以简化为: db0.t_order${0..1},db1.t_order${2..4}
或者 db0.t_order$->{0..1},db1.t_order$->{2..4}
对于有前缀的数据节点,也可以通过行表达式灵活配置,如果数据结构如下: db0 ├── t_order_00 ├── t_order_01 ├── t_order_02 ├── t_order_03 ├── t_order_04 ├── t_order_05 ├── t_order_06 ├── t_order_07 ├── t_order_08 ├── t_order_09 ├── t_order_10 ├── t_order_11 ├── t_order_12 ├── t_order_13 ├── t_order_14 ├── t_order_15 ├── t_order_16 ├── t_order_17 ├── t_order_18 ├── t_order_19 └── t_order_20 db1 ├── t_order_00 ├── t_order_01 ├── t_order_02 ├── t_order_03 ├── t_order_04 ├── t_order_05 ├── t_order_06 ├── t_order_07 ├── t_order_08 ├── t_order_09 ├── t_order_10 ├── t_order_11 ├── t_order_12 ├── t_order_13 ├── t_order_14 ├── t_order_15 ├── t_order_16 ├── t_order_17 ├── t_order_18 ├── t_order_19 └── t_order_20 可以使用分开配置的方式,先配置包含前缀的数据节点,再配置不含前缀的数据节点,再利用行表达式笛卡尔积的特性,自动组合即可。 上面的示例,用行表达式可以简化为: db${0..1}.t_order_0${0..9}, db${0..1}.t_order_${10..20}
或者 db->${0..1}.t_order_0$->{0..9}, db$->{0..1}.t_order_$->{10..20}
对于只有一个分片键的使用 行表达式内部的表达式本质上是一段Groovy代码,可以根据分片键进行计算的方式,返回相应的真实数据源或真实表名称。 例如:分为10个库,尾数为0的路由到后缀为0的数据源, 尾数为1的路由到后缀为1的数据源,以此类推。用于表示分片算法的行表达式为: ds${id % 10}
或者 ds$->{id % 10}
|
其他概念请自行阅览官方文档,这里只把上面的概念摘录一下,因为下面的配置要用到一些概念,方便阅览。下图的内容,标题3相关的内容,建议还是仔细阅读一下。
1.单实例分片配置
server.yaml 这个都一样,直接把配置贴在这里了,下面不写了,改其他配置文件就行 |
#governance:# name: governance_ds# registryCenter:# type: ZooKeeper# serverLists: localhost:2181# props:# retryIntervalMilliseconds: 500# timeToLiveSeconds: 60# maxRetries: 3# operationTimeoutMilliseconds: 500# overwrite: false authentication: users: root: password:12345678 sharding: password:12345678 authorizedSchemas: sharding_db props: max-connections-size-per-query:1 acceptor-size:16# The default value is available processors count * 2. executor-size:16# Infinite by default. proxy-frontend-flush-threshold:128# The default value is 128.# LOCAL: Proxy will run with LOCAL transaction.# XA: Proxy will run with XA transaction.# BASE: Proxy will run with B.A.S.E transaction. proxy-transaction-type: LOCAL proxy-opentracing-enabled:false proxy-hint-enabled:false query-with-cipher-column:true sql-show:true check-table-metadata-enabled:false |
1.1 单实例,单库,单表分片配置😉
建表 |
我就直接在第一篇文章中建立的mysql docker容器中新建了,建议还是用docker快速实现,把坑都踩一踩,别上了生成就尴尬了,使用mysql节点4,33083来建立单表示例,注意这是基于真实容器mysql的连接中新建数据表。 |
DROP SCHEMA IF EXISTS demo_singleton; CREATE SCHEMA IF NOT EXISTS demo_singleton; |
ShardingProxy代理配置 |
config-sharding.yaml |
schemaName: sharding_db # 数据源配置 dataSources: ds_0: url: jdbc:mysql://192.168.32.132:33083/demo_singleton username: root password:12345678 connectionTimeoutMilliseconds:30000 idleTimeoutMilliseconds:60000 maxLifetimeMilliseconds:1800000 maxPoolSize:50#分片规则配置 shardingRule:#表分片规则 tables: t_order: actualDataNodes: ds_${0}.t_order_${0..1} tableStrategy:inline: shardingColumn: order_id algorithmExpression: t_order_${order_id %2} keyGenerator: type: SNOWFLAKE column: order_id bindingTables:- t_order |
启动服务,连接代理 |
创建表 CREATE TABLE IF NOT EXISTS ds_0.t_order (order_id BIGINT NOT NULL AUTO_INCREMENT, user_id INT NOT NULL, status VARCHAR(50), PRIMARY KEY (order_id)); |
结果如下,配置成功
1.2 单实例,单库,多表分片配置😉
修改config-sharding.yaml |
# 逻辑库,也可以说是虚拟表空间,实际上并不存在 schemaName: sharding_db # 数据源配置 dataSources: ds_0: url: jdbc:mysql://192.168.32.132:33083/demo_singleton username: root password:12345678 connectionTimeoutMilliseconds:30000 idleTimeoutMilliseconds:60000 maxLifetimeMilliseconds:1800000 maxPoolSize:50#分片规则配置 shardingRule:#表分片规则 tables: t_order: actualDataNodes: ds_${0}.t_order_${0..1} tableStrategy:inline: shardingColumn: order_id algorithmExpression: t_order_${order_id %2} keyGenerator: type: SNOWFLAKE column: order_id t_order_item: actualDataNodes: ds_${0}.t_order_item_${0..1} tableStrategy:inline: shardingColumn: order_id algorithmExpression: t_order_item_${order_id %2} keyGenerator: type: SNOWFLAKE column: order_item_id bindingTables:- t_order |
重启服务,建表 |
CREATE TABLE IF NOT EXISTS ds_0.t_order (order_id BIGINT NOT NULL AUTO_INCREMENT, user_id INT NOT NULL, status VARCHAR(50), PRIMARY KEY (order_id)); CREATE TABLE IF NOT EXISTS ds_0.t_order_item (order_item_id BIGINT NOT NULL AUTO_INCREMENT, order_id BIGINT NOT NULL, user_id INT NOT NULL, status VARCHAR(50), PRIMARY KEY (order_item_id)); |
1.3 单实例,多库,单表分片配置😉
建库33080mysql容器服务中 |
DROP SCHEMA IF EXISTS demo_singleton_ds_a;
CREATE SCHEMA IF NOT EXISTS demo_singleton_ds_a;
DROP SCHEMA IF EXISTS demo_singleton_ds_b;
CREATE SCHEMA IF NOT EXISTS demo_singleton_ds_b; |
修改config-sharding.yaml,重启服务 |
schemaName: sharding_db dataSources: ds_0: url: jdbc:mysql://192.168.32.132:33083/demo_singleton_ds_a username: root password:12345678 connectionTimeoutMilliseconds:30000 idleTimeoutMilliseconds:60000 maxLifetimeMilliseconds:1800000 maxPoolSize:50 ds_1: url: jdbc:mysql://192.168.32.132:33083/demo_singleton_ds_b username: root password:12345678 connectionTimeoutMilliseconds:30000 idleTimeoutMilliseconds:60000 maxLifetimeMilliseconds:1800000 maxPoolSize:50 shardingRule: tables: t_order: actualDataNodes: ds_${0..1}.t_order_${0..1} tableStrategy:inline: shardingColumn: order_id algorithmExpression: t_order_${order_id %2} keyGenerator: type: SNOWFLAKE column: order_id bindingTables:- t_order defaultDatabaseStrategy:inline: shardingColumn: user_id algorithmExpression: ds_${user_id %2} defaultTableStrategy: none: |
建表,连接代理服务 |
CREATE TABLE IF NOT EXISTS ds_0.t_order (order_id BIGINT NOT NULL AUTO_INCREMENT, user_id INT NOT NULL, status VARCHAR(50), PRIMARY KEY (order_id)); CREATE TABLE IF NOT EXISTS ds_1.t_order (order_id BIGINT NOT NULL AUTO_INCREMENT, user_id INT NOT NULL, status VARCHAR(50), PRIMARY KEY (order_id)); |
1.4 单实例,多库,单表分片配置😉
修改config-sharding.yaml,重启服务 |
schemaName: sharding_db dataSources: ds_0: url: jdbc:mysql://192.168.32.132:33083/demo_singleton_ds_a username: root password:12345678 connectionTimeoutMilliseconds:30000 idleTimeoutMilliseconds:60000 maxLifetimeMilliseconds:1800000 maxPoolSize:50 ds_1: url: jdbc:mysql://192.168.32.132:33083/demo_singleton_ds_b username: root password:12345678 connectionTimeoutMilliseconds:30000 idleTimeoutMilliseconds:60000 maxLifetimeMilliseconds:1800000 maxPoolSize:50 shardingRule: tables: t_order: actualDataNodes: ds_${0..1}.t_order_${0..1} tableStrategy:inline: shardingColumn: order_id algorithmExpression: t_order_${order_id %2} keyGenerator: type: SNOWFLAKE column: order_id t_order_item: actualDataNodes: ds_${0..1}.t_order_item_${0..1} tableStrategy:inline: shardingColumn: order_id algorithmExpression: t_order_item_${order_id %2} keyGenerator: type: SNOWFLAKE column: order_item_id bindingTables:- t_order,t_order_item defaultDatabaseStrategy:inline: shardingColumn: user_id algorithmExpression: ds_${user_id %2} defaultTableStrategy: none: |
在代理服务中建表 |
CREATE TABLE IF NOT EXISTS ds_0.t_order (order_id BIGINT NOT NULL AUTO_INCREMENT, user_id INT NOT NULL, status VARCHAR(50), PRIMARY KEY (order_id)); CREATE TABLE IF NOT EXISTS ds_1.t_order (order_id BIGINT NOT NULL AUTO_INCREMENT, user_id INT NOT NULL, status VARCHAR(50), PRIMARY KEY (order_id)); CREATE TABLE IF NOT EXISTS ds_0.t_order_item (order_item_id BIGINT NOT NULL AUTO_INCREMENT, order_id BIGINT NOT NULL, user_id INT NOT NULL, status VARCHAR(50), PRIMARY KEY (order_item_id)); CREATE TABLE IF NOT EXISTS ds_1.t_order_item (order_item_id BIGINT NOT NULL AUTO_INCREMENT, order_id BIGINT NOT NULL, user_id INT NOT NULL, status VARCHAR(50), PRIMARY KEY (order_item_id)); |
2.多实例分片配置
分为以下二种,与单实例类似,只不过,修改了数据源配置,贴下配置,大家做下对比,建表还是和之前的一样,就不贴了 |
建库,先在具体对应的数据源把库建好
33080实例 |
DROP SCHEMA IF EXISTS demo_many_ds_a; CREATE SCHEMA IF NOT EXISTS demo_many_ds_a; |
33081实例 |
DROP SCHEMA IF EXISTS demo_many_ds_b; CREATE SCHEMA IF NOT EXISTS demo_many_ds_b; |
2.1 多实例,多库,单表分片配置😉
schemaName: sharding_db
dataSources:
ds_0:
url: jdbc:mysql://192.168.32.132:33080/demo_many_ds_a
username: root
password:12345678
connectionTimeoutMilliseconds:30000
idleTimeoutMilliseconds:60000
maxLifetimeMilliseconds:1800000
maxPoolSize:50
ds_1:
url: jdbc:mysql://192.168.32.132:33081/demo_many_ds_b
username: root
password:12345678
connectionTimeoutMilliseconds:30000
idleTimeoutMilliseconds:60000
maxLifetimeMilliseconds:1800000
maxPoolSize:50
shardingRule:
tables:
t_order:
actualDataNodes: ds_${0..1}.t_order_${0..1}
tableStrategy:
inline:
shardingColumn: order_id
algorithmExpression: t_order_${order_id %2}
keyGenerator:
type: SNOWFLAKE
column: order_id
bindingTables:
- t_order
defaultDatabaseStrategy:
inline:
shardingColumn: user_id
algorithmExpression: ds_${user_id %2}
defaultTableStrategy:
none:
2.2 多实例,多库,多表分片配置😉
schemaName: sharding_db
dataSources:
ds_0:
url: jdbc:mysql://192.168.32.132:33080/demo_many_ds_a
username: root
password:12345678
connectionTimeoutMilliseconds:30000
idleTimeoutMilliseconds:60000
maxLifetimeMilliseconds:1800000
maxPoolSize:50
ds_1:
url: jdbc:mysql://192.168.32.132:33081/demo_many_ds_b
username: root
password:12345678
connectionTimeoutMilliseconds:30000
idleTimeoutMilliseconds:60000
maxLifetimeMilliseconds:1800000
maxPoolSize:50
shardingRule:
tables:
t_order:
actualDataNodes: ds_${0..1}.t_order_${0..1}
tableStrategy:
inline:
shardingColumn: order_id
algorithmExpression: t_order_${order_id %2}
keyGenerator:
type: SNOWFLAKE
column: order_id
t_order_item:
actualDataNodes: ds_${0..1}.t_order_item_${0..1}
tableStrategy:
inline:
shardingColumn: order_id
algorithmExpression: t_order_item_${order_id %2}
keyGenerator:
type: SNOWFLAKE
column: order_item_id
bindingTables:
- t_order,t_order_item
defaultDatabaseStrategy:
inline:
shardingColumn: user_id
algorithmExpression: ds_${user_id %2}
defaultTableStrategy:
none:
测试
INSERT INTO t_order (user_id, status) VALUES (1,'init');
INSERT INTO t_order (user_id, status) VALUES (1,'init');
INSERT INTO t_order (user_id, status) VALUES (2,'init');
select*from t_order;
查看实际实例中的数据,按照分片规则,user_id为1的数据,应该在ds1
条件 | 虚拟库名称 | 真实库实例 |
user_id=1 | ds_1 | 33081 |
user_id=2 | ds_0 | 33080 |
如下所示,分片规则,达到预期,分片配置成功