DDL 流程概括

OceanBase 支持传统数据库的 DDL 语句,DDL 类型的 SQL 语句不会被优化器处理,而是作为 command 发送到RootServer,由 RootServer 进行处理,比如典型的建表语句。在 OceanBase 上的执行流程如下图所示:

image

DDL 产生的 schema 信息使用内部表记录,优点如下:

  • 直接利用 SQL 的事务及持久化能力
  • schema 变更信息可以通过 SQL 语句直接获取,避免了多集群之间的同步
  • 天然地享受 OceanBase 自身的多副本容灾特性

所有的 DDL 在线变更都记录在内部表中,内部表本身也支持 DDL 变更,这样使得集群升级变得非常方便。

image

__all_table_history 记录用户表每个版本的 schema 信息,如下例所示:

  1. OceanBase (root@oceanbase)> desc __all_table_history ;
  2. +--------------------------------------+---------------------+------+-----+----------------------+-----------------------------+
  3. | Field | Type | Null | Key | Default | Extra |
  4. +--------------------------------------+---------------------+------+-----+----------------------+-----------------------------+
  5. | gmt_create | timestamp(6) | YES | | CURRENT_TIMESTAMP(6) | |
  6. | gmt_modified | timestamp(6) | YES | | CURRENT_TIMESTAMP(6) | ON UPDATE CURRENT_TIMESTAMP |
  7. | tenant_id | bigint(20) | NO | PRI | NULL | |
  8. | table_id | bigint(20) | NO | PRI | NULL | |
  9. | schema_version | bigint(20) | NO | PRI | NULL | |
  10. | is_deleted | bigint(20) | NO | | NULL | |
  11. | table_name | varchar(128) | YES | | | |
  12. | database_id | bigint(20) | YES | | NULL | |
  13. | table_type | bigint(20) | YES | | NULL | |
  14. | load_type | bigint(20) | YES | | NULL | |
  15. | def_type | bigint(20) | YES | | NULL | |
  16. | rowkey_column_num | bigint(20) | YES | | NULL | |
  17. | index_column_num | bigint(20) | YES | | NULL | |

__all_core_table 记录内部表的 schema 信息(KV 方式存储),如下例所示:

  1. OceanBase (root@oceanbase)> desc __all_core_table;
  2. +--------------+----------------+------+-----+----------------------+-----------------------------+
  3. | Field | Type | Null | Key | Default | Extra |
  4. +--------------+----------------+------+-----+----------------------+-----------------------------+
  5. | gmt_create | timestamp(6) | YES | | CURRENT_TIMESTAMP(6) | |
  6. | gmt_modified | timestamp(6) | YES | | CURRENT_TIMESTAMP(6) | ON UPDATE CURRENT_TIMESTAMP |
  7. | table_name | varchar(128) | NO | PRI | NULL | |
  8. | row_id | bigint(20) | NO | PRI | NULL | |
  9. | column_name | varchar(128) | NO | PRI | NULL | |
  10. | column_value | varchar(65536) | YES | | NULL | |
  11. +--------------+----------------+------+-----+----------------------+-----------------------------+

由于支持多节点自动同步以及多版本,OceanBase 内部很多的功能模块都设计成了类 DDL 语句,所以 OceanBase 的 DDL 已经是个庞大的模块系统,包括:

  • tenant

  • user

  • database

  • table

  • table_group/partition_group

  • outline

  • planbase_line

  • package

  • privilege

  • synonym

Schema多版本

对于单机数据库,schema 对多版本的需求并不强,只需保证 schema 变更的时序性。典型的实现中,通过 DML 与 DDL 之间的锁来保持时序性。OceanBase 中 schema 支持多版本的需求体现在如下几个方面:

  • DML 与 DDL 互相不阻塞,提高性能

  • 分布式事务。对于分布式执行计划,多个机器上的schema必须保持一致

  • 版本冻结。OceanBase 是存储架构基于 LSM-Tree,当内存增量数据达到阈值时需要冻结。发起冻结的 leader 需要将schema 的版本同步给 follower, follower 才能冻结

  • 分区合并。 OceanBase 总是先冻结后合并,合并是延后调度的,当执行合并时,需要取相应冻结点的 schema

  • 异构同步。OceanBase 数据同步到其它数据库中时,是基于事务日志回放来完成的。解析历史事务日志时,需要获取相对应的 schema 信息

为了支持多版本,需要将表的变更信息全部持久化到内部表中,包括删除动作,多版本 schema 内存管理如下图所示:

image

集群同步

所有 DDL 都会被转发给 RS 来统一调度。将 schema 从内部表 load 到内存的过程称为刷 schema。RootServer 执行内部表的更新后,需要将 schema 刷到最新,以保持最新的 schema 信息。然后将新的版本号通知所有在线的 OBServer,每个OBServer 都收到通知后,此 DDL 即可正常返回。除了正常通知外,心跳包也会将最新版本号发给每个 OBServer。当OBServer 收到 DDL 变更后的版本号后,构造刷 schema 任务以刷新此 OBServer 的 schema。

全量刷新与增量刷新

Schema的更新分为全量和增量两种,具体的更新流程如下图所示:

image