数据库性能优化一例之Hash Join

登陆操作系统,发现有一个Oracle进程CPU占用率达到99%.

Memory: 2617704K (860220K) real, 2774560K (942392K) virtual, 38440908K free  Page# 1/19

CPU TTY     PID USERNAME PRI NI   SIZE    RES STATE    TIME %WCPU  %CPU COMMAND
 2   ?    20219 oracle   234 20  6390M  5384K run     31:02 100.13 99.96 oracletest1
--

根据Pid查会话的SID:
SQL>select * from v$session where paddr in (select addr from v$process where spid = &Pid)--20219
--
SQL>select * from v$session_wait where sid = 50
1 50 383 db file sequential read file# 119 0000000000000077 block# 35698 0000000000008B72 blocks 1 0000000000000001 -1 9 WAITED KNOWN TIME
-------------------------------------------------
通过v$sqltext关联之后发现如下语句:
--------------------------------------------------------------------------------------------------
select a.Approve_Serial_Nbr,
       a.Approve_Type,
       a.approve_flag,
       a.acct_id,
       a.Approve_charge,
       a.Approve_Staff_Id,
       a.Approve_Site_Id,
       a.Approve_User_Name,
       a.Approve_reason,
       to_char(a.approve_date, 'mm/dd hh24:mi'),
       a.Used_flag,
       b.acc_nbr,
       b.approve_value,
       b.acct_month,
       b.acct_item_type_id
  from bill_cc.approve_serial_t a, bill_cc.approve_item_t b
 where a.approve_serial_nbr = b.approve_serial_nbr
   and a.Partition_id_region between 110101 and 110199
   and b.Partition_id_region between 110101 and 110199
   and a.Approve_Staff_Id = 'sys110101'
   and to_char(a.Approve_Date, 'yyyy/mm/dd') between '2007/05/01' and
       '2007/05/30'
   and a.Approve_Type = 'DERATE'
   and a.Approve_Type = b.Approve_Type
 order by a.approve_serial_nbr,
          a.ACCT_ID,
          b.acct_month,
          b.acct_item_type_id
---------------------------------------------------------------------------------

2 --------------------------------------------------------------------------------------------------------------
3 | Id  | Operation                             |  Name                | Rows  | Bytes | Cost  | Pstart| Pstop |
4 --------------------------------------------------------------------------------------------------------------
5 |   0 | SELECT STATEMENT                      |                      |     1 |   336 |    11 |       |       |
6 |   1 |  SORT ORDER BY                        |                      |     1 |   336 |    11 |       |       |
7 |   2 |   TABLE ACCESS BY LOCAL INDEX ROWID   | APPROVE_ITEM_T       |     1 |    82 |     1 |       |       |
8 |   3 |    NESTED LOOPS                       |                      |     1 |   336 |     2 |       |       |
9 |   4 |     PARTITION RANGE ALL               |                      |       |       |       |     1 |     2 |
10 |   5 |      TABLE ACCESS BY LOCAL INDEX ROWID| APPROVE_SERIAL_T     |     1 |   254 |     1 |     1 |     2 |
11 |   6 |       INDEX RANGE SCAN                | I1_APPROVE_SERIAL_T  |     1 |       |     3 |     1 |     2 |
12 |   7 |     PARTITION RANGE ALL               |                      |       |       |       |     1 |     2 |
13 |   8 |      INDEX RANGE SCAN                 | I1_APPROVE_ITEM_T    |     1 |       |     2 |     1 |     2 |
14 --------------------------------------------------------------------------------------------------------------
15 
16 Note: cpu costing is off, PLAN_TABLE' is old version

--------------------------------------------------------
检查各表数据:
select count(*) from bill_cc.approve_serial_t --1353163
select count(*) from  bill_cc.approve_item_t --1306518
--一般来说当2个大表进行连接涉及数据量较大时hash_join比nested loops更为合适!
  如果两个表都走索引的话后果很可能是恶梦。
                 
------------------------------------
下面强制执行全表扫描模式:
------------------------------------
select                                                  //用时17s
       /*+ full(a) full(b) */ *
  from bill_cc.approve_serial_t a, bill_cc.approve_item_t b
 where a.approve_serial_nbr = b.approve_serial_nbr
   and a.Partition_id_region between 110101 and 110199
   and b.Partition_id_region between 110101 and 110199
   and a.Approve_Staff_Id = 'sys110101'
   and to_char(a.Approve_Date, 'yyyy/mm/dd') between '2007/05/01' and
       '2007/05/30'
   and a.Approve_Type = 'DERATE'
   and a.Approve_Type = b.Approve_Type
 order by a.approve_serial_nbr,
          a.ACCT_ID,
          b.acct_month,
          b.acct_item_type_id


2 -------------------------------------------------------------------------------------------
3 | Id  | Operation             |  Name             | Rows  | Bytes | Cost  | Pstart| Pstop |
4 -------------------------------------------------------------------------------------------
5 |   0 | SELECT STATEMENT      |                   |     1 |   734 |    14 |       |       |
6 |   1 |  SORT ORDER BY        |                   |     1 |   734 |    14 |       |       |
7 |   2 |   HASH JOIN           |                   |     1 |   734 |     5 |       |       |
8 |   3 |    PARTITION RANGE ALL|                   |       |       |       |     1 |     2 |
9 |   4 |     TABLE ACCESS FULL | APPROVE_SERIAL_T  |     1 |   544 |     2 |     1 |     2 |
10 |   5 |    PARTITION RANGE ALL|                   |       |       |       |     1 |     2 |
11 |   6 |     TABLE ACCESS FULL | APPROVE_ITEM_T    |     1 |   190 |     2 |     1 |     2 |
12 -------------------------------------------------------------------------------------------
13 
14 Note: cpu costing is off, PLAN_TABLE' is old version

--------------------
察看原来的索引如下:
create index BILL_CC.I1_APPROVE_ITEM_T on BILL_CC.APPROVE_ITEM_T (APPROVE_TYPE, ACCT_ID, APPROVE_SERIAL_NBR);
create index BILL_CC.I1_APPROVE_SERIAL_T on BILL_CC.APPROVE_SERIAL_T (APPROVE_TYPE, ACCT_ID, APPROVE_SERIAL_NBR);

分析索引的创建应该是不合理的:
drop index BILL_CC.I1_APPROVE_ITEM_T;
drop index BILL_CC.I1_APPROVE_SERIAL_T;

create index BILL_CC.I1_APPROVE_ITEM_T on
bill_cc.approve_serial_t(Approve_Staff_Id,Approve_Type);

--考虑acct_id被经常用:
create index BILL_CC.I2_APPROVE_ITEM_T on
bill_cc.approve_serial_t(acct_id);

create index BILL_CC.I1_APPROVE_SERIAL_T on
bill_cc.approve_serial_t(ACCT_ID);

--最后确认一下其他情形是否有对原来索引有所依赖?
--最终计划如下:
--------------------------------------------------------

2 -----------------------------------------------------------------------------------------------------------
3 | Id  | Operation                            |  Name              | Rows  | Bytes | Cost  | Pstart| Pstop |
4 -----------------------------------------------------------------------------------------------------------
5 |   0 | SELECT STATEMENT                     |                    |     1 |   336 |  1623 |       |       |
6 |   1 |  SORT ORDER BY                       |                    |     1 |   336 |  1623 |       |       |
7 |   2 |   HASH JOIN                          |                    |     1 |   336 |  1614 |       |       |
8 |   3 |    TABLE ACCESS BY GLOBAL INDEX ROWID| APPROVE_SERIAL_T   |     1 |   254 |     2 | ROWID | ROW L |
9 |   4 |     INDEX RANGE SCAN                 | I1_APPROVE_ITEM_T  |    43 |       |     1 |       |       |
10 |   5 |    PARTITION RANGE ALL               |                    |       |       |       |     1 |     2 |
11 |   6 |     TABLE ACCESS FULL                | APPROVE_ITEM_T     |    34 |  2788 |  1611 |     1 |     2 |
12 -----------------------------------------------------------------------------------------------------------
13 
14 Note: cpu costing is off, PLAN_TABLE' is old version

--进一步考虑前台查询组合繁多,approve_type类型不断变化,所以这种
create index BILL_CC.I1_APPROVE_ITEM_T on
bill_cc.approve_serial_t(Approve_Staff_Id,Approve_Type);
是不能通用的,为什么没走skip index还是值得考虑的?

应该更改为:[ysdb]
drop index BILL_CC.I1_APPROVE_ITEM_T;
drop index BILL_CC.I1_APPROVE_SERIAL_T;
--
create index BILL_CC.I1_APPROVE_ITEM_T on
bill_cc.approve_serial_t(pprove_Type);
--
acct_id索引的建立方式不变.
create index BILL_CC.I2_APPROVE_ITEM_T on
bill_cc.I2_APPROVE_ITEM_T(acct_id);

create index BILL_CC.I1_APPROVE_SERIAL_T on
bill_cc.approve_serial_t(ACCT_ID);


2 -----------------------------------------------------------------------------------------------------------
3 | Id  | Operation                            |  Name              | Rows  | Bytes | Cost  | Pstart| Pstop |
4 -----------------------------------------------------------------------------------------------------------
5 |   0 | SELECT STATEMENT                     |                    |     1 |   336 |  1623 |       |       |
6 |   1 |  SORT ORDER BY                       |                    |     1 |   336 |  1623 |       |       |
7 |   2 |   HASH JOIN                          |                    |     1 |   336 |  1614 |       |       |
8 |   3 |    TABLE ACCESS BY GLOBAL INDEX ROWID| APPROVE_SERIAL_T   |     1 |   254 |     2 | ROWID | ROW L |
9 |   4 |     INDEX RANGE SCAN                 | I1_APPROVE_ITEM_T  | 10831 |       |     1 |       |       |
10 |   5 |    PARTITION RANGE ALL               |                    |       |       |       |     1 |     2 |
11 |   6 |     TABLE ACCESS FULL                | APPROVE_ITEM_T     |    34 |  2788 |  1611 |     1 |     2 |
12 -----------------------------------------------------------------------------------------------------------
13 
14 Note: cpu costing is off, PLAN_TABLE' is old version

补充总结:根据v$session_wait可以看到等待事件是db file sequential read,通常来说索引读是好的,会很快,但是如果过多的索引读就令人畏惧的。 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值