Database
 sql >> Teknologi Basis Data >  >> RDS >> Database

Prosedur Tersimpan untuk Mendapatkan Status Indeks di Semua Basis Data

Sebagai SQL Server DBA, kami telah mendengar bahwa struktur indeks dapat secara dramatis meningkatkan kinerja kueri apa pun (atau kumpulan kueri). Namun, ada detail tertentu yang diabaikan oleh banyak DBA, seperti berikut ini:

  • Struktur indeks dapat menjadi terfragmentasi, berpotensi menyebabkan masalah penurunan kinerja.
  • Setelah struktur indeks telah digunakan untuk tabel database, SQL Server memperbaruinya setiap kali operasi penulisan dilakukan untuk tabel tersebut. Ini terjadi jika kolom yang sesuai dengan indeks terpengaruh.
  • Ada metadata di dalam SQL Server yang dapat digunakan untuk mengetahui kapan statistik untuk struktur indeks tertentu diperbarui (jika pernah) untuk terakhir kalinya. Statistik yang tidak memadai atau kedaluwarsa dapat memengaruhi kinerja kueri tertentu.
  • Ada metadata di dalam SQL Server yang dapat digunakan untuk mengetahui berapa banyak struktur indeks yang telah dikonsumsi oleh operasi baca, atau diperbarui oleh operasi tulis oleh SQL Server itu sendiri. Informasi ini dapat berguna untuk mengetahui apakah ada indeks yang volume tulisnya jauh melebihi yang dibaca. Ini berpotensi menjadi struktur indeks yang tidak berguna untuk disimpan.*

*Sangat penting untuk diingat bahwa tampilan sistem yang menyimpan metadata khusus ini akan dihapus setiap kali instance SQL Server dimulai ulang, sehingga tidak akan menjadi informasi dari konsepsinya.

Karena pentingnya detail ini, saya telah membuat Prosedur Tersimpan untuk melacak informasi mengenai struktur indeks di lingkungannya, untuk bertindak seproaktif mungkin.

Pertimbangan Awal

  • Pastikan bahwa akun yang menjalankan Prosedur Tersimpan ini memiliki cukup hak istimewa. Anda mungkin bisa memulai dengan yang sysadmin dan kemudian pergi sedetail mungkin untuk memastikan pengguna memiliki hak minimum yang diperlukan agar SP berfungsi dengan baik.
  • Objek database (tabel database dan prosedur tersimpan) akan dibuat di dalam database yang dipilih pada saat skrip dijalankan, jadi pilihlah dengan hati-hati.
  • Skrip dibuat sedemikian rupa sehingga dapat dieksekusi beberapa kali tanpa menimbulkan kesalahan. Untuk Prosedur Tersimpan, saya menggunakan pernyataan CREATE OR ALTER PROCEDURE, tersedia sejak SQL Server 2016 SP1.
  • Jangan ragu untuk mengubah nama objek database yang dibuat jika Anda ingin menggunakan konvensi penamaan yang berbeda.
  • Bila Anda memilih untuk mempertahankan data yang dikembalikan oleh Stored Procedure, tabel target akan dipotong terlebih dahulu sehingga hanya kumpulan hasil terbaru yang akan disimpan. Anda dapat membuat penyesuaian yang diperlukan jika Anda ingin ini berperilaku berbeda, untuk alasan apa pun (mungkin untuk menyimpan informasi historis?).

Bagaimana Cara Menggunakan Stored Procedure?

  1. Copy &paste Kode T-SQL (tersedia dalam artikel ini).
  2. SP mengharapkan 2 parameter:
    1. @persistData:‘Y’ jika DBA ingin menyimpan output dalam tabel target, dan ‘N’ jika DBA hanya ingin melihat output secara langsung.
    2. @db:'all' untuk mendapatkan informasi untuk semua database (sistem &pengguna), 'user' untuk menargetkan database pengguna, 'system' untuk menargetkan hanya database sistem (tidak termasuk tempdb), dan terakhir nama sebenarnya dari database tertentu.

Bidang yang Disajikan dan Maknanya

  • dbName: nama database tempat objek indeks berada.
  • nama skema: nama skema tempat objek indeks berada.
  • namatabel: nama tabel tempat objek indeks berada.
  • namaindeks: nama struktur indeks.
  • ketik: jenis indeks (mis. Clustered, Non-Clustered).
  • allocation_unit_type: menentukan jenis data yang dirujuk (misalnya data dalam baris, data lob).
  • fragmentasi: jumlah fragmentasi (dalam %) yang dimiliki struktur indeks saat ini.
  • laman: jumlah halaman 8 KB yang membentuk struktur indeks.
  • menulis: jumlah penulisan yang dialami struktur indeks sejak instance SQL Server terakhir kali dimulai ulang.
  • membaca: jumlah pembacaan yang dialami struktur indeks sejak instance SQL Server terakhir kali dimulai ulang.
  • dinonaktifkan: 1 jika struktur indeks saat ini dinonaktifkan atau 0 jika struktur diaktifkan.
  • stats_timestamp: nilai stempel waktu kapan statistik untuk struktur indeks tertentu terakhir diperbarui (NULL jika tidak pernah).
  • data_collection_timestamp: hanya terlihat jika 'Y' diteruskan ke parameter @persistData, dan digunakan untuk mengetahui kapan SP dijalankan dan informasi berhasil disimpan di tabel DBA_Indexes.

Tes Eksekusi

Saya akan mendemonstrasikan beberapa eksekusi Stored Procedure sehingga Anda bisa mendapatkan gambaran tentang apa yang diharapkan darinya:

*Anda dapat menemukan kode T-SQL lengkap dari skrip di bagian belakang artikel ini, jadi pastikan untuk mengeksekusinya sebelum melanjutkan dengan bagian berikut.

*Kumpulan hasil akan terlalu lebar untuk dimasukkan dalam 1 tangkapan layar, jadi saya akan membagikan semua tangkapan layar yang diperlukan untuk menyajikan informasi lengkap.

/* Menampilkan semua informasi indeks untuk semua sistem &database pengguna */

EXEC GetIndexData @persistData = 'N',@db = 'all'

/* Menampilkan semua informasi indeks untuk semua database sistem */

EXEC GetIndexData @persistData = 'N',@db = 'system'

/* Menampilkan semua informasi indeks untuk semua database pengguna */

EXEC GetIndexData @persistData = 'N',@db = 'user'

/* Menampilkan semua informasi indeks untuk database pengguna tertentu */

Dalam contoh saya sebelumnya, hanya database DBA yang muncul sebagai satu-satunya database pengguna saya dengan indeks di dalamnya. Oleh karena itu, izinkan saya membuat struktur indeks di database lain yang telah saya tempatkan dalam contoh yang sama sehingga Anda dapat melihat apakah SP melakukan tugasnya atau tidak.

EXEC GetIndexData @persistData = 'N',@db = 'db2'

Semua contoh yang ditampilkan sejauh ini menunjukkan output yang Anda dapatkan saat Anda tidak ingin menyimpan data, untuk berbagai kombinasi opsi untuk parameter @db. Outputnya kosong saat Anda menentukan opsi yang tidak valid atau database target tidak ada. Tapi bagaimana ketika DBA ingin menyimpan data dalam tabel database? Mari kita cari tahu.

*Saya akan menjalankan SP untuk satu kasus saja karena opsi lainnya untuk parameter @db sudah cukup banyak ditampilkan di atas dan hasilnya sama tetapi tetap ada di tabel database.

EXEC GetIndexData @persistData = 'Y',@db = 'user'

Sekarang, setelah Anda menjalankan Stored Procedure, Anda tidak akan mendapatkan output apa pun. Untuk mengkueri kumpulan hasil, Anda harus mengeluarkan pernyataan SELECT terhadap tabel DBA_Indexes. Daya tarik utama di sini adalah Anda dapat mengkueri kumpulan hasil yang diperoleh, untuk pasca-analisis, dan penambahan bidang data_collection_timestamp yang akan memberi tahu Anda seberapa baru/lama data yang Anda lihat.

Kueri Samping

Sekarang, untuk memberikan nilai lebih kepada DBA, saya telah menyiapkan beberapa kueri yang dapat membantu Anda mendapatkan informasi yang berguna dari data yang disimpan dalam tabel.

*Kueri untuk menemukan indeks yang sangat terfragmentasi secara keseluruhan.

*Pilih jumlah % yang Anda anggap pas.

*1500 halaman didasarkan pada artikel yang saya baca, berdasarkan rekomendasi Microsoft.

SELECT * FROM DBA_Indexes WHERE fragmentation >= 85 AND pages >= 1500;

*Kueri untuk menemukan indeks yang dinonaktifkan dalam lingkungan Anda.

SELECT * FROM DBA_Indexes WHERE disabled = 1;

*Kueri untuk menemukan indeks (kebanyakan non-clustered) yang tidak terlalu banyak digunakan oleh kueri, setidaknya tidak sejak terakhir kali instance SQL Server dimulai ulang.

SELECT * FROM DBA_Indexes WHERE writes > reads AND type <> 'CLUSTERED';

*Kueri untuk menemukan statistik yang belum pernah diperbarui atau sudah lama.

*Anda menentukan apa yang lama di lingkungan Anda, jadi pastikan untuk menyesuaikan jumlah hari yang sesuai.

SELECT * FROM DBA_Indexes WHERE stats_timestamp IS NULL OR DATEDIFF(DAY, stats_timestamp, GETDATE()) > 60;

Berikut kode lengkap Stored Procedure:

*Di awal skrip, Anda akan melihat nilai default yang diasumsikan oleh Prosedur Tersimpan jika tidak ada nilai yang diteruskan untuk setiap parameter.

IF NOT EXISTS (SELECT * FROM dbo.sysobjects where id = object_id(N'DBA_Indexes') and OBJECTPROPERTY(id, N'IsTable') = 1)
BEGIN
CREATE TABLE DBA_Indexes(
    [dbName]                    VARCHAR(128) NOT NULL,
    [schemaName]                VARCHAR(128) NOT NULL,
    [tableName]                 VARCHAR(128) NOT NULL,
    [indexName]                 VARCHAR(128) NOT NULL,
    [type]                      VARCHAR(128) NOT NULL,
    [allocation_unit_type]      VARCHAR(128) NOT NULL,
    [fragmentation]             DECIMAL(10,2) NOT NULL,
    [pages]                     INT NOT NULL,
    [writes]                    INT NOT NULL,
    [reads]                     INT NOT NULL,
    [disabled]                  TINYINT NOT NULL,
    [stats_timestamp]           DATETIME NULL,
    [data_collection_timestamp] DATETIME NOT NULL

    CONSTRAINT PK_DBA_Indexes PRIMARY KEY CLUSTERED ([dbName],[schemaName],[tableName],[indexName],[type],[allocation_unit_type],[data_collection_timestamp])
) ON [PRIMARY]
END
GO

DECLARE @sqlCommand NVARCHAR(MAX)

SET @sqlCommand = '
CREATE OR ALTER PROCEDURE GetIndexData 
	@persistData CHAR(1) = ''N'',
	@db          NVARCHAR(64)
AS
BEGIN
	SET NOCOUNT ON

	DECLARE @query NVARCHAR(MAX)    
	
	DECLARE @tmp_IndexInfo TABLE(       
	[dbName] VARCHAR(128),       
	[schemaName] VARCHAR(128),       
	[tableName] VARCHAR(128),       
	[indexName] VARCHAR(128),       
	[type] VARCHAR(128),       
	[allocation_unit_type] VARCHAR(128),       
	[fragmentation] DECIMAL(10,2),       
	[pages] INT,       
	[writes] INT,       
	[reads] INT,       
	[disabled] TINYINT,    
	[stats_timestamp] DATETIME)      
	
	SET @query = ''
	USE [?]
	''

	IF(@db = ''all'')
	SET @query += ''
	IF DB_ID(''''?'''') > 0 AND DB_ID(''''?'''') != 2 
	''

	IF(@db = ''system'')
	SET @query += ''
	IF DB_ID(''''?'''') > 0 AND DB_ID(''''?'''') < 5 AND DB_ID(''''?'''') != 2
	''

	IF(@db = ''user'')
	SET @query += ''
	IF DB_ID(''''?'''') > 4 
	''

	IF(@db != ''user'' AND @db != ''all'' AND @db != ''system'')
	SET @query += ''
	IF DB_NAME() = ''+CHAR(39)[email protected]+CHAR(39)+''
	''

	SET @query += ''
	BEGIN
	DECLARE @DB_ID INT;    
	SET @DB_ID = DB_ID();      
	SELECT 
		db_name(@DB_ID) AS db_name,     
		s.name,    
		t.name,    
		i.name,    
		i.type_desc,    
		ips.alloc_unit_type_desc,    
		CONVERT(DECIMAL(10,2),ips.avg_fragmentation_in_percent),    
		ips.page_count,    
		ISNULL(ius.user_updates,0),    
		ISNULL(ius.user_seeks + ius.user_scans + ius.user_lookups,0),    
		i.is_disabled,     
		STATS_DATE(st.object_id, st.stats_id)    
	FROM sys.indexes i     
	JOIN sys.tables t ON i.object_id = t.object_id     
	JOIN sys.schemas s ON s.schema_id = t.schema_id    
	JOIN sys.dm_db_index_physical_stats (@DB_ID, NULL, NULL, NULL, NULL) ips ON ips.database_id = @DB_ID AND ips.object_id = t.object_id AND ips.index_id = i.index_id    
	LEFT JOIN sys.dm_db_index_usage_stats ius ON ius.database_id = @DB_ID AND ius.object_id = t.object_id AND ius.index_id = i.index_id    
	JOIN sys.stats st ON st.object_id = t.object_id AND st.name = i.name    
	WHERE i.index_id > 0
	END''       
	
	INSERT INTO @tmp_IndexInfo    
	EXEC sp_MSForEachDB @query      
	   
	IF @persistData = ''N''
		SELECT * FROM @tmp_IndexInfo ORDER BY [dbName],[schemaName],[tableName] 
	ELSE 
	BEGIN
		TRUNCATE TABLE DBA_Indexes

		INSERT INTO DBA_Indexes
		SELECT *,GETDATE() FROM @tmp_IndexInfo ORDER BY [dbName],[schemaName],[tableName] 
	END
END
'
EXEC (@sqlCommand)
GO

Kesimpulan

  • Anda dapat menerapkan SP ini di setiap instance SQL Server di bawah dukungan Anda dan menerapkan mekanisme peringatan di seluruh tumpukan instance yang didukung.
  • Jika Anda menerapkan tugas agen yang menanyakan informasi ini relatif sering, Anda dapat tetap menjadi yang teratas untuk menjaga struktur indeks dalam lingkungan yang didukung.
  • Pastikan untuk menguji mekanisme ini dengan benar di lingkungan sandbox dan, saat Anda merencanakan penerapan produksi, pastikan untuk memilih periode aktivitas rendah.

Masalah fragmentasi indeks bisa rumit dan membuat stres. Untuk menemukan dan memperbaikinya, Anda dapat menggunakan alat yang berbeda, seperti dbForge Index Manager yang dapat diunduh di sini.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Cara Membuat Tabel dengan Beberapa Kunci Asing dan Tidak Bingung

  2. Mengumumkan Ketersediaan Umum SQL Compliance Manager 5.9

  3. Cara Menemukan Nilai Maksimum dalam Baris

  4. Model Data Tanggal Penting

  5. Cara Menemukan Rata-rata Kolom Numerik di SQL