Meminimalkan Bacaan Terindeks dengan Kriteria Kompleks

12

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 Pendingstatus.

Loop pemrosesan saya adalah:

  1. Ambil Tiket 1 di mana Pending
  2. Bekerja dengan Tiket.
  3. Perbarui Status Tiket => Complete
  4. 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_Idkolom 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'.

gddc
sumber
Apa yang Anda maksud dengan "pertama" di "Ambil Tiket Pertama di mana 'Tertunda'" ?
ypercubeᵀᴹ
Jika "pertama" berarti terkecil ticket_id, Anda mungkin perlu indeks pada(status, ticket_id)
ypercubeᵀᴹ
Dan seberapa yakin Anda bahwa penurunan kinerja disebabkan oleh prosedur ini dan bukan oleh pertanyaan / pernyataan lain?
ypercubeᵀᴹ
@ ypercube - Tidak, saya tidak yakin di situlah penurunan kinerja. Itu sebabnya pertanyaan saya adalah "apakah saya perlu khawatir dengan ini, atau apakah itu perilaku normal dari suatu indeks?". Itu adalah sesuatu yang saya perhatikan saat memantau basis data, dan saya menganggapnya tidak terduga. Saya tidak akan mengharapkannya untuk terus memindai baris sebelumnya ketika saya memberikan klausa di mana terhadap kolom yang diindeks. FWIW, memodifikasi indeks untuk memasukkan ticket_idbenar - benar dilakukan lebih buruk daripada hanya memiliki Status diindeks.
gddc
Apakah id(tipe data) domain yang Anda tetapkan?
a_horse_with_no_name

Jawaban:

1

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.

gumpalan
sumber
0

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 1hack 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:

Select Job_ID, Ticket_Id
From   Tickets
Where Job_ID = ( 
  select min(Job_ID) from Tickets 
  where Status = 'Pending'
);

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 Statusbyte tunggal, dibatasi, dan biarkan aplikasi memasok string "Pending". Itu akan menjaga terhadap Statusnilai - nilai yang salah , dan mungkin membuat indeks lebih kecil dalam tawar-menawar. Sesuatu seperti:

CREATE TABLE TICKETS (
  TICKET_ID id PRIMARY KEY,
  JOB_ID id,
  ACTION_ID id,
  STATUS char(1) not NULL 
     DEFAULT 'P'
     CHECK( STATUS in ('P', 'C', 'X') ) -- whatever the domain is
);

Tentu saja, Anda dapat menggunakan tampilan (atau mungkin kolom turunan) untuk memasok string kanonik untuk Status.

James K. Lowden
sumber