逻辑备份数据导入原分区表遇到的那些事儿

一、背景

​ 2022年的年终决算前期,为了加快2022年的年终决算的跑批进度,让大家能早早的回家过年甚至跨年,我们开始了对相关核心系统的优化及数据清理工作。根据业务及业务运维人员相关的分析,认为其中有几张交易日志表相对较大,而且跑批均会读取这些表的当天数据,历史数据无太大用处。因此,我们通过在线重定义的方式对几张日志表进行了分区操作,并只保留最近3个月的数据,历史数据则通过expdp的方式导出到nas存储上进行永久保留。2022年12月31日,经过一系列的优化和数据清理,我们的年终决算效率有了一定的加快,最终在十一点半就完成了系统的跑批工作,顺利结束了2022年的年终决算工作。

​ 但是新的问题出现了,在跨年过后,业务部门反馈,部分业务出现异常,无法取到到数据。经过业务运维人员的分析,发现是部分业务需要取已经清理的其中一张日志表的数据,从而触发了一些bug,导致部分业务出现异常。该异常通过后台进行了处理,但是为了避免下个月再次出现异常,则需要重新恢复该表中的数据。

二、遇到问题

​ 问题一:impdp导入出现enq : TM - contention及enq : TX - row lock contention

​ 当得知需要重新将数据恢复后,因为历史数据是通过impdp导出的逻辑备份文件且历史交易数据不会有改变。因此,理所当然,我们想到的是通过impdp的方式直接导入历史数据,具体方法和语句如下(使用append是因为我没有指定具体分区,如果不加则会提示表已存在,无法导入,而append则会在原有基础上增加导入):

impdp "'/ as sysdba'" directory=DMP_DIR dumpfile=TABLE_NAME_BEFORE_P20220901_EXPDP_20221221_%U.dmp logfile=TABLE_NAME_BEFORE_P20220901_IMPDP_20221221.log tables=USERNAME.TABLE_NAME parallel=4 table_exists_action=append

当该语句执行下去后,本以为顺利导入数据即可,但是经过观察,发现数据库中突然出现大量的enq: TM - contention和enq: TX - row lock contention阻塞。


​没过多久,业务BPC开始告警,发现业务出现交易成功率过低的问题。收到告警后,我们立即将导入语句杀掉,业务立即恢复正常。

问题二:原表索引失效

​ 由于直接通过impdp的方式会造成业务阻塞,那么我们考虑了第二种方式,通过交换分区的方式将表进行恢复,方法步骤如下:

1)获取原表的ddl(假设为table_a,分区表);

2)创建原表的备份表(table_a_bak,非分区表);

3)将三个月前的数据,假设1-9月数据,导入table_a_bak中;

4)保留table_a表中9月数据,删除之前1-8月所有分区;

5)通过alter table table_a exchange partition partition_name with table table_a_bak update indexes的方式来交换分区;

于是在第二次进行操作的时候,第一步、第二步均非常正常,但是到了第三步,历史数据导入table_a_bak的时候确出现了原表,即table_a表的全局索引失效的情况。由于该全局索引是唯一的,所以此时该表无法正常使用,业务报错。立即通过重建索引的方式恢复业务。
​ 问题三:交换分区交换报错ORA-14097

​ 问题三是第二个问题衍生出来的错误,即假设我们通过了3、4步骤,在第五步交换分区的时候还是会遇到一个非常棘手的问题,就是该表在最开始通过在线重定义进行分区的时候,由于没有主键,故是通过rowid进行识别的,这样做在重定义的表上则会多一个隐藏列。

​ 但是在手动创建的表上并没有隐藏列,因此做分区交换的时候则会报错:

ORA-14097: column type or size mismatch in ALTER TABLE EXCHANGE PARTITION

即两个表的表结构不一致,直接通过desc的方式还无法确认到问题所在,只能通过dba_tab_columns的数据字典视图查询才发现隐藏列造成分区交换无法成功的问题。

三、问题分析和模拟

问题遇到了,但是工作还是要做起走,那么我们就需要分析上述三个问题的原因,从而进行改进,找到合适的方法进行数据导入操作了。因此,我们在测试环境上对上述的三个问题进行模拟和问题分析。

​ 1)问题一分析:

​ 针对问题一,impdp的时候产生了大量的enq : TM - contention及enq : TX - row lock contention的阻塞。首先我们需要清楚enq:TM-contention锁是数据库在执行dml期间,为防止对与dml相关的对象进行修改,执行dml的进程必须对该表获得TM锁,若在获得TM锁的过程中发生争用则会导致enq:TM-contention事件。

​ 但是在这里,我们一直是对历史数据进行操作,并没有对当前的分区或者数据进行操作,即并没有修改到相同的对象,但是产生了enq:TM-contention锁就不太合理,因此,我们认为是由于在impdp的过程中加入了parallel的操作,而该表又是实时交易表,因此导入数据和原表写入数据过高,触发了一些限制,所以造成了大量的enq:TM-contention等待事件,进一步又造成了行锁,而我们不加parallel可能就没有问题了。

​ 为了确认该原因,我们在测试环境进行了测试,测试步骤和生产操作完全保持一致,即导出并清理历史数据,然后通过insert的方式向新的分区不断写入数据,最后通过impdp的方式导入历史数据,观察等待事件,具体操作如下:
1)备份数据

expdp "'/ as sysdba'" directory=dmpdir dumpfile=expdp_TABLE_A_CS_BEFORE_20230101_%U.dmp logfile=expdp_TABLE_A_CS_BEFORE_20230101.log tables=TABLE_A parallel=4 compression=all

2)清理历史数据

alter table TABLE_A truncate partition P_20160701 update global indexes;
alter table TABLE_A truncate partition P_20160801 update global indexes;
alter table TABLE_A truncate partition P_20160901 update global indexes;
alter table TABLE_A truncate partition P_20161001 update global indexes;
alter table TABLE_A truncate partition P_20161101 update global indexes;
alter table TABLE_A truncate partition P_20161201 update global indexes;
.........
alter table TABLE_A truncate partition P_20220901 update global indexes;

3)导入数据并向新的分区写入数据:

session 1:
impdp "'/ as sysdba'" directory=dmpdir dumpfile=expdp_TABLE_A_CS_BEFORE_20230101_%U.dmp logfile=expdp_TABLE_A_CS_BEFORE_20230101_IMPDP.log parfile=partition_TABLE_A_table_restore_20230104.par content=data_only parallel=4
或者
impdp "'/ as sysdba'" directory=dmpdir dumpfile=expdp_TABLE_A_CS_BEFORE_20230101_%U.dmp logfile=expdp_TABLE_A_CS_BEFORE_20230101_.log parfile=partition_TABLE_A_table_restore_20230104.par content=data_onlysession2:
insert into TABLE_A select * from TABLE_A_BAK1  where JYTIME >= '20221101';

​ 其中partition_TABLE_A_table_restore_20230104.par的内容即为指定的tables参数,包含了需要导入的所有历史分区数据。
此时的阻塞源为1节点的11441会话,被阻塞的则是1节点的604会话,该会话正在执行insert语句,此时它正在等待11441会话释放TM锁。那么我们那么我们则需要看看11441会话正在做什么?

​ 我们分别对两个会话进行查看:

11441会话,也在执行一个insert语句:
INSERT /*+ APPEND PARALLEL("TABLE_A",1)+*/ INTO RELATIONAL("TABLE_A" NOT XMLTYPE) ("A", "B", "C", "D", "E", "F", "G")    SELECT ("A", "B", "C", "D", "E", "F", "G")     FROM "ET$2CB107FD604会话,则是我们执行的新分区写入语句:
insert into TABLE_A select * from TABLE_A_BAK1  where JYTIME >= '20221101';

第一个语句以及其program上来看,就是我们的impdp导入产生的语句,该语句正在通过append的方式向TABLE_A中写入数据。而它则阻塞了我正常业务向新的分区写数据的操作。

其实分析到这里,生产上通过impdp导入,产生大量的enq : TM - contention及enq : TX - row lock contention的阻塞问题就已经很明显了,其原因和导入参数是否加入parallel并无关系,而是impdp写入数据表过是通过append的方式进行insert操作的。

​ 稍微深入一层,了解简单了解一下enq:TM-contention等待的发生通常的情况有哪些:

1)修改无索引外键(foreign key)的父键时;
2)DML与DDL之间的TM争用;
3)LOCK TABLE 引起的TM锁争用;
4)direct load工作引起的TM锁争用;

而我们知道通过APPEND的方式对数据进行INSERT,就是以direct load的方式进行工作的,该方式不经过sga,而是直接写入到数据文件里,所以在执行工作期间,会以exclusive模式获得相应表的tm锁。通过对锁的情况进行观察,确实此时11441会话确实是以lmod=6的模式获取了表的tm锁:

最终返回到生产,查看了历史执行记录和锁记录,则梳理清楚了生产上发生的事情:

​ 当我们通过impdp导入历史数据的时候,该进程通过append的方式insert数据,此时由于是direct load的方式工作,故以独占模式获取了该表的TM锁,而后续的业务SQL中交易会对该表写入数据,故会产生enq : TM - contention的阻塞,此外业务表上由于交易的唯一性也会出现select …for update的操作不断执行,因此又造成了大量的enq : TX - row lock contention行锁竞争。
2)问题二分析:

​ 针对问题二,即我们在导入TABLE_A_BAK表的时候,原表索引失效。在alert日志中也出现了相关的提示信息:Some indexes or index [sub]partitions of table TABLE_A have been marked unusable。而我们的导入语句如下:

impdp "'/ as sysdba'" directory=dmp_dir dumpfile=TABLE_A_BEFORE_P20220901_EXPDP_20221221_%U.dmp logfile=TABLE_A_BEFORE_P20220901_IMPDP_20221221_20230104.log parfile=partition_TABLE_A_table_restore_20230104.par remap_table=v7cbs.TABLE_A:TABLE_A_BAK content=data_only parallel=4

​ 这里我们比较奇怪的是,我们导入的是目标表数据,为什么原表的索引会出现失效的问题呢?我们没有在任何帖子中找到该导致原表失效的原因,但是找到一篇老杨曾今写过的帖子,老杨帖子中解释,在impdp的remap_table参数中,只是remap我们指定的table,而不会remap其索引(其实,我们如果操作多了也会发现,在导入的过程中,如果该表有索引,那么通常会提示索引已经存在,这也说明了这个问题)。而在我们这次的操作中,又加入了content=data_only的方式,这里我们认为会包括用户数据和索引数据。因此,我们从理论上分析的结论为:导入过程中加入了remap_table和content=data_only参数,而remap_table参数不具备索引的remap。因此,在导入data的时候,也会重新导入index_data,数据库为了成功导入索引数据,所以破坏了唯一索引的一致性,将唯一索引标为无效。

​ 但这只是理论上的一种猜测,可是比较奇怪的是,在出现索引失效的时候,从impdp的日志上来看,此时导入的还是table和table_data,并没有到index_data这一步:

​ 因此,我们在测试环境上进行相同操作,准备复现问题。但是最终并未将问题复现,经过多次导入测试,并没有出现索引失效的情况。因此,怀疑是否触发了某bug导致索引失效。

​ 当然,为了避免在生产上再次发生索引失效的情况,我们也改进了导入语句,不再使用content=data_only的参数,而是使用include参数进行直接指定内容,确保不导入索引数据(这种方式在测试上也不会造成索引失效,但是生产环境无法做测试)。

impdp "'/ as sysdba'" directory=dmp_dir dumpfile=TABLE_A_BEFORE_P20220901_EXPDP_20221221_%U.dmp logfile=TABLE_A_BEFORE_P20220901_IMPDP_20221221_20230104.log parfile=partition_TABLE_A_table_restore_20230104.par remap_table=v7cbs.TABLE_A:TABLE_A_BAK include=table,table_data parallel=4

3)问题三分析:

​ 针对问题三,交换分区交换报错ORA-14097。造成该问题有两个:一是我们将该表通过rowid进行在线重定定义的时候,新的分区表会有一个隐藏列产生。二是由于隐藏列导致分区表和手动创建的非分区表结构不一致,造成无法交换分区。

通常,我们通过desc的方式去查看表结构,是无法看到隐藏列的,因此我们可以通过DBA_TAB_COLS来查看该表的真实表组成列:

select owner,table_name,COLUMN_NAME,HIDDEN_COLUMN from dba_tab_cols where table_name='TABLE_A';OWNER                          TABLE_NAME                     COLUMN_NAME                  HID
------------------------------ ------------------------------ ------------------------     ---
.......
USERNAME                        TABLE_A                    SYS_C00037_22122118:49:32$     YES

​ 或者通过以下SQL对比两个表的表结构是存在差异:

select col#, name, type#, length, precision# from sys.col$
where obj# =(select object_id from dba_objects where object_name='TABLE_A' and object_type='TABLE')
minus
select col#, name, type#, length, precision# from sys.col$
where obj# = (select object_id from dba_objects where object_name='TABLE_A_BAK' and object_type='TABLE');COL# NAME                                TYPE#     LENGTH PRECISION#
---------- ------------------------------ ---------- ---------- ----------0 SYS_C00037_23010517:11:20$              1        255

​ 那么该问题该如何处理呢?既然我们遇到了表结构不一致,那么我们就将表结构调整成一致的即可,而这又有两种思路,分别是将原表上的隐藏列删除,另外一种就是在新的表的表结构上加入隐藏列:

方法一:删除原表上的隐藏列:
ALTER TABLE TABLE_A DROP UNUSED COLUMNS;
方法二:新增表上增加隐藏列:
alter table table_a_bak add "SYS_C00037_23010517:11:20$" varchar(255);
alter table table_a_bak set UNUSED("SYS_C00037_23010517:11:20$");

当然,这样的操作更建议做第二种,因为第一种是在原表上进行操作,删除列是一个DDL语句,该列中虽然没有数据,且是一个unused列,但是经过测试,删除该列还是需要执行十几甚至几十秒的时间,而这个时间是我们的业务无法接受的。

​ 当表结构完全一致后(通过上述的minus方法进行比对),我们则可以进行分区交换操作了:

alter table v7cbs.TABLE_A exchange partition P_20220901 with table TABLE_A_BAK update indexes;

由于是操作历史分区,因此虽然是ddl语句,正常情况下是不会造成阻塞的(当然,我们在进行交换分区的时候也通过insert和select …for update语句进行了同时操作的验证,并确认事物可以正常运行)。

当然,我们还有一点需要注意的就是undo的使用情况,我们可以适当的加大undo表空间减小undo_retention的参数以防undo因为爆满无法而交换分区操作失败。

四、结论

​ 数据的导出导入操作以及简单的数据恢复是每个dba必备的基础技能,但是操作看似简单,实际上暗藏玄机,稍有不慎就可能造成一些严重的问题甚至事故。通过上述的操作、测试以及分析,我们可以知道当使用逻辑备份导入原表的时候,如果该表的操作非常频繁,是系统中的重要业务表,那么我们是肯定不能使用impdp的方式直接导回原表的。往往这样的数据恢复需求,需要通过一些其他的手段,比如设置中间表,通过中间表向原表中进行insert into select 方式写入(但是这种方式最好通过存储过程进行批量提交,否则undo撑不住),如果是存在分区的话,使用分区交换或者insert into select的方式也都比较好,同时分区表则可以通过insert into select partition的方式减小每次操作的数据量,是恢复历史数据的一种好的方法。

逻辑备份数据导入原分区表遇到的那些事儿相关推荐

  1. 一文带你了解GaussDB(DWS) 的Roach逻辑备份实现原理

    摘要:Roach工具是GaussDB(DWS)推出的一款主力的备份恢复工具,包含物理与逻辑备份两种主要能力,本文着重于讲解Roach逻辑备份的实现原理. 一.简介 在大数据时代,数据的完整和可靠性成为 ...

  2. 技术解读丨GaussDB数仓高可用容灾利器之逻辑备份

    摘要:GaussDB数仓的Roach工具,同时提供物理备份和逻辑备份两种主要形态的备份.逻辑备份针对数据库的逻辑对象进行抽取和备份,能够有效地应对单表.schema级等较细粒度的备份,较为灵活和便利. ...

  3. 数据备份数据迁移方案

    数据备份方式 物理备份 冷备:cp.tar.- 逻辑备份 mysqldump //备份 mysql //恢复 物理备份及恢复 备份 ] cp -r /var/lib/mysql 目录/mysql.ba ...

  4. Fundebug是这样备份数据的

    摘要: 数据还是要备份的,万一删库了呢? 本文代码仓库: fundebug-mongodb-backup 引言 今年8月,腾讯云竟然把客户前沿数据的数据弄没了,Fundebug在第一时间进行了一些简单 ...

  5. 分区表需要数据备份吗oracle,Oracle 分区表数据的导入与导出(1)

    --**************************** -- 导入导出 Oracle 分区表数据 --**************************** 导入导入Oracle 分区表数据是 ...

  6. Oracle数据库数据备份导出导入

    Oracle数据库导入导出命令 Exp/Imp 此组合命令属于客户端命令,在Oracle客户端和数据库服务器上皆可使用. 导出: exp system/manager@orcl  file=d:\te ...

  7. mysql 8.0数据备份恢复_MySQL 8.0 增强逻辑备份恢复工具介绍-爱可生

    作者:杨涛涛 资深数据库专家,专研 MySQL 十余年.擅长 MySQL.PostgreSQL.MongoDB 等开源数据库相关的备份恢复.SQL 调优.监控运维.高可用架构设计等.目前任职于爱可生, ...

  8. WSUS2.0数据导入、导出(备份、还原)

    以前我们做的WSUS实验都是让WSUS服务器通过互联网连接到微软网站去下 载更新补丁,但是现实的生活中有些网络是与互联网隔绝的,这种情况下可以采用导入导出WSUS数据的方法来解决WSUS的更新问题.先 ...

  9. 数据导入导出、备份恢复

    1.数据导入(文件内容导入数据库) 1.作用 :把文件系统的内容导入到数据库中 2.语法 load data infile "/var/lib/mysql-files/文件名" i ...

最新文章

  1. java 获取_java获取类的信息
  2. java 日期只计算年月日大小_Java按自然月计算两个日期相差的年月日?
  3. 【教程】VsCode搭建Java开发环境
  4. 编译安装PHP出现Cannot load /usr/local/apache/modules/libphp5.so
  5. 【BZOJ-1324】Exca王者之剑 最小割
  6. 用ffmpeg在命令行下,对文件进行转码H264
  7. 图形验证码知识点整理 Object.prototype.toString.call()等
  8. Ambari技术介绍-尚硅谷大数据培训
  9. rubyinstaller下载安装 redis集群
  10. 二路归并排序和基数排序
  11. 中小企业如何选择OA协同办公产品?最全的对比都在这里了
  12. CloudComparePCL 点云OOB包围盒
  13. 第三方支付接口之微信扫码支付
  14. 罗晨:梦想照进现实,一个独立开发者的田园诗
  15. 查找WebSphere Application Server 管理控制台端口号
  16. Android Gradle进阶配置指南 1
  17. elementui+vue+springboot企业员工工资考勤请假系统
  18. 市场调研很难做?这些软件帮你理清思绪
  19. 一周电商零售news汇总(1.18-1.25)​
  20. 留学目的地选择之亚利桑那州

热门文章

  1. 网站建设(1)——域名的类型、选择和购买
  2. 东方博宜OJ 1043 - 【入门】行李托运价格
  3. location 拦截所有_AdGuard for Mac(广告拦截软件)
  4. 【SDR】srsLTE安装、运行及测试
  5. 什么是video codec? video codec在实际业务的应用。
  6. 如皋中学2021高考成绩查询,喜报!如皋八所高中高考成绩公布
  7. man命令后带的数字含义
  8. kestrel java_Kestrel Web 服务器学习笔记
  9. 小米8Lite刷机教程(大多数手机操作基本通用)
  10. esp8266编译固件问题 踏坑之旅