SQL Server 2005
Saya harus dapat terus memproses sekitar 350 juta catatan dalam tabel 900 juta catatan. Kueri yang saya gunakan untuk memilih catatan yang akan diproses menjadi sangat terfragmentasi saat saya memproses dan saya harus menghentikan pemrosesan untuk membangun kembali indeks. Model data semu & kueri ...
/**************************************/
CREATE TABLE [Table]
(
[PrimaryKeyId] [INT] IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED,
[ForeignKeyId] [INT] NOT NULL,
/* more columns ... */
[DataType] [CHAR](1) NOT NULL,
[DataStatus] [DATETIME] NULL,
[ProcessDate] [DATETIME] NOT NULL,
[ProcessThreadId] VARCHAR (100) NULL
);
CREATE NONCLUSTERED INDEX [Idx] ON [Table]
(
[DataType],
[DataStatus],
[ProcessDate],
[ProcessThreadId]
);
/**************************************/
/**************************************/
WITH cte AS (
SELECT TOP (@BatchSize) [PrimaryKeyId], [ProcessThreadId]
FROM [Table] WITH ( ROWLOCK, UPDLOCK, READPAST )
WHERE [DataType] = 'X'
AND [DataStatus] IS NULL
AND [ProcessDate] < DATEADD(m, -2, GETDATE()) -- older than 2 months
AND [ProcessThreadId] IS NULL
)
UPDATE cte
SET [ProcessThreadId] = @ProcessThreadId;
SELECT * FROM [Table] WITH ( NOLOCK )
WHERE [ProcessThreadId] = @ProcessThreadId;
/**************************************/
Konten data ...
Sementara kolom [Tipe Data ] diketik sebagai CHAR (1), sekitar 35% dari semua catatan sama dengan 'X' dan sisanya sama dengan 'A'.
Dari hanya catatan di mana [Jenis Data] sama dengan 'X', sekitar 10% akan memiliki nilai NOT NULL [DataStatus].
Kolom [ProcessDate] dan [ProcessThreadId] akan diperbarui untuk setiap catatan yang diproses.
Kolom [DataType] diperbarui ('X' diubah menjadi 'A') sekitar 10% dari waktu.
Kolom [DataStatus] diperbarui kurang dari 1% dari waktu.
Untuk saat ini solusi saya adalah memilih kunci utama dari semua catatan untuk diproses menjadi tabel pemrosesan yang terpisah. Saya menghapus kunci saat saya memprosesnya sehingga sebagai fragmen indeks saya berurusan dengan lebih sedikit catatan.
Namun, ini tidak sesuai dengan alur kerja yang saya inginkan sehingga data ini diproses terus menerus, tanpa intervensi manual dan downtime yang signifikan. Saya mengantisipasi downtime setiap tiga bulan untuk pekerjaan rumah tangga. Tapi sekarang, tanpa tabel pemrosesan yang terpisah, saya tidak bisa melewati pemrosesan bahkan setengah dari set data tanpa fragmentasi menjadi begitu buruk sehingga mengharuskan berhenti dan membangun kembali indeks.
Adakah rekomendasi untuk pengindeksan atau model data yang berbeda? Apakah ada pola yang perlu saya teliti?
Saya memiliki kontrol penuh terhadap model data dan perangkat lunak proses sehingga tidak ada yang salah.
sumber
Jawaban:
Apa yang Anda lakukan adalah Anda menggunakan tabel sebagai antrian. Pembaruan Anda adalah metode dequeue. Tetapi indeks berkerumun di atas meja adalah pilihan yang buruk untuk antrian. Menggunakan tabel sebagai Antrian sebenarnya memberlakukan persyaratan yang cukup ketat pada desain tabel. Indeks berkerumun Anda harus menjadi urutan dequeue, dalam hal ini mungkin
([DataType], [DataStatus], [ProcessDate])
. Anda dapat menerapkan kunci utama sebagai nonclustered kendala. Jatuhkan indeks yang tidak berkerumunIdx
, karena kunci yang dikelompokkan mengambil perannya.Bagian penting lain dari teka-teki adalah menjaga ukuran baris konstan selama pemrosesan. Anda telah mendeklarasikan
ProcessThreadId
sebagaiVARCHAR(100)
yang menyiratkan baris tumbuh dan menyusut seperti sedang 'diproses' karena nilai bidang berubah dari NULL ke non-null. Pola tumbuh-dan-susutkan pada baris ini menyebabkan perpecahan dan fragmentasi halaman. Saya tidak dapat membayangkan ID utas 'VARCHAR (100)'. Gunakan tipe panjang tetap, mungkin sebuahINT
.Sebagai catatan tambahan, Anda tidak perlu keluar dalam dua langkah (UPDATE diikuti oleh SELECT). Anda dapat menggunakan klausa OUTPUT, seperti yang dijelaskan dalam artikel yang ditautkan di atas:
Selain itu saya akan mempertimbangkan untuk memindahkan item yang berhasil diproses menjadi tabel, arsip, yang berbeda. Anda ingin tabel antrian Anda melayang-layang di dekat ukuran nol, Anda tidak ingin mereka tumbuh karena mereka mempertahankan 'riwayat' dari entri lama yang tidak dibutuhkan. Anda juga dapat mempertimbangkan mempartisi dengan
[ProcessDate]
sebagai alternatif (mis. Satu partisi aktif saat ini yang bertindak sebagai antrian dan menyimpan entri dengan NULL ProcessDate, dan partisi lain untuk semua yang bukan nol. Atau beberapa partisi untuk bukan-nol jika Anda ingin menerapkan efisien menghapus (beralih) untuk data yang telah melewati periode retensi yang diamanatkan. Jika semuanya menjadi panas Anda dapat mempartisi dengan tambahan[DataType]
jika memiliki selektivitas yang cukup, tetapi desain itu akan sangat rumit karena membutuhkan partisi dengan kolom yang dikomputasi tetap (kolom komposit yang menempel bersama [DataType] dan [ProcessingDate]).sumber
Saya akan mulai dengan memindahkan bidang
ProcessDate
danProcessthreadid
ke tabel lain.Saat ini, setiap baris yang Anda pilih dari indeks yang cukup luas ini juga perlu diperbarui.
Jika Anda memindahkan kedua bidang tersebut ke tabel lain, volume pembaruan Anda di tabel utama dipotong 90%, yang seharusnya menangani sebagian besar fragmentasi.
Anda masih memiliki fragmentasi di tabel BARU, tetapi akan lebih mudah untuk mengelola di tabel yang lebih sempit dengan data yang jauh lebih sedikit.
sumber