Mengapa InnoDB tidak menyimpan jumlah baris?

19

Semua orang tahu bahwa, dalam tabel yang menggunakan InnoDB sebagai mesin, kueri seperti SELECT COUNT(*) FROM mytablesangat tidak eksak dan sangat lambat, terutama ketika tabel semakin besar dan ada penyisipan / penghapusan baris konstan saat query dijalankan.

Seperti yang saya pahami, InnoDB tidak menyimpan jumlah baris dalam variabel internal, yang merupakan alasan untuk masalah ini.

Pertanyaan saya adalah: Mengapa demikian? Akankah sangat sulit untuk menyimpan informasi seperti itu? Ini informasi penting untuk diketahui dalam banyak situasi. Satu-satunya kesulitan yang saya lihat apakah penghitungan internal seperti itu akan dilaksanakan adalah ketika transaksi terlibat: jika transaksi tidak dikomit, apakah Anda menghitung baris yang dimasukkan atau tidak?

PS: Saya bukan ahli tentang DB, saya hanya seseorang yang memiliki MySQL sebagai hobi sederhana. Jadi jika saya hanya menanyakan sesuatu yang bodoh, jangan terlalu kritis: D.

Radu Murzea
sumber
6
Lambat ya. Tidak eksak, tidak. Ini lambat karena memberikan hasil yang tepat. Ketika Anda memiliki tabel baris 200M, dan mungkin banyak transaksi lain yang menyisipkan / menghapus ke dalam tabel yang sama, mungkin banyak baris per detik, pertanyaan lain adalah "Apakah Anda memerlukan nomor pastinya?"
ypercubeᵀᴹ
@ ypercube Saya tahu saya melihat beberapa kali di phpmyadmin beberapa nilai jumlah baris yang sangat tidak aktif. Plus, ada komentar di sana yang mengatakan sesuatu seperti "mungkin tidak akurat".
Radu Murzea
1
@RaduMurzea phpMyAdmin pengguna metode alternatif untuk menghitung jumlah tabel untuk tabel InnoDB untuk alasan kecepatan yang Anda ketahui. Di sinilah ketidakakuratan yang Anda sebutkan berperan. Permintaan aktual SELECT COUNT(*) FROM ...sangat tepat. Jika Anda mau, phpMyAdmin dapat dikonfigurasi untuk selalu menggunakan jumlah baris yang tepat dengan mengorbankan kecepatan. Info lebih lanjut: stackoverflow.com/questions/11926259/…
DOOManiac

Jawaban:

9

Saya setuju dengan @RemusRusanu (+1 untuk jawabannya)

SELECT COUNT(*) FROM mydb.mytabledi InnoDB berperilaku seperti seharusnya mesin penyimpanan transaksional. Bandingkan dengan MyISAM.

MyISAM

Jika mydb.mytableadalah tabel MyISAM, meluncurkan SELECT COUNT(*) FROM mydb.mytable;seperti menjalankan SELECT table_rows FROM information_schema.table WHERE table_schema = 'mydb' AND table_name = 'mytable';. Ini memicu pencarian cepat dari jumlah baris di header tabel MyISAM.

InnoDB

Jika mydb.mytabletabel InnoDB, Anda mendapatkan hodge-podge hal-hal yang terjadi. Anda memiliki MVCC yang sedang berlangsung, mengatur hal-hal berikut:

  • ib_logfile0 / ib_logfile1 (Redo Logs)
  • ibdata1
    • Batalkan Log
    • Kembalikan
    • Perubahan Kamus Data
  • Manajemen Buffer Pool
  • Isolasi Transaksi (4 jenis)
    • Baca Berulang
    • Baca Berkomitmen
    • Baca Tidak Berkomitmen
    • Serializable

Meminta InnoDB untuk menghitung tabel membutuhkan navigasi melalui hal-hal yang tidak menyenangkan ini. Faktanya, seseorang tidak pernah benar-benar tahu jika SELECT COUNT(*) from mydb.mytablemenghitung hanya bacaan berulang atau menyertakan bacaan yang telah dilakukan dan yang tidak berkomitmen.

Anda dapat mencoba menstabilkan hal-hal sedikit dengan mengaktifkan innodb_stats_on_metadata .

Menurut Dokumentasi MySQL di innodb_stats_on_meta_data

Ketika variabel ini diaktifkan (yang merupakan standar, seperti sebelum variabel dibuat), InnoDB memperbarui statistik selama pernyataan metadata seperti SHOW TABLE STATUS atau SHOW INDEX, atau ketika mengakses tabel INFORMATION_SCHEMA TABLES atau STATISTICS. (Pembaruan ini mirip dengan apa yang terjadi pada ANALYZE TABLE.) Ketika dinonaktifkan, InnoDB tidak memperbarui statistik selama operasi ini. Menonaktifkan variabel ini dapat meningkatkan kecepatan akses untuk skema yang memiliki sejumlah besar tabel atau indeks. Itu juga dapat meningkatkan stabilitas rencana eksekusi untuk kueri yang melibatkan tabel InnoDB.

Menonaktifkannya mungkin atau mungkin tidak memberi Anda hitungan yang lebih stabil dalam hal menyiapkan paket EXPLAIN. Ini dapat memengaruhi kinerja SELECT COUNT(*) from mydb.mytablebaik cara yang baik, cara yang buruk, atau tidak sama sekali. Cobalah dan Lihat !!!

RolandoMySQLDBA
sumber
16

Untuk pemula tidak ada yang namanya 'hitungan saat ini' untuk menyimpan dalam variabel. Permintaan seperti SELECT COUNT(*) FROM ...tunduk pada tingkat isolasi saat ini dan semua transaksi yang tertunda bersamaan. Bergantung pada tingkat isolasi, kueri dapat melihat atau tidak melihat baris dimasukkan atau dihapus dengan menunggu transaksi yang tidak dikomit. Satu-satunya cara untuk menjawab adalah dengan menghitung baris yang terlihat oleh transaksi saat ini.

Perhatikan bahwa saya bahkan tidak menyentuh subjek yang lebih sulit dari transaksi bersamaan yang dimulai atau berakhir selama penghitungan. Belum lagi kembalikan ...

Remus Rusanu
sumber
1
Ok, jadi itu tergantung pada tingkat isolasi, itu masuk akal. Tapi itu masih bisa diimplementasikan.
Radu Murzea
@ SoboLAN Ada banyak alasan mengapa tidak & tidak bisa, sebagian besar tercantum di atas. Apakah Anda menerapkannya dengan mempertahankan daftar jumlah per tabel per mulai transaksi (apa pun SCN Oracle di MySQL)? Mengelola penghitungan seperti itu akan menjadi overhead yang besar - pikirkan sebuah database dengan 100-an atau 1000-an sesi bersamaan yang masing-masing melakukan sejumlah besar INSERT / HAPUS pada tabel yang sama. Tidak mungkin dipertahankan.
Philᵀᴹ
Menerapkan ini cukup sulit. Bayangkan saja bahwa penghitungan harus dipertahankan dalam DB, itu berarti suatu tempat di dalam metadata, dan penghitungan ini harus dipertahankan oleh setiap transaksi yang memasukkan atau menghapus satu baris. Bagaimana Anda mengunci metadata itu? Dan bagaimana Anda menangani pengembalian uang? Jauh dari hal sepele. Dan hasilnya akan dapat digunakan untuk subset pertanyaan yang sangat sangat sempit.
Remus Rusanu
3
@JackDouglas Menarik. Dari apa yang saya lihat di COUNT(*)kueri sebelumnya jarang diperlukan dalam kenyataan & biasanya hasil dari pengalaman pengembang (hitung baris sebelum kita memilihnya!) Atau desain aplikasi yang buruk.
Philᵀᴹ
1
@ SoboLAN - tidak, tidak akan. Memiliki layanan yang memperbarui semacam tabel statistik pada interval waktu yang telah ditentukan jauh lebih baik. Bayangkan memiliki basis data besar dan beberapa administrator menanyakan sebagian besar tabel SELECT COUNT(*), tambahkan non-dioptimalkan WHEREke tabel dan Anda akan memiliki beberapa pengguna yang membawa db berlutut untuk beberapa penghitung stat yang bermanfaat.
NB
0

Meskipun secara teori dimungkinkan untuk menjaga penghitungan jumlah baris yang akurat untuk tabel tertentu dengan InnoDB, itu akan mengorbankan banyak penguncian, yang akan berdampak negatif pada kinerja. Itu juga akan berbeda berdasarkan tingkat isolasi.

MyISAM sudah melakukan penguncian tingkat tabel, jadi tidak ada biaya tambahan di sana.

Saya jarang memerlukan jumlah baris untuk sebuah tabel, meskipun saya menggunakan COUNT (*) sedikit. Saya biasanya memiliki klausa WHERE terlampir. Menggunakan indeks yang efisien pada set hasil yang kecil, saya menemukan bahwa mereka cukup cepat.

Saya tidak setuju bahwa penghitungan tidak akurat. Hitungannya mewakili snapshot data, dan saya selalu menganggapnya tepat.

Singkatnya, MySQL menyerahkan kepada Anda untuk mengimplementasikan ini untuk InnoDB. Anda bisa menyimpan hitungan dan menambah / mengurangi setelah setiap kueri. Padahal, solusi yang lebih mudah mungkin untuk beralih ke MyISAM.

Marcus Adams
sumber
2
Ini tidak mungkin untuk menjaga hitungan akurat dari baris dalam sistem transaksional. Karena ada banyak jumlah baris yang berbeda (dan benar) sebagai transaksi aktif.
a_horse_with_no_name
5
Saya memberi -1 di sini untuk 'Padahal, solusi yang lebih mudah mungkin adalah beralih ke MyISAM.' Saya tidak akan merekomendasikan beralih ke MyISAM hanya untuk mendapatkan jumlah baris.
Derek Downey
@a_horse_with_no_name, jadi Anda setuju bahwa akan ada jumlah baris yang "benar" untuk setiap transaksi. Tampaknya mungkin bagi saya.
Marcus Adams
1
@Dest, saya tidak pernah mengatakan "hanya untuk mendapatkan jumlah baris".
Marcus Adams
@a_horse_with_no_name, Itu sepertinya tidak benar. Tentunya kita hanya menghitung jumlah baris ketika transaksi dilakukan kan?
Pacerier