Oracle优化:大量数据插入或更新

最近遇到的一个面试题,印象很深记录如下:

面试官:现在有一张表数据量达很大,要把里面记录时间的那行更新到当前最新日期,每次更新都很卡,机器变慢影响业务,怎么优化。

我的想法是:大量的数据更新肯定会写记录,而大量的写记录又会触发lgwr,所以机器变卡的原因是内存暂满还有在写重做日记。如果操作是添加日志组或增大日志的大小,又或者调整SGA里面各种池的大小,其实也是没有用,顶多就是延迟发生故障。


所以只要跳过写记录那块,那么问题就可以根本解决。


一般数据库都是在归档模式下运行(Oracle数据库有联机重做日志,这个日志是记录对数据库所做的修改,比如插入,删除,更新数据等,对这些操作都会记录在联机重做日志里),跳过就要关闭归档模式,还有就是修改表在nologging状态下。这样两个状态下就可以不写日志,但是也是在万分确定不会出错的情况下才能使用。


不过这样操作,数据库就要关闭重启到mount的状态,再关闭归档,这样在生产库是不允许的,而且关闭归档是影响整个数据库的,其他业务也会陷入无法恢复的境地,整个数据库无法使用rman做增量备份,很多备份方案都会受到限制。


所以我最后说了先清空缓存,然后把那个表设置为nologging的状态,再进行操作。(单独这个表没有写日志,其他表也照常。)
面试官:是基本这样,但是还有一种更加优化的方法。我最后也没想出来,面试官就告诉我:用create table 复制....+ nologging的方法,当时就心领神会大。虽然不明白为什么同样在nologging的情况下create 会比 直接update 会优化的好,是因为create 是 DDL 吗?继续百度。但我还是先整理这个最终优化方案。


1.假设t表里面需要修改的字段为v_time,创建一个表t_1先不要把v_time放进去: create table t_1 as select v1,v2 from t  nologging;

2.再使用添加列默认值的方式把v_time添加进入:alter table t_1 add v_time date default sysdate;

这样就完成这个‘最终优化方案。


这个是更新数据,那么大量插入新数据呢?也是用到nologging,但还有一个‘append’;

append 个人理解解析就是:插入数据的时候在表的高水位线之上直接插入数据,数据比较快

分别验证

1、非归档模式+表logging下对产生redo的量的影响

2、非归档模式+表nologging下对产生redo的量的影响

3、归档模式+表logging下对产生redo的量的影响

4、归档模式+表nologging下对产生redo的量的影响


使用如下命令查看redo size,注意,这个是累加的,当你exit后,在查看会变成0

select b.name,a.value from v$mystat a,v$statname b where a.statistic#=b.statistic# and b.name='redo size';

复制表结构 where 1=0

create table redo_test as select * from dba_objects where 1=0;

1、非归档模式+logging选项

insert into redo_test select * from dba_objects;

SQL> insert into redo_test select * from dba_objects;

72305 rows created.

SQL> select b.name,a.value from v$mystat a,v$statname b where a.statistic#=b.statistic# and b.name='redo size';

NAME      VALUE
-------------------------------------------------------------------------------------------------------------------------------- ----------
redo size    8441916

大约产生了8M的redo

exit 


SQL> insert  /*+ append */ into redo_test select * from dba_objects;

72305 rows created.

SQL> select b.name,a.value from v$mystat a,v$statname b where a.statistic#=b.statistic# and b.name='redo size';

NAME      VALUE
-------------------------------------------------------------------------------------------------------------------------------- ----------
redo size       2048

结果对比:
非归档模式+logging的情况下,普通insert产生的redo有8M,但是使用append的方式插入产生的redo只有2048bye,基本上可以忽略不计,效果非常明显的。


2、非归档模式+nologging选项

alter table redo_test nologging; 开启不用生成日记模式
exit

SQL> insert into redo_test select * from dba_objects;

72305 rows created.

SQL> select b.name,a.value from v$mystat a,v$statname b where a.statistic#=b.statistic# and b.name='redo size';

NAME      VALUE
-------------------------------------------------------------------------------------------------------------------------------- ----------
redo size    8245308

还是8M


SQL> insert  /*+ append */ into redo_test select * from dba_objects;

72305 rows created.

SQL> select b.name,a.value from v$mystat a,v$statname b where a.statistic#=b.statistic# and b.name='redo size';

NAME      VALUE
-------------------------------------------------------------------------------------------------------------------------------- ----------
redo size    8260224

SQL> select 8260224-8245308 from dual;

8260224-8245308
---------------
          14916

在非归档模式下通过insert /*+ append */ into方式批量加载数据可以大大减少redo产生。


3、归档模式+logging选项下对产生redo的量的影响

先启动归档模式

SQL> alter table redo_test logging; 修改回去写记录模式

Table altered.

exit

---insert插入数据
SQL> insert into redo_test select * from dba_objects;

72305 rows created.

SQL> select b.name,a.value from v$mystat a,v$statname b where a.statistic#=b.statistic# and b.name='redo size';

NAME      VALUE
-------------------------------------------------------------------------------------------------------------------------------- ----------
redo size    8228800

SQL> 

发现产生了8m的日志


--使用append的方式插入
SQL> insert  /*+ append */ into redo_test select * from dba_objects;

72305 rows created.

SQL> select b.name,a.value from v$mystat a,v$statname b where a.statistic#=b.statistic# and b.name='redo size';

NAME      VALUE
-------------------------------------------------------------------------------------------------------------------------------- ----------
redo size    8481404

在归档模式+logging下,不管有没有使用append(高水位插入)的方式都会产生大量的redo,日志还是要写的,与预想的结果吻合。


4、归档模式+nologging选项下对产生redo的量的影响-重点

--修改为nologging模式--切莫忘
  alter table redo_test nologging;
  
 exit
 
 

SQL> insert into redo_test select * from dba_objects;

72305 rows created.

SQL> select b.name,a.value from v$mystat a,v$statname b where a.statistic#=b.statistic# and b.name='redo size';

NAME      VALUE
-------------------------------------------------------------------------------------------------------------------------------- ----------
redo size    8422872

使用普通插入的方式生成了8m日记

SQL> insert  /*+ append */ into redo_test select * from dba_objects;

72305 rows created.

SQL> 
SQL> select b.name,a.value from v$mystat a,v$statname b where a.statistic#=b.statistic# and b.name='redo size';

NAME      VALUE
-------------------------------------------------------------------------------------------------------------------------------- ----------
redo size    8438572

SQL> select 8438572-8422872 from dual;

8438572-8422872
---------------
          15700

在归档+nologging的情况下使用append高水位插入只有15700的redo,536倍的大小被减去了,所以nologging+append是在归档模式下性能优化的好方法。

记得还有一个小插曲,最近身体不舒服,喉咙痛痰多,声音都变老了,一个人事见到我说你还挺年轻的啊。

你可能感兴趣的:(ORACLE数据库)