Oracle SQL性能优化案例----数据入库和更新任务
一、需求(每月定时一次)
1、订购关系入库和更新任务
1)原订购关系表数据备份
2)本月订购关系数据入库(通过sqlldr脚本,大约10-15G,1-10亿条数据)
3)对入库的订购关系表进行更新
2、用户状态入库和更新任务
1)原用户状态表数据备份
2)本月用户状态数据入库(通过sqlldr脚本,大约10-15G,1-10亿条数据)
3)对入库的用户状态表进行更新
备注:
1)任务期间,表无需对外访问,可以独占资源
2)订购关系表和用户状态表,表上都有索引
二、完成任务的步骤
1、订购关系入库和更新任务
1)清理原先的备份表(任务每月一次,即清理上个月的备份表数据),语句如下
truncate table user_order_bak;
-- user_order_bak表为备份表
2)原订购关系表数据备份
insert into user_order_bak t
select * from user_order a;
commit;
3)清理原订购关系表
truncate table user_order;
4)本月的订购关系数据入库
insert_order.ctl
LOAD DATA
INFILE 'xxxxxxxxx'
append into TABLE user_order
FIELDS TERMINATED BY ","
(
A_XXXX,
B_XXXX,
C_XXXX,
D_XXXX,
E_XXXX,
F_XXXX,
G_XXXX date 'yyyy-mm-dd hh24:mi:ss',
H_XXXX,
I_XXXX,
J_XXXX,
K_XXXX
)
sqlldr 用户名/密码 control=insert_order.ctl log=insert_order.log
5)对入库的订购关系表进行更新,划分归属地(以归属表的某列条件来更新该表)
DECLARE
BEGIN
FOR GET_REGOIN IN( SELECT C1,REGION_CODE FROM TAB_NUMBER_XXXX)
LOOP
UPDATE user_order SET E_XXXX=GET_REGOIN.REGION_CODE
WHERE C_XXXX LIKE GET_REGOIN.C1||'%';
COMMIT;
END LOOP;
COMMIT;
END;
6)表重新收集统计信息
BEGIN
DBMS_STATS.GATHER_TABLE_STATS(OWNNAME=>user,TABNAME=>'user_order',ESTIMATE_PERCENT=>1,METHOD_OPT=>'FOR ALL COLUMNS SIZE 1',NO_INVALIDATE=>FALSE,CASCADE=>TRUE,DEGREE => 10);
END;
/
2、用户状态入库和更新任务
与"订购关系入库和更新任务"基本相似,不再重复赘述
三、重点待优化的步骤
"订购关系入库"步骤,如果不做优化,需要15个小时以上
"对入库的订购关系表进行更新,划分归属地"步骤,需要15个小时以上
四、具体优化的步骤
以"订购关系入库和更新任务"为例,另一个基本相似,不重复赘述
1、"清理原先的备份表、原订购关系表数据备份",这两个步骤,优化方法如下
1)直接删除原先的备份表
drop table user_order_bak;
2)ctas+nologging+parallel方式备份原订购关系表数据
create table user_order_bak nologging parallel 4
as
select /*+ parallel(a 4) */ * from user_order a;
alter table user_order_bak logging noparallel;
结论:对于大表而言,ctas+nologging+parallel方式,速度比 insert+select方式快很多
2、"本月订购关系数据入库"步骤,优化方法如下
1)删除表上索引
drop INDEX IDX_user_order_C_XXXX ;
drop index IDX_user_order_A_XXXX
drop index IDX_user_order_D_XXXX
drop index IDX_user_order_F_XXXX
drop index IDX_user_order_G_XXXX
2)sqlldr方式入库,添加如下选项,提高sqlldr加载速度
direct=y parallel=true bindsize=100000000 readsize=20971520 date_cache=10000000
备注:索引先不创建,后续优化步骤需要
结论:
1)对于大批量数据导入或者插入,先删除表上的索引,数据导入完成后,再重建索引,速度会快几个量级;
2)表数据插入/导入期间,如果表中有索引,DML期间需要维护索引结构,涉及到索引的redo日志,索引数据的有序组织,索引的叶子块分配、分裂等等,随着数据量的增加,插入/导入的速度会越来越慢!!
3、"对入库的订购关系表进行更新"步骤,优化方法如下
1)将新入库的订单表重命名
alter table user_order rename to user_order_TMPBAK;
2)CTAS+NOLOGGING+PARALLEL方式重建新的订单表,并且以order by C_XXXX的方式组织表中的数据,这一步非常重要
create table user_order parallel 10 nologging
as
select /*+ parallel(a 10) */ *
from user_order_TMPBAK a
order by C_XXXX;
-- order by C_XXXX顺序组织数据
alter table user_order noparallel logging;
3)验证数据量,无误后,删除TMPBAK表
select /*+ parallel(t 10) */ count(*) from user_order t;
select /*+ parallel(t 10) */ count(*) from user_order_DHBAK t;
DROP TABLE user_order_TMPBAK;
4)重建索引
drop INDEX IDX_user_order_C_XXXX ;
drop index IDX_user_order_A_XXXX
drop index IDX_user_order_D_XXXX
drop index IDX_user_order_F_XXXX
drop index IDX_user_order_G_XXXX
CREATE INDEX IDX_user_order_C_XXXX ON user_order(A_XXXX) parallel 10 nologging;
CREATE INDEX IDX_user_order_C_XXXX ON user_order(C_XXXX) parallel 10 nologging;
CREATE INDEX IDX_user_order_C_XXXX ON user_order(D_XXXX) parallel 10 nologging;
CREATE INDEX IDX_user_order_C_XXXX ON user_order(F_XXXX) parallel 10 nologging;
CREATE INDEX IDX_user_order_C_XXXX ON user_order(G_XXXX) parallel 10 nologging;
alter index IDX_user_order_A_XXXX noparallel logging;
alter index IDX_user_order_C_XXXX noparallel logging;
alter index IDX_user_order_D_XXXX noparallel logging;
alter index IDX_user_order_F_XXXX noparallel logging;
alter index IDX_user_order_G_XXXX noparallel logging;
5)收集统计信息,为后续更新作铺垫
BEGIN
DBMS_STATS.GATHER_TABLE_STATS(OWNNAME=>'CBOSS',TABNAME=>'user_order',ESTIMATE_PERCENT=>1,METHOD_OPT=>'FOR ALL COLUMNS SIZE 1',NO_INVALIDATE=>FALSE,CASCADE=>TRUE,DEGREE => 10);
END;
/
6)对入库的订购关系表进行更新,划分归属地(以归属表的某列条件来更新该表)
DECLARE
BEGIN
FOR GET_REGOIN IN( SELECT C1,REGION_CODE FROM TAB_NUMBER_XXXX)
LOOP
UPDATE user_order SET E_XXXX=GET_REGOIN.REGION_CODE
WHERE C_XXXX LIKE GET_REGOIN.C1||'%';
COMMIT;
END LOOP;
COMMIT;
END;
/
结论:优化C_XXXX列索引的聚簇因子,使得C_XXXX的索引顺序和表中的数据存储顺序两者接近一致,对于使用C_XXXX LIKE 前缀的查询条件(同时更新改行),通过该条件返回的索引行,进行回表时,所需数据会集中在表中几个数据块中(索引列顺序和表存储顺序一致),这样会大大提升查询和更新速度
五、总结
经过整体优化后,整个入库和更新的每月定时任务,在3个小时内完成!
整个过程中,是否还有可以优化的地方呢?肯定还是有很多的,例如,是否可以考虑临时表?剩下的留给大家思考吧。