Elemen umum yang digunakan dalam desain database adalah kendala. Kendala datang dalam berbagai rasa (misalnya default, unik) dan menegakkan integritas kolom tempat mereka ada. Ketika diimplementasikan dengan baik, kendala adalah komponen yang kuat dalam desain database karena mencegah data yang tidak memenuhi kriteria yang ditetapkan untuk masuk ke database. Namun, batasan dapat dilanggar menggunakan perintah seperti WITH NOCHECK
dan IGNORE_CONSTRAINTS
. Selain itu, saat menggunakan REPAIR_ALLOW_DATA_LOSS
opsi dengan DBCC CHECK
perintah untuk memperbaiki kerusakan basis data, batasan tidak dipertimbangkan.
Akibatnya, dimungkinkan untuk memiliki data yang tidak valid dalam database – baik data yang tidak mematuhi batasan, atau data yang tidak lagi mempertahankan hubungan kunci asing utama yang diharapkan. SQL Server menyertakan DBCC CHECKCONSTRAINTS
pernyataan untuk menemukan data yang melanggar batasan. Setelah opsi perbaikan dijalankan, jalankan DBCC CHECKCONSTRAINTS
untuk seluruh database untuk memastikan tidak ada masalah, dan mungkin ada saat-saat yang tepat untuk menjalankan CHECKCONSTRAINTS
untuk batasan pilih atau tabel. Menjaga integritas data sangat penting, dan meskipun menjalankan DBCC CHECKCONSTRAINTS
bukanlah hal yang biasa. secara terjadwal untuk menemukan data yang tidak valid, saat Anda perlu menjalankannya, sebaiknya pahami dampak kinerja yang mungkin ditimbulkannya.
DBCC CHECKCONSTRAINTS
dapat mengeksekusi untuk satu kendala, tabel, atau seluruh database. Seperti perintah pemeriksaan lainnya, diperlukan waktu yang cukup lama untuk menyelesaikannya dan akan menghabiskan sumber daya sistem, terutama untuk database yang lebih besar. Tidak seperti perintah cek lainnya, CHECKCONSTRAINTS
tidak menggunakan snapshot database.
Dengan Acara yang Diperpanjang, kita dapat memeriksa penggunaan sumber daya saat kita menjalankan DBCC CHECKCONSTRAINTS
untuk meja. Untuk menunjukkan dampaknya dengan lebih baik, saya menjalankan skrip Create Enlarged AdventureWorks Tables.sql dari Jonathan Kehayias (blog | @SQLPoolBoy) untuk membuat tabel yang lebih besar. Skrip Jonathan hanya membuat indeks untuk tabel, jadi pernyataan di bawah ini diperlukan untuk menambahkan beberapa batasan yang dipilih:
USE [AdventureWorks2012]; GO ALTER TABLE [Sales].[SalesOrderDetailEnlarged] WITH CHECK ADD CONSTRAINT [FK_SalesOrderDetailEnlarged_SalesOrderHeaderEnlarged_SalesOrderID] FOREIGN KEY([SalesOrderID]) REFERENCES [Sales].[SalesOrderHeaderEnlarged] ([SalesOrderID]) ON DELETE CASCADE; GO ALTER TABLE [Sales].[SalesOrderDetailEnlarged] WITH CHECK ADD CONSTRAINT [CK_SalesOrderDetailEnlarged_OrderQty] CHECK (([OrderQty]>(0))) GO ALTER TABLE [Sales].[SalesOrderDetailEnlarged] WITH CHECK ADD CONSTRAINT [CK_SalesOrderDetailEnlarged_UnitPrice] CHECK (([UnitPrice]>=(0.00))); GO ALTER TABLE [Sales].[SalesOrderHeaderEnlarged] WITH CHECK ADD CONSTRAINT [CK_SalesOrderHeaderEnlarged_DueDate] CHECK (([DueDate]>=[OrderDate])) GO ALTER TABLE [Sales].[SalesOrderHeaderEnlarged] WITH CHECK ADD CONSTRAINT [CK_SalesOrderHeaderEnlarged_Freight] CHECK (([Freight]>=(0.00))) GO
Kami dapat memverifikasi kendala apa yang ada menggunakan sp_helpconstraint
:
EXEC sp_helpconstraint '[Sales].[SalesOrderDetailEnlarged]'; GO
sp_helpconstraint keluaran
Setelah kendala ada, kita dapat membandingkan penggunaan sumber daya untuk DBCC CHECKCONSTRAINTS
untuk satu kendala, tabel, dan seluruh database menggunakan Extended Events. Pertama kita akan membuat sesi yang hanya menangkap sp_statement_completed
acara, termasuk sql_text
tindakan, dan mengirimkan output ke ring_buffer
:
CREATE EVENT SESSION [Constraint_Performance] ON SERVER ADD EVENT sqlserver.sp_statement_completed ( ACTION(sqlserver.database_id,sqlserver.sql_text) ) ADD TARGET package0.ring_buffer ( SET max_events_limit=(5000) ) WITH ( MAX_MEMORY=32768 KB, EVENT_RETENTION_MODE=ALLOW_SINGLE_EVENT_LOSS, MAX_DISPATCH_LATENCY=30 SECONDS, MAX_EVENT_SIZE=0 KB, MEMORY_PARTITION_MODE=NONE, TRACK_CAUSALITY=OFF, STARTUP_STATE=OFF ); GO
Selanjutnya kita akan memulai sesi dan menjalankan setiap DBCC CHECKCONSTRAINT
perintah, lalu keluarkan buffer cincin ke tabel temp untuk dimanipulasi. Perhatikan bahwa DBCC DROPCLEANBUFFERS
dijalankan sebelum setiap pemeriksaan sehingga masing-masing dimulai dari cache dingin, menjaga bidang pengujian level.
ALTER EVENT SESSION [Constraint_Performance] ON SERVER STATE=START; GO USE [AdventureWorks2012]; GO DBCC DROPCLEANBUFFERS; GO DBCC CHECKCONSTRAINTS ('[Sales].[CK_SalesOrderDetailEnlarged_OrderQty]') WITH NO_INFOMSGS; GO DBCC DROPCLEANBUFFERS; GO DBCC CHECKCONSTRAINTS ('[Sales].[FK_SalesOrderDetailEnlarged_SalesOrderHeaderEnlarged_SalesOrderID]') WITH NO_INFOMSGS; GO DBCC DROPCLEANBUFFERS; GO DBCC CHECKCONSTRAINTS ('[Sales].[SalesOrderDetailEnlarged]') WITH NO_INFOMSGS; GO DBCC DROPCLEANBUFFERS; GO DBCC CHECKCONSTRAINTS WITH ALL_CONSTRAINTS, NO_INFOMSGS; GO DECLARE @target_data XML; SELECT @target_data = CAST(target_data AS XML) FROM sys.dm_xe_sessions AS s INNER JOIN sys.dm_xe_session_targets AS t ON t.event_session_address = s.[address] WHERE s.name = N'Constraint_Performance' AND t.target_name = N'ring_buffer'; SELECT n.value('(@name)[1]', 'varchar(50)') AS event_name, DATEADD(HOUR ,DATEDIFF(HOUR, SYSUTCDATETIME(), SYSDATETIME()),n.value('(@timestamp)[1]', 'datetime2')) AS [timestamp], n.value('(data[@name="duration"]/value)[1]', 'bigint') AS duration, n.value('(data[@name="physical_reads"]/value)[1]', 'bigint') AS physical_reads, n.value('(data[@name="logical_reads"]/value)[1]', 'bigint') AS logical_reads, n.value('(action[@name="sql_text"]/value)[1]', 'varchar(max)') AS sql_text, n.value('(data[@name="statement"]/value)[1]', 'varchar(max)') AS [statement] INTO #EventData FROM @target_data.nodes('RingBufferTarget/event[@name=''sp_statement_completed'']') AS q(n); GO ALTER EVENT SESSION [Constraint_Performance] ON SERVER STATE=STOP; GO
Mengurai ring_buffer
ke dalam tabel temp mungkin memerlukan waktu tambahan (sekitar 20 detik di mesin saya), tetapi kueri data yang berulang lebih cepat dari tabel temp daripada melalui ring_buffer
. Jika kita melihat outputnya kita melihat ada beberapa pernyataan yang dieksekusi untuk setiap DBCC CHECKCONSTRAINTS
:
SELECT * FROM #EventData WHERE [sql_text] LIKE 'DBCC%';
Output Acara yang Diperpanjang
Menggunakan Acara yang Diperpanjang untuk menggali cara kerja bagian dalam CHECKCONSTRAINTS
adalah tugas yang menarik, tetapi yang benar-benar kami minati di sini adalah konsumsi sumber daya – khususnya I/O. Kami dapat menggabungkan physical_reads
untuk setiap perintah cek untuk membandingkan I/O:
SELECT [sql_text], SUM([physical_reads]) AS [Total Reads] FROM #EventData WHERE [sql_text] LIKE 'DBCC%' GROUP BY [sql_text];
I/O Gabungan untuk Cek
Untuk memeriksa batasan, SQL Server harus membaca data untuk menemukan baris yang mungkin melanggar batasan. Definisi CK_SalesOrderDetailEnlarged_OrderQty
batasannya adalah [OrderQty] > 0
. Batasan kunci asing, FK_SalesOrderDetailEnlarged_SalesOrderHeaderEnlarged_SalesOrderID
, membuat hubungan pada SalesOrderID
antara [Sales].[SalesOrderHeaderEnlarged]
dan [Sales].[SalesOrderDetailEnlarged]
tabel. Secara intuitif, sepertinya memeriksa batasan kunci asing akan membutuhkan lebih banyak I/O, karena SQL Server harus membaca data dari dua tabel. Namun, [SalesOrderID]
ada di tingkat daun IX_SalesOrderHeaderEnlarged_SalesPersonID
indeks nonclustered pada [Sales].[SalesOrderHeaderEnlarged]
tabel, dan di IX_SalesOrderDetailEnlarged_ProductID
indeks pada [Sales].[SalesOrderDetailEnlarged]
meja. Dengan demikian, SQL Server memindai kedua indeks tersebut untuk membandingkan [SalesOrderID]
nilai antara dua tabel. Ini membutuhkan lebih dari 19.000 pembacaan. Dalam kasus CK_SalesOrderDetailEnlarged_OrderQty
kendala, [OrderQty]
kolom tidak termasuk dalam indeks apa pun, sehingga pemindaian penuh dari indeks berkerumun terjadi, yang membutuhkan lebih dari 72.000 pembacaan.
Ketika semua kendala untuk tabel diperiksa, persyaratan I/O lebih tinggi daripada jika satu kendala diperiksa, dan mereka meningkat lagi ketika seluruh database diperiksa. Pada contoh di atas, [Sales].[SalesOrderHeaderEnlarged]
dan [Sales].[SalesOrderDetailEnlarged]
tabel secara tidak proporsional lebih besar dari tabel lain dalam database. Ini tidak biasa dalam skenario dunia nyata; sangat sering database memiliki beberapa tabel besar yang terdiri dari sebagian besar database. Saat menjalankan CHECKCONSTRAINTS
untuk tabel ini, perhatikan potensi konsumsi sumber daya yang diperlukan untuk pemeriksaan. Jalankan pemeriksaan selama jam nonaktif jika memungkinkan untuk meminimalkan dampak pengguna. Jika pemeriksaan harus dijalankan selama jam kerja normal, memahami kendala apa yang ada, dan indeks apa yang ada untuk mendukung validasi, dapat membantu mengukur efek pemeriksaan. Anda dapat melakukan pemeriksaan di lingkungan pengujian atau pengembangan terlebih dahulu untuk memahami dampak kinerja, namun variasi mungkin ada berdasarkan perangkat keras, data yang sebanding, dll. Dan terakhir, ingatlah bahwa setiap kali Anda menjalankan perintah pemeriksaan yang menyertakan REPAIR_ALLOW_DATA_LOSS
pilihan, ikuti perbaikan dengan DBCC CHECKCONSTRAINTS
. Perbaikan basis data tidak memperhitungkan kendala apa pun karena kerusakan telah diperbaiki, jadi selain berpotensi kehilangan data, Anda mungkin berakhir dengan data yang melanggar satu atau beberapa kendala dalam basis data Anda.