国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

一條包含rowid的SQL引發(fā)的血案

IT那活兒 / 2625人閱讀
一條包含rowid的SQL引發(fā)的血案
某公司核心系統(tǒng)從11.2.0.3升級(jí)到11.2.0.4,升級(jí)后CPU市盈率下降幅度很大,系統(tǒng)平穩(wěn),然而在第二天的業(yè)務(wù)高峰期某核心語(yǔ)句執(zhí)行時(shí)間從原先的幾毫秒變成幾百秒,嚴(yán)重影響業(yè)務(wù)。很顯然,在數(shù)據(jù)量變化不大的情況下,SQL語(yǔ)句執(zhí)行效率下降,那大概率是執(zhí)行計(jì)劃發(fā)生了變化,至于執(zhí)行計(jì)劃變化的原因,因?yàn)橄到y(tǒng)版本變化,考慮如下:
1)統(tǒng)計(jì)信息變化
2)CBO優(yōu)化器變化導(dǎo)致問(wèn)題
3)優(yōu)化器BUG
語(yǔ)句其實(shí)很簡(jiǎn)單,模擬如下:
create table t as select * from dba_objects;
--表t的object_id列有索引,其實(shí)這里的last_ddl_time也是有索引的,而且可以走索引,為了簡(jiǎn)化,不進(jìn)行模擬
create index idx_t on t(object_id);

exec dbms_stats.gather_table_stats(ownname=>user,tabname=>t,no_invalidate=>false);
SQL> select count(*) from t;
  COUNT(*)
----------
    261898
原始語(yǔ)句:
select *
from(
select rowid,t.*
from t where t.object_id in
(
 select object_id
 from(
 select object_id
 from t
 where mod(object_id,10
)
=0 
 and status=VALID
 and last_ddl_time > trunc(sysdate-200)
 order by timestamp,last_ddl_time
    ) where rownum<=100
)   and t.status=VALID
 and t.last_ddl_time > trunc(sysdate-200)
 order by last_ddl_time
) where rownum<=100;

本來(lái)在11.2.0.3上平穩(wěn)運(yùn)行,執(zhí)行計(jì)劃走NESTED LOOPS,子查詢結(jié)果作為驅(qū)動(dòng),然后驅(qū)動(dòng)外層表,從而走object_id索引。但是升級(jí)后的執(zhí)行計(jì)劃卻是這樣的:

--執(zhí)行12分鐘還沒(méi)有出現(xiàn)結(jié)果
SQL> set autotrace traceonly
SQL> select *
  2  from(
  3  select rowid,t.*
  4  from t where t.object_id in
  5  (
  6     select object_id
  7     from(
  8             select object_id
  9             from t
 10             where mod(object_id,10)=0 
 11             and status=VALID
 12             and last_ddl_time > trunc(sysdate-200)
                order by last_ddl_time
 13   14      ) where rownum<=100
 15  )   and t.status=VALID
 16             and t.last_ddl_time > trunc(sysdate-200)
 17             order by last_ddl_time
 18  ) where rownum<=100;
^Cselect *
*
ERROR at line 1:
ORA-01013: user requested cancel of current operation
Elapsed: 00:12:05.73

--問(wèn)題執(zhí)行計(jì)劃如下:
Execution Plan
----------------------------------------------------------
Plan hash value: 3028954274
-------------------------------------------------------------------------------------
| Id  | Operation                    | Name | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |      |     1 |   219 |  2100K  (2)| 07:00:08 |
|*  1 |  COUNT STOPKEY               |      |       |       |            |          |
|   2 |   VIEW                       |      |     1 |   219 |  2100K  (2)| 07:00:08 |
|*  3 |    SORT ORDER BY STOPKEY     |      |     1 |   100 |  2100K  (2)| 07:00:08 |
|*  4 |     FILTER                   |      |       |       |            |          |
|*  5 |      TABLE ACCESS FULL       | T    |  4936 |   482K|   855   (3)| 00:00:11 |
|*  6 |      FILTER                  |      |       |       |            |          |
|*  7 |       COUNT STOPKEY          |      |       |       |            |          |
|   8 |        VIEW                  |      |    49 |   637 |   851   (2)| 00:00:11 |
|*  9 |         SORT ORDER BY STOPKEY|      |    49 |  1960 |   851   (2)| 00:00:11 |
|*  10 |          TABLE ACCESS FULL   | T    |    49 |  1960 |   850   (2)| 00:00:11 |
-------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter(ROWNUM<=100)
   3 - filter(ROWNUM<=100)
   4 - filter( EXISTS ()
   5 - filter("T"."STATUS"=VALID AND
              "T"."LAST_DDL_TIME">TRUNC(SYSDATE@!-200))
   6 - filter("OBJECT_ID"=:B1)
   7 - filter(ROWNUM<=100)
   9 - filter(ROWNUM<=100)
  10 - filter("STATUS"=VALID AND MOD("OBJECT_ID",10)=0 AND
              "LAST_DDL_TIME">TRUNC(SYSDATE@!-200))

 

和原先預(yù)計(jì)的走NESTED LOOPS不一樣,走了FILTER操作,F(xiàn)ILTER這種有2個(gè)子節(jié)點(diǎn)的,說(shuō)明子查詢未展開(kāi),也就是查詢轉(zhuǎn)換失敗。一般遇到這種情況,首先為了快速解決問(wèn)題,肯定是用SQL PROFILE之類的工具先綁定正確的執(zhí)行計(jì)劃,然而使用SQL PROFILE無(wú)效。那只能進(jìn)一步分析解決問(wèn)題了。查看10053:
*****************************
Cost-Based Subquery Unnesting
*****************************
SU: Unnesting query blocks in query block SEL$1 (#1) that are valid to unnest.
Subquery removal for query block SEL$3 (#3)
RSW: Not valid for subquery removal SEL$3 (#3)
Subquery unchanged.
Subquery Unnesting on query block SEL$2 (#2)SU: Performing unnesting that does not require costing.
SU: Considering subquery unnest on query block SEL$2 (#2).
SU:   Checking validity of unnesting subquery SEL$3 (#3)
SU:     SU bypassed: Subquery in a view with rowid reference.                   
--含有ROWID的subquery unnest失敗
SU:   Validity checks failed.


10053顯示因?yàn)樽硬樵兊囊晥D含有rowid導(dǎo)致subquery unnest失敗,遇到這種情況要么去MOS上看看是不是BUG,要么就是改寫語(yǔ)句。通過(guò)查詢MOS,發(fā)現(xiàn)含有rowid的查詢有不少問(wèn)題:

通過(guò)搜索"subquery rowid"找到個(gè)比較相近的:
Query Referencing ROWID of Subquery With Join Fails With ORA-01445 (Doc ID 1929880.1)
上面的內(nèi)容如下:

SYMPTOMS

A query referencing a rowid from a subquery with a join fails with the following error:
SQL> select rowid
from
  (
    select e.empno
    from emp e left outer join (select deptno from dept
) d
      on (e.deptno = d.deptno)
  )
;

ORA-01445: cannot select ROWID from, or sample, a join view without a key-preserved table


CAUSE

The error is observed in query with ANSI joins because the way the query is written.
A rowid is only defined for individual rows in a table, so it is not legal to select a rowid from a subquery unless each row from that subquery can be guaranteed to be uniquely associated with exactly one row in one table.  Therefore, the subquery may have only one item in its FROM list; that item must also have a rowid; and the subquery must not use DISTINCT, GROUP BY, or anything else that might gather multiple rows into one.


SOLUTION
Reference the rowid when it is valid as an explicit select list item:
SQL> select rid as "ROWID"
from
  ( select e.empno, e.rowid as rid
    from emp e left outer join (select deptno from dept) d
      on (e.deptno = d.deptno)
  );

可以按照上面的思路將rowid改寫成別名,再在最外層將別名改回來(lái),以保證SQL語(yǔ)句的查詢列名一致,改寫如下:

——改寫方案       
select rd as "ROWID",object_id,object_name,last_ddl_time
from(
select rowid rd,t.*
from t where t.object_id in
(
 select object_id
 from(
 select object_id
 from t
 where mod(object_id,10)=0 
 and status=VALID
 and last_ddl_time > trunc(sysdate-200)
 order by timestamp,last_ddl_time
    ) where rownum<=100
)   and t.status=VALID
 and t.last_ddl_time > trunc(sysdate-200)
 order by last_ddl_time
) where rownum<=100;

 改寫后的執(zhí)行計(jì)劃正確,如下所示:

——改寫方案       
selectExecution Plan
----------------------------------------------------------
Plan hash value: 16082276
-------------------------------------------------------------------------------------------
| Id  | Operation                      | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |          |    49 |  4900 |   931   (2)| 00:00:12 |
|*  1 |  COUNT STOPKEY                 |          |       |       |            |          |
|   2 |   VIEW                         |          |    49 |  4900 |   931   (2)| 00:00:12 |
|*  3 |    SORT ORDER BY STOPKEY       |          |    49 |  5145 |   931   (2)| 00:00:12 |
|*  4 |     TABLE ACCESS BY INDEX ROWID| T        |     1 |   100 |     2   (0)| 00:00:01 |
|   5 |      NESTED LOOPS              |          |    49 |  5145 |   930   (2)| 00:00:12 |
|   6 |       VIEW                     | VW_NSO_1 |    49 |   245 |   851   (2)| 00:00:11 |
|   7 |        HASH UNIQUE             |          |    49 |   245 |            |          |
|*  8 |         COUNT STOPKEY          |          |       |       |            |          |
|   9 |          VIEW                  |          |    49 |   245 |   851   (2)| 00:00:11 |
|* 10 |           SORT ORDER BY STOPKEY|          |    49 |  1960 |   851   (2)| 00:00:11 |
|* 11 |            TABLE ACCESS FULL   | T        |    49 |  1960 |   850   (2)| 00:00:11 |
|12 |       INDEX RANGE SCAN         | IDX_T    |     1 |       |     1   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter(ROWNUM<=100)
   3 - filter(ROWNUM<=100)
   4 - filter("T"."STATUS"=VALID AND "T"."LAST_DDL_TIME">TRUNC(SYSDATE@!-200))
   8 - filter(ROWNUM<=100)
  10 - filter(ROWNUM<=100)
  11 - filter("STATUS"=VALID AND MOD("OBJECT_ID",10)=0 AND
              "LAST_DDL_TIME">TRUNC(SYSDATE@!-200))
  12 - access("T"."OBJECT_ID"="OBJECT_ID")
Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
       4026  consistent gets
          0  physical reads
          0  redo size
       7402  bytes sent via SQL*Net to client
        586  bytes received via SQL*Net from client
          8  SQL*Net roundtrips to/from client
          2  sorts (memory)
          0  sorts (disk)
        100  rows processed
) where rownum<=100;

現(xiàn)在執(zhí)行計(jì)劃正確走NESTED LOOPS,并且被驅(qū)動(dòng)表走OBJECT_ID索引。再次思考一個(gè)問(wèn)題,從SQL語(yǔ)句的語(yǔ)義和業(yè)務(wù)上分析,這個(gè)語(yǔ)句其實(shí)就是按照條件查詢?nèi)缓笈判颍樵兂銮?00行的rowid和指定列,也就是沒(méi)有必要用子查詢或關(guān)聯(lián)查詢,可以將語(yǔ)句進(jìn)一步簡(jiǎn)化:

--其實(shí)原來(lái)的業(yè)務(wù)就是這么簡(jiǎn)單
select rowid,object_id,object_name,last_ddl_time
from (
 select object_id
 from t
 where mod(object_id,10)=0 
 and status=VALID
 and last_ddl_time > trunc(sysdate-200)
 order by timestamp,last_ddl_time
 )
) where rownum<=100;

通過(guò)業(yè)務(wù)分析后改寫的SQL很簡(jiǎn)單,其實(shí)本質(zhì)就是查詢之后排序,然后找前100行。由于原始語(yǔ)句寫成子查詢并且又包含了rowid,導(dǎo)致觸發(fā)優(yōu)化器的限制,從而子查詢無(wú)法unnest,最終走了FILTER操作影響執(zhí)行效率。

通過(guò)這個(gè)案例,我們知道,一些oracle的key words,在做別名的時(shí)候還是需要謹(jǐn)慎,盡量避免使用key words作為別名,以防在不同版本中觸發(fā)oracle的限制或bug。

END


更多精彩干貨分享

點(diǎn)擊下方名片關(guān)注

IT那活兒

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/129912.html

相關(guān)文章

  • 由for update引發(fā)血案

    摘要:微信公眾號(hào)后端進(jìn)階,專注后端技術(shù)分享框架分布式中間件服務(wù)治理等等。 微信公眾號(hào)「后端進(jìn)階」,專注后端技術(shù)分享:Java、Golang、WEB框架、分布式中間件、服務(wù)治理等等。 老司機(jī)傾囊相授,帶你一路進(jìn)階,來(lái)不及解釋了快上車! 公司的某些業(yè)務(wù)用到了數(shù)據(jù)庫(kù)的悲觀鎖 for update,但有些同事沒(méi)有把 for update 放在 Spring 事務(wù)中執(zhí)行,在并發(fā)場(chǎng)景下發(fā)生了嚴(yán)重的線程阻...

    roundstones 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<