go backsqlschool blogs list

Missing and Unused Indexes

by Antonios Chatzipavlis

Είναι γνωστό ότι οι indexes αποτελούν την κηροζίνη των queries.

Το να φτιάξω ένα index είναι εύκολο. Το επιλέξω όμως ποιο ή ποια πεδία θα γίνουν index είναι το δύσκολο καθώς θα πρέπει να ληφθούν πολλοί παράμετροι υπόψη.

Στο SQL Server από το 2005 και μετά έχω στην διάθεση μου αρκετά εργαλεία τα οποία μπορώ να χρησιμοποιήσω και τα οποία μου προτείνουν την δημιουργία indexes με σκοπό την βελτίωση του performance.

Σε κάθε όμως περίπτωση δεν θα πρέπει τυφλά να προχωρώ στην δημιουργία των προτεινόμενων αλλά θα πρέπει να αναλύσω τα δεδομένα μας ώστε να είμαι 100% πεπεισμένος ότι θα έχει αποτέλεσμα η δημιουργία ενός index και μετά να προχωρήσω στην υλοποίηση του.

Σε αυτά που έχω στην διάθεση μου με τα οποία ο SQL Server μου επικοινωνεί τις προτάσεις του για την δημιουργία indexes είναι τα DMV sys.dm_db_missing_index_*.

Συνδυαστικά αυτά μεταξύ τους δίνουν αρκετά καλή πληροφορία την οποία και μπορώ να αξιοποιήσω αρκεί πρώτα να λάβω υπόψη μερικά στοιχεία όπως ότι δεν παρέχουν προτάσεις για τον τύπο του index (clustered, nonclustered), δεν προτείνουν partitioning, στις περιπτώσεις που συμπεριλαμβάνουν πολλά πεδία δεν προτείνουν την ιδανική σειρά αυτών στον index. Όμως όλα αυτά δεν με εμποδίζουν να τα χρησιμοποιήσω και να κάνω δουλειά με αυτά καθώς όπως έχω αναφέρει πρώτα γίνεται αξιολόγηση.

Finding Missing Indexes

Προσωπικά χρησιμοποιώ το παρακάτω query με το οποίο μπορώ να βγάλω ασφαλή συμπεράσματα για τους indexes που λείπουν.

Τα αποτέλεσματα που αυτό μου δίνει είναι το improvement_pct το οποίο μου δείχνει ένα αρκετά καλό estimation για το κέρδος που θα έχω με την δημιουργία του συγκεκριμένου index. Επίσης σημαντική πληροφορία είναι τα last_user_seek & last_user_scan με τα οποία μπορώ να καταλάβω πότε έχουν γίνει τελευταία φορά οι διαδικασίες αυτές και από αυτό μπορώ να καταλάβω την σημαντικότητα του index καθώς αν οι ημερομηνίες είναι μακρινές σημαίνει ήταν ενδεχομένως ένα ad-hoc query που ίσως δεν θα επαναληφθεί στο μέλλον. Για αυτό το λόγο και λέω πάντα ότι πρέπει να έχουν γίνει πρώτα μερικές εκτελέσεις σε ένα διάστημα μερικών ημερών των οποίων τα αποτελέσματα πρέπει να έχω κρατήσει ώστε να μπορώ να κάνω την αξιολόγηση μου συγκρίνοντας αυτά.

SELECT 
        db_name(mid.database_id) AS database_name
    ,    OBJECT_NAME( mid.OBJECT_ID , mid.database_id) AS table_name
    ,    p.TableRows AS table_rows
    ,    cast ( migs.avg_total_user_cost * (migs.avg_user_impact / 100.0) * (migs.user_seeks + migs.user_scans) as decimal(12,2)) AS improvement_pct
    ,    migs.last_user_seek AS last_user_seek
    ,    migs.last_user_scan AS last_user_scan
    ,    'CREATE INDEX [IX_' + OBJECT_NAME(mid.OBJECT_ID,mid.database_id) + '_' 
        + REPLACE(REPLACE(REPLACE(ISNULL(mid.equality_columns,''),', ','_'),'[',''),']','') 
        + CASE
          WHEN mid.equality_columns IS NOT NULL AND mid.inequality_columns IS NOT NULL THEN '_'
          ELSE ''
          END
        + REPLACE(REPLACE(REPLACE(ISNULL(mid.inequality_columns,''),', ','_'),'[',''),']','') + ']'
        + ' ON ' + mid.statement
        + ' (' + ISNULL (mid.equality_columns,'')
        + CASE 
          WHEN mid.equality_columns IS NOT NULL AND mid.inequality_columns IS NOT NULL THEN ',' 
          ELSE '' 
          END
        + ISNULL (mid.inequality_columns, '') + ')'
        + ISNULL (' INCLUDE (' + mid.included_columns + ')', '') AS create_statement
FROM    sys.dm_db_missing_index_groups AS mig
JOIN    sys.dm_db_missing_index_group_stats as migs
        ON migs.group_handle = mig.index_group_handle
JOIN    sys.dm_db_missing_index_details AS mid
        ON mig.index_handle = mid.index_handle
JOIN    (
        SELECT SUM(p.rows) AS TableRows, p.OBJECT_ID
        FROM sys.partitions AS p 
        WHERE p.index_id = 0 or p.index_id = 1
        GROUP BY p.index_id,p.OBJECT_ID
        ) as p 
        ON mid.OBJECT_ID = p.OBJECT_ID
WHERE    mid.database_ID = DB_ID()
ORDER BY table_name , improvement_pct DESC;

Finding Unused Indexes

Όπως όπως είπα και στην αρχή του άρθρου αυτού οι indexes είναι η κηροζίνη των queries, αλλά αυτό το ρόλο μπορούν να παίξουν μόνοι οι σωστοί indexes, αυτοί δηλαδή που καλύπτουν τις ανάγκες των queries.

Αρκετοί δημιουργούν indexes κυρίως από άγνοια με αποτέλεσμα να επιβαρύνονται τα transactions άσκοπα με την ενημέρωση indexes που πότε δεν χρησιμοποιούνται.

Για να μπορέσω να εντοπίσω τέτοιους indexes που δεν χρησιμοποιούνται τρέχω σε τακτά χρονικά διαστήματα πριν αποφασίσω την διαγραφή τους το παρακάτω query κρατώντας φυσικά τα αποτελέσματα κάθε φορά ώστε να κάνω τις απαραίτητες συγκρίσεις.

SELECT 
        db_name(ius.database_id)    AS database_name
    ,    o.name                        AS table_name
    ,    p.TableRows                    AS table_rows
    ,    i.name                        AS index_name
    ,    i.index_id                    AS index_id
    ,    STATS_DATE(o.object_id, i.index_id) as create_date
    ,    ius.user_seeks                AS user_seek
    ,    ius.user_scans                AS user_scans
    ,    ius.user_lookups            AS user_lookups
    ,    ius.user_updates            AS user_updates
    ,    'DROP INDEX ' + QUOTENAME(i.name) + ' ON ' + QUOTENAME(s.name) + '.' + QUOTENAME(OBJECT_NAME(ius.OBJECT_ID)) AS drop_statement
FROM    sys.dm_db_index_usage_stats AS ius
JOIN    sys.indexes AS i 
        ON i.index_id = ius.index_id AND ius.OBJECT_ID = i.OBJECT_ID
JOIN    sys.objects AS o 
        ON ius.OBJECT_ID = o.OBJECT_ID
JOIN    sys.schemas AS s 
        ON o.schema_id = s.schema_id
JOIN    (
            SELECT SUM(p.rows) AS TableRows, p.index_id, p.OBJECT_ID
            FROM sys.partitions AS p 
            GROUP BY p.index_id, p.OBJECT_ID
        ) as p 
        ON p.index_id = ius.index_id AND ius.OBJECT_ID = p.OBJECT_ID
WHERE 
        OBJECTPROPERTY(ius.OBJECT_ID,'IsUserTable') = 1 
        AND 
        ius.database_id = DB_ID()
        AND 
        i.type_desc = 'nonclustered'
        AND 
        i.is_primary_key = 0
        AND 
        i.is_unique_constraint = 0
        AND 
        ( ius.user_lookups = 0 AND ius.user_scans = 0 AND ius.user_seeks = 0 )
ORDER BY create_date, ius.user_updates DESC;

Από αυτό το query βλέπω τα seek, scans, lookups (σημείωση έχω βάλει στο where να παίρνω μόνο τα μηδενικά) , την ημερομηνία δημιουργίας του index και πόσα updates έχουν γίνει πάνω σε αυτό.

Από τις συγκρίσεις που κάνω στα αποτελέσματα ένας index που έχει δημιουργηθεί πριν πχ 6 μήνες και συνεχίζει να έχει μηδενικά seek, scans, lookups είναι υποψήφιος για διαγραφή.

ΠΡΟΣΟΧΗ ΠΡΟΣΟΧΗ ΠΡΟΣΟΧΗ

Tα DMVs αυτά κρατάνε πληροφορίες από την στιγμή που έχει ξεκινήσει το SQL Server service. Αν κάποιος έχει κάνει πρόσφατα restart αυτό θα πρέπει να περιμένει ένα εύλογο χρονικό διάστημα μέσα στο οποίο έχει εκτελεστεί όλη η ποικιλία των ερωτημάτων που γίνονται στην database ώστε να μπορεί να βγάλει ασφαλή συμπεράσματα.



//antonch

Ημερομηνία: 10 May 2017 14:32
Αξιολόγηση: ( 1 )
Κατηγορίες:
Tags:
Share it:

Σχετικά Blog Post

Αφήστε το σχόλιο σας - Leave your comment

Τα σχόλια έχουν κλείσει.
Επιτρέπονται μόνο τα σχόλια από τα μέλη του SqlSchool.gr.


newsletter subscription

Εάν επιθυμείτε να λαμβάνετε ενημέρωση από εμάς, δώστε μας το e-mail σας.
PASS chapter logo
Official Professional Association for SQL Server (PASS) chapter for Greece
Join to PASS