分区表 的 读写 并不全通过主副本执行吗 ?

【 使用环境 】测试环境
【 使用版本 】OceanBase_CE_V4.2.1.7 和 OceanBase_CE_V4.3.2.1
【问题描述】
我们使用 OCP 安装了如上两个版本的 OceanBase,都是常规 1-1-1 集群模式。
我们并没有设置读写分离,用的是OB默认的强一致性读

不过,我们通过 OCP 的 SQL诊断 功能发现:

  1. INSERT INTO 某分区表 这个 SQL 在 集群 的 3个 Zone 中都有被执行(都能搜索到)。
  2. 作为对比,我们也检查了一下非分区表,发现 INSERT INTO 某非分区表 的 SQL 就只有在该表主副本所在的 Zone 上被执行。

至于这个 某分区表,它有2000个左右的分区,我们通过将该表加入一个表组( SHARDING = 'NONE' ),从而让该表的所有表分区都集中在同一个 Zone 上。

理论上,在强一致性的前提下,该表的 读写 都应该由 主副本所在的 Zone 来执行才对。
为什么另外两个 Zone 上也有该表的 读写SQL 执行痕迹 呢?

1 个赞

估计租户的 zone优先级(primary_zone)是 random 或者 zone1,zone2,zone3 这种。

当租户的primary_zone 是多个zone 的时候,租户的表的分区会自动做负载均衡。其中分区表的多个分区会分布在不同节点上,所以 insert sql 会出现在多个节点上。

关于这个 primary_zone 应该设置为单zone 还是多zone,不同客户有不同的考虑点。

一种观点是 业务刚迁移到 ob上来,性能问题比较多(并不全是ob的问题,sql自身问题更多),先将 primary_zone 设置为 单zone,这样使用起来跟单机效果一样。此时可以专注优化慢sql。这种是比较贴切实际的变通用法。
一种观点是 OB 是分布式的,业务就是奔着发挥多机处理能力的目标去的。如果业务sql不能发挥ob这个能力,那就业务优化 sql 逻辑。这种是运维相对强势且看问题看得比较深远的。代价是刚迁移到ob上来,业务性能很可能还更差。

第二种观点还有个变通用法就是将 odp 版本升级到 4.2.2 以上最新版本。 enable_transaction_internal_routing-V4.2.2-OceanBase 数据库代理文档-分布式数据库使用文档


如果租户的zone 优先级不是上面猜测的这种而是单zone,那就是事务路由问题。 insert into sql 在事务中路由到哪个节点默认是跟开启事务的第一个 sql 有关(前提是odp 版本低于4.2.1 )。


表组的用途不是说把一个分区表的所有分区几种在一起,而是在租户的zone优先级是多个zone 的时候,让表组中的同号分区的同类型副本(主或备)在同一个节点上,不同分区的同类副本是会分布在不同节点上。

3 个赞

赞同obpilot的说法,感觉是primary zone设置成了多zone的原因。 楼主对tablegroup的理解不太准确 。

1 个赞

obpilot老师的分析你可以参考下,大概率是这个情况,可以确认下:

–查下租户zone的优先级设置
SELECT * FROM oceanbase.DBA_OB_TENANTS WHERE tenant_id =xxx

–查下分区表leader的分布情况
SELECT svr_ip,svr_port,ls_id,count(*) FROM oceanbase.CDB_OB_TABLE_LOCATIONS WHERE tenant_id=xxx AND role=‘leader’ AND table_type=‘USER TABLE’ GROUP BY svr_ip,svr_port,ls_id;

@obpilot @旭辉 @shiyh

我们的 Primary Zone 确实是 RANDOM

但是,我们的表组设置是 SHARDING = 'NONE',这种设置是不需要考虑分区表和非分区表的,什么表都可以往里面添加。都会集中在同一个 Zone。

SELECT
  zone, tablegroup_name, COUNT( table_name ) AS table_count
FROM
  oceanbase.DBA_OB_TABLE_LOCATIONS 
WHERE
  database_name IN ( '数据库名称') 
  AND role = 'LEADER'
  AND table_type = 'USER TABLE' 
  AND table_name = '分区表名称'
GROUP BY
  database_name, zone;

image

我们查询这个表的主副本分布,也能看到它的所有表分区的主副本全部集中在 zone2 这个节点上。

SELECT
  zone, table_name, COUNT(*) AS table_count
FROM
  oceanbase.DBA_OB_TABLE_LOCATIONS 
WHERE
  database_name IN ( '数据库名称') 
  AND role = 'LEADER'
  AND table_type = 'USER TABLE' 
  AND tablegroup_name = '表组名称'
GROUP BY
  database_name, zone, table_name;

image

我们直接查询这个表组中所有表分区的主副本分布,也可以看到,全部都集中在 zone2 。
所以,才有此疑问。

INSERT INTO 某分区表 这个 SQL 在 集群 的 3个 Zone 中都有被执行(都能搜索到)

这个你是怎么搜索的?gv$ob_sql_audit 吗?

是通过 OCP 的 【SQL诊断】 界面查询出来的。
下拉框 OBServer 依次切换3个zone的 IP,都能搜索出执行的SQL记录数据。

如果是非分区表,就只有在下拉框 OBServer 中选择其主副本所在的 Zone IP 才能被搜索出来。

SELECT * FROM oceanbase.gv$ob_sql_audit
WHERE QUERY_SQL LIKE 'INSERT INTO `数据表名`%'
AND SVR_IP = '每个Zone IP'
LIMIT 10;

我通过 gv$ob_sql_audit 也是能够搜索出来的。

@旭辉

很奇怪,我用 oceanbase.gv$ob_sql_audit 查询另一个分区表(简称 表2,也加入了一个 SHARDING = 'NONE' 的表组 ),又只能从其主副本所在的 Zone 中查询到记录。

我能想到唯一的区别是, 表1(即 在所有zone都能查询到执行记录的这个分区表) 的 INSERT INTO 会和 另外一个分区表INSERT INTO 是在同一个事务里完成的,不过这两个分区表都在同一个表组中,分区键和分区方式也完全不一样,不至于出现这种情况才对。

这里表分组 的使用目的理解不对。

如果只是单纯的想让所有分区主副本在一个zone,设置 primary_zone 为 单个 zone 就是了,不要用表分组。
表分组适合业务上表很多,然后按业务联系划为为多个独立的模块,每个模块的表因此彼此连接或在一个事务里概率更高,此时适合把这个模块下的表放到一个表分组里。

表分组误用最极端的形式就是所有表放到一个表分组里了,那还不如设置 primary_zone

我不是想让所有表的主副本都在一个 Zone,而是根据业务划分,分成了几个表组。
这样可以让具有业务关联的表尽量聚集在同一个Zone,可以减少分布式的处理开销。
又为了尽量负载均衡,让这几个表组分散到不同的 Zone。

最终实现的效果就是:

  1. 表组1 全部位于 Zone 1
  2. 表组2 全部位于 Zone 2
  3. 表组3 全部位于 Zone 3
  4. 其他表(没有业务关联 或 负载极低) 让 OB 自行随机负载均衡

这样做,既避免了业务上跨Zone的分布式处理开销,又起到了负载均衡的作用。

这比使用单一的 Primary Zone —— 所有写操作热点全部集中在一个 Zone,效果要好多了。

我们本来就是按照业务关联来规划表组的,用法没有问题。

只不过 我们疑惑的 就是这个分区表,明明主副本都聚集在同一个Zone,却在其他2个Zone上也看到了它相关的读写SQL执行。按照 OB 官方的文档描述,这都解释不通。

恩,你这个目标效果是合理的对的。

你的表分组尽量不要把非分区表跟分区表放在一起,虽然技术上是可以。那个有点别扭。强用也行。

假设这一步不是问题。当主副本位置已经符合你的预期了。

事务里的insert sql 实际还是有可能不在那个表的主副本所在位置。前面说了,早期的版本(odp 4.2.1以及以前版本)路由下,事务路由是跟随第一条开启事务的sql 的。

你看看你的 odp 的版本。

然后要在 gv$ob_sql_audit里把这个事务的所有 sql 都抓出来 (同一个 tx_id ),分析看看。

SELECT usec_to_time(request_time) req_time,svr_ip, client_ip, sid, tx_id, tenant_name,user_name,sql_id,query_sql,ret_code,affected_rows+return_rows total_rows,trace_id,plan_id
FROM oceanbase.gv$ob_sql_audit 
WHERE 1=1 -- AND client_ip='0.0.0.0' 
  AND tenant_id = 1002 
 AND tx_id=29291526
 AND is_executor_rpc=0
ORDER BY request_time DESC LIMIT 100;

从事务第一条sql看起,看实际执行节点 svr_ip 和 sql 类型 (plan_type) 去分析。

我这个分区表的 INSERT INTO 语句 是和另一个分区表的 INSERT INTO 语句在一个事务里面。

这个事务里面,就只有这两个表的 插入语句,而且这两个表都在同一个表组里面。

那就一步步来分析。

  1. OB 版本、ODP 版本、测试会话连接命令
  2. 表结构 语句、表分组语句、业务事务的完整 SQL(从 事务第一条sql到commit)。
  3. 表的分区的主副本位置查询语句和结果。
  4. 事务的sql 审计视图(is_executor_rpc=0 and tenant_id=xxx and tx_id=yyyy),查询sql参考上面
  1. OB 版本:OceanBase_CE_V4.3.2.1 ( OceanBase_CE_V4.2.1.7 也有类似的情况 )
  2. ODP 版本:是最新的 4.3.1 ( OceanBase_CE_V4.2.1.7 用的是 4.2.3 )
  3. JDBC 连接字符串:jdbc:mysql://proxy地址:2883/db_name?useUnicode=true&allowMultiQueries=true&rewriteBatchedStatements=true
  4. 表结构有二级分区,一级 LIST,二级 HASH。
  5. 两个分区表的所有表分区的所有主副本 都在 zone 2。

我查了一下SQL审计,部分分区表的 INSERT INTO 语句,其 EVENT 属性显示为 das wait remote response

执行你给的SQL语句,结果如下

5、 不看 event ,看 svr_ip, plan_type 字段。 svr_ip 是 sql 执行节点,plan_type 表示 sql 执行计划类型(1:本地;2:远程;3:分布式)。where 条件:is_executor_rpc=0

其实从这个图的 sid 是一样的,也看出来了这 4 条 sql 是在同一个节点执行的。
这个符合你的预期。

但是,我查的这个IP,它并不是主副本所在的 Zone。
每一个 Zone 上都有它们的执行痕迹,虽然每一个事务都是在同一个 Zone 内完成的。

我重新找了一个,sidsvr_ip 都是相同的。plan_type 如下所示:

image