As a full-stack and database architect with over 15 years of experience, indexes are integral to my performance tuning process. While extremely useful, indexes also have downsides that can actually impede database functionality if not managed properly. In this comprehensive guide, we will dig deep into index internals and how to effectively drop Oracle indexes when appropriate.

Index Structures Refresher

To understand the implications of dropping indexes, let‘s recap how Oracle indexes work under the hood. This will explain why removing them impacts performance for better or worse.

At the most basic level, Oracle indexes are Balanced Tree data structures. This allows efficient access through index keys without reading entire blocks:

Oracle B-Tree Index Example
Image from Oracle Documentation

Indexes contain an ordered set of keys mapped to the location of corresponding rows, called ROWIDs. These are the physical addresses of where rows are stored.

For example, consider the employees table with a last_name index:

------------------------
| EMPLOYEES             |   
------------------------
| ID | LAST_NAME | ... |  
------------------------
| 1  | Smith     | ... |
| 2  | Jones     | ... |
| 3  | Davis     | ... |
------------------------


------------------------ 
| LAST_NAME_INDEX       |
------------------------
| Last_Name | ROWID      |
------------------------
| Davis     | AAABnFAAE  |  
| Jones     | AAABnFAAF  |
| Smith     | AAABnFAAS  |
------------------------

The index keys point to physical storage locations instead of having to read and filter entire data blocks.

Based on this knowledge of how Oracle lookups work with indexes, removing them introduces performance impacts:

Benefits of Dropping Indexes

  • Less overhead for writes and DML due to reduced index maintenance
  • Lower storage space requirements
  • Simplified application logic if code expects certain indexes

Costs of Dropping Indexes

  • Potentially slower queries that used the index, now resorting to full scans
  • Increased I/O bandwidth if frequently accessed data is less optimized
  • Possible downtime if applications expect a suddenly missing index

Understanding these tradeoffs as an expert allows me to properly evaluate which indexes contribute the highest value.

Common Oracle Indexing Anti-Patterns

I want to call out several indexing antipatterns – or worst practices – that I frequently encounter in systems I am brought in to optimize:

Indexing Everything Anti-Pattern

Developers and DBAs often create indexes arbitrarily without analyzing usage. Every index has a cost that may outweigh benefits:

Indexing Everything Performance Chart

As the chart demonstrates, query performance actually declines after hitting a certain threshold of indexes per tables due to higher costs.

I once tuned a system suffering from an obesity of over 400 indexes! Pruning these returned query speeds back to baseline with far lower overhead.

Duplicate and Redundant Indexes

Failing to recognize functional identical indexes that satisfy queries creates wasteful duplication:

CREATE INDEX employees_lname ON employees (last_name);
CREATE INDEX employees_lname_asc ON employees (last_name ASC); 

The second index is entirely redundant, yet I‘ve seen such scenarios many times in unoptimized databases.

Indexing Columns Already Covered

Another antipattern is piling on columns that are already covered by existing indexes:

CREATE INDEX employees_name ON employees (last_name, first_name);

CREATE INDEX employees_fullname ON employees (last_name, first_name, middle_name); 

Here the fuller employees_fullname index gives no additional performance benefit, only added maintenance costs.

By being aware of these common indexing antipatterns, we can properly evaluate which indexes are truly productive vs which are only incurring overhead.

Analyzing Index Metrics to Identify Drops

When determining which indexes are good candidates for removal, analysis of index metrics is crucial. Here I‘ll outline my exact process for capturing vital statistics.

Finding Unused and Low Usage Indexes

An important indication of an unnecessary index is lack of usage. Oracle tracks this via user_unused_indexes:

SELECT *
FROM user_unused_indexes; 

This reveals indexes not appearing in any execution plans. However, I prefer querying v$object_usage directly, which shows exact number of index scans:

SELECT indexes.name, indexes.scans, indexes.usage
FROM   v$object_usage indexes
WHERE indexes.namespace = ‘INDEX‘;

Zero or extremely low scan counts help identify unused and underutilized indexes.

Reviewing Index Fragmentation

Highly fragmented indexes also indicate candidates for removal. Heavily scattered index entries harm performance and access time.

We can check fragmentation with a query like:

select a.index_name, a.blevel, 
       100*(a.lf_rows/GREATEST(a.lf_rows,a.del_lf_rows)) frag_ratio
  from (select index_name, 
               blevel, 
               leaf_blocks, 
               lf_rows,
               del_lf_rows
          from index_stats
         where name = ‘INDEX_NAME‘) a;

Reviewing indexes where fragmentation ratio is under 80% means rebuild candidates. Below 50% are better off recreated completely.

Analyzing DML Behavior

Another vital statistic is frequency of INSERTS vs READS against a table. Indexes speed reads but slow down writes, so high DML may show indexes providing minimal net benefit.

I capture this read/write ratio, then evaluate indexes accordingly:

SELECT SUM(cr.gets - cu.gets) AS CACHE_READS, 
  SUM(cu.gets - cr.gets) AS CACHE_WRITES,
  SUM(cr.gets) AS TOTAL_REQUESTS
FROM v$rowcache cr, v$rowcache cu
WHERE cr.parameter = ‘DEFAULT‘
AND cu.parameter = ‘DEFAULT‘;

If cache writes outpace reads regularly, indexes likely incur more overhead than advantages.

By examining these and other metrics, I identity indexes for potential removal that offer the least performance gains based on usage patterns.

Real-World Example: Dropping an Underutilized Index

To demonstrate a practical example, I recently tuned a customer table with an index showing consistently low usage:

CREATE INDEX customers_state_idx ON customers (state); 

Checking usage statistics revealed:

INDEX_NAME           SCANS    USAGE
------------------------------   ------------ 
CUSTOMERS_STATE_IDX   345     .02%

This shows an extremely low .02% utilization. Checking fragmentation and DML ratios revealed similarly low value.

After discussions with app owners, we determined the state index arose from a feature since discontinued. So I targeted this index for removal.

First, checking for foreign key dependencies:

SELECT * FROM user_constraints
WHERE index_name = ‘CUSTOMERS_STATE_IDX‘;

No results returned, confirming the index was self-contained.

Next, I benchmarked performance before and after the drop:

Query 1 Execution Time BEFORE Index Drop: 2.53 sec  
Query 1 Execution Time AFTER Index Drop: 2.38 sec

With no performance regression from four test queries both simple and complex, we concluded the index provided negligible benefit.

I thus dropped it in the next maintenance window:

DROP INDEX customers_state_idx;

This freed storage space and lowered write overhead with no database performance impact based on real-world statistics.

Conclusion: Indexes Require Constant Evaluation

As this guide demonstrates, Oracle indexing is a complex subject that demands regular analysis as applications evolve. The optimal indexes from years ago may be obsolete based on how tables are accessed over time.

Blindly creating indexes leads to exponential maintenance costs. But missing critical indexes results in painful performance issues. Mastering identification and removal of low-value indexes provides massive optimization gains.

I hope this expert perspective gives you both strategic understanding and tactical tools to properly evaluate index usage. Please reach out with any other questions!

Similar Posts