Logo
对象管理

对象管理 #

SphereEx-DBPlusEngine 按照逻辑库、对象层次进行管理。

逻辑库 #

逻辑库的概念 #

SphereEx-DBPlusEngine 中的逻辑库,是管理一组对象的单元。其形式上模拟了数据库的Database/Schema 的概念。

逻辑库的操作 #

  • 创建
mysql> create database mydb;
  • 删除
mysql> drop database mydb;

注意:

  1. 删除逻辑库,将导致逻辑库下的相关对象被删除,且无法恢复
  2. 逻辑库中存在未完成的作业时,无法被删除
  • 查询
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外键不支持支持
UKUK部分支持支持支持包含分片键的唯一性约束
Not NULL非空支持支持
CHECK检查部分支持支持支持不含跨分片校验的检查逻辑

限制情况 #

部分与约束相关的数据字典暂未支持,如 MySQL 下的 INFORMATION_SCHEMA.KEY_COLUMN_USAGE。

对象:索引 #

SphereEx-DBPlusEngine 本身未对底层数据源索引方面做过多处理,原生数据库支持的索引依然有效。但在数据分片场景下,数据分布在多个节点上,部分索引功能受到限制。 此外,为了满足数据分片场景下多维度查询的需求,SphereEx-DBPlusEngine 提供了全局索引的支持,通过配置全局索引规则,用户可以像使用单机索引一样使用全局索引,从而有效提升数据分片后不同维度的查询性能。

全局索引用法 #

  • 相关语法

具体参见DistSQL。除了可用上述方式创建外,还可以在分片规则中定义全局索引,然后执行 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 本身未对底层数据源视图方面做过多处理,原生数据库支持的视图依然有效。但在数据分片场景下,数据分布在多个节点上,部分视图功能受到限制。

对象:序列或自增长类型 #

序列或自增长类型,是数据库保证数据唯一性的有效手段。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 存储引擎的主键。

Snowflake 原理图

  • 格式

使用雪花算法生成的主键,二进制表示形式包含 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 影响可见性
DATABASECREATE支持回退无法执行无法执行无法执行可以执行无法执行不可见
DATABASEDROP不支持回退无法执行无法执行无法执行可以执行无法执行不可见
SCHEMACREATE支持回退无法执行无法执行无法执行可以执行无法执行不可见
SCHEMADROP不支持回退无法执行无法执行无法执行可以执行无法执行不可见
SCHEMAALTER支持回退无法执行可以执行无法执行可以执行无法执行不可见
TABLECREATE支持回退无法执行可部分执行无法执行可以执行无法执行不可见
TABLEDROP不支持回退,需重试无法执行无法执行无法执行无法执行无法执行不可见
TABLEALTER (CREATE/ADD INDEX)支持回退无法执行可以执行无法执行可以执行无法执行不可见
TABLEALTER (DROP INDEX)不支持回退,需重试无法执行可以执行无法执行可以执行无法执行不可见
TABLEALTER (RENAME INDEX)支持回退无法执行可以执行无法执行可以执行无法执行不可见
TABLEALTER (ADD COLUMN)支持回退无法执行可部分执行无法执行可以执行无法执行不可见
TABLEALTER (DROP COLUMN)不支持回退,需重试无法执行可部分执行无法执行可以执行无法执行不可见
TABLEALTER (MODIFY COLUMN)不支持回退,需重试无法执行可以执行无法执行可以执行无法执行不可见
TABLEALTER (RENAME COLUMN)支持回退无法执行可部分执行无法执行可以执行无法执行不可见
TABLEALTER (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;

限制情况 #

实验性功能,暂不推荐生产环境使用。