问题现象
在 OceanBase 数据库 V3.1.2 BP6 以下版本中,使用涉及多表的 PDML 时,会出现正确性相关问题。在 OceanBase 数据库 V3.1.2 BP5 及其 Hotfix 版本中,由于 DML 防御检查的存在,使用 PDML 校验出正确性问题会报错 -4377;而在 OceanBase 数据库 V3.1.2 BP5 以下版本(> V2.2.77)中可能会执行成功,但存在正确性问题。
测试示例
准备复现测试表与数据。
CREATE TABLE "ABA1" ( "BAZ002" NUMBER(16) NOT NULL ENABLE, "AAZ001" NUMBER(16) NOT NULL ENABLE, "BAB001" NUMBER(16) NOT NULL ENABLE, "AAB001" NUMBER(16) NOT NULL ENABLE, CONSTRAINT "ABA1_PK_HN" PRIMARY KEY ("AAZ001", "AAB001") ) COMPRESS FOR ARCHIVE REPLICA_NUM = 3 BLOCK_SIZE = 16384 USE_BLOOM_FILTER = FALSE TABLET_SIZE = 134217728 PCTFREE = 0 partition by hash("AAB001") partitions 2;CREATE TABLE "AC04" ( "AAZ189" NUMBER(20) NOT NULL ENABLE, "AAC001" NUMBER(20) NOT NULL ENABLE, "AAB001" NUMBER(20) NOT NULL ENABLE, "AAE140" VARCHAR2(3) NOT NULL ENABLE, "AAE001" NUMBER(4) NOT NULL ENABLE, "AAE750" NUMBER(4) NOT NULL ENABLE, CONSTRAINT "AC04_OBPK_1617707763333473" PRIMARY KEY ("AAZ189", "AAC001") ) COMPRESS FOR ARCHIVE REPLICA_NUM = 3 BLOCK_SIZE = 16384 USE_BLOOM_FILTER = FALSE ENABLE ROW MOVEMENT TABLET_SIZE = 134217728 PCTFREE = 0 partition by hash("AAC001") PARTITIONS 2; create index IDX_AC04_AAE001 on ac04(AAE001, AAZ189, AAC001) local; INSERT INTO ABA1 (BAZ002, AAZ001, BAB001, AAB001) VALUES (1,1,1,4000002000324057 ); INSERT INTO ABA1 (BAZ002, AAZ001, BAB001, AAB001) VALUES (1,1,1,4000002000324069 ); INSERT INTO ABA1 (BAZ002, AAZ001, BAB001, AAB001) VALUES (1,1,1,4000002000324067 ); INSERT INTO ABA1 (BAZ002, AAZ001, BAB001, AAB001) VALUES (1,1,1,4000002000324039 ); INSERT INTO ABA1 (BAZ002, AAZ001, BAB001, AAB001) VALUES (1,1,1,4000002000324041 ); INSERT INTO ABA1 (BAZ002, AAZ001, BAB001, AAB001) VALUES (1,1,1,4000002000324061 ); INSERT INTO ABA1 (BAZ002, AAZ001, BAB001, AAB001) VALUES (1,1,1,4000002000324087 ); INSERT INTO ABA1 (BAZ002, AAZ001, BAB001, AAB001) VALUES (1,1,1,4000002000324049 ); INSERT INTO ABA1 (BAZ002, AAZ001, BAB001, AAB001) VALUES (1,1,1,4000032419830125 ); INSERT INTO ABA1 (BAZ002, AAZ001, BAB001, AAB001) VALUES (1,1,1,4000002000324035 ); INSERT INTO ABA1 (BAZ002, AAZ001, BAB001, AAB001) VALUES (1,1,1,4000038658419181 ); INSERT INTO ABA1 (BAZ002, AAZ001, BAB001, AAB001) VALUES (1,1,1,4000002000324077 ); INSERT INTO ABA1 (BAZ002, AAZ001, BAB001, AAB001) VALUES (1,1,1,4000002000324047 ); INSERT INTO ABA1 (BAZ002, AAZ001, BAB001, AAB001) VALUES (1,1,1,4000006004269873 ); INSERT INTO ABA1 (BAZ002, AAZ001, BAB001, AAB001) VALUES (1,1,1,4000002000324063 ); INSERT INTO ABA1 (BAZ002, AAZ001, BAB001, AAB001) VALUES (1,1,1,4000002000324037 ); INSERT INTO ABA1 (BAZ002, AAZ001, BAB001, AAB001) VALUES (1,1,1,4000002000324053 ); INSERT INTO ABA1 (BAZ002, AAZ001, BAB001, AAB001) VALUES (1,1,1,4000002000324051 ); INSERT INTO ABA1 (BAZ002, AAZ001, BAB001, AAB001) VALUES (1,1,1,4000015000060398 ); INSERT INTO ABA1 (BAZ002, AAZ001, BAB001, AAB001) VALUES (1,1,1,4000002000324043 ); INSERT INTO ABA1 (BAZ002, AAZ001, BAB001, AAB001) VALUES (1,1,1,4000002000324083 ); INSERT INTO ABA1 (BAZ002, AAZ001, BAB001, AAB001) VALUES (1,1,1,4000002000324081 ); INSERT INTO ABA1 (BAZ002, AAZ001, BAB001, AAB001) VALUES (1,1,1,4000000001716012 ); INSERT INTO ABA1 (BAZ002, AAZ001, BAB001, AAB001) VALUES (1,1,1,4000002000324045 ); INSERT INTO ABA1 (BAZ002, AAZ001, BAB001, AAB001) VALUES (1,1,1,4000002000324065 ); INSERT INTO ABA1 (BAZ002, AAZ001, BAB001, AAB001) VALUES (1,1,1,4000002000324031 ); INSERT INTO ABA1 (BAZ002, AAZ001, BAB001, AAB001) VALUES (1,1,1,4000002000324089 ); INSERT INTO ABA1 (BAZ002, AAZ001, BAB001, AAB001) VALUES (1,1,1,4000002000324085 ); INSERT INTO ABA1 (BAZ002, AAZ001, BAB001, AAB001) VALUES (1,1,1,4000002000324075 ); INSERT INTO ABA1 (BAZ002, AAZ001, BAB001, AAB001) VALUES (1,1,1,4000002000324055 ); INSERT INTO ABA1 (BAZ002, AAZ001, BAB001, AAB001) VALUES (1,1,1,4000002000324073 ); INSERT INTO ABA1 (BAZ002, AAZ001, BAB001, AAB001) VALUES (1,1,1,4000002000324079 ); INSERT INTO ABA1 (BAZ002, AAZ001, BAB001, AAB001) VALUES (1,1,1,4000002000324033 ); INSERT INTO ABA1 (BAZ002, AAZ001, BAB001, AAB001) VALUES (1,1,1,4000002000324071 ); INSERT INTO ABA1 (BAZ002, AAZ001, BAB001, AAB001) VALUES (1,1,1,4000002000324059 ); INSERT INTO ABA1 (BAZ002, AAZ001, BAB001, AAB001) VALUES (1,1,1,4000038620089300 ); INSERT INTO ABA1 (BAZ002, AAZ001, BAB001, AAB001) VALUES (1,1,1,4000031756831682 ); INSERT INTO AC04 (aab001,AAZ189,AAC001,AAE140,AAE001,AAE750) VALUES (4000006004243942 , 46100000001588907423 , 4000000001957811, '310', 2022, 22); INSERT INTO AC04 (aab001,AAZ189,AAC001,AAE140,AAE001,AAE750) VALUES (4100044676606955 , 46100000001593369987 , 4100044833010726, '310', 2022, 22); INSERT INTO AC04 (aab001,AAZ189,AAC001,AAE140,AAE001,AAE750) VALUES (4100044014124913 , 46100000001594020812 , 4100044705217120, '310', 2022, 22); INSERT INTO AC04 (aab001,AAZ189,AAC001,AAE140,AAE001,AAE750) VALUES (4000002000304088 , 46005200000137364000 , 4000004000846453, '310', 2022, 22); INSERT INTO AC04 (aab001,AAZ189,AAC001,AAE140,AAE001,AAE750) VALUES (4100045967101824 , 46100000001590348600 , 4100046000997886, '310', 2022, 22); INSERT INTO AC04 (aab001,AAZ189,AAC001,AAE140,AAE001,AAE750) VALUES (4000006004269873 , 46100000001593192182 , 4100041614112376, '310', 2022, 22); INSERT INTO AC04 (aab001,AAZ189,AAC001,AAE140,AAE001,AAE750) VALUES (4000006004269873 , 46100000001587674702 , 4000000002324697, '310', 2022, 22); INSERT INTO AC04 (aab001,AAZ189,AAC001,AAE140,AAE001,AAE750) VALUES (4100043094635292 , 46100000001592440235 , 4100045197270471, '310', 2022, 22); INSERT INTO AC04 (aab001,AAZ189,AAC001,AAE140,AAE001,AAE750) VALUES (4000002000324041 , 46100000001593723083 , 4000036722430825, '310', 2022, 22);测试 PDML 的 SQL 语句。
update /*+ leading(ABA1) PQ_DISTRIBUTE(@"UPD$1" ("TEST.AC04"@"UPD$1" ) BROADCAST NONE) enable_parallel_dml parallel(2)*/ ac04 set AAE750 = 3 where aae001 = 2022 and aae140 = '310' AND aab001 not in ( select aab001 from aba1);
在 OceanBase 数据库 V3.1.2 BP6 以下版本执行计划
下图为该 PDML 的执行计划,算子 2 3 之间没有分配 EXCHANGE 算子,AC04 表待更新数据的 partition id 由 ABA1 表计算出来(即由 4 5 6 7 算子吐出来),这是不符合预期的。

OceanBase 数据库 V3.1.2 BP4 Hotfix(oceanbase-3.1.2-20211019145543)版本执行结果
9 行数据经 update 后变成 13 行,不符合预期,存在正确性问题。

OceanBase 数据库 V3.1.2 BP5(oceanbase-3.1.2-20211209200749)版本执行结果
报 -4377 DML 防御错误。

OceanBase 数据库 3.1.2 BP6 及以上版本执行计划
算子 2 5 间分配了 EXCHANGE 算子,确保生成正确的 partition id。

关键诊断信息
触发条件
使用 PDML 且涉及多表。
问题原因
在 OceanBase 数据库 V3.1.x BP6 以下版本中,不推荐使用涉及多张表的 PDML(如:update t1 set c1 = xx where xxx in (select ... from t2) ; 涉及到了t1、t2 两张表),单表 PDML 不受影响(如:update t1 set c1 = c2;)。
在 OceanBase 数据库 V3.1.x 版本中使用 PDML 时,OceanBase 会先根据过滤条件获取相关操作数据的 PARTITION ID 信息,之后依据此 PARTITION ID 信息去存储层中进行相关 DML 操作。
在问题版本中,若涉及多张表的 PDML 执行计划中的 DML 算子和 TABLE SCAN 算子间形成了 PARTITION WISE,在没有分配 EXCHANGE 算子的情况下,可能会分配出与数据不匹配的错误 PARTITION ID,如果拿着错误的 PARTITION ID 去执行 DML 就会引发正确性问题。该问题在 OceanBase 数据库 V3.1.2 BP6 版本通过分配 EXCHANGE 算子修复。
- 在 OceanBase 数据库 < V3.1.2 BP5 版本,OceanBase 会直接拿着分配的 PARTITION ID 去进行 DML,即使该 PARTITION ID 是错误的,也可能会执行成功(有正确性问题)。
在 OceanBase 数据库 V3.1.2 BP5 及 Hotfix 版本后,DML 在向存储层写数据时,会先根据 DML 防御机制拿着 rowkey 与 PARTITION ID 信息去坐 old row 校验,此时如果拿到的 PARTITION ID 信息是错误的(与 rowkey 不匹配),则会出现存储层读不出 old row 的错误(debug 模式编译下的 OceanBase,日志中打印:
old row in storage is not exists(rowkey=..., ret=-4377)),OceanBase 报错 -4377。报错信息如下:
WARN [SQL.DTL] process_base (ob_dtl_channel_loop.cpp:232) [116982][2526][xxxxx-xxxxx] [lt=6] [dc=0] message loop is interrupted(code={code:-4377, info:{msg:"tid:73549,from:"xx.xx.xx.xx:port",TASK ABORT QC"}}, ret=-4377)
问题的风险及影响
在问题版本上使用 PDML 且涉及多表时,会有正确性问题,因此不推荐使用涉及多张表的 PDML ,单表 PDML 则不受影响。
影响租户
影响 OceanBase 数据库中的 Oracle 租户和 MySQL 租户,对于 SYS 租户无影响。
影响的版本
OceanBase 数据库企业版 V3.1.2 GA(oceanbase-3.1.2-20210618150922)及之后版本。
解决方法
升级至问题已修复版本。目前已修复的版本包括 OceanBase 数据库企业版 V3.1.2 BP6 (oceanbase-3.1.2-20211230114204) 及之后版本。
在 OceanBase 数据库 V3.1.x BP6 以下版本中,不推荐使用涉及多张表的 PDML(如:update t1 set c1 = xx where xxx in (select ... from t2) ; 涉及到了 t1、t2 两张表),单表 PDML 不受影响(如:update t1 set c1 = c2;)。
规避方式
在 OceanBase 数据库 V3.1.x BP6 以下版本中,不推荐使用涉及多张表的 PDML(如:update t1 set c1 = xx where xxx in (select ... from t2) ; 涉及到了 t1、t2 两张表),单表 PDML 不受影响(如:update t1 set c1 = c2;)。