本文作者:镇楠,OceanBase 内核研发工程师,对于分布式集群中副本位置信息的管理方法有着丰富的理解。
在前文源码解读第13篇《一号表》中我们为大家介绍了 __all_core_table 的定义以及其对应的内容,本文我们将聚焦在《集群位置信息的存储与汇报》上,为大家解读 OceanBase 集群 location 发现过程、__all_core_table 的位置以及 location 汇报机制。
1 OceanBase 集群的 location 发现过程
思考这样一个问题:在单机数据库中,数据库中的表都存储在本地,但在 OceanBase 这样的分布式数据库中,一张实体表可能存在多个分区,每个分区又有多个副本,分散在集群中的多个 server 上。那么当用户想去查询一张实体表时,数据库内部如何在众多 server 上定位这张表呢?
OceanBase 依靠内部的一套系统表来实现实体表的位置发现过程,这套系统表我们一般统称为 meta table。
涉及 location 的 meta table 主要包括 __all_core_table、__all_root_table、__all_tenant_meta_table 三张内部表。OceanBase 集群中所有实体表的 location(位置信息),以分区副本为粒度,记录在该层级关系中。
从下往上看:
1)用户表的 partition 的 replica 位置信息记录在该租户的内部表 __all_tenant_meta_table 中;
2)每个租户的 __all_tenant_meta_table 的位置信息、系统表(包括用户租户和系统租户的)位置信息记录在 __all_root_table 中;
3)__all_root_table 的位置信息记录在 __all_core_table 中;
4)__all_core_table 的位置信息存储在 RS 所在机器的内存中。
之所以设计成层级结构,一是易于维护,二是可以存储更多的实体表信息。当集群启动、实体表的分区副本变更时,都会将 location 信息主动汇报到 meta table 中。通过维护并查询这个层级结构,数据库内部就能够确定每一个实体表的位置所在。
2 meta table 的更新
meta table 的更新与查询都是通过 ObPartitionTableOperator 实现。ObPartitionTableOperator 会根据实体表的 table_id,调用不同的 ObPartitionTable 进行处理,然后再交给 ObPartitionTableProxy 执行更新/查询的具体操作。
2.1 __all_core_table 的位置
__all_core_table 作为1号表,是 OceanBase 集群自举与服务发现的关键。
__all_core_table leade r 的位置在集群启动时就已经确定了。
RS 的 bootstrap 过程中,需要输入 rs_list,该 rs_list 中会包括每个 zone 的一个 server 地址(IP:port)。例如:
ZONE='ZONE1' SERVER='10.232.144.13:2209', ZONE='zone2' SERVER='10.244.4.14:2203',ZONE='zone3' SERVER='10.244.4.15:2203'
prepare_bootstrap 过程中,会在 rs_list 中的 server 上创建 __all_core_table 的 partition,并选择一个合适的 server 作为 __all_core_table 的 leader。(所谓合适的 leader,通过 primary zone 的形式指定偏好位置,但底层可能由于异常原因通过无主选举返回另一 leader,因此 prepare_bootstrap 中会 create_partition 后会通过循环监测 replica 的 to_leader_time 的形式,等待选举完成,确定真实 leader)最后会发送 RPC 到该 leader 上 execute_bootstrap,启动 RS。
其他 server 如何寻找 __all_core_table 的 leader?
每个 OceanBase server 会有一个 RsMgr ,在内存中维持 master rs 的位置信息。而 __all_core_table 的副本位置信息都存放在 rs 的内存中。其他 server 通过 rpc 的方式,到 master_rs_中拿取 partition_info。若拿取失败,则代表当前内存状态落后,会触发一套多层的刷新寻址过程:
(核心函数:int ObRpcPartitionTable::fetch_root_partition_v2(ObPartitionInfo &partition_info))
- 调用 RsMgr 的接口,通过 ObInnerConfigRootAddr 从本地配置项中获取 rootservice_list,并在其中遍历寻找 master_rs。(遍历优化为 rpc 询问各个 server,通过其 partition service 查询 partition leader;rs_list 变化通过心跳刷新并持久化到本地)
- 通过 partition service 获取 leader 和 memberlist(leader 无效则在 memberlist 中找一遍)(observer 上有 replica 就能拿到 memberlist )
- 在 config 中的 all_server_list 中查找
除以上方式外,RS 还会通过心跳机制,检查 observer 的状态,若10s以上无心跳,则认为该 server 的租期超时,向其主动广播自己的位置(broadcast_rs_list)。
2.2 location 汇报机制
汇报是各 observer 向 meta 表更新本机的 replica 相关信息的过程,各 partition 的 location 信息就包含其中。
2.2.1 什么时候汇报?
- ObService 启动时,server 主动向RS汇报自身所有 partition 信息
- 创建 partition
- partition 的 leader 发生变化时,通过回调函数的形式触发汇报
- RS 主动让 server 汇报
- meta table 迁移
- 其他...
2.2.2 汇报什么?
以 __all_root_table 为例,系统表需要向其全量汇报的内容包括该表的所有列,其中包含的 svr_ip、svr_port(sql_port)就是核心 location 信息:
+-----------+---------------+--------------+----------------+----------+----------+---------+---------------+-------+------+---------------------------------------+-----------+-----------+--------------+---------------+--------------+-----------------+--------------------+--------------------+-------------+---------+--------------+---------------+-----------------------+------------+--------------------+--------+-----------+--------------------+------------------+------------------+
| tenant_id | table_id | partition_id | svr_ip | svr_port | sql_port | unit_id | partition_cnt | zone | role | member_list | row_count | data_size | data_version | data_checksum | row_checksum | column_checksum | is_original_leader | is_previous_leader | create_time | rebuild | replica_type | required_size | status | is_restore | partition_checksum | quorum | fail_list | recovery_timestamp | memstore_percent | data_file_id |
+-----------+---------------+--------------+----------------+----------+----------+---------+---------------+-------+------+---------------------------------------+-----------+-----------+--------------+---------------+--------------+-----------------+--------------------+--------------------+-------------+---------+--------------+---------------+-----------------------+------------+--------------------+--------+-----------+--------------------+------------------+------------------+
| 1 | 1099511627779 | 0 | 100.81.215.214 | 21100 | 21101 | 1 | 0 | zone1 | 1 | 100.81.215.214:21100:1625130315574320 | 0 | 0 | 1 | 0 | 0 | | 0 | 1625130317425721 | 0 | 0 | 0 | 0 | REPLICA_STATUS_NORMAL | 0 | 0 | 1 | | 0 | 100 | 1625130308620639 |
| 1 | 1099511627779 | 1 | 100.81.215.214 | 21100 | 21101 | 1 | 0 | zone1 | 1 | 100.81.215.214:21100:1625130315575073 | 0 | 0 | 1 | 0 | 0 | | 0 | 1625130317425986 | 0 | 0 | 0 | 0 | REPLICA_STATUS_NORMAL | 0 | 0 | 1 | | 0 | 100 | 1625130308620639 |
| 1 | 1099511627779 | 2 | 100.81.215.214 | 21100 | 21101 | 1 | 0 | zone1 | 1 | 100.81.215.214:21100:1625130315575862 | 0 | 0 | 1 | 0 | 0 | | 0 | 1625130317426233 | 0 | 0 | 0 | 0 | REPLICA_STATUS_NORMAL | 0 | 0 | 1 | | 0 | 100 | 1625130308620639 |
+-----------+---------------+--------------+----------------+----------+----------+---------+---------------+-------+------+---------------------------------------+-----------+-----------+--------------+---------------+--------------+-----------------+--------------------+--------------------+-------------+---------+--------------+---------------+-----------------------+------------+--------------------+--------+-----------+--------------------+------------------+------------------+
系统表的汇报过程均为全量汇报。
但是百万分区情况下,用户表需要汇报的副本数量巨大,针对 leader 切换、无主选举等场景,只向上汇报 leader 信息可有效提高感知 leader 的速度。因此针对用户表,将全量汇报优化为 role 汇报和非 role 汇报两部分。role 汇报只让 leader 更新 role 和 to_leader_time(is_previous_leader)两列信息。
2.2.3 汇报的实现
通过任务队列的方式,异步汇报副本信息,其中通过队列分类区分任务处理的优先级,并且进行批量处理的优化。例如 core_table_queue 中存放需要 __all_core_table 和 __all_root_table 的汇报任务,优先单独处理每个 task。user_table_queue 中存放用户表的汇报任务,进行批量处理优化。
role 汇报时,leader 不仅汇报自身 role 和 to_leader_time ,还会将其他副本的 role 全部更新为 follower ,因此只需要 leader 进行汇报,follower 副本不向 meta table 汇报。
全量汇报时,leader 开启事务修改 meta table ,为保证 leader 的正确性和唯一性,会比较 to_leader_time,并将其他副本的 role 更改为 follower。若此时 follower 汇报,会产生锁冲突。若发生汇报冲突,汇报任务失败后重新放入任务队列进行重试。
————————————————
(未完待续)
附录:前十三篇可参考:
1、OceanBase 数据库源码解读(一)引言
2、OceanBase 数据库源码解读(二)目录结构
3、OceanBase 源码解读(三)SQL 的一生
4、OceanBase 源码解读(四)分区的一生
5、OceanBase 源码解读(五)事务的一生
6、OceanBase 源码解读(六)租户的一生
7、OceanBase源码解读(七)带你看透虚拟表
8、OceanBase源码解读(八)OceanBase 高性能执行引擎
9、OceanBase源码解读(九)TableAPI 和 OceanBase 多模型
10、OceanBase 源码解读(十)分布式事务
11、OceanBase 源码解读(十一):表达式和函数
12、OceanBase 源码解读(十二):事务日志的提交和回放
13、OceanBase 源码解读(十三):一号表
最后的最后,您有任何疑问都可以通过以下方式联系到我们~
联系我们
欢迎广大 OceanBase 爱好者、用户和客户随时与我们联系、反馈,方式如下:
社区版官网论坛
社区版项目网站提 Issue
钉钉群:33254054