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)。例如:
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 信息:
系统表的汇报过程均为全量汇报。
但是百万分区情况下,用户表需要汇报的副本数量巨大,针对 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 汇报,会产生锁冲突。若发生汇报冲突,汇报任务失败后重新放入任务队列进行重试。
评论