Saya memiliki permintaan yang saat ini sedang menyelesaikan rata-rata 2500 ms. Meja saya sangat sempit, tetapi ada 44 juta baris. Opsi apa yang saya miliki untuk meningkatkan kinerja, atau apakah ini sebagus yang didapat?
The Query
SELECT TOP 1000 * FROM [CIA_WIZ].[dbo].[Heartbeats]
WHERE [DateEntered] BETWEEN '2011-08-30' and '2011-08-31';
Meja
CREATE TABLE [dbo].[Heartbeats](
[ID] [int] IDENTITY(1,1) NOT NULL,
[DeviceID] [int] NOT NULL,
[IsPUp] [bit] NOT NULL,
[IsWebUp] [bit] NOT NULL,
[IsPingUp] [bit] NOT NULL,
[DateEntered] [datetime] NOT NULL,
CONSTRAINT [PK_Heartbeats] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
Indeks
CREATE NONCLUSTERED INDEX [CommonQueryIndex] ON [dbo].[Heartbeats]
(
[DateEntered] ASC,
[DeviceID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
Apakah menambahkan indeks tambahan akan membantu? Jika demikian, akan seperti apa mereka? Kinerja saat ini dapat diterima, karena kueri hanya berjalan sesekali, tetapi saya bertanya-tanya sebagai latihan pembelajaran, adakah yang bisa saya lakukan untuk membuatnya lebih cepat?
MEMPERBARUI
Ketika saya mengubah kueri untuk menggunakan petunjuk indeks gaya, kueri dijalankan dalam 50ms:
SELECT TOP 1000 * FROM [CIA_WIZ].[dbo].[Heartbeats] WITH(INDEX(CommonQueryIndex))
WHERE [DateEntered] BETWEEN '2011-08-30' and '2011-08-31'
Menambahkan klausa DeviceID selektif dengan benar juga mencapai kisaran 50ms:
SELECT TOP 1000 * FROM [CIA_WIZ].[dbo].[Heartbeats]
WHERE [DateEntered] BETWEEN '2011-08-30' and '2011-08-31' AND DeviceID = 4;
Jika saya menambahkan ORDER BY [DateEntered], [DeviceID]
ke permintaan asli, saya berada di kisaran 50ms:
SELECT TOP 1000 * FROM [CIA_WIZ].[dbo].[Heartbeats]
WHERE [DateEntered] BETWEEN '2011-08-30' and '2011-08-31'
ORDER BY [DateEntered], [DeviceID];
Ini semua menggunakan indeks yang saya harapkan (CommonQueryIndex) jadi, saya kira pertanyaan saya sekarang, apakah ada cara untuk memaksa indeks ini digunakan pada pertanyaan seperti ini? Atau apakah ukuran meja saya terlalu banyak membuang pengoptimal dan saya harus menggunakan ORDER BY
atau memberi petunjuk?
Jawaban:
Mengapa pengoptimal tidak sesuai dengan indeks pertama Anda:
Apakah masalah selektivitas Kolom [DateEntered].
Anda memberi tahu kami bahwa meja Anda memiliki 44 juta baris. ukuran baris adalah:
4 byte, untuk ID, 4 byte untuk Device ID, 8 byte untuk tanggal, dan 1 byte untuk kolom 4 bit. itu 17 byte + 7 byte overhead untuk (tag, bitmap Null, var col offset ,, jumlah col) total 24 Bytes per baris.
Itu akan menerjemahkan secara kasar ke 140 ribu halaman. Untuk menyimpan 44 juta baris itu.
Sekarang pengoptimal dapat melakukan dua hal:
Sekarang pada titik tertentu itu menjadi lebih mahal untuk melakukan semua pencarian tunggal dalam indeks berkerumun untuk setiap entri indeks yang ditemukan dalam indeks nonkluster Anda. Ambang batas untuk itu umumnya jumlah total pencarian harus melebihi 25% total 33% dari total jumlah halaman tabel.
Jadi dalam hal ini: 140k / 25% = 35000 baris 140k / 33% = 46666 baris.
(@RBarryYoung, 35k adalah 0,08% dari total baris dan 46666 adalah 0,10%, jadi saya pikir di situlah kebingungan itu)
Jadi, jika klausa Anda di mana akan menghasilkan baris antara 35.000 dan 46666 (ini di bawah klausa teratas!) Sangat mungkin bahwa non-cluster Anda tidak akan digunakan dan bahwa pemindaian indeks berkerumun akan digunakan.
Hanya dua cara untuk mengubahnya adalah:
sekarang yakin Anda dapat membuat indeks penutup bahkan ketika Anda menggunakan pilih *. Tapi itu hanya menciptakan overhead besar untuk memasukkan / memperbarui / menghapus Anda. Kami harus tahu lebih banyak tentang beban kerja Anda (baca vs tulis) untuk memastikan apakah itu solusi terbaik.
Mengubah dari datetime ke smalldatetime adalah pengurangan 16% dalam ukuran pada indeks berkerumun dan pengurangan 24% dalam ukuran pada indeks non-clustered Anda.
sumber
Apakah ada alasan tertentu bahwa PK Anda mengelompok? Banyak orang melakukan ini karena default seperti itu, atau mereka berpikir bahwa PK harus dikelompokkan. Tidak juga. Indeks berkerumun biasanya terbaik untuk kueri rentang (seperti ini) atau pada kunci asing tabel anak.
Efek dari indeks pengelompokan adalah bahwa pengelompokan semua data bersama-sama karena data disimpan pada node daun dari pohon cluster b. Jadi, dengan asumsi bahwa Anda tidak meminta 'terlalu luas' dari suatu rentang, pengoptimal akan tahu dengan pasti bagian b pohon mana yang berisi data dan itu tidak harus menemukan pengidentifikasi baris dan kemudian melompat ke tempat data adalah (seperti halnya ketika berurusan dengan indeks NC). Apa yang 'terlalu luas' dari suatu rentang? Contoh konyol akan meminta data 11 bulan dari tabel yang hanya memiliki catatan satu tahun. Menarik data satu hari seharusnya tidak menjadi masalah, dengan asumsi bahwa statistik Anda mutakhir. (Meskipun, pengoptimal mungkin mendapat masalah jika Anda mencari data kemarin dan Anda belum memperbarui statistik selama tiga hari.)
Karena Anda menjalankan kueri "SELECT *", mesin harus mengembalikan semua kolom dalam tabel (bahkan jika seseorang menambahkan yang baru yang tidak dibutuhkan aplikasi Anda pada saat itu) sehingga indeks yang meliputi atau indeks dengan kolom yang disertakan tidak akan banyak membantu, jika sama sekali. (Jika Anda memasukkan setiap kolom dari tabel dalam indeks, Anda melakukan sesuatu yang salah.) Pengoptimal mungkin akan mengabaikan indeks NC tersebut.
Jadi, apa yang harus dilakukan?
Saran saya adalah untuk menjatuhkan indeks NC, mengubah PK berkerumun menjadi nonclustered dan membuat indeks berkerumun di [DateEntered]. Simpler lebih baik, sampai terbukti sebaliknya.
sumber
Selama Anda memiliki "*" di sana, maka satu-satunya hal yang dapat saya bayangkan yang akan membuat banyak perbedaan adalah mengubah definisi indeks Anda menjadi ini:
Seperti yang saya catat di komentar, harus menggunakan indeks itu, tetapi jika tidak, Anda dapat membujuknya dengan ORDER BY atau petunjuk indeks.
sumber
Saya akan melihat ini sedikit berbeda.
Saya akan membuang kolom datetime - ubah ke int. Miliki tabel pencarian atau lakukan konversi untuk kencan Anda.
Dump indeks berkerumun - biarkan sebagai tumpukan dan buat indeks non-berkerumun pada kolom INT baru yang mewakili tanggal. yaitu hari ini adalah 20121015. Perintah itu penting. Bergantung pada seberapa sering Anda memuat tabel, lihat membuat indeks itu dalam urutan DESC. Biaya pemeliharaan akan lebih tinggi dan Anda ingin memperkenalkan faktor pengisian atau partisi. Partisi juga akan membantu mengurangi waktu tayang Anda.
Terakhir, jika Anda bisa menggunakan SQL 2012, coba gunakan SEQUENCE - itu akan mengungguli identitas () untuk menyisipkan.
sumber