Kueri yang lambat, kueri yang tidak efisien, atau kueri yang berjalan lama adalah masalah yang sering mengganggu DBA. Mereka selalu ada di mana-mana, namun merupakan bagian tak terelakkan dari kehidupan bagi siapa pun yang bertanggung jawab untuk mengelola database.
Desain database yang buruk dapat memengaruhi efisiensi kueri dan kinerjanya. Kurangnya pengetahuan atau penggunaan yang tidak tepat dari panggilan fungsi, prosedur tersimpan, atau rutinitas juga dapat menyebabkan penurunan kinerja database dan bahkan dapat membahayakan seluruh cluster database MySQL.
Untuk replikasi master-slave, penyebab paling umum dari masalah ini adalah tabel yang tidak memiliki indeks primer atau sekunder. Hal ini menyebabkan lag budak yang dapat bertahan untuk waktu yang sangat lama (dalam skenario kasus yang lebih buruk).
Dalam blog seri dua bagian ini, kami akan memberi Anda kursus penyegaran tentang cara mengatasi memaksimalkan kueri database Anda di MySQL untuk mendorong efisiensi dan kinerja yang lebih baik.
Selalu Tambahkan Indeks Unik ke Tabel Anda
Tabel yang tidak memiliki kunci utama atau unik biasanya menimbulkan masalah besar saat data bertambah besar. Ketika ini terjadi, modifikasi data sederhana dapat menghentikan database. Kurangnya indeks yang tepat dan pernyataan UPDATE atau DELETE telah diterapkan ke tabel tertentu, pemindaian tabel lengkap akan dipilih sebagai rencana kueri oleh MySQL. Itu dapat menyebabkan I/O disk tinggi untuk membaca dan menulis dan menurunkan kinerja database Anda. Lihat contoh di bawah ini:
root[test]> show create table sbtest2\G
*************************** 1. row ***************************
Table: sbtest2
Create Table: CREATE TABLE `sbtest2` (
`id` int(10) unsigned NOT NULL,
`k` int(10) unsigned NOT NULL DEFAULT '0',
`c` char(120) NOT NULL DEFAULT '',
`pad` char(60) NOT NULL DEFAULT ''
) ENGINE=InnoDB DEFAULT CHARSET=latin1
1 row in set (0.00 sec)
root[test]> explain extended update sbtest2 set k=52, pad="xx234xh1jdkHdj234" where id=57;
+----+-------------+---------+------------+------+---------------+------+---------+------+---------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+------+---------+------+---------+----------+-------------+
| 1 | UPDATE | sbtest2 | NULL | ALL | NULL | NULL | NULL | NULL | 1923216 | 100.00 | Using where |
+----+-------------+---------+------------+------+---------------+------+---------+------+---------+----------+-------------+
1 row in set, 1 warning (0.06 sec)
Sedangkan tabel dengan kunci utama memiliki rencana kueri yang sangat baik,
root[test]> show create table sbtest3\G
*************************** 1. row ***************************
Table: sbtest3
Create Table: CREATE TABLE `sbtest3` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`k` int(10) unsigned NOT NULL DEFAULT '0',
`c` char(120) NOT NULL DEFAULT '',
`pad` char(60) NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
KEY `k` (`k`)
) ENGINE=InnoDB AUTO_INCREMENT=2097121 DEFAULT CHARSET=latin1
1 row in set (0.00 sec)
root[test]> explain extended update sbtest3 set k=52, pad="xx234xh1jdkHdj234" where id=57;
+----+-------------+---------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+
| 1 | UPDATE | sbtest3 | NULL | range | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | Using where |
+----+-------------+---------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
Kunci utama atau unik menyediakan komponen vital untuk struktur tabel karena ini sangat penting terutama saat melakukan pemeliharaan pada sebuah tabel. Misalnya, menggunakan alat dari Percona Toolkit (seperti pt-online-schema-change atau pt-table-sync) menyarankan Anda harus memiliki kunci unik. Perlu diingat bahwa PRIMARY KEY sudah menjadi kunci unik dan kunci utama tidak dapat menyimpan nilai NULL tetapi kunci unik. Menetapkan nilai NULL ke Kunci Utama dapat menyebabkan kesalahan seperti,
ERROR 1171 (42000): All parts of a PRIMARY KEY must be NOT NULL; if you need NULL in a key, use UNIQUE instead
Untuk node slave, juga umum bahwa dalam kesempatan tertentu, kunci utama/unik tidak ada di tabel yang oleh karena itu merupakan perbedaan dari struktur tabel. Anda dapat menggunakan mysqldiff untuk mencapai ini atau Anda dapat mysqldump --no-data … params dan menjalankan diff untuk membandingkan struktur tabelnya dan memeriksa apakah ada perbedaan.
Pindai Tabel Dengan Indeks Duplikat, Lalu Hapus
Indeks duplikat juga dapat menyebabkan penurunan kinerja, terutama jika tabel berisi sejumlah besar catatan. MySQL harus melakukan beberapa upaya untuk mengoptimalkan kueri dan melakukan lebih banyak rencana kueri untuk diperiksa. Ini mencakup pemindaian distribusi indeks besar atau statistik dan yang menambahkan overhead kinerja karena dapat menyebabkan pertentangan memori atau penggunaan memori I/O yang tinggi.
Degradasi untuk kueri ketika indeks duplikat diamati pada tabel juga atribut untuk menjenuhkan kumpulan buffer. Ini juga dapat mempengaruhi kinerja MySQL ketika pos pemeriksaan mem-flush log transaksi ke dalam disk. Ini karena pemrosesan dan penyimpanan indeks yang tidak diinginkan (yang sebenarnya membuang-buang ruang di tablespace tertentu dari tabel itu). Perhatikan bahwa indeks duplikat juga disimpan di tablespace yang juga harus disimpan di buffer pool.
Lihat tabel di bawah ini yang berisi beberapa kunci duplikat:
root[test]#> show create table sbtest3\G
*************************** 1. row ***************************
Table: sbtest3
Create Table: CREATE TABLE `sbtest3` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`k` int(10) unsigned NOT NULL DEFAULT '0',
`c` char(120) NOT NULL DEFAULT '',
`pad` char(60) NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
KEY `k` (`k`,`pad`,`c`),
KEY `kcp2` (`id`,`k`,`c`,`pad`),
KEY `kcp` (`k`,`c`,`pad`),
KEY `pck` (`pad`,`c`,`id`,`k`)
) ENGINE=InnoDB AUTO_INCREMENT=2048561 DEFAULT CHARSET=latin1
1 row in set (0.00 sec)
dan memiliki ukuran 2.3GiB
root[test]#> \! du -hs /var/lib/mysql/test/sbtest3.ibd
2.3G /var/lib/mysql/test/sbtest3.ibd
Mari kita lepaskan indeks duplikat dan bangun kembali tabel dengan perubahan tanpa operasi,
root[test]#> drop index kcp2 on sbtest3; drop index kcp on sbtest3 drop index pck on sbtest3;
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
root[test]#> alter table sbtest3 engine=innodb;
Query OK, 0 rows affected (28.23 sec)
Records: 0 Duplicates: 0 Warnings: 0
root[test]#> \! du -hs /var/lib/mysql/test/sbtest3.ibd
945M /var/lib/mysql/test/sbtest3.ibd
Telah mampu menghemat hingga ~59% dari ukuran lama ruang tabel yang sangat besar.
Untuk menentukan indeks duplikat, Anda dapat menggunakan pt-duplicate-checker untuk menangani pekerjaan untuk Anda.
Tune Up Buffer Pool Anda
Untuk bagian ini saya hanya mengacu pada mesin penyimpanan InnoDB.
Buffer pool adalah komponen penting dalam ruang kernel InnoDB. Di sinilah InnoDB menyimpan tabel dan indeks data saat diakses. Ini mempercepat pemrosesan karena data yang sering digunakan disimpan dalam memori secara efisien menggunakan BTREE. Misalnya, Jika Anda memiliki beberapa tabel yang terdiri dari>=100GiB dan banyak diakses, maka kami sarankan Anda mendelegasikan memori volatil cepat mulai dari ukuran 128GiB dan mulai menetapkan kumpulan buffer dengan 80% dari memori fisik. 80% harus dipantau secara efisien. Anda dapat menggunakan SHOW ENGINE INNODB STATUS \G atau Anda dapat memanfaatkan perangkat lunak pemantauan seperti ClusterControl yang menawarkan pemantauan terperinci yang mencakup kumpulan buffer dan metrik kesehatan yang relevan. Juga atur variabel innodb_buffer_pool_instances yang sesuai. Anda dapat mengatur ini lebih besar dari 8 (default jika innodb_buffer_pool_size>=1GiB), seperti 16, 24, 32, atau 64 atau lebih tinggi jika perlu.
Saat memantau kumpulan buffer, Anda perlu memeriksa variabel status global Innodb_buffer_pool_pages_free yang memberi Anda pemikiran jika ada kebutuhan untuk menyesuaikan kumpulan buffer, atau mungkin mempertimbangkan jika ada juga indeks yang tidak diinginkan atau duplikat yang menggunakan penyangga. SHOW ENGINE INNODB STATUS \G juga menawarkan aspek yang lebih detail dari informasi kumpulan buffer termasuk kumpulan buffer individualnya berdasarkan jumlah innodb_buffer_pool_instances yang telah Anda tetapkan.
Gunakan Indeks FULLTEXT (Tetapi Hanya Jika Berlaku)
Menggunakan kueri seperti,
SELECT bookid, page, context FROM books WHERE context like '%for dummies%';
di mana konteksnya adalah kolom tipe string (char, varchar, teks), adalah contoh kueri yang sangat buruk! Menarik konten besar catatan dengan filter yang harus serakah berakhir dengan pemindaian tabel penuh, dan itu gila. Pertimbangkan untuk menggunakan indeks FULLTEXT. Indeks FULLTEXT memiliki desain indeks terbalik. Indeks terbalik menyimpan daftar kata, dan untuk setiap kata, daftar dokumen tempat kata tersebut muncul. Untuk mendukung pencarian kedekatan, informasi posisi untuk setiap kata juga disimpan, sebagai offset byte.
Untuk menggunakan FULLTEXT untuk mencari atau memfilter data, Anda perlu menggunakan kombinasi sintaks MATCH() ...AGAINST dan tidak seperti kueri di atas. Tentu saja, Anda perlu menentukan bidang tersebut sebagai bidang indeks FULLTEXT Anda.
Untuk membuat indeks FULLTEXT, cukup tentukan dengan FULLTEXT sebagai indeks Anda. Lihat contoh di bawah ini:
root[minime]#> CREATE FULLTEXT INDEX aboutme_fts ON users_info(aboutme);
Query OK, 0 rows affected, 1 warning (0.49 sec)
Records: 0 Duplicates: 0 Warnings: 1
root[jbmrcd_date]#> show warnings;
+---------+------+--------------------------------------------------+
| Level | Code | Message |
+---------+------+--------------------------------------------------+
| Warning | 124 | InnoDB rebuilding table to add column FTS_DOC_ID |
+---------+------+--------------------------------------------------+
1 row in set (0.00 sec)
Meskipun menggunakan indeks FULLTEXT dapat menawarkan manfaat saat mencari kata dalam konteks yang sangat besar di dalam kolom, ini juga menimbulkan masalah jika digunakan secara tidak benar.
Saat melakukan pencarian FULLTEXT untuk tabel besar yang terus-menerus diakses (di mana sejumlah permintaan klien mencari kata kunci yang berbeda dan unik), hal itu bisa sangat membutuhkan CPU.
Ada kesempatan tertentu juga bahwa FULLTEXT tidak berlaku. Lihat posting blog eksternal ini. Meskipun saya belum mencoba ini dengan 8.0, saya tidak melihat ada perubahan yang relevan dengan ini. Kami menyarankan agar tidak menggunakan FULLTEXT untuk mencari lingkungan data besar, terutama untuk tabel dengan lalu lintas tinggi. Jika tidak, coba manfaatkan teknologi lain seperti Apache Lucene, Apache Solr, tsearch2, atau Sphinx.
Hindari Menggunakan NULL di Kolom
Kolom yang berisi nilai null benar-benar baik-baik saja di MySQL. Tetapi jika Anda menggunakan kolom dengan nilai nol ke dalam indeks, ini dapat memengaruhi kinerja kueri karena pengoptimal tidak dapat menyediakan rencana kueri yang tepat karena distribusi indeks yang buruk. Namun, ada cara tertentu untuk mengoptimalkan kueri yang melibatkan nilai nol tetapi tentu saja, jika ini sesuai dengan persyaratan. Silakan periksa dokumentasi MySQL tentang Null Optimization. Anda juga dapat memeriksa pos eksternal ini yang juga bermanfaat.
Desain Topologi Skema dan Struktur Tabel Anda Secara Efisien
Sampai batas tertentu, menormalkan tabel database Anda dari 1NF (Bentuk Normal Pertama) ke 3NF (Bentuk Normal Ketiga) memberi Anda beberapa manfaat untuk efisiensi kueri karena tabel yang dinormalisasi cenderung menghindari catatan yang berlebihan. Perencanaan dan desain yang tepat untuk tabel Anda sangat penting karena ini adalah cara Anda mengambil atau menarik data dan dalam setiap tindakan ini ada biayanya. Dengan tabel yang dinormalisasi, tujuan database adalah untuk memastikan bahwa setiap kolom non-kunci di setiap tabel secara langsung bergantung pada kunci; seluruh kunci dan tidak ada apa-apa selain kuncinya. Jika tujuan ini tercapai, ini akan memberikan manfaat dalam bentuk pengurangan redundansi, lebih sedikit anomali, dan peningkatan efisiensi.
Menormalkan tabel memiliki banyak manfaat, bukan berarti Anda perlu menormalkan semua tabel dengan cara ini. Anda dapat mengimplementasikan desain untuk database Anda menggunakan Star Schema. Mendesain tabel Anda menggunakan Skema Bintang memiliki manfaat kueri yang lebih sederhana (menghindari gabungan silang yang rumit), mudah mengambil data untuk pelaporan, menawarkan peningkatan kinerja karena tidak perlu menggunakan gabungan atau gabungan kompleks, atau agregasi cepat. Skema Bintang mudah diterapkan, tetapi Anda perlu merencanakan dengan hati-hati karena dapat menimbulkan masalah dan kerugian besar ketika meja Anda menjadi lebih besar dan memerlukan pemeliharaan. Skema Bintang (dan tabel yang mendasarinya) rentan terhadap masalah integritas data, jadi Anda mungkin memiliki kemungkinan besar bahwa banyak data Anda berlebihan. Jika menurut Anda tabel ini harus konstan (struktur dan desain) dan dirancang untuk memanfaatkan efisiensi kueri, maka ini adalah kasus yang ideal untuk pendekatan ini.
Menggabungkan desain basis data Anda (selama Anda dapat menentukan dan mengidentifikasi jenis data apa yang harus ditarik pada tabel Anda) sangat penting karena Anda dapat memperoleh manfaat dengan kueri yang lebih efisien dan juga bantu DBA dengan pencadangan, pemeliharaan, dan pemulihan.
Singkirkan Data Konstan dan Lama
Baru-baru ini kami menulis beberapa Praktik Terbaik untuk Mengarsipkan Basis Data Anda di Cloud. Ini mencakup tentang bagaimana Anda dapat memanfaatkan pengarsipan data sebelum masuk ke cloud. Jadi, bagaimana cara menyingkirkan data lama atau mengarsipkan data konstan dan lama Anda membantu efisiensi kueri? Seperti yang sudah saya sampaikan di blog saya sebelumnya, ada keuntungan untuk tabel yang lebih besar yang terus-menerus dimodifikasi dan disisipkan dengan data baru, tablespace dapat berkembang dengan cepat. MySQL dan InnoDB bekerja secara efisien ketika catatan atau data berdekatan satu sama lain dan memiliki arti penting untuk baris berikutnya dalam tabel. Artinya, jika Anda tidak memiliki catatan lama yang tidak lagi perlu digunakan, maka pengoptimal tidak perlu memasukkannya ke dalam statistik yang menawarkan hasil yang jauh lebih efisien. Masuk akal, bukan? Dan juga, efisiensi query tidak hanya pada sisi aplikasi, tetapi juga perlu mempertimbangkan efisiensinya saat melakukan backup dan saat maintenance atau failover. Misalnya, jika Anda memiliki kueri yang buruk dan panjang yang dapat memengaruhi periode pemeliharaan atau kegagalan, itu bisa menjadi masalah.
Aktifkan Logging Kueri Sesuai Kebutuhan
Selalu atur log kueri lambat MySQL Anda sesuai dengan kebutuhan khusus Anda. Jika Anda menggunakan Server Percona, Anda dapat memanfaatkan logging kueri lambat yang diperpanjang. Ini memungkinkan Anda untuk mendefinisikan variabel tertentu secara biasa. Anda dapat memfilter jenis kueri dalam kombinasi seperti full_scan, full_join, tmp_table, dll. Anda juga dapat mendikte kecepatan log kueri lambat melalui variabel log_slow_rate_type, dan banyak lainnya.
Pentingnya mengaktifkan logging kueri di MySQL (seperti kueri lambat) bermanfaat untuk memeriksa kueri Anda sehingga Anda dapat mengoptimalkan atau menyetel MySQL Anda dengan menyesuaikan variabel tertentu yang sesuai dengan kebutuhan Anda. Untuk mengaktifkan log kueri lambat, pastikan bahwa variabel ini telah diatur:
- long_query_time - tetapkan nilai yang tepat untuk berapa lama waktu yang dibutuhkan kueri. Jika kueri memakan waktu lebih dari 10 detik (default), kueri akan jatuh ke file log kueri lambat yang Anda tetapkan.
- slow_query_log - untuk mengaktifkannya, setel ke 1.
- slow_query_log_file - ini adalah jalur tujuan untuk file log kueri lambat Anda.
Log kueri lambat sangat membantu untuk analisis kueri dan mendiagnosis kueri buruk yang menyebabkan kemacetan, penundaan budak, kueri yang berjalan lama, memori atau CPU intensif, atau bahkan menyebabkan server mogok. Jika Anda menggunakan pt-query-digest atau pt-index-usage, gunakan file log kueri lambat sebagai target sumber Anda untuk melaporkan kueri ini secara serupa.
Kesimpulan
Kami telah membahas beberapa cara yang dapat Anda gunakan untuk memaksimalkan efisiensi kueri basis data di blog ini. Di bagian selanjutnya kita akan membahas lebih banyak faktor yang dapat membantu Anda memaksimalkan kinerja. Tetap disini!