Santa's little (index) helper

Santa's little (index) helper 

Getting a little unexpected help is always nice. 

This was the case for me when I was tasked with troubleshooting a slow web application that had already received user complaints. Moreover there will be a significant increase in data in the future.

As I started working on this issue, I quickly discovered that a significant amount of the application's response time was spent in an Oracle Database. 

This was revealed when analyzing  a Java Flight Recording of the app (Socket Reads)


I used dashtop by Tanel Poder to investigate which Statements where slow in the past 14 days:

 
@D:\tpt-oracle-scripts\ash\dashtop sql_id,sql_plan_operation,sql_plan_options  username='MYSCHEMA' sysdate-14 sysdate
    Total                                                                                                                                                                                                      
  Seconds     AAS %This SQL_ID        SQL_PLAN_OPERATION    SQL_PLAN_OPTIONS            FIRST_SEEN          LAST_SEEN          
--------- ------- ----- ------------- --------------------- --------------------------- ------------------- -------------------
     6540      .0   36% 1f5jftdg1yddg TABLE ACCESS          FULL                        2023-02-06 06:39:06 2023-02-13 06:56:42
     4400      .0   24% avx5a4xgw1pdy HASH JOIN                                         2023-02-06 07:17:51 2023-02-10 18:01:41
     2930      .0   16% 4rhf5f97hh666 TABLE ACCESS          FULL                        2023-02-06 06:47:38 2023-02-10 16:24:24
      830      .0    5% avx5a4xgw1pdy TABLE ACCESS          FULL                        2023-02-06 07:49:25 2023-02-10 15:57:37
      650      .0    4% 2yz69huy5xdu2 TABLE ACCESS          FULL                        2023-02-06 07:01:38 2023-02-10 15:38:30
      380      .0    2% bds7syfuff0k6 TABLE ACCESS          FULL                        2023-02-06 07:34:34 2023-02-10 16:28:00
      170      .0    1% 22116p9bzas0s TABLE ACCESS          FULL                        2023-02-06 08:34:39 2023-02-10 12:52:16
...

The script shows that the top statements were mostly doing Full Table Scans, which was strange for an application outputting only few lines when a user interacts with it. 

This led me to believe that some indexes might be missing. 

In order to find proof I would have to analyze various factors such as execution statistics from AWR, plan cardinalities, filter/access predicates, the amount of rows returned in past executions, and table sizes. 

A tedious process, especially since I'm not familiar with the underlying data schema.

 

Thinking about how to approach this situation I remembered a tweet by Tanel three years ago: 


Challenge accepted! 

 

Instead of asking him directly I tried his script search function to look for scripts regarding indexes : 

@D:\tpt-oracle-scripts\help index;
NAME                      DESCRIPTION                                                  USAGE                                                                                                             
------------------------- ------------------------------------------------------------ -------------------------------------------------------------------------------------------
ash_index_helper.sql      Santa's Little (Index) Helper BETA                           @ash/ash_index_helper <sql_id> [<owner>.]<table_name> <from_time> <to_time>                                       
                                                                                       @ash/ash_index_helper 8zz6y2yzdqjp0 %.% sysdate-1/24 sysdate                                                      
                                                                                       @ash/ash_index_helper % TPCDS.% sysdate-1/24 sysdate                                                              
 
ind.sql                   Display indexes                                              @ind [<owner>.]<index_name|table_name>                                                                            
                                                                                       @ind orders                                                                                                       
                                                                                       @ind soe.ord_customer_ix                                                                                          
                                                                                       @ind soe.%                                                                                                        
 
indf.sql                  Display function-based index expressions                     @indf [<owner>.]<index_name|table_name>                                                                           
                                                                                       @indf orders                                                                                                      
                                                                                       @indf soe.ord_customer_ix                                                                                         
                                                                                       @indf soe.%  

Hmm. ash_index_helper sounds interesting. Let's give it a try:

@D:\tpt-oracle-scripts\ash\ash_index_helper % MYSCHEMA.%  sysdate-1 sysdate

-- I'm interested in every Statement that took place in the schema of the webapp in the past 24 hours 

-- Santa's Little (Index) Helper BETA v0.5 - by Tanel Poder ( https://tanelpoder.com )

   SECONDS     AAS CPU   WAIT  Accessed_Table   Plan_Operation                           PLAN_CARD TABLE_ROWS FILTER_PCT  SQL_EXECS ELA_SEC/EXEC PREDICATES                                      DIST_SQLIDS DIST_PLANS MIN(AP.SQL_ID MAX(AP.SQL_ID
---------- ------- ----- ----- ---------------- --------------------------------------- ---------- ---------- ---------- ---------- ------------ ------------------------------------------------ ----------- ---------- ------------- -------------
       582      .0  35%   65%  MYSCHEMA.TAB_A       TABLE ACCESS FULL [MYSCHEMA.TAB_A]      323296    6465922 4,99999845         95        6.212  [F:] LOWER("AUFTRAG") LIKE LOWER(:1)                     1          1 1f5jftdg1yddg 1f5jftdg1yddg
       153      .0  98%    2%  MYSCHEMA.TAB_B       TABLE ACCESS FULL [MYSCHEMA.TAB_B]          12      68731 ,017459370       4136        0.040  [F:] (LOWER("SVNURL") LIKE :1                            1          1 4rhf5f97hh666 4rhf5f97hh666
        80      .0  41%   59%  MYSCHEMA.TAB_C       TABLE ACCESS FULL [MYSCHEMA.TAB_C]       50267    1005341 4,99999503        185        0.429  [F:] LOWER("TAB_C"."AUFTRAG") LIKE LOWER(:1)             1          1 2yz69huy5xdu2 2yz69huy5xdu2
...


Excellent! The script uses ASH to identify the most expensive plan steps within the specified timeframe. 

Moreover, it calculates FILTER_PCT, which represents the proportion of the table that might be returned by the plan step. This is achieved by dividing the cardinality estimated by the optimizer for this step by the number of table rows and then multiplying it by 100.

Additionally, it provides information about the SQL execution frequency (SQL_EXECS), the number of individual plans linked with the SQL (DIST_PLANS), and the historical execution time of the SQL (ELA_SEC/EXEC).

 

The first line displays data related to the most expensive statement I encountered in dashtop (SQLDID: 1f5jftdg1yddg). This plan step retrieves roughly 5% of the table rows using the "LOWER("AUFTRAG")" filter predicate. There is only one SQL (DIST_SQLIDS) and one plan (DIST_PLANS) associated with this plan step and predicate. All things considered, indexing MYSCHEMA.TAB_A (lower(auftrag)) could be a useful optimization.

 

This way, I was able to deploy some helpful indexes easily in a fraction of the time it would have taken me otherwise. 

Thank you, Tanel, for the unexpected help! 

 

It would be great if there was a version of the script that utilizes AWR as a source, rather than ASH. Since Oracle 20c, the database stores the predicates not only in ASH but also in AWR: 

20c-awr-now-stores-explain-plan-predicates

As a result, it would be possible to do analyses over a more extensive time frame.

 

The mentioned scripts and other useful ones can be found on Tanel's GitHub repository: https://github.com/tanelpoder/tpt-oracle.

Comments

Popular posts from this blog

a shut mouth catches no flies ...

Finally, dangerous Oracle bug fixed after several years in the wild