对象管理 #
SphereEx-DBPlusEngine 按照逻辑库、对象层次进行管理。
逻辑库 #
逻辑库的概念 #
SphereEx-DBPlusEngine 中的逻辑库,是管理一组对象的单元。其形式上模拟了数据库的Database/Schema 的概念。
逻辑库的操作 #
- 创建
mysql> create database mydb;
- 删除
mysql> drop database mydb;
注意:
- 删除逻辑库,将导致逻辑库下的相关对象被删除,且无法恢复
- 逻辑库中存在未完成的作业时,无法被删除
- 查询
mysql> show databases;
- 使用
mysql> use mydb;
- 其他约束
- 不支持引用其他 Database 的对象,例如 select * from db2.t1。
- 不支持修改逻辑库名称。
- 不支持设置逻辑库的字符集、排序规则、只读状态、加密状态等。
后置步骤:添加数据源 #
在完成逻辑库创建后,还不能管理表等对象。在这之前还有一个关键步骤—添加数据源。添加数据源可以理解为为后续对象增加容器,来承载这些对象。关于数据源的操作,可参考 RESOURCE 相关的 DistSQL。
- 操作示例
REGISTER STORAGE UNIT ds_3307 (
HOST=127.0.0.1,
PORT=3307,
DB=test,
USER=testuser,
PASSWORD=testpwd
)
使用约束
SphereEx-DBPlusEngine 仅支持在一个逻辑库内添加同构的数据源,同构可理解为使用相同通信协议的数据库产品。
对象:表 #
表,是数据的基本承载主体。SphereEx-DBPlusEngine 通过封装底层数据源内的原始数据表,提供对外标准对象能力,并扩展出多种表类型,可满足不同场景需求。
表概念 #
| 名称 | 可见性 | 含义 |
|---|---|---|
| 逻辑表 | 可见 | 在 SphereEx-DBPlusEngine 对底层原始表的封装,是用户操作的主体,用户可视为标准数据库表使用。 |
| 物理表 | 不可见 | 由 SphereEx-DBPlusEngine 封装的底层数据库的真实物理表。这类表用户是直接不可访问的。 |
| 分片表 | 可见 | 由 SphereEx-DBPlusEngine 实现的一种逻辑表,是在数据分片场景中具有相同结构的一组水平拆分数据库(表)的逻辑对象。 |
| 广播表 | 可见 | 由 SphereEx-DBPlusEngine 实现的一种逻辑表,是在逻辑库中所有数据源中都具有相同结构的一组数据表的逻辑对象,其结构和数据完全一致。 |
| 单表 | 可见 | 由 SphereEx-DBPlusEngine 实现的一种逻辑表,是某数据源表的上层映射。需要通过命令单独加载到逻辑库中方可使用。 |
| 加密表 | 可见 | 由 SphereEx-DBPlusEngine 实现的一种逻辑表,是在数据加密场景中一种数据库(表)的逻辑对象。 |
| 归档表 | 可见 | 由 SphereEx-DBPlusEngine 实现的一种逻辑表,其按照时间字段进行生命周期管理,可满足条件的分片实现删除或归档操作。现仅支持 MySQL |
表操作 #
针对表的操作分为两种情况,一种是对逻辑表的操作(分片表、加密表等都是逻辑表的一种),另一种对物理表的操作。针对后者,SphereEx-DBPlusEngine 的处理与在数据源的操作是一致的,需要注意的当后者关联多个数据源时,是无法在指定数据源操作的。建议创建操作在数据源进行,并刷新元数据后,再在逻辑库进行其他操作。下文主要针对第一种情况进行说明。
- 增加
SphereEx-DBPlusEngine 针对表的增加操作,共分为三个步骤。
- 第一步:添加数据源 参见之前步骤
- 第二步:创建规则 根据需要需要的功能,创建对应的规则。下面以分片为例进行说明,其他参考插件说明。
sql> CREATE SHARDING TABLE RULE t_order(
RESOURCES(ds_3307,ds_3308),
SHARDING_COLUMN=order_id,
TYPE(NAME=hash_mod,PROPERTIES("sharding-count"=4)),
KEY_GENERATE_STRATEGY(
COLUMN=order_id,
TYPE(NAME=snowflake,PROPERTIES("worker-id"=123))
)
);
- 第三步:创建表
sql> CREATE TABLE t_order (
order_id int NOT NULL,
user_id int NOT NULL,
status varchar(50) DEFAULT NULL,
PRIMARY KEY (order_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
删除
SphereEx-DBPlusEngine 针对表的增加操作,共分为两个步骤。
- 删除表
sql> DROP TABLE IF EXISTS t_order;
- 删除规则
sql> DROP SHARDING TABLE RULE t_order;
需要注意的,目前 SphereEx-DBPlusEngine 尚未提供级联删除能力,如仅删除表会出现所谓“孤儿”现象,且可能造成后续创建同名对象的错误。建议可提供 Console 进行删除处理,或通过 “show sharding table rules” 命令定期查看是否存在未删除的无效对象。
修改
SphereEx-DBPlusEngine 针对表的修改操作,分为多种情况。
- 针对规则的修改 针对规则的修改,将改变现有数据存储方式。在 SphereEx-DBPlusEngine 中通过与迁移能力相结合来完成的,这其中涉及到数据迁移问题。具体参见数据迁移
- 针对物理表的修改 针对物理表的修改,建议通过命令行或图形方式,针对逻辑表进行修改。其对应操作将下发到物理表上。针对个别 SphereEx-DBPlusEngine 无法支持的表,可通过直接修改物理表后刷新元数据的方式完成。
查询
数据查询
在 SphereEx-DBPlusEngine 中创建的逻辑表,可通过正常的 DQL 方式查询。
结构查询
支持使用 DESC 或方言(如 MySQL 中的 show create table )查看元数据。但这种方式无法查看分片等细节,仅限于数据结构的查询。
其他问题 #
- 物理表分布
逻辑表的存储是通过若干张物理表组成。逻辑表与物理表间存在映射关系,可分为均匀分布和自定义分布两种形式。前者是指物理表在每个数据源内呈现均匀分布的态势, 例如:
db0
├── t_order0
└── t_order1
db1
├── t_order0
└── t_order1
后者则可以通过表达式的配置方式,实现特定规则的分布,例如:
db0
├── t_order0
└── t_order1
db1
├── t_order2
├── t_order3
└── t_order4
- 物理表发现机制
默认物理表是无法在逻辑库中使用,需要加载进来。SphereEx-DBPlusEngine 中可通过 Load 命令完成物理表的加载,也可通过 Unload 命令取消加载。上述命令也支持通配符(如*),批量加载表。如果发生物理表的结构变更,建议使用 refresh 语句来刷新元数据。
- 同名表覆盖问题
当出现逻辑表与物理表同名的情况,SphereEx-DBPlusEngine 会优先使用逻辑表,即此时物理表无法在 SphereEx-DBPlusEngine 中识别并使用。
- 广播表
广播表,是在 SphereEx-DBPlusEngine 集群中全局分布的,即在所有数据源中都会有广播表且数据一致。默认情况下,SphereEx-DBPlusEngine 不保证广播表数据一致性。如用户有刚需,可自行通过启动 XA 事务来保障。
mysql> SHOW BROADCAST TABLE RULES;
Empty set (0.16 sec)
mysql> CREATE BROADCAST TABLE RULE t_address;
Query OK, 0 rows affected (0.05 sec)
mysql> SHOW BROADCAST TABLE RULES;
+-----------------+
| broadcast_table |
+-----------------+
| t_address |
+-----------------+
1 row in set (0.03 sec)
mysql> COUNT BROADCAST RULE;
+-----------------+-------------+-------+
| rule_name | database | count |
+-----------------+-------------+-------+
| broadcast_table | sharding_db | 1 |
+-----------------+-------------+-------+
1 row in set (0.02 sec)
mysql> CREATE TABLE IF NOT EXISTS `t_address` (
-> `address_id` bigint(20) NOT NULL,
-> `address_name` varchar(100) NOT NULL,
-> `status` varchar(50) DEFAULT NULL,
-> PRIMARY KEY (`address_id`)
-> ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Query OK, 0 rows affected (0.57 sec)
mysql> INSERT INTO t_address (address_id, address_name, status) VALUES
-> (1,'beijing','OK'),(2,'chengdu','OK');
Query OK, 2 rows affected (0.18 sec)
mysql> select * from t_address;
+------------+--------------+--------+
| address_id | address_name | status |
+------------+--------------+--------+
| 1 | beijing | OK |
| 2 | chengdu | OK |
+------------+--------------+--------+
2 rows in set (0.21 sec)
- 归档表
归档表,目前仅支持 MySQL,其底层实现是依赖于 MySQL 的分区特性。目前仅支持定期删除超期数据,尚不支持归档。支持代表时间的字段类型为代表毫秒数的bigint或date、datatime类型字段。
mysql> SHOW ARCHIVE RULES;
Empty set (0.36 sec)
mysql> CREATE ARCHIVE RULE t_order (
ARCHIVE_STRATEGY(ARCHIVE_COLUMN=createtime, ARCHIVE_ALGORITHM(TYPE(NAME="SphereEx:TIME_PARTITION", PROPERTIES("interval-unit"="MONTHS", "interval-amount"=1, "retain-amount"=12, "pre-allocate-amount"=2)))),
SCHEDULER_ALGORITHM(TYPE(NAME="SphereEx:TIMED_TASK", PROPERTIES("schedule-cron-expr"="0 15 10 ? * *", "time-zone"="+08:00")))
);
mysql> SHOW ARCHIVE TABLE RULE t_order;
+---------+----------------+-------------------------+-------------------------------------------------------------------------------+------------------------+-------------------------+--------------------------+---------------------------------------------------+
| table | archive_column | archive_algorithm_type | archive_algorithm_props | storage_algorithm_type | storage_algorithm_props | scheduler_algorithm_type | scheduler_algorithm_props |
+---------+----------------+-------------------------+-------------------------------------------------------------------------------+------------------------+-------------------------+--------------------------+---------------------------------------------------+
| t_order | createtime | SphereEx:TIME_PARTITION | interval-amount=1,pre-allocate-amount=2,interval-unit=MONTHS,retain-amount=12 | | | SphereEx:TIMED_TASK | schedule-cron-expr=0 15 10 ? * *,time-zone=+08:00 |
+---------+----------------+-------------------------+-------------------------------------------------------------------------------+------------------------+-------------------------+--------------------------+---------------------------------------------------+
mysql> CREATE TABLE `t_order` (
`order_id` bigint NOT NULL,
`user_id` int NOT NULL,
`status` varchar(50) NOT NULL,
`merchant_id` int DEFAULT NULL,
`remark` varchar(50) NOT NULL,
`createtime` datetime NOT NULL,
PRIMARY KEY (`order_id`,`creation_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
/*!50500 PARTITION BY RANGE COLUMNS(createtime)
(PARTITION p20230122 VALUES LESS THAN ('2023-01-25') ENGINE = InnoDB,
PARTITION p20230125 VALUES LESS THAN ('2023-01-28') ENGINE = InnoDB,
PARTITION p20230128 VALUES LESS THAN ('2023-01-31') ENGINE = InnoDB,
PARTITION p20230131 VALUES LESS THAN ('2023-02-03') ENGINE = InnoDB,
PARTITION p20230203 VALUES LESS THAN ('2023-02-06') ENGINE = InnoDB,
PARTITION p20230206 VALUES LESS THAN ('2023-02-09') ENGINE = InnoDB,
PARTITION p20230209 VALUES LESS THAN ('2023-02-12') ENGINE = InnoDB,
PARTITION p20230212 VALUES LESS THAN ('2023-02-15') ENGINE = InnoDB,
PARTITION p20230215 VALUES LESS THAN ('2023-02-18') ENGINE = InnoDB,
PARTITION p20230218 VALUES LESS THAN ('2023-02-21') ENGINE = InnoDB,
PARTITION p20230221 VALUES LESS THAN ('2023-02-24') ENGINE = InnoDB,
PARTITION p20230224 VALUES LESS THAN ('2023-02-27') ENGINE = InnoDB,
PARTITION p20230227 VALUES LESS THAN ('2023-03-02') ENGINE = InnoDB,
PARTITION p20230302 VALUES LESS THAN ('2023-03-05') ENGINE = InnoDB,
PARTITION p20230305 VALUES LESS THAN ('2023-03-08') ENGINE = InnoDB,
PARTITION p20230308 VALUES LESS THAN ('2023-03-11') ENGINE = InnoDB) */;
对象:字段 #
SphereEx-DBPlusEngine 通过对底层数据源原始数据的封装,提供对外标准对象能力。针对表内字段上,SphereEx-DBPlusEngine 也做了封装对外服务。从用户侧,几乎无感,可视为数据库字段同等处理。
字段类型 #
SphereEx-DBPlusEngine 支持底层数据源的所有字段类型,具体参考数据类型。
特殊场景:数据加密 #
在数据加密场景下,SphereEx-DBPlusEngine 针对字段有一些特殊处理。在实现上,SphereEx-DBPlusEngine 通过引入逻辑列来屏蔽底层加密字段的差异。可简单理解为是一层映射关系,可根据需要映射到明文字段或密文字段。可根据用户使用需求,SphereEx-DBPlusEngine 完成对应的转换过程。针对逻辑列的添加、删除是由加密规则操作来维护的,用户不能手工干预。
对象:约束 #
SphereEx-DBPlusEngine 本身未对底层数据源约束方面做过多处理,原生数据库支持的约束依然有效。但在数据分片场景下,数据分布在多个节点上,部分约束受到限制。
支持情况 #
| 约束名称 | 约束含义 | 支持情况(分片表) | 支持情况(非分片表) | 备注 |
|---|---|---|---|---|
| PK | 主键 | 部分支持 | 支持 | 支持包含分片键的主键约束 |
| FK | 外键 | 不支持 | 支持 | |
| UK | UK | 部分支持 | 支持 | 支持包含分片键的唯一性约束 |
| Not NULL | 非空 | 支持 | 支持 | |
| CHECK | 检查 | 部分支持 | 支持 | 支持不含跨分片校验的检查逻辑 |
限制情况 #
部分与约束相关的数据字典暂未支持,如 MySQL 下的 INFORMATION_SCHEMA.KEY_COLUMN_USAGE。
对象:索引 #
SphereEx-DBPlusEngine 支持基于原生数据库的本地索引(Local Index)以及基于内部索引表的全局索引(Global Index)。
本地索引和全局索引的使用方式和原生数据库的使用方式一致,用户可以通过 CREATE TABLE、ALTER TABLE ADD INDEX、ALTER TABLE DROP INDEX、CREATE INDEX 及 DROP INDEX 等语句进行索引的维护。
需要注意的是,使用全局索引时,需要提前创建全局索引策略,指定全局索引的名称、所属表、覆盖字段、存储单元及分片策略等信息,然后再执行原生的索引维护语句进行管理。
本地索引 #
- 概念
本地索引指的是索引数据与主表数据的分布规则一致,由原生数据库存储和维护的索引。SphereEx-DBPlusEngine 创建本地索引时,会经过路由、改写引擎处理后,在所有分片的真实表上创建索引。 这些本地索引和分片的真实表一一对应,本地索引里面的键只映射到对应分片真实表中的数据,不会映射到其它分片对应的真实表。
本地索引可以使用在分片表、单表和广播表中,单表和广播表由于不存在逻辑表名和真实表名之间的映射转换,所以本地索引的使用方式和原生数据库一致。 由于某些数据库的索引唯一性约束是 Schema 级别的,而分片表存在逻辑表名和真实表名之间的映射转换,如果索引名不进行改写,则会出现同一个 Schema 下多个相同索引名冲突。 为了避免 Schema 级别索引名冲突,DBPlusEngine 会判断数据库的索引唯一性级别,对于 PostgreSQL、openGauss 和 Oracle 等要求索引在 Schema 内唯一的数据库,DBPlusEngine 会对索引名进行改写,索引名改写规则如下:
真实本地索引名(user_id_idx_t_order_0) = 逻辑本地索引名(user_id_idx) + "_" + 分片真实表名(t_order_0)
注意:分片表使用本地索引时,会改写增加分片真实表名后缀,因此需要注意索引名长度限制,避免原生数据库限制导致异常。
而对于 MySQL、SQLServer 等要求索引在表内唯一的数据库,DBPlusEngine 不会对索引名进行改写。
- 语法
本地索引使用方式和原生数据库一致,索引操作语句可以参考数据库官方文档。用户执行索引操作时,推荐通过 SphereEx-DBPlusEngine 执行相关语句,内部会屏蔽逻辑本地索引和真实本地索引的映射关系等细节,用户只需要感知逻辑本地索引即可。
考虑到用户执行 DDL 语句时期望尽可能减少对业务的影响,因此在创建本地索引时,也支持用户使用支持 OnlineDDL 的第三方工具,在原生数据库上执行索引操作。
此时,如果操作的表为分片表,则需要注意索引名的改写规则,为特定数据库手动增加分片真实表名后缀,当所有节点执行成功后,使用 REFRESH TABLE METADATA t_order 刷新对应表的元数据(更多相关 DistSQL 语句请参考元数据 Metadata,元数据刷新操作建议低峰期执行)。
- 示例
下面通过示例说明本地索引配合第三方工具的用法。
-- 1. Engine Proxy 查看分片表本地索引 & 元数据
mysql> SHOW INDEX FROM t_order;
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| t_order | 0 | PRIMARY | 1 | order_id | A | 0 | NULL | NULL | | BTREE | | | YES | NULL |
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
1 row in set (0.05 sec)
mysql> SHOW TABLE METADATA t_order;
+-------------+------------+--------+---------------+
| schema_name | table_name | type | name |
+-------------+------------+--------+---------------+
| sharding_db | t_order | COLUMN | order_id |
| sharding_db | t_order | COLUMN | user_id |
| sharding_db | t_order | COLUMN | status |
| sharding_db | t_order | COLUMN | merchant_id |
| sharding_db | t_order | COLUMN | remark |
| sharding_db | t_order | COLUMN | creation_date |
+-------------+------------+--------+---------------+
6 rows in set (0.40 sec)
-- 2. 原生数据库使用第三方工具创建本地索引
-- 注意:MySQL 数据库创建索引时,由于是表级别唯一性约束,无需手动增加分片真实表名后缀,对于 PostgreSQL 等数据库,需要手动增加分片真实表名后缀
-- ds_0 数据源上执行
CREATE INDEX user_id_idx ON t_order_0 (user_id);
CREATE INDEX user_id_idx ON t_order_1 (user_id);
-- ds_1 数据源上执行
CREATE INDEX user_id_idx ON t_order_0 (user_id);
CREATE INDEX user_id_idx ON t_order_1 (user_id);
-- 3. Engine Proxy 刷新元数据
REFRESH TABLE METADATA t_order;
-- 4. Engine Proxy 查看分片表本地索引 & 元数据
mysql> SHOW INDEX FROM t_order;
+---------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+---------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| t_order | 0 | PRIMARY | 1 | order_id | A | 0 | NULL | NULL | | BTREE | | | YES | NULL |
| t_order | 1 | user_id_idx | 1 | user_id | A | 0 | NULL | NULL | | BTREE | | | YES | NULL |
+---------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
2 rows in set (0.00 sec)
mysql> SHOW TABLE METADATA t_order;
+-------------+------------+--------+---------------+
| schema_name | table_name | type | name |
+-------------+------------+--------+---------------+
| sharding_db | t_order | COLUMN | order_id |
| sharding_db | t_order | COLUMN | user_id |
| sharding_db | t_order | COLUMN | status |
| sharding_db | t_order | COLUMN | merchant_id |
| sharding_db | t_order | COLUMN | remark |
| sharding_db | t_order | COLUMN | creation_date |
| sharding_db | t_order | INDEX | user_id_idx |
+-------------+------------+--------+---------------+
7 rows in set (0.00 sec)
限制
- 使用分片表本地索引时,不支持跨分片的唯一性索引。
全局索引 #
- 概念
全局索引指的是索引数据与主表数据的分布规则不一致,由 DBPlusEngine 内部维护索引表实现的索引。用户在使用数据分片功能时,由于数据分布在多个节点上,无法支持用户的多维度查询需求。 为了满足用户常用的多维度查询需求,SphereEx-DBPlusEngine 提供了全局索引的支持,通过配置全局索引规则,用户可以像使用单机索引一样使用全局索引,从而有效提升数据分片后不同维度的查询性能。
- 语法
具体参见全局索引 DistSQL。除了可以使用 DistSQL 语句创建外,还可以在分片规则中定义全局索引 YAML 配置,然后执行 SQL 方言创建全局索引。
- 示例
下面通过示例说明下全局索引的用法。
-- 1. 创建分片规则,按照 order_id 维度分片
CREATE SHARDING TABLE RULE t_order (
STORAGE_UNITS(ds_0),
SHARDING_COLUMN=order_id, TYPE(NAME="mod", PROPERTIES("sharding-count"=10))
);
-- 2. 创建全局索引策略 & 规则,按照 user_id 维度分片
CREATE GLOBAL INDEX STRATEGY t_order (
(
GLOBAL_INDEX_NAME=t_order_user_id_idx,
COVERING_COLUMNS=order_id,
STORAGE_UNITS(ds_0),
SHARDING_COLUMN=user_id, TYPE(NAME="mod", PROPERTIES("sharding-count"=10))
),
(
GLOBAL_INDEX_NAME=t_order_merchant_id_idx,
COVERING_COLUMNS=order_id,
STORAGE_UNITS(ds_0),
SHARDING_COLUMN=merchant_id, TYPE(NAME="mod", PROPERTIES("sharding-count"=10))
),
CONSISTENCY_LEVEL=STRONG,
TABLE_ACCESS_BY_INDEX_THRESHOLD=1000,
SYNC_DELAY_MILLISECONDS_THRESHOLD=1000
);
-- 3. 查看全局索引规则
SHOW GLOBAL INDEX STRATEGIES\G;
*************************** 1. row ***************************
primary_table: t_order
global_index_table: t_order_user_id_idx
covering_columns: order_id
actual_data_nodes:
actual_data_sources: ds_0
database_strategy_type:
database_sharding_column:
database_sharding_algorithm_type:
database_sharding_algorithm_props:
table_strategy_type: STANDARD
table_sharding_column: user_id
table_sharding_algorithm_type: mod
table_sharding_algorithm_props: {"sharding-count":"10"}
*************************** 2. row ***************************
primary_table: t_order
global_index_table: t_order_merchant_id_idx
covering_columns: order_id
actual_data_nodes:
actual_data_sources: ds_0
database_strategy_type:
database_sharding_column:
database_sharding_algorithm_type:
database_sharding_algorithm_props:
table_strategy_type: STANDARD
table_sharding_column: merchant_id
table_sharding_algorithm_type: mod
table_sharding_algorithm_props: {"sharding-count":"10"}
2 rows in set (0.00 sec)
-- 4. 创建分片表
CREATE TABLE t_order(order_id INT PRIMARY KEY, user_id INT NOT NULL, status VARCHAR(50) NOT NULL, merchant_id INT NOT NULL, remark VARCHAR(50), creation_date DATE NOT NULL);
-- 5. 创建全局索引(索引名称需要和全局索引表匹配)
CREATE INDEX t_order_user_id_idx ON t_order(user_id);
CREATE INDEX t_order_merchant_id_idx ON t_order(merchant_id);
-- 6. 查看全局索引
SHOW INDEX FROM t_order;
+---------+------------+-------------------------+--------------+-------------+-----------+-------------+----------+--------+------+--------------+-----------------+-------------------+---------+------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+---------+------------+-------------------------+--------------+-------------+-----------+-------------+----------+--------+------+--------------+-----------------+-------------------+---------+------------+
| t_order | 0 | PRIMARY | 1 | order_id | A | 0 | NULL | NULL | | BTREE | | | YES | NULL |
| t_order | 1 | t_order_user_id_idx | 1 | order_id | A | 0 | NULL | NULL | | GLOBAL_INDEX | COVERING_COLUMN | STATUS: PUBLISHED | YES | NULL |
| t_order | 1 | t_order_user_id_idx | 1 | user_id | A | 0 | NULL | NULL | | GLOBAL_INDEX | | STATUS: PUBLISHED | YES | NULL |
| t_order | 1 | t_order_merchant_id_idx | 1 | order_id | A | 0 | NULL | NULL | | GLOBAL_INDEX | COVERING_COLUMN | STATUS: PUBLISHED | YES | NULL |
| t_order | 1 | t_order_merchant_id_idx | 1 | merchant_id | A | 0 | NULL | NULL | | GLOBAL_INDEX | | STATUS: PUBLISHED | YES | NULL |
+---------+------------+-------------------------+--------------+-------------+-----------+-------------+----------+--------+------+--------------+-----------------+-------------------+---------+------------+
-- 7. 写入数据
PREVIEW INSERT INTO t_order(order_id, user_id, status, merchant_id, remark, creation_date) VALUES (1, 1, 'SUCCESS', 1, 'remark', '2023-01-22');
+------------------+------------------------------------------------------------------------------------------------------------------------------------------+
| data_source_name | actual_sql |
+------------------+------------------------------------------------------------------------------------------------------------------------------------------+
| ds_0 | INSERT INTO t_order_1(order_id, user_id, status, merchant_id, remark, creation_date) VALUES (1, 1, 'SUCCESS', 1, 'remark', '2023-01-22') |
| ds_0 | INSERT INTO t_order_user_id_idx_1 (order_id, user_id) VALUES (1, 1) |
| ds_0 | INSERT INTO t_order_merchant_id_idx_1 (order_id, merchant_id) VALUES (1, 1) |
+------------------+------------------------------------------------------------------------------------------------------------------------------------------+
-- 8. 查询数据
PREVIEW SELECT * FROM t_order WHERE merchant_id = 1;
+------------------+-----------------------------------------------------------------------------------+
| data_source_name | actual_sql |
+------------------+-----------------------------------------------------------------------------------+
| ds_0 | SELECT COUNT(1) AS COUNT FROM t_order_merchant_id_idx_1 WHERE merchant_id = 1 |
| ds_0 | SELECT order_id, merchant_id FROM t_order_merchant_id_idx_1 WHERE merchant_id = 1 |
| ds_0 | SELECT * FROM t_order_1 WHERE (merchant_id = 1) AND (order_id IN (1)) |
+------------------+-----------------------------------------------------------------------------------+
使用全局索引会对写性能产生一定影响,对于写性能要求很高的业务场景需要进行性能压测后方可使用。
限制
- 使用分片表全局索引时,不支持跨分片的唯一性索引。
对象:视图 #
SphereEx-DBPlusEngine 本身未对底层数据源视图方面做过多处理,原生数据库支持的视图依然有效。但在数据分片场景下,数据分布在多个节点上,部分视图功能受到限制。具体使用说明如下:
- 支持基于单个单表,或多个相同存储节点的单表创建、修改和删除视图;
- 支持基于任意个广播表创建、修改和删除视图;
- 支持基于任意个分片表创建、修改和删除视图,视图必须和分片表一样配置分片规则,并且视图和分片表必须为绑定表关系;
- 支持基于广播表和分片表创建、修改和删除视图,分片表规则同单独使用分片表创建视图;
- 支持基于广播表和单表创建、修改和删除视图;
- 支持 MySQL
SHOW CREATE TABLE viewName查看视图的创建语句。
对象:序列或自增长类型 #
序列或自增长类型,是数据库保证数据唯一性的有效手段。SphereEx-DBPlusEngine 本身未对底层数据源序列或自增长方面做处理,原生数据库支持依然有效。但在数据分片场景下,是有所不同的,受限于分布式环境的限制,很难如单机数据库的方式支持。一般推荐在分布式环境下,使用“分布式 ID”的功能替代,SphereEx-DBPlusEngine 通过一种名为“键生成器”的方式来支持多种分布式 ID。
键生成器用法 #
- 相关语法
具体参见DistSQL。除了可用上述方式创建外,还可以在分片规则定义中直接使用。
- 生成示例
下面通过示例说明下键生成器的用法。
sql> CREATE SHARDING TABLE RULE t_order(
-> RESOURCES(ds_3307,ds_3308),
-> SHARDING_COLUMN=order_id,TYPE(NAME=hash_mod,PROPERTIES("sharding-count"=4)),
-> KEY_GENERATE_STRATEGY(COLUMN=order_id,TYPE(NAME=uuid,PROPERTIES("worker-id"=123)))
-> );
Query OK, 0 rows affected (0.03 sec)
sql> CREATE TABLE t_order (
-> order_id varchar(50) NOT NULL,
-> user_id int NOT NULL,
-> status varchar(50) DEFAULT NULL,
-> PRIMARY KEY (order_id)
-> ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
Query OK, 0 rows affected (0.12 sec)
sql> INSERT INTO t_order(user_id, status) VALUES
-> (1, 'SUCCESS'),
-> (2, 'FAIL'),
-> (3, 'FAIL'),
-> (4, 'FAIL'),
-> (5, 'FAIL');
Query OK, 5 rows affected (0.01 sec)
sql> select * from t_order;
+----------------------------------+---------+---------+
| order_id | user_id | status |
+----------------------------------+---------+---------+
| 48e10218c7414bdbaf640aea2c079d32 | 12 | FAIL |
| adcb6343fb43424baed8f297832d6897 | 9 | FAIL |
| d01fe0d8f99744949f6f734803a6fbad | 1 | SUCCESS |
| 0a52a963254f4c889924fcef27afa497 | 16 | FAIL |
| 0bee54f2ee8a4af8bb09250f01b2565b | 15 | FAIL |
+----------------------------------+---------+---------+
定义并使用键生成器,会消耗 SphereEx-DBPlusEngine 的资源,经评估测试会有 60%~70% 左右的性能损耗,对于高并发、密集插入的场景需要关注。
内置分布式 ID #
UUID #
SphereEx-DBPlusEngine 内置 UUID 算法,可生成 36 位随机字符串。
- 与 MySQL 原生 UUID() 区别
- 字段类型不同。如果原生 MySQL 是使用 BINARY 类型插入的,则使用 SphereEx-DBPlusEngine 无法插入,需定义为 VARCHAR 才可以。此处需关注未来用户迁移系统过程中的改造成本(数据类型需变化)。
- 数据分配不均衡。在 HASH 分布策略下使用 UUID 数据分布是不均衡的。
NanoID #
SphereEx-DBPlusEngine 内置 NanoID 算法,相较于 UUID 其具备更小、更安全、更快等优势。
Snowflake #
SphereEx-DBPlusEngine 内置 Snowflake 算法,相较于 UUID 其具备更小和有序性等特点。
- 原理
在同一个进程中,它首先是通过时间位保证不重复,如果时间相同则是通过序列位保证。同时由于时间位是单调递增的,且各个服务器如果大体做了时间同步,那么生成的主键在分布式环境可以认为是总体有序的,这就保证了对索引字段的插入的高效性。例如 MySQL 的 Innodb 存储引擎的主键。

- 格式
使用雪花算法生成的主键,二进制表示形式包含 4 部分,从高位到低位分表为:1bit 符号位、41bit 时间戳位、10bit 工作进程位以及 12bit 序列号位。
- 符号位(1bit)
预留的符号位,恒为零。
- 时间戳位(41bit)
41 位的时间戳可以容纳的毫秒数是 2 的 41 次幂,一年所使用的毫秒数是:365 * 24 * 60 * 60 * 1000。通过计算可知:Math.pow(2, 41) / (365 * 24 * 60 * 60 * 1000L);
结果约等于 69.73 年。SphereEx-DBPlusEngine 的雪花算法的时间纪元从 2016 年 11 月 1 日零点开始,可以使用到 2086 年,相信能满足绝大部分系统的要求。
- 工作进程位(10bit)
该标志在 Java 进程内是唯一的,如果是分布式应用部署应保证每个工作进程的 id 是不同的。该值默认为0,可通过属性设置。
- 序列号位(12bit)
该序列是用来在同一个毫秒内生成不同的 ID。如果在这个毫秒内生成的数量超过 4096 (2 的 12 次幂),那么生成器会等待到下个毫秒继续生成。
- 时钟回拨
服务器时钟回拨会导致产生重复序列,因此默认分布式主键生成器提供了一个最大容忍的时钟回拨毫秒数。如果时钟回拨的时间超过最大容忍的毫秒数阈值,则程序报错;如果在可容忍的范围内,默认分布式主键生成器会等待时钟同步到最后一次主键生成的时间后再继续工作。最大容忍的时钟回拨毫秒数的默认值为 0,可通过属性设置。
- 限制条件
使用 Snowflake 算法,SphereEx-DBPlusEngine 集群规模不能超过 1024 个计算节点。
对象:库内计算 #
针对数据库场景的内置计算对象,如存储过程、触发器、函数等,SphereEx-DBPlusEngine 提供有限支持。对于原始数据库内部对象,SphereEx-DBPlusEngine 可通过直接透传下发方式支持。对于在数据分片、读写分离等场景下,不支持上述对象。
DDL 一致性 #
针对部分数据分片的场景,SphereEx-DBPlusEngine(包括 Proxy 和 Driver 方式) 提供 DDL 一致性保证。即执行过程中,要么全部分片完成此 DDL 操作,要么全部分片取消此 DDL 操作,不会出现数据结构不一致的情况。其实现原理是当执行过程异常时,提供反向 DDL SQL,完成回退操作。当前版本已支持 MySQL、PostgreSQL、openGauss 数据库反向 SQL 的生成。对于部分操作,SphereEx-DBPlusEngine 尚不提供回退能力或需要用户手工介入重试完成。
执行中操作影响 #
| 类别 | 语句 | 支持回退 | 对 DDL 影响 | 对 DQL 影响 | 对 DML 影响 | 对 TCL 影响 | 对 DistSQL 影响 | 可见性 |
|---|---|---|---|---|---|---|---|---|
| DATABASE | CREATE | 支持回退 | 无法执行 | 无法执行 | 无法执行 | 可以执行 | 无法执行 | 不可见 |
| DATABASE | DROP | 不支持回退 | 无法执行 | 无法执行 | 无法执行 | 可以执行 | 无法执行 | 不可见 |
| SCHEMA | CREATE | 支持回退 | 无法执行 | 无法执行 | 无法执行 | 可以执行 | 无法执行 | 不可见 |
| SCHEMA | DROP | 不支持回退 | 无法执行 | 无法执行 | 无法执行 | 可以执行 | 无法执行 | 不可见 |
| SCHEMA | ALTER | 支持回退 | 无法执行 | 可以执行 | 无法执行 | 可以执行 | 无法执行 | 不可见 |
| TABLE | CREATE | 支持回退 | 无法执行 | 可部分执行 | 无法执行 | 可以执行 | 无法执行 | 不可见 |
| TABLE | DROP | 不支持回退,需重试 | 无法执行 | 无法执行 | 无法执行 | 无法执行 | 无法执行 | 不可见 |
| TABLE | ALTER (CREATE/ADD INDEX) | 支持回退 | 无法执行 | 可以执行 | 无法执行 | 可以执行 | 无法执行 | 不可见 |
| TABLE | ALTER (DROP INDEX) | 不支持回退,需重试 | 无法执行 | 可以执行 | 无法执行 | 可以执行 | 无法执行 | 不可见 |
| TABLE | ALTER (RENAME INDEX) | 支持回退 | 无法执行 | 可以执行 | 无法执行 | 可以执行 | 无法执行 | 不可见 |
| TABLE | ALTER (ADD COLUMN) | 支持回退 | 无法执行 | 可部分执行 | 无法执行 | 可以执行 | 无法执行 | 不可见 |
| TABLE | ALTER (DROP COLUMN) | 不支持回退,需重试 | 无法执行 | 可部分执行 | 无法执行 | 可以执行 | 无法执行 | 不可见 |
| TABLE | ALTER (MODIFY COLUMN) | 不支持回退,需重试 | 无法执行 | 可以执行 | 无法执行 | 可以执行 | 无法执行 | 不可见 |
| TABLE | ALTER (RENAME COLUMN) | 支持回退 | 无法执行 | 可部分执行 | 无法执行 | 可以执行 | 无法执行 | 不可见 |
| TABLE | ALTER (CHANG COLUMN) | 不支持回退,需重试 | 无法执行 | 可部分执行 | 无法执行 | 可以执行 | 无法执行 | 不可见 |
TABLE 包括分片表、广播表和单表。
操作示例(正常) #
-- 启动一致性
sql> SET DIST VARIABLE meta_data_consistency_enabled=true;
-- 确保一致性已启动
sql> SHOW DIST VARIABLE WHERE NAME = meta_data_consistency_enabled;
-- 执行 DDL
sql> alter table ...
--查看 DDL 作业
sql> show ddl list;
-- 执行正常结束后,提交作业
sql> commit ddl '<id>';
-- 执行异常后,回滚作业
sql> rollback ddl '<id>';
-- 执行中间因故障停止,可选择人工修复后重试作业
sql> start ddl '<id>';
操作示例(异常) #
sql> create table t_order (order_id INT NOT NULL, user_id INT NOT NULL, status VARCHAR(45) NULL, `test_column` bigint(20) NOT NULL DEFAULT '0', `test_column2` bigint(20) NOT NULL DEFAULT '0');
-- 模拟故障
sql> alter table `t_order` modify column `test_column` varchar(10), modify column `test_column2` varchar(10);
-- ERROR 30000 (HY000): Unknown exception: Table 'demo_ds_1.t_order_0' doesn't exist,
-- please use SHOW DDL STATUS 'j5601839ceed1d09d73d9d1f7e7f77de75228' to check status.
-- 此时会有一个存储节点ds_0上执行成功,ds_1上执行失败。由于不支持回滚,会触发job重试操作。
mysql> show ddl list;
+---------------------------------------+---------+--------------+----------------+--------+---------------------+---------------------+
| id | tables | ddl_type | job_item_count | active | create_time | stop_time |
+---------------------------------------+---------+--------------+----------------+--------+---------------------+---------------------+
| j5601839ceed1d09d73d9d1f7e7f77de75228 | t_order | ALTER_TABLE | 1 | false | 2023-02-19 16:27:
-- 修复故障
-- proxy上重试job
sql> start ddl 'j5601839ceed1d09d73d9d1f7e7f77de75228';
-- proxy查看job执行完成并自动删除
mysql> show ddl list;
Empty set (0.02 sec)
异常说明 #
针对分布式对象执行 DDL 操作,在极端情况下会出现在不同计算节点看到元数据不一致的情况。针对这种情况可通过在对应计算节点执行 REFRESH DATABASE METADATA db_name FROM GOVERNANCE CENTER; 命令手工完成元数据刷新。具体参见DistSQL。
Online DDL #
SphereEx-DBPlusEngine 针对 MySQL 数据库支持在线执行 DDL,根据不同版本实现机制有所差异,具体参见下面。当使用 Online DDL 执行 DDL 即直接开始执行,不存在 start ddl 操作。
props:
proxy-ddl-strategy: ONLINE
# Options: ONLINE, DIRECT, AUTO
# ONLINE:proxy 干预并保障 ddl 是 online 执行的,
# 如果用户指定了instant 参数并且底层是 mysql8.0,则会使用 instant 方式执行;
# 如果用户未指定 instant 参数并且底层是 mysql8.0,则会使用 instant 方式执行;
# 如果不是 mysql8.0,则使用在线拷贝数据的方式执行。
# DIRECT:proxy 干预一致性但是直接下发语句到db执行ddl
# AUTO:依据用户配置由 proxy 决定 online、instant、在线拷贝数据的方式执行
ghost-ddl-row-threshold: 1000000 # 触发复制表执行 DDL 行数阈值
ghost-ddl-auto-cutover: true # 自动切换模式。禁用则需要手动执行 COMMIT DDL 完成切换
ghost-ddl-drop-table: true # DDL 完成后自动删除原表
也可以使用 Hint 方式在语句中指定,Hint 中指定的配置优先级高于 props 中的配置。
/* SPHEREEX_HINT: PROXY_DDL_STRATEGY=AUTO, GHOST_DDL_ROW_THRESHOLD=1000000, GHOST_DDL_AUTO_CUTOVER=true, GHOST_DDL_DROP_TABLE=true */
ALTER TABLE t MODIFY COLUMN target_col BIGINT;
限制情况 #
实验性功能,暂不推荐生产环境使用。