问题现象
问题场景:
create table xml_test(c1 number, c2 clob, c3 xmltype);
insert into xml_test values(1, 'abcd', '<a>test</a>');
alter table xml_test drop column c2;
select * from xml_test;
对于包含 XMLType 的表,混合 offline 删除列后 XMLType 列的数据丢失。V4.2.1 BP4 以后,新增快速删列特性,需要重整数据例如 ALTER TABLE FORCE 后就能出现。
例如。
create table xml_test(c1 number, c2 clob, c3 xmltype);
insert into xml_test values(1, 'abcd', '<a>test</a>');
alter table xml_test drop column c2;
alter table xml_test force; --重整数据
select * from xml_test;
关键诊断信息
触发条件
Oracle 模式,创建一个混合 XML 列的表,写入数据后 offline 删除非 XML 的列。
事前巡检
第一步:看是否使用 XMLTYPE
查看是否含有 Oracle 租户。
-- 查询是否含有 Oracle 租户,如果没有就无风险 SELECT TENANT_ID FROM oceanbase.DBA_OB_TENANTS where COMPATIBILITY_MODE = 'ORACLE';-- 如果有 Oracle 租户 -- 检查是否有 xmltype 列,如果没有就无风险 select table_id, column_id, column_name from oceanbase.__all_virtual_column where data_type = 49 and tenant_id in (SELECT TENANT_ID FROM oceanbase.DBA_OB_TENANTS where COMPATIBILITY_MODE = 'ORACLE');查看版本是否是 V4.x 版本。
在 Oracle 模式下查看版本信息,命令如下。
obclient [SYS]> show variables like '%version%';在 MySQL 模式下查看版本信息,命令如下。
obclient [(none)]> select OB_VERSION();
查看表 Schema 中是否用了 XMLTYPE (使用 XML 功能不代表用了 XMLTYPE,Clob 也可以使用 XML 功能)。
-- 如果有 XMLTYPE 列 -- 检查是否涉及到删列的操作,如果没有就无风险,如果有联系 OceanBase 技术支持确认 select vc.table_id, vc.column_name, vdo.ddl_stmt_str, vdo.operation_type from oceanbase.__all_virtual_column vc join oceanbase.__all_virtual_ddl_operation vdo on vc.table_id = vdo.table_id where vc.data_type = 49 and vc.tenant_id in (SELECT TENANT_ID FROM oceanbase.DBA_OB_TENANTS where COMPATIBILITY_MODE = 'ORACLE') and vdo.ddl_stmt_str like '%drop%';
第二步:看是否触发。
OceanBase 数据库企业版 V4.2.1 BP4(oceanbase-4.2.1.4-104000062024022914)之后,因为有快速删列逻辑,删列不会立即触发该 BUG。但是删列之后,如果执行了重整数据的DDL 例如
ALTER TABLE FORCE触发数据重整,该问题会被重新触发。OceanBase 数据库企业版 V4.2.1 BP4(oceanbase-4.2.1.4-104000062024022914)之前,以及 V4.2.x,V4.3.x 版本,因为没有 Patch 快速删列功能,删列后,会直接触发该 BUG 。
事后诊断
同事前巡检。
问题原因
OFFLINE 删除列后会对剩下的数据进行补全,补全数据这一块有两个 table schema,一个是原始数据 data_table_schema,一个是目标数据 hidden_table_schema,当前的逻辑是遍历每一列根据列的名字找到对应的 column schema。XML 列和普通列不一样, XML 列除了表层的 UDT 列外,还有一列通过 udt_set_id 对应的 CLOB 隐藏列,实际上的数据也是存储在隐藏列上。隐藏列是根据列 id 按顺序取名的,因此删除前面的数据后,隐藏列的 id 变更了,隐藏列名也同样变更了。隐藏列无法通过列名字将原始数据和目标数据进行匹配,因此在补全数据的时候XML隐藏列的数据无法补全到结果中导致数据丢失。 例如下面的 case,创建了一个 number 列和 2 个 XML 列,删除 number列。
create table xml_test(c1 number, c2 xmltype, c3 xmltype);
insert into xml_test values(1, '<a/>', '<b/>');
alter table xml_test drop column c1;
表格中展示在目标数据 hidden_table_schema 中的列名、列类型、列 ID 信息。
| 序号 | 列名 | 列类型 | 列 ID |
|---|---|---|---|
| 0 | __pk_increment | 1 | |
| 1 | c2 | UDT | 16 |
| 2 | c3 | UDT | 17 |
| 3 | SYS_NC00018$ | LONGTEXT | 18 |
| 4 | SYS_NC00019$ | LONGTEXT | 19 |
表格中展示在原始数据 data_table_schema 中的列名、列类型、列 ID 信息。
| 序号 | 列名 | 列类型 | 列 ID |
|---|---|---|---|
| a | __pk_increment | 1 | |
| b | c2 | UDT | 17 |
| c | c3 | UDT | 18 |
| d | SYS_NC00019$ | LONGTEXT | 19 |
| e | SYS_NC00020$ | LONGTEXT | 20 |
补全数据时 hidden_column_schema 对应表格中序号为 3 列名为 SYS_NC00018$ 是无法通过名字匹配到任何一个 data_table_column 的列信息,因此获取的 data_table_column 为空,因此 c2 数据为空。而序号为 4 列名为 SYS_NC00019$ 是可以通过名字匹配到 data_table_column 的序号为 d 列名为 SYS_NC00019$,列类型为 LONGTEXT 以及列 ID 为 19 的信息,但是匹配到的结果不正确,因此 c3 列数据丢失变成 c2 列的数据。
问题的风险及影响
在 Oracle 模式下使用了 XML 列的表删除列都会有影响。在包含 XMLType 的表中 OFFLINE 删除其它列会导致 XML 数据丢失。
影响租户
影响 OceanBase 数据库中的 Oracle 租户,对于 SYS 租户和 MySQL 租户无影响。
影响版本
OceanBase 数据库企业版 V4.2.1 GA(oceanbase-4.2.1.0-100000182023092722)及之后版本、V4.2.2 GA(oceanbase-4.2.2.0-100000082024011317)及之后版本、V4.3.0(oceanbase-4.3.0.0-100000072024020200)及之后版本。
解决方法及规避方式
解决方法:
升级至问题已修复版本。目前已修复的版本包括 V4.2.1 BP7(oceanbase-4.2.1.7-107000112024052920)及之后版本、V4.2.4(oceanbase-4.2.4.0-100000252024070621)及之后版本、V4.3.1(oceanbase-4.3.1.0-100000212024051522)及之后的版本。
规避方式:
如果是 OceanBase 数据库企业版 V4.2.1 BP4(oceanbase-4.2.1.4-104000062024022914)之后的版本,有删除 XMLTYPE 前面列,但是未触发 BUG,需要尽快升级版本,避免丢失数据。
在有潜在使用或者已经使用 XMLTYPE 的场景,需要尽快升级版本,并且在升级版本之前尽量不要做 DDL,避免后续触发该 BUG 。
已经触发该 BUG 的场景,暂时只能通过备份恢复等手段来恢复数据。