OceanBase作为关系型数据库,本身完全兼容MySQL协议。但与传统单机数据库不同,OceanBase是分布式数据库,每个表甚至每个表的不同分区都可能存放在不同的机器上,而想要对表进行读写,必须先要定位到数据所属的表或是分区的位置。尽管任何一台OBServer均有路由转发和本地执行的能力,但实际中这样做将严重影响性能和整体吞吐量。ObProxy作为OceanBase的高性能反向代理服务器,其核心功能就是路由转发。ObProxy路由的目标是将具体SQL转发到最恰当的Server上执行。本文详细描述了ObProxy的路由逻辑。
1. 解析SQL
proxy需要从DML类型SQL中解析出Hint/数据库名/表名
。DML类型SQL包括select/insert/delete/update/replace。 只解析DML类型SQL是因为其对路由是否准确最关心,也是用户最常用的SQL,其他类型的SQL路由规则是相同的。解析出Hint可以用来确定SQL的一致性读属性(强一致性读/弱一致性读),这将决定第2步中路由规则。 解析出”数据库名+表名”可以用来在确定该SQL的目标server路由表,这将在第3步中发挥作用。
2.确定路由规则
proxy需要根据请求类型/一致性读属性/OceanBase集群部署属性/用户指定路由策略等因素决定单条SQL的具体路由规则:
- 请求类型:DML请求,其他请求;
- 一致性读属性:强一致性读,弱一致性读;
- OceanBase集群部署属性:普通部署,读写分离部署;
- 用户指定路由策略:读zone优先策略,只限读zone策略;
不同路由规则将导致目标server的优先级排序不一样。 排序考虑的主要维度有:
- proxy和server是否同城? 同城 > 异地。
- proxy和server是否同机房? 同机房 > 不同机房。
- server是否在合并? 不在合并 > 在合并。
- server是否含有SQL涉及的partition? 含有partition > 不含有partition。
- server是否为partition leader? leader > follower。
当OceanBase使用默认的普通部署时,proxy当前的路由规则主要有:
- LDC优先
在路由表的所有server中LDC优先选择,即路由排序维度的考虑优先级为:是否同城 > 是否含有SQL涉及的partition > 是否在合并 > 是否同机房
使用此规则的场景有:
- 非DML类型的请求
使用弱一致性读的DML
Leader优先
优先选择路由表中的leader server,其次在路由表剩余server中根据LDC优先选择。 即路由排序维度的考虑优先级为:是否为partition leader > 是否同城 > 是否含有SQL涉及的partition > 是否在合并 > 是否同机房
使用此规则的场景有:
- 使用强一致性读操作的DML请求(select)
- 使用写操作的DML请求(insert/delete/update/replace)
当OceanBase使用读写分离部署时,proxy路由规则还包括:读zone优先,只限读zone,只限写zone,非合并优先等。 这里进行不详细描述。
3.获取路由表
路由表包含两类,一类是含有SQL涉及partition的目标server集合(本文简称partition server),另外一类是不含有SQL涉及partition的目标server集合(本文简称non-partition server)。 两者公共构成用户可以访问目标server集合,简称tenant server。 完整的tenant server根据用户的登录信息即可获得,而partition server则需要根据具体SQL信息确定。
路由表获取流程如下:
- 在登录认证阶段,仅需要tenant server路由表。 proxy根据用户的登录信息(租户名+集群名),从本地缓存中读取tenant server,并保存在当前连接上。
如果本地缓存中没有,或者本地缓存已经失效,则发起后台异步任务去向后端的observer查询。 查询得到的结果会在本地缓存一份。
在非登录认证阶段
proxy根据第一步获取到SQL信息(数据库名+表名),去本地路由表缓存中找到其对应的table entry。
如果本地缓存没有,或者缓存已经失效,则发起后台异步任务去向后端的observer查询。 查询得到的结果会在本地缓存一份。
如果发现该SQL涉及的table entry是非分区表,则提取table entry中server作为目标partition server。
如果发现该SQL涉及的table entry是分区表,则通过进一步的parse和resolve,计算出涉及的partition_id,根据”数据库名+表名+partition_id”,去本地路由表缓存中找到其对应的partition server。
如果本地缓存没有,或者缓存已经失效,则发起后台异步任务去向后端的observer查询。 查询得到的结果会在本地缓存一份。
为什么要区分partition server和non-partition server? 这是因为不包含partition的server生成的执行计划并不是最优的。
4. 选择目标 Server
proxy需要根据第3步获取的路由表,按照第2步中确定的路由规则,对partition server和non-partition server组成的tenant server进行优先级排序,从优先级最高的server开始,第一次输出优先级最高的server,第二次输出优先级次高的server,以此类推,当遍历完输出完所有server后,从优先级最高的server重新开始输出。
因为选择的目标sever并不能直接使用,还需要经过第5步的黑名单检查。 当第4步选择的server没有通过第5步的黑名单检查时,根据proxy路由逻辑的重试机制,将重新进行第4步,输出下一个server。 因此选择目标server将可能是一个多次的操作。
5. 黑名单检查
对第4步选择的目标server,需要经过黑名单的检查才能真正的确定server是否可用,才能将请求转发给目标server。 对于处于/宕机/下线/升级中的server,将不能通过黑名单检查。 对于处于初始化中/退出中/访问超时的server,如果在N秒内有有M次失败,那么也将不通过黑名单检查。
如果第4步中的所有server都不能通过黑名单检查,那么根据proxy路由逻辑的重试机制,将重新进行第4步,输出下一个server,但是此时不在进行黑名单检查。
6. 转发Request
对于用户的SQL请求,proxy进行透明流式转发,并不会改写用户的请求,也不会拦截用户请求自己对用户的请求直接回应。