原始SQL:

CREATE OR REPLACE PROCEDURE sp_upd_suppressed_emails
(
  A_LIMIT_BULK IN PLS_INTEGER DEFAULT 2000,
  A_COMMIT_AFTER IN PLS_INTEGER DEFAULT 2000
) is   v_operation Varchar2(20) := 'START';
  v_last         PLS_INTEGER;   
  v_start        PLS_INTEGER;   
  v_end          PLS_INTEGER;  
  l_row          PLS_INTEGER;
  
  CURSOR curs_contact IS SELECT id FROM contact
  WHERE active_ind = 2 and suppress_flag = 'Y' and bounce_flag = 'Y' 
  and consumer_id in (select id from consumers where login_name IN ('ASPIRAFOCUS', 'CDFW', 'PSACSFSUSR') ) 
  and  email is not null ORDER BY id; 
  
  TYPE contactId IS RECORD ( id contact.id%TYPE); 
 
  TYPE v_contactId_tbl IS TABLE OF contactId INDEX BY PLS_INTEGER;
  
  v_contact v_contactId_tbl;    
   
BEGIN
    
    DBMS_OUTPUT.PUT_LINE ('start reading data..' );
    v_operation := 'BEFORE OPEN';
OPEN curs_contact; LOOP v_operation := 'BEFORE FETCH';
FETCH curs_contact BULK COLLECT INTO v_contact LIMIT A_LIMIT_BULK;
v_operation := 'AFTER FETCH'; EXIT WHEN v_contact.COUNT = 0; v_start := 1;   
v_last := v_contact.COUNT; 
l_row := 0; LOOP
DBMS_OUTPUT.PUT_LINE ('LOOP 2 top ');
EXIT WHEN v_start > v_last;
v_end := LEAST (v_start + A_COMMIT_AFTER - 1, v_last);
            DBMS_OUTPUT.PUT_LINE ('LOOP 2 v_start ' || v_start || ' end ' || v_end );
BEGIN
v_operation := 'UPDATE_LOAD';
FORALL i IN v_start .. v_end 
                UPDATE contact SET active_ind = 1, suppress_flag = 'N', bounce_flag = 'N'  WHERE id = v_contact (i).id;
            END;
DBMS_OUTPUT.PUT_LINE ('after forall start ' || v_start || ' end ' || v_end );
            COMMIT;
v_start := v_end + 1;         
END LOOP;
DBMS_OUTPUT.PUT_LINE ('LOOP 2 bottom v_start ' || v_start || ' end ' || v_end );
        COMMIT;
END LOOP;
DBMS_OUTPUT.PUT_LINE ('stop reading data..' );
    
    EXCEPTION 
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE ('Exception:Others-> v_operation = ' || v_operation || ', rowcount # ' || l_row || ' SQLCODE ' || SQLCODE || ' ERR_MSG = ' || SUBSTR(SQLERRM, 1, 500) ); END sp_upd_suppressed_emails;

原始SQL在执行过程中经过几小时未完成,查看sql统计信息:

Stat Name Statement Total Per Execution % Snap Total
Elapsed Time (ms) 2,997,435 187,339.68 53.56
CPU Time (ms) 2,896,748 181,046.72 65.48
Executions 16    
Buffer Gets 521,592,791 32,599,549.44 69.21
Disk Reads 10,974 685.88 1.12
Parse Calls 1 0.06 0.00
Rows 32,000 2,000.00  
User I/O Wait Time (ms) 2,525    
Cluster Wait Time (ms) 1,146    
Application Wait Time (ms) 4    
Concurrency Wait Time (ms) 1    
Invalidations 0    
Version Count 1    
Sharable Mem(KB) 51    

Back to Plan 1(PHV: 3689516641)

Execution Plan

Id Operation Name Rows Bytes Cost (%CPU) Time Pstart Pstop
0 UPDATE STATEMENT       16798 (100)      
1    UPDATE CONTACT            
2      PARTITION RANGE ALL   1 51 16798 (1) 00:00:01 1 1048575
3        PARTITION HASH SINGLE   1 51 16798 (1) 00:00:01 KEY KEY
4          TABLE ACCESS FULL CONTACT 1 51 16798 (1) 00:00:01    

表数据量非常大,根据created_date时间字段分区,ID字段进行子hash分区,上面建立了基于ID字段的global hash分区索引。

从上面情况看出,原始SQL分批2000个ID进行批量update,但执行计划走的是TABLE ACCESS FULL, 并且进行了分区的PRATITION RANGE ALL操作,可以看出2000条记录的批量更新由于不能利用global hash分区索引进行检索,主要原因是由于hash分区适用于等值操作,对于范围或者批量操作,可能遍历很多hash分区,导致结果不如TABLE ACCESS FULL。

针对这种情况,根据业务逻辑,将批量条件从ID改成其他非global hash索引列,执行时间降低为5分钟。

​CREATE OR REPLACE PROCEDURE sp_upd_suppressed_emails
(
A_LIMIT_BULK IN PLS_INTEGER DEFAULT 2000,
A_COMMIT_AFTER IN PLS_INTEGER DEFAULT 2000
) is v_operation Varchar2(20) := 'START';
v_last PLS_INTEGER;
v_start PLS_INTEGER;
v_end PLS_INTEGER;
l_row PLS_INTEGER; CURSOR curs_contact IS SELECT distinct email FROM contact
WHERE active_ind = 2 and suppress_flag = 'Y' and bounce_flag = 'Y'
and consumer_id in (select id from consumers where login_name IN ('ASPIRAFOCUS', 'CDFW', 'PSACSFSUSR') )
and email is not null ORDER BY email; TYPE contactId IS RECORD ( email contact.email%TYPE); TYPE v_contactId_tbl IS TABLE OF contactId INDEX BY PLS_INTEGER; v_contact v_contactId_tbl; v_upd_date date ; BEGIN DBMS_OUTPUT.PUT_LINE ('start reading data..' );
v_operation := 'BEFORE OPEN'; select sysdate into v_upd_date from dual; OPEN curs_contact; LOOP v_operation := 'BEFORE FETCH';
FETCH curs_contact BULK COLLECT INTO v_contact LIMIT A_LIMIT_BULK;
v_operation := 'AFTER FETCH'; EXIT WHEN v_contact.COUNT = 0; v_start := 1;
v_last := v_contact.COUNT;
l_row := 0; LOOP
DBMS_OUTPUT.PUT_LINE ('LOOP 2 top ');
EXIT WHEN v_start > v_last;
v_end := LEAST (v_start + A_COMMIT_AFTER - 1, v_last);
DBMS_OUTPUT.PUT_LINE ('LOOP 2 v_start ' || v_start || ' end ' || v_end );
BEGIN
v_operation := 'UPDATE_LOAD';
FORALL i IN v_start .. v_end
UPDATE contact SET active_ind = 1, suppress_flag = 'N', bounce_flag = 'N' , modified_date = v_upd_date
WHERE email = v_contact (i).email
and active_ind = 2 and suppress_flag = 'Y' and bounce_flag = 'Y'
and consumer_id in (select id from consumers where login_name IN ('ASPIRAFOCUS', 'CDFW', 'PSACSFSUSR') ) ;
END;
DBMS_OUTPUT.PUT_LINE ('after forall start ' || v_start || ' end ' || v_end );
COMMIT;
v_start := v_end + 1;
END LOOP;
DBMS_OUTPUT.PUT_LINE ('LOOP 2 bottom v_start ' || v_start || ' end ' || v_end );
COMMIT;
END LOOP;
DBMS_OUTPUT.PUT_LINE ('stop reading data..' ); EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE ('Exception:Others-> v_operation = ' || v_operation || ', rowcount # ' || l_row || ' SQLCODE ' || SQLCODE || ' ERR_MSG = ' || SUBSTR(SQLERRM, 1, 500) ); END sp_upd_suppressed_emails;

记一次全局分区索引update调优的更多相关文章

  1. Oracle非分区索引,全局分区索引和本地分区索引。

    1.如果按照索引是否分区作为划分依据,Oracle 的索引类型可以分为非分区索引,全局分区索引和本地分区索引. 2.创建演示实例 --创建非分区表create table test_partition ...

  2. 【Kafka】Kafka-副本-分区设置-性能调优

    Kafka-副本-分区设置-性能调优 SparkKafkaDemo - Executors kafka replication 负载均衡_百度搜索 Kafka 高性能吞吐揭秘 - 友盟博客 - Seg ...

  3. Day 18: 记filebeat内存泄漏问题分析及调优

    ELK 从发布5.0之后加入了beats套件之后,就改名叫做elastic stack了.beats是一组轻量级的软件,给我们提供了简便,快捷的方式来实时收集.丰富更多的数据用以支撑我们的分析.但由于 ...

  4. Spark踩坑记:Spark Streaming+kafka应用及调优

    前言 在WeTest舆情项目中,需要对每天千万级的游戏评论信息进行词频统计,在生产者一端,我们将数据按照每天的拉取时间存入了Kafka当中,而在消费者一端,我们利用了spark streaming从k ...

  5. 记Oracle中regexp_substr的一次调优(速度提高95.5%)

    项目中需要做一个船舶代理费的功能,针对代理的船进行收费,那么该功能的第一步便是选择进行代理费用信息的录入,在进行船舶选择的时候,发现加载相关船舶信息十分的慢,其主要在sql语句的执行,因为测试的时候数 ...

  6. 记一次Web服务的性能调优

    前言 一个项目在经历开发.测试.上线后,当时的用户规模还比较小,所以刚刚上线的项目一般会表现稳定.但是随着时间的推移,用户数量的增加,qps的增加等因素会造成项目慢慢表现出网页半天无响应的状况.在之前 ...

  7. 记一次tomcat线程创建异常调优:unable to create new native thread

    测试在进行一次性能测试的时候发现并发300个请求时出现了下面的异常: HTTP Status 500 - Handler processing failed; nested exception is ...

  8. Atitit.分区对索引的影响 分区索引和全局索引 attilax总结

    Atitit.分区对索引的影响 分区索引和全局索引 attilax总结 1. 分区的好处1 2. 分区键:2 3. 分区的建议:2 4. 分区索引和全局索引:2 5. 全局索引就是在全表上创建索引, ...

  9. Oracle分区索引

    索引与表类似,也可以分区: 分区索引分为两类: Locally partitioned index(局部分区索引) Globally partitioned index(全局分区索引) 下面就来详细解 ...

随机推荐

  1. P4868 Preprefix sum

    传送门 挺显然的一题?单点修改,前缀和数组前缀查询 树状数组就可以维护了 考虑每个位置对应询问的贡献,设询问的位置为 $x$,对于原数组 $a[i]$ 的某个位置 $i$,它会贡献 $(x-i+1)* ...

  2. 简单CSS实现闪烁动画(+1白话讲解)

    原文:简单CSS实现闪烁动画(+1白话讲解) 本文转载于:猿2048网站⇒https://www.mk2048.com/blog/blog.php?id=icj2chj2ab 背景 本文承接自上文&l ...

  3. grunt接触

    grunt使用 以下内容均为已经安装好grunt,具体grunt的安装过程不述,可以参考grunt的相关资料. 1.项目初始化grunt 在项目文件夹的根目录下面,打开命令行grunt init,执行 ...

  4. shell统计mysql当前连接数

    [root@push-- scripts]# mysql -S /var/lib/mysql//mysql.sock -uroot -phlsms_push_Zaq1xsw@ -e "sho ...

  5. mongodb,robomongo 数据查询

    可视化管理工具:Robomongo 是开源,免费的MongoDB管理工具,下载地址:Robomongo下载 1.  基本查询:    构造查询数据.    > db.test.findOne() ...

  6. nslookup 工具的使用方法记录

    查询IP地址 nslookup最简单的用法就是查询域名对应的IP地址,包括A记录和CNAME记录,如果查到的是CNAME记录还会返回别名记录的设置情况.其用法是: nslookup 域名 定查询记录类 ...

  7. 如何代替set get方法

    博主刚刚看其他人的博客的时候,发现好多人还在用 生成set get方法  虽然是自动生成的 但是看起来很复杂,影响代码的可读性 那么有什么办法能代替set  get方法吗? 当然有啦!!! 只需要导入 ...

  8. bootstrap-table(2)问题集

    参考;https://www.cnblogs.com/landeanfen/p/4993979.html 1.分页参数sidePagination 如果是服务端分页,返回的结果必须包含total.ro ...

  9. LNMP 多版本PHP同时运行

    首先需要装好两个版本以上的PHP(例如:php5.6和php7两个版本).这里假设你已安装完成.1.配置并启动php默认版本: (设置 nginx 的 vhost 域名配置文件监听端口就好) 1).打 ...

  10. mysql:You can't specify target table 'sessions' for update in FROM clause

    更新数据时,在where条件子句里面如果想使用子查询按条件更新部分数据,需要将查询的结果设为临时表.可以参考: https://blog.csdn.net/poetssociety/article/d ...