Saya mengoptimalkan basis data Firebird 2.5 tiket kerja. Mereka disimpan dalam tabel yang dinyatakan sebagai:
CREATE TABLE TICKETS (
TICKET_ID id PRIMARY KEY,
JOB_ID id,
ACTION_ID id,
STATUS str256 DEFAULT 'Pending'
);
Saya biasanya ingin menemukan tiket pertama yang belum diproses dan dalam Pending
status.
Loop pemrosesan saya adalah:
- Ambil Tiket 1 di mana
Pending
- Bekerja dengan Tiket.
- Perbarui Status Tiket =>
Complete
- Ulang.
Tidak ada yang terlalu mewah. Jika saya menonton database saat loop ini berjalan, saya melihat jumlah tanjakan yang diindeks dibaca untuk setiap iterasi. Performanya sepertinya tidak terlalu buruk, tetapi mesin yang saya uji cukup cepat. Namun, saya telah menerima laporan penurunan kinerja dari waktu ke waktu dari beberapa pengguna saya.
Saya sudah memiliki indeks Status
, tapi sepertinya masih memindai Ticket_Id
kolom setiap iterasi. Sepertinya saya mengabaikan sesuatu, tapi saya tidak yakin apa. Apakah jumlah pendakian yang diindeks dibaca untuk sesuatu seperti ini yang diharapkan, atau apakah indeks tersebut mengalami kesalahan dalam beberapa hal?
- Suntingan untuk komentar -
Di Firebird Anda membatasi pencarian baris seperti:
Select First 1
Job_ID, Ticket_Id
From
Tickets
Where
Status = 'Pending'
Jadi ketika saya mengatakan "pertama", saya hanya meminta catatan terbatas yang ditetapkan di mana Status = 'Pending'
.
sumber
ticket_id
, Anda mungkin perlu indeks pada(status, ticket_id)
ticket_id
benar - benar dilakukan lebih buruk daripada hanya memiliki Status diindeks.id
(tipe data) domain yang Anda tetapkan?Jawaban:
Degradasi dari waktu ke waktu terjadi karena meningkatnya jumlah item yang dalam status "Lengkap". Pikirkan ini sebentar - Anda tidak akan mendapatkan penurunan kinerja saat pengujian karena Anda mungkin memiliki sedikit baris dengan status sebagai "Lengkap". Tetapi dalam produksi, mereka mungkin memiliki jutaan baris dengan status "Lengkap" dan jumlah ini akan meningkat seiring waktu. Ini, pada dasarnya, membuat indeks Anda pada Status semakin tidak bermanfaat seiring waktu. Dengan demikian, database mungkin hanya memutuskan bahwa karena Status hampir selalu memiliki nilai 'Lengkap', itu hanya akan memindai tabel daripada menggunakan indeks.
Dalam SQL Server (dan mungkin RDBMS lainnya?), Ini dapat dikerjakan menggunakan Filtered Indexes. Dalam SQL Server Anda akan menambahkan kondisi WHERE ke akhir definisi indeks Anda untuk mengatakan "terapkan indeks ini hanya untuk catatan dengan Status <> 'Lengkap'". Maka setiap permintaan yang menggunakan predikat ini kemungkinan besar akan menggunakan indeks pada sejumlah kecil catatan yang tidak disetel ke 'Lengkap'. Namun, berdasarkan dokumentasi di sini: http://www.firebirdsql.org/refdocs/langrefupd25-ddl-index.html , itu tidak terlihat seperti Firebird mendukung indeks yang difilter.
Solusinya adalah dengan meletakkan catatan 'Lengkap' di tabel ArchiveTickets. Buat tabel dengan definisi yang sama persis (meskipun tanpa ID yang dihasilkan secara otomatis) sebagai tabel Tiket Anda dan pertahankan baris di antara mereka dengan mendorong catatan 'Lengkap' ke tabel ArchiveTickets. Indeks pada tabel Tiket Anda kemudian akan lebih banyak jumlah catatan dan kinerja yang jauh lebih tinggi. Ini kemungkinan berarti Anda perlu mengubah laporan, dll, yang merujuk tiket 'Lengkap' untuk menunjuk ke tabel Arsip atau melakukan UNION di seluruh Tiket dan ArchiveTickets. Ini akan memiliki keuntungan tidak hanya karena cepat, tetapi juga berarti bahwa Anda dapat membuat indeks spesifik untuk tabel ArchiveTickets untuk membuatnya berkinerja lebih baik untuk kueri lain (misalnya:
Anda harus khawatir dengan ini jika produksi Anda akan masuk ke ribuan baris. Kinerja akan menurun seiring waktu dan berdampak negatif pada pengalaman pengguna Anda.
sumber
Apakah kinerja terpengaruh atau tidak akan menjadi fungsi volume data dan kapasitas mesin. Mengingat kapasitas perangkat keras modern, sulit untuk membayangkan volume penjualan tiket yang tidak dapat ditangani oleh desain yang Anda gambarkan. Namun, ada perubahan yang saya rekomendasikan untuk kebenaran, dan dapat meningkatkan kinerja sebagai manfaat sekunder.
Kueri pending Anda dapatkan pertama adalah non-deterministik. Pertama sesuai dengan pesanan apa? Tabel SQL tidak memiliki urutan intrinsik; yang
First 1
hack hanya memberikan Anda beberapa sewenang-wenang pertama. Untuk membuatnya deterministik, mengapa tidak memproses pekerjaan yang tertunda dalam urutan Job_ID?Jika Anda memiliki dua indeks {Job_ID} dan {Status, Job_ID}, kueri ini akan menghasilkan satu baris yang dapat diprediksi dan efisien:
Saya bukan pengguna Firebird, jadi Anda harus memeriksa rencana kueri, tetapi harus efisien karena referensi subquery hanya indeks kedua, menghasilkan nilai untuk yang pertama. (Mungkin ada trik efisiensi lain yang tersedia untuk Anda. Anda mungkin dapat mengatur tabel fisik sebagai pohon B +, atau memiliki akses ke row_id tersembunyi, misalnya.)
Perubahan lain yang saya buat untuk pembenaran adalah membuat
Status
byte tunggal, dibatasi, dan biarkan aplikasi memasok string "Pending". Itu akan menjaga terhadapStatus
nilai - nilai yang salah , dan mungkin membuat indeks lebih kecil dalam tawar-menawar. Sesuatu seperti:Tentu saja, Anda dapat menggunakan tampilan (atau mungkin kolom turunan) untuk memasok string kanonik untuk Status.
sumber