Toast 部分记录丢失问题处理

在日常数据库运维过程中,我们可能会遇到类似以下的错误:

test=# create table t2_text as select * from t1_text;
ERROR:  unexpected chunk size -4 (expected 1996) in chunk 0 of 4 for toast value 61962 in pg_toast_61919

 该错误信息是由于部分tuple的 toast 字段丢失,导致数据无法访问。需要通过游标方式,将可以访问的数据备份出来。

注意:设置 zero_damaged_pages = on 无法解决该问题。

以下例子模拟 t1_text表部分tuple 的 text 字段出现损坏,需要通过游标将数据从 t1_text 迁移至 t2_text 。游标如下: 

create table ctid_done_tmp123(rid tid,result varchar(9));
create table t2_text as select * from t1_text where 1=2;

create or replace procedure process_error_ctid as 
  v_tid tid;
  cursor cur_t1_text is select ctid from t1_text where ctid not in (select rid from ctid_done_tmp123);
begin
  open cur_t1_text;
  loop
    fetch cur_t1_text into v_tid;
    exit when cur_t1_text%NOTFOUND;
    begin
      insert into t2_text select * from t1_text where ctid=v_tid;
      insert into ctid_done_tmp123 values(v_tid,'SUCCESS');
    exception
      when others then
        insert into ctid_done_tmp123 values(v_tid,'ERROR');
        commit;
        exit;
    end;
  end loop;
end;
/

declare
  v_oldcnt integer;
  v_newcnt integer;
begin
  select count(*) into v_oldcnt from ctid_done_tmp123;
  v_newcnt := 0;
  while (true) loop
    call process_error_ctid();
    if v_oldcnt = v_newcnt then
      exit;
    end if;
  end loop;
end;

 注意:以上的例子通过不停的调用函数process_error_ctid来实现,这是由于我们当前的游标不支持跨事务,后续可以修改该脚本。

这里有几个问题需要注意:

  1. 需要设置 ora_statement_level_rollback = on。PG 过程块默认在进入 exception 之前,会将之前的所有操作 rollback,因此,即使在exception 处理时,将事务 commit,实际也没任何意义。对于 Oracle ,如果用户有捕获异常,可以选择将对异常之前的数据commit or rollback。KINGBASE  ora_statement_level_rollback  参数的作用就是:如果 ora_statement_level_rollback = on,在遇到异常后,只是回退引发异常的操作,而之前的事务操作可以选择commit or rollback。
  2. 对于oracle,过程块中间可以commit or rollback ,而cursor 不会被close;KINGBASE 当前的游标还不允许跨事务,也就是说,如果事务commit,或者碰到异常,事务就结束,游标也会被关闭,比如以上的例子就会报 “cursor "cur_t1_text" does not exist” 错误。这点 KINGBASE 还在开发当中,后续会支持。
上一篇:2021-07-29


下一篇:mysql回滚点 Save point语句用法