User Tools

Site Tools


dba:oracle:docs:killing_hardparse

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
dba:oracle:docs:killing_hardparse [2023/01/31 08:22] – removed - external edit (Unknown date) 127.0.0.1dba:oracle:docs:killing_hardparse [2023/01/31 08:22] (current) – ↷ Page moved from dba:oracle:killing_hardparse to dba:oracle:docs:killing_hardparse dodger
Line 1: Line 1:
 +====== [DOC] Kill hardparse ======
  
 +====== Descripcion ======
 +El hard parse es un performance killer.
 +Hay que asesinarlo :-P
 +
 +======Pasos a seguir======
 +No es fácil identificar qué genera el hard parse, los targets por este orden son:
 +  * Querys sin bind variables
 +  * Querys con muchas versiones
 +  * Uso de la SharedPool
 +  
 +
 +=====Querys sin bind variables=====
 +La siguiente query no es definitiva ni excesivamente buena, pero puede servir.
 +Hacer match sobre querys SIN bind variables no es precisamente fácil.
 +<code sql>
 +SET PAGES 0
 +SET LINESIZE 250
 +COL PERCENT_PARSES FORMAT 999.99
 +COL SCHEMA FORMAT A15
 +COL SQL_ID FORMAT A15
 +-- COL SQL_TEXT FORMAT A10000
 +COL TRIM_SQL FORMAT A190
 +SET SERVEROUT ON SIZE UNLIMITED
 +
 +SELECT *
 +FROM (
 +    SELECT  VERSION_COUNT,
 +            EXECUTIONS,
 +            PARSING_SCHEMA_NAME SCHEMA,
 +            SQL_ID,                     
 +            SUBSTR(TRIM(REPLACE(REPLACE(REPLACE(SQL_FULLTEXT, CHR(10)), CHR(9)), CHR(13))),1,100) TRIM_SQL                                                                                                                                                                                                                                                   
 +--             SQL_FULLTEXT
 +    FROM V$SQLAREA
 +    WHERE PARSING_SCHEMA_NAME IN ( 'SCHEMA1', 'SCHEMA2', 'SCHEMAn' )
 +    AND REGEXP_LIKE(SQL_FULLTEXT,'[A-Z][[:alnum:]_]{1,33} {0,1}= {0,1}"{0,1}[[:alnum:]]{1,3}','inm')
 +    ORDER BY VERSION_COUNT DESC
 +    )
 +ORDER BY VERSION_COUNT DESC,EXECUTIONS DESC
 +;
 +</code>
 +
 +La query busca en todos los SQL's de la SQLAREA con la REGEXP:
 +<code>[A-Z][[:alnum:]_]{1,33} {0,1}= {0,1}"{0,1}[[:alnum:]]{1,3}</code>
 +Es decir, intenta identificar "equals" (no likes, no debería haber :-P) que sean con bind.
 +
 +
 +
 +
 +
 +
 +=====Identificar SQL's con alto porcentaje de versiones=====
 +<code sql>
 +COL PERCENT_PARSES FORMAT 999.99
 +COL TRIM_SQL FORMAT A120
 +SELECT * 
 +FROM (
 +    SELECT SQL_ID,
 +            VERSION_COUNT,
 +            EXECUTIONS,
 +            ((VERSION_COUNT*100)/EXECUTIONS) PERCENT_PARSES,
 +            PARSING_SCHEMA_NAME
 +    FROM V$SQLAREA
 +    WHERE VERSION_COUNT > 1
 +    AND EXECUTIONS > 1
 +    AND PARSING_SCHEMA_NAME IN ( 'SCHEMA1', 'SCHEMA2', 'SCHEMAn' )
 +    ORDER BY VERSION_COUNT DESC
 +    )
 +WHERE PERCENT_PARSES > 10
 +ORDER BY PERCENT_PARSES DESC
 +;
 +</code>
 +
 +
 +=====Identificar SQL's generando hard parse=====
 +
 +<code sql>
 +COL PARSE_CALLS FORMAT 999999
 +COL EXECUTIONS FORMAT 999999999
 +COL TRIM_SQLTEXT FORMAT A100
 +SELECT PARSE_CALLS, EXECUTIONS,  SQL_ID, SUBSTR(SQL_TEXT,1,100) TRIM_SQLTEXT
 +FROM V$SQLAREA
 +WHERE COMMAND_TYPE IN (2,3,6,7)
 +ORDER BY EXECUTIONS 
 +;
 +</code>
 +
 +Lo mismo, limitando resultados:
 +
 +<code sql>
 +COL PARSE_CALLS FORMAT 999999
 +COL EXECUTIONS FORMAT 9999999999
 +COL TRIM_SQLTEXT FORMAT A100
 +COL SQL_TEXT FORMAT A180
 +SELECT * FROM
 +  (
 +  SELECT PARSE_CALLS, EXECUTIONS,  SQL_ID, SQL_FULLTEXT
 +  FROM V$SQLAREA
 +  WHERE COMMAND_TYPE IN (2,3,6,7)
 +  ORDER BY EXECUTIONS DESC
 +  )
 +  WHERE ROWNUM < 30
 +;
 +</code>
 +
 +
 +Porcentaje de parses respecto al de ejecuciones:
 +<code sql>
 +COL PARSE_CALLS FORMAT 999999
 +COL EXECUTIONS FORMAT 9999999999
 +COL SQL_FULLTEXT FORMAT A180
 +SELECT * FROM
 +  (
 +  SELECT ((PARSE_CALLS*100)/EXECUTIONS) PERCENT_PARSES, PARSE_CALLS, EXECUTIONS,  SQL_ID, SQL_FULLTEXT
 +  FROM V$SQLAREA
 +  WHERE COMMAND_TYPE IN (2,3,6,7)
 +  AND EXECUTIONS>0
 +  AND EXECUTIONS>PARSE_CALLS
 +  ORDER BY ((PARSE_CALLS*100)/EXECUTIONS) DESC
 +  )
 +  WHERE ROWNUM < 30
 +;
 +</code>
 +<code sql>
 +SELECT *
 +FROM (
 +    SELECT * FROM (
 +        SELECT PARSE_CALLS, 
 +            EXECUTIONS,
 +            ((PARSE_CALLS*100)/EXECUTIONS) PERCENT_PARSES,
 +            SQL_ID, 
 +            PARSING_SCHEMA_NAME
 +    --         , SUBSTR(SQL_TEXT,1,100) TRIM_SQLTEXT
 +        FROM V$SQLAREA
 +        WHERE COMMAND_TYPE IN (2,3,6,7)
 +        AND PARSING_SCHEMA_NAME NOT IN ('SYS','SYSTEM','DBSNMP')
 +        ORDER BY ((PARSE_CALLS*100)/EXECUTIONS) DESC
 +    )
 +    WHERE PERCENT_PARSES>50
 +    ORDER BY EXECUTIONS DESC , PARSE_CALLS DESC , PERCENT_PARSES DESC
 +    )    
 +WHERE ROWNUM<30
 +;
 +</code>
 +
 +
 +
 +Los command types son que se buscan son:
 +<code sql>
 +SELECT * FROM V$SQLCOMMAND WHERE COMMAND_TYPE IN (2,3,6,7);
 +</code>
 +
 +
 +===== Versiones de un SQL =====
 +<code sql>
 +col SQL_TEXT FORMAT A200 WORD WRAPPED
 +select SQL_ID, VERSION_COUNT, sql_text from v$sqlarea where version_count > 1 ORDER BY VERSION_COUNT ;
 +</code>
 +
 +
 +
 +
 +
 +===== Identificar SESIONES con mucho hard parse=====
 +Localicar los SIDS con más hard parse:
 +<code sql>
 +SELECT * FROM 
 +  (
 +  SELECT * FROM  V$SESSTAT WHERE STATISTIC# = 
 +    (
 +    SELECT STATISTIC# FROM V$STATNAME WHERE NAME LIKE 'parse count (hard)'
 +    )
 +  AND VALUE > 50 ORDER BY VALUE DESC
 +  ) WHERE ROWNUM <20;
 +</code>
 +
 +Y localizar quien es el que genera el hard parse:
 +<code sql>
 +col "SID/SERIAL" format a10
 +col username format a25
 +col osuser format a25
 +COL USERHOST FORMAT A30
 +col program format a80
 +
 +SELECT  s.sid || ',' || s.serial# "SID/SERIAL", 
 +        s.username, 
 +        s.osuser, 
 +        p.spid "OS PID", 
 +        s.program
 +FROM v$session s, v$process p
 +WHERE s.paddr = p.addr
 +AND S.SID IN 
 +        (
 +        SELECT * FROM 
 +          (
 +          SELECT SID FROM  V$SESSTAT WHERE STATISTIC# = 
 +            (
 +            SELECT STATISTIC# FROM V$STATNAME WHERE NAME LIKE 'parse count (hard)'
 +            )
 +          AND VALUE > 50 ORDER BY VALUE DESC
 +          )
 +        )
 +AND S.USERNAME IS NOT NULL
 +/
 +</code>
 +
 +
 +====== Uso de la shared pool ======
 +Existe la siguiente vista/tabla indocumentada que nos permite ver qué querys están causando que otras querys sean expulsadas de la shared pool (aged out):
 +<code sql>
 +SELECT * FROM X$KSMLRU WHERE ksmlrsiz > 0;
 +</code>
 +es EXTREMADAMENTE útil
 +
 +La siguiente query da información sobre como está siendo usada la shared pool y la posible fragmentación de la misma.
 +En concreto muestra la lista de chunks disponibles:
 +<code sql>
 +select '0 (<140)' BUCKET,
 +        KSMCHCLS, 
 +        KSMCHIDX, 
 +        10*trunc(KSMCHSIZ/10) "From",
 +        count(*) "Count"
 +        max(KSMCHSIZ) "Biggest",
 +        trunc(avg(KSMCHSIZ)) "AvgSize", 
 +        trunc(sum(KSMCHSIZ)) "Total" 
 +from x$ksmsp 
 +where KSMCHSIZ<140 
 +and KSMCHCLS='free' 
 +group by KSMCHCLS, KSMCHIDX, 10*trunc(KSMCHSIZ/10) 
 +UNION ALL 
 +select '1 (140-267)' BUCKET, 
 +        KSMCHCLS, 
 +        KSMCHIDX,
 +        20*trunc(KSMCHSIZ/20), 
 +        count(*),
 +        max(KSMCHSIZ),
 +        trunc(avg(KSMCHSIZ)) "AvgSize",
 +        trunc(sum(KSMCHSIZ)) "Total"
 +from x$ksmsp 
 +where KSMCHSIZ between 140 and 267 
 +and KSMCHCLS='free' 
 +group by KSMCHCLS, KSMCHIDX, 20*trunc(KSMCHSIZ/20) 
 +UNION ALL 
 +select '2 (268-523)' BUCKET, 
 +        KSMCHCLS, 
 +        KSMCHIDX, 
 +        50*trunc(KSMCHSIZ/50) ,
 +        count(*) ,
 +        max(KSMCHSIZ) ,
 +        trunc(avg(KSMCHSIZ)) "AvgSize", 
 +        trunc(sum(KSMCHSIZ)) "Total" 
 +from x$ksmsp 
 +where KSMCHSIZ between 268 and 523 
 +and KSMCHCLS='free' 
 +group by KSMCHCLS, KSMCHIDX, 50*trunc(KSMCHSIZ/50) 
 +UNION ALL 
 +select '3-5 (524-4107)' BUCKET, 
 +        KSMCHCLS, 
 +        KSMCHIDX, 
 +        500*trunc(KSMCHSIZ/500),
 +        count(*) , 
 +        max(KSMCHSIZ) , 
 +        trunc(avg(KSMCHSIZ)) "AvgSize", 
 +        trunc(sum(KSMCHSIZ)) "Total" 
 +from x$ksmsp 
 +where KSMCHSIZ between 524 and 4107 
 +and KSMCHCLS='free' 
 +group by KSMCHCLS, KSMCHIDX, 500*trunc(KSMCHSIZ/500) 
 +UNION ALL 
 +select '6+ (4108+)' BUCKET, 
 +        KSMCHCLS, 
 +        KSMCHIDX, 
 +        1000*trunc(KSMCHSIZ/1000) , 
 +        count(*) , 
 +        max(KSMCHSIZ) , 
 +        trunc(avg(KSMCHSIZ)) "AvgSize", 
 +        trunc(sum(KSMCHSIZ)) "Total" 
 +from x$ksmsp 
 +where KSMCHSIZ >= 4108 
 +and KSMCHCLS='free' 
 +group by KSMCHCLS, KSMCHIDX, 1000*trunc(KSMCHSIZ/1000);
 +</code>
 +
 +Y por último la siguiente query muestra el uso general de la SGA:
 +<code sql>
 +SELECT  KSMCHCLS CLASS, 
 +        COUNT(KSMCHCLS) NUM, 
 +        SUM(KSMCHSIZ) SIZ, 
 +        TO_CHAR( ((SUM(KSMCHSIZ)/COUNT(KSMCHCLS)/1024)),'999,999.00')||'k' "AVG SIZE" 
 +FROM X$KSMSP GROUP BY KSMCHCLS; 
 +</code>
 +
 +
 +Thanks to "[[http://kundanjha-blogs.blogspot.com.es/2010/10/shared-pool-fragmentation.html|kundan]]"
 +
 +
 +====== External doc ======
 +  * How to Identify Hard Parse Failures (Doc ID 1353015.1)
 +
 +
 +====== Identifying Failed parsing ======
 +
 +Failed parsing is the worst scenario, queries sent to Oracle that are not syntactically o semantically well done.
 +Oracle does not provide any way to troubleshot it (normally).
 +Here's a way to do it.
 +
 +===== trace 10035 =====
 +Oracle describe how to identify parse failures on the following document:
 +How to Identify Hard Parse Failures (Doc ID 1353015.1)
 +
 +So let's enable:
 +<code sql>
 +ALTER SYSTEM SET EVENTS '10035 trace name context forever, level 1';
 +</code>
 +
 +After enabling this trace, I found this parameter extremely useful, since then till now I didn't notified any performance lack on our live instance...
 +
 +So you have the query logged, that's fine, but if you have ~60k different queries coming from your app, and some of them are dynamic, try to ask for them to the developers, the less they'll do is have a good time laughing.
 +
 +So I tried enabling audit on failed select's for users and.. bingo!
 +You'll have on the audit trail all the info you need to find what is causing the failed parsing :-)
 +<code sql>
 +AUDIT SELECT TABLE BY <USERNAME> WHENEVER NOT SUCCESSFUL; 
 +</code>
 +
 +
 +
 +===== Queries for getting info =====
 +
 +  * Formatting:
 +<code sql>
 +COL ETIMESTAMP FORMAT A22
 +COL DB_USER FORMAT         A10
 +COL OS_USER FORMAT         A10
 +COL USERHOST FORMAT        A25
 +COL OBJECT_SCHEMA FORMAT   A15
 +COL OBJECT_NAME FORMAT     A30
 +COL STATEMENT_TYPE FORMAT  A20
 +COL SORTSQL_TEXT FORMAT        A150
 +</code>
 +  * Summary:
 +<code sql>
 +SELECT TO_CHAR(EXTENDED_TIMESTAMP,('DD-MM-YYYY HH24:MI:SS')) ETIMESTAMP,
 + DB_USER ,
 +--      STATEMENTID,
 + OBJECT_SCHEMA , 
 + OBJECT_NAME , 
 +-- STATEMENT_TYPE ,
 +        RETURNCODE,
 + SUBSTR(SQL_TEXT,1,150) SORTSQL_TEXT
 +FROM DBA_COMMON_AUDIT_TRAIL 
 +WHERE STATEMENT_TYPE NOT IN ('LOGON','LOGOFF','LOGOFF BY CLEANUP')
 +AND DB_USER='<USERNAME>'
 +AND NOT RETURNCODE=0
 +AND NOT (SUBSTR(SQL_TEXT,1,30) LIKE '%INSERT%' or SUBSTR(SQL_TEXT,1,30) LIKE '%MERGE%')
 +ORDER BY EXTENDED_TIMESTAMP
 +;
 +</code>
 +  * Failed per day:
 +<code sql>
 +SELECT THEDAY, COUNT(*)
 +FROM (
 +    SELECT TO_CHAR(EXTENDED_TIMESTAMP,('YYYY-MM-DD')) THEDAY
 +    FROM DBA_COMMON_AUDIT_TRAIL 
 +    WHERE STATEMENT_TYPE NOT IN ('LOGON','LOGOFF','LOGOFF BY CLEANUP')
 +    AND DB_USER='<USERNAME>'
 +    AND NOT RETURNCODE=0
 +    AND NOT (SUBSTR(SQL_TEXT,1,30) LIKE '%INSERT%' or SUBSTR(SQL_TEXT,1,30) LIKE '%MERGE%')
 +    ORDER BY EXTENDED_TIMESTAMP
 +    )
 +GROUP BY THEDAY
 +order by THEDAY
 +;
 +</code>
 +
 +
 +
 +
 +
 +
 +