Perbedaan antara SQL yang ditulis dengan baik dan tidak sangat luas, dan dalam produksi di situs dengan request yang tinggi menyebabkan dampak serius pada kinerja dan keandalan layanan. Dalam panduan ini saya akan membahas cara menulis kueri yang cepat dan faktor apa yang menyebabkannya berjalan lambat. Show
Mengapa MySQL?Hari ini ada banyak pembicaraan tentang Big Data dan teknologi baru. NoSQL dan solusi berbasis cloud sangat bagus, tetapi banyak software web populer (seperti WordPress, phpBB, Drupal, software Forum VBulletin, dll.) yang masih berjalan di MySQL. Migrasi ke solusi baru ini mungkin tidak sesederhana hanya dengan mengoptimalkan konfigurasi yang sudah kamu miliki di produksi. Selain itu, kinerja MySQL sangat bagus, terutama versi Percona. Jangan membuat kesalahan umum dengan membuang lebih banyak kekuatan komputasi saat berurusan dengan masalah kueri yang lambat dan beban server yang tinggi, daripada benar-benar mengatasi akar masalah yang mendasarinya. Menambahkan daya CPU, SSD, atau RAM adalah bentuk pengoptimalan jika kamu sukai, tetapi bukan itu yang akan saya bicarakan di sini. Selain itu, tanpa situs yang dioptimalkan, saat kamu tumbuh dengan perangkat keras, masalah akan berlipat ganda secara eksponensial. Jadi itu bukan solusi jangka panjang yang solid. Menjadi baik di SQL selalu merupakan alat vital bagi pengembang web, dan dengan memperbaiki sering sesederhana hanya dengan menambahkan indeks atau sedikit memodifikasi cara tabel digunakan, itu benar-benar membantu untuk mengetahui cara menggunakan RDBMS-mu dengan baik. Dalam hal ini kita berfokus pada database open source yang sering digunakan bersama dengan PHP, dan itu adalah MySQL. Untuk Siapa Panduan Ini?Web Developer, Database Architect / DBA dan System Administrator yang terbiasa dengan MySQL. Jika kamu tidak terbiasa dengan MySQL sebagai pemula, maka panduan ini kemungkinan besar tidak akan masuk akal, tapi saya akan mencoba untuk tetap informatif sebisa mungkin untuk pendatang baru ke MySQL. Backup Terlebih DuluSaya sarankan untuk mencoba langkah-langkah yang disediakan pada database MySQL-mu sendiri (backup semuanya terlebih dahulu!). Jika kamu tidak memiliki basis data untuk dikerjakan, contoh buat skema basis data yang disediakan jika berlaku. Melakukan backup MySQL dapat dengan mudah melalui utilitas baris
perintah $ mysqldump myTable > myTable-backup.sql Kamu dapat mempelajari lebih lanjut tentang mysqldump. Apa yang Membuat Kueri Lambat?Secara singkat dan tanpa urutan kepentingan, berikut ini semua yang memainkan faktor penting dalam kinerja kueri dan server:
Kita akan membahas semua area ini dalam panduan ini. Juga, jika kamu belum menggunakannya, silakan menginstal Percona, yang merupakan pengganti drop-in untuk MySQL yang akan membawa peningkatan kinerja yang serius. Untuk melihat tolok ukur Percona vs. MySQL, lihat perbandingan ini. Apa Itu Indeks?Indeks digunakan oleh MySQL untuk menemukan baris dengan nilai kolom tertentu
dengan cepat, misalnya di dalam Jika tabel memiliki indeks untuk kolom yang dimaksud, MySQL dapat dengan cepat menentukan posisi untuk mencari di tengah file data tanpa harus melihat semua data. Ini jauh lebih cepat daripada membaca setiap baris secara berurutan. Koneksi Yang Tidak Persisten?Ketika bahasa scripting-mu terhubung dengan database, jika kamu telah mengkonfigurasi koneksi persisten maka ia akan dapat menggunakan kembali koneksi yang ada tanpa harus membuat yang baru. Ini optimal untuk penggunaan produksi dan harus diaktifkan. Pengguna PHP dapat membaca lebih lanjut di PHP Manual. Mengurangi Frekuensi Permintaan Secara BersamaanCara tercepat dan paling efektif yang
saya temukan untuk memperbaikinya adalah dengan memanfaatkan sepasang pasangan nilai-kunci seperti Dengan <?php $cache = new Memcache; $cache->connect('localhost',11211); $cacheResult = $cache->get('key-name'); if($cacheResult){ //.. no need to query $result = $cacheResult; } else { //.. run your query $mysqli = mysqli('p:localhost','username','password','table'); //prepend p: to hostname for persistancy $sql = 'SELECT * FROM posts LEFT JOIN userInfo using (UID) WHERE posts.post_type = 'post' || posts.post_type = 'article' ORDER BY column LIMIT 50'; $result = $mysqli->query($sql); $memc->set('key-name', $result->fetch_array(), MEMCACHE_COMPRESSED,86400); } //Pass the $cacheResult to template $template->assign('posts', $cacheResult); ?> Sekarang contoh kueri Catatan: Tambahkan Pemecahan / PengelompokkanKetika datamu menjadi besar atau permintaan untuk layananmu meningkat, kepanikan dapat diatur. Perbaikan cepat untuk memastikan layananmu tetap online dapat menjadi pemecahan. Tapi saya tidak merekomendasikan itu, karena pemecahan secara inheren tampaknya membuat struktur data terlalu rumit. Dan seperti yang dijelaskan dengan sangat fasih dalam artikel ini dari blog Percona, jangan memecah. Desain Tabel Yang BurukMembuat skema basis data tidak terlalu sulit ketika kamu menerima beberapa aturan yang sangat baik, seperti bekerja dengan batasan dan menyadari apa yang akan efisien. Menyimpan gambar dalam database sebagai tipe data Memaastikan bahwa desainnya benar untuk penggunaan yang diperlukan sangat penting dalam membuat aplikasimu. Tetap memisahkan data tertentu (misalnya kategori dan pos) dan memastikan hubungan many-to-one atau one-to-many dapat dengan mudah dikaitkan dengan ID. Memanfaatkan fasilitas Saat membangun tabelmu, coba ingat yang berikut:
Dasar-dasar OptimasiAgar dapat mengoptimalkan secara efektif, kita harus melihat tiga kumpulan data mendasar terkait aplikasimu:
Analisis dapat dilakukan dengan beberapa cara. Pertama-tama, kita akan mengambil rute yang paling langsung untuk mencari di bawah kapak kueri MySQL. Tool pertama dalam toolbox optimasimu adalah mysql> EXPLAIN SELECT * FROM `wp_posts` WHERE `post_type`='post'; +----+-------------+----------+------+------------------+------------------+---------+-------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+----------+------+------------------+------------------+---------+-------+------+-------------+ | 1 | SIMPLE | wp_posts | ref | type_status_date | type_status_date | 82 | const | 2 | Using where | +----+-------------+----------+------+------------------+------------------+---------+-------+------+-------------+ 1 row in set (0.00 sec) Kolom yang tercantum masing-masing menyimpan informasi yang berguna
tentang kueri yang sedang dijalankan. Kolom yang perlu kamu perhatikan adalah
Kolom EXPLAIN SELECT main_text FROM posts WHERE user = 'myUsername' && status = '1' && ( status_spam_user = 'no_spam' || ( status_spam_user = 'neutral' && status_spam_system = 'neutral' ) ) ORDER BY datum DESC LIMIT 6430 , 10 Jenis
kueri ini bisa masuk ke disk karena ada kondisi di mana, yang terjadi jika kita melihat pada id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE posts ref index_user,index_status index_user 32 const 7800 Using where; Using filesort Jadi kueri ini memiliki kemungkinan untuk menggunakan dua indeks dan saat ini sedang memukul disk karena Apa yang
Terusan ekstra ini akan memperlambat aplikasimu dan harus dihindari di semua biaya. Hasil Untuk memperbaiki masalah dengan Indeks PetunjukPengoptimal MySQL akan menggunakan statistik berdasarkan tabel kueri untuk memilih indeks terbaik pada cakupan kueri. Ia melakukannya berdasarkan logika statistik pengoptimalnya yang ada di dalamnya, meskipun dengan beberapa pilihan ini tidak selalu benar tanpa memberi petunjuk. Untuk memastikan kunci yang benar digunakan (atau tidak digunakan), gunakan
kata kunci Untuk melihat kunci-kunci tabel, gunakan perintah Kamu dapat menentukan beberapa petunjuk untuk pengoptimal agar digunakan, misalnya: SELECT * FROM table1 USE INDEX (col1_index,col2_index) WHERE col1=1 AND col2=2 AND col3=3; ``` Running an `EXPLAIN` will show you which index was used in the final outcome. So to fix the previous example we will add the `USE INDEX` as so: ```sql EXPLAIN SELECT main_text FROM posts USE INDEX (index_user) WHERE user = 'myUsername' && status = '1' && ( status_spam_user = 'no_spam' || ( status_spam_user = 'neutral' && status_spam_system = 'neutral' ) ) ORDER BY datum DESC LIMIT 6430 , 10 Sekarang MySQL memiliki id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE posts ref index_user,index_status index_user 32 const 7800 Using where Bersama menjelaskan
adalah kata kunci yang menggambarkan. Dengan mysql> DESCRIBE City; +------------+----------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +------------+----------+------+-----+---------+----------------+ | Id | int(11) | NO | PRI | NULL | auto_increment | | Name | char(35) | NO | | | | | Country | char(3) | NO | UNI | | | | District | char(20) | YES | MUL | | | | Population | int(11) | NO | | 0 | | +------------+----------+------+-----+---------+----------------+ Menambahkan IndeksKamu membuat indeks di MySQL dengan sintaks Untuk menambahkan indeks ke tabelmu, gunakan sintaks berikut ini misalnya: mysql> CREATE INDEX idx_start_of_username ON `users` (username(10)); Ini akan membuat indeks pada tabel Dalam hal ini, pencarian apa pun yang membutuhkan Indeks KompositIndeks memiliki efek besar pada kecepatan yang diperlukan untuk mengembalikan data kueri. Hanya menetapkan primary key dan indeks unique umumnya tidak cukup—kunci komposit adalah tempat di mana tuning reluk
yang sebenarnya terletak di MySQL, dan paling sering ini memerlukan beberapa pemeriksaan A/B dengan Sebagai contoh, jika kita perlu mereferensikan dua kolom di dalam mysql> CREATE INDEX idx_composite ON users (username, active); Di sini kunci ini dibuat pada kolom Seberapa Cepatkah MySQL Mu?Aktifkan profiling untuk melihat lebih dekat pada query MySQL-mu. Ini dapat dilakukan pada waktu proses melalui Dengan PDO, ini adalah cuplikan kode yang tidak hanya itu: $db->query('set profiling=1'); $db->query('select headline, body, tags from posts'); $rs = $db->query('show profiles'); $db->query('set profiling=0'); // Disable profiling after the query has been run $records = $rs->fetchAll(PDO::FETCH_ASSOC); // Get the results from profiling $errmsg = $rs->errorInfo()[2]; //Catch any errors here Jika kamu tidak menggunakan PDO, hal yang sama dapat dilakukan dengan $db = new mysqli($host,$username,$password,$dbname); $db->query('set profiling=1'); $db->query('select headline, body, tags from posts'); if ($result = $db->query("SHOW profiles", MYSQLI_USE_RESULT)) { while ($row = $result->fetch_row()) { var_dump($row); } $result->close(); } if ($result = $db->query("show profile for query 1", MYSQLI_USE_RESULT)) { while ($row = $result->fetch_row()) { var_dump($row); } $result->close(); } $db->query('set profiling=0'); Ini akan mengembalikan kepadamu data profil, yang akan mencakup waktu eksekusi dalam nilai kedua dari array asosiatif: array(3) { [0]=> string(1) "1" [1]=> string(10) "0.00024300" [2]=> string(17) "select headline, body, tags from posts" } Kueri membutuhkan waktu 0.00024300 detik untuk dapat selesai. Itu cukup cepat untuk tidak perlu khawatir. Tetapi ketika jumlah meningkat, kita harus melihat lebih dalam. Sebagai contoh kerja, kenali aplikasimu. Tempatkan cek untuk konstanta Sepenuhnya Mengaudit AplikasimuUntuk melakukan audit penuh atas kuerimu, aktifkan pencatatan log. Beberapa pengembang saya telah bekerja dengan khawatir bahwa ini adalah masalah dua sisi yang memungkinkan pencatatan log sedikit mempengaruhi kinerja, dan statistik yang kamu rekam akan sedikit lebih rendah daripada dalam kenyataan. Meskipun ini benar, banyak patokan menunjukkan itu tidak terlalu banyak perbedaan. Untuk mengaktifkan logging di MySQL versi 5.1.6, kamu menggunakan global set global log_slow_queries = 1; set global slow_query_log_file = /dev/slow_query.log; Kamu dapat mengatur ini secara tetap di file konfigurasi log_slow_queries = 1; slow_query_log_file = /dev/slow_query.log; Setelah melakukan perubahan ini, kamu harus me-restart server MySQL,
misalnya. Di MySQL 5.6.1 yang lebih baru, log_output = TABLE; log_queries_not_using_indexes = 1; long_query_time = 1
Ini akan masuk ke tabel Untuk menonaktifkan pencatatan log, set
Pilihan ini tidak selalu berarti tidak ada indeks yang digunakan. Misalnya, saat kueri menggunakan pemindaian indeks lengkap, ini akan dicatat karena indeks tidak akan membatasi jumlah baris. Pencatatan Log di Produksi?Mengaktifkan pencatatan di situs produksi dengan lalu lintas akan cukup banyak harus dilakukan dalam waktu singkat, sambil memantau beban untuk memastikannya tidak memengaruhi layanan. Jika kamu berada di bawah beban berat dan memerlukan perbaikan mendesak, mulailah dengan mengatasi
masalah pada prompt dengan Mencatat semua kueri dalam produksi dapat memberitahumu banyak hal dan merupakan praktik yang baik untuk tujuan penelitian ketika kamu mengaudit proyek, tetapi membiarkannya berjalan dengan sering selama berhari-hari tidak akan memberimu data yang lebih bermanfaat daripada paling lama 48 jam ( rata-rata, paling tidak menangkap waktu puncak penggunaan agar dapat melihat dengan baik kuerinya dan mendapatkan beberapa ide frekuensi). Catatan: jika kamu menjalankan situs yang mengalami lonjakan lalu lintas puncak dan kemudian periode tidak banyak sama sekali (seperti situs web olahraga selama dan di luar musim), logislah dengan caramu melihat pencatatan log. Jangan anggap situs ini bekerja dengan cepat. Lakukan audit dan yang paling penting mengatur beberapa grafik. Logging dan Percona’s pt-query-digestPercona memiliki beberapa tool hebat yang digabungkan dengannya,
dan Kamu dapat menggunakan Menganalisis file *.log (dikeluarkan dari pencatatan log kuerimu yang lambat misalnya): $ pt-query-digest slow.log Laporkan kueri yang paling lambat dari host1 secara real time (sangat berguna!): $ pt-query-digest --processlist h=host1 Gunakan tcpdump untuk melaporkan kueri yang paling lambat dari data protokol MySQL: $ tcpdump -s 65535 -x -nn -q -tttt -i any -c 1000 port 3306 > mysql.tcp.txt $ pt-query-digest --type tcpdump mysql.tcp.txt Akhirnya kita dapat menyimpan data kueri yang lambat dari satu host ke yang lain untuk ditinjau nantinya. Di sini kita menyimpan intisari kueri untuk slow.log ke host2: $ pt-query-digest --review h=host2 --no-report slow.log Untuk mempelajari cara menggunakan tool Penggrafikan Kinerja MySQL dan ServerGrafik InnoDB Row Operations ini menunjukkan operasi baris InnoDB yang telah dilakukan: pembaruan, membaca, menghapus dan menyisipkan. Ini adalah topik yang besar dan saya akan cukup menyentuhnya di panduan ini untuk memulai dengan pemantauan MySQL. Penting untuk dicatat secara umum, bagaimanapun, bahwa pemantauan semua layanan situs web-mu sangat ideal untuk benar-benar mengetahui apa kinerja dan penggunaanmu. Untuk mencapai ini, saya sarankan menyiapkan solusi berbasis Setelah kamu menyiapkan Cacti dan dapat mulai menganalisis aplikasimu, biarkan beberapa waktu berlalu sehingga grafik dapat meningkat. Setelah beberapa hari kamu akan mulai melihat irama siang dan malam lalu lintasmu dan melihat seberapa server benar-benar sibuk. Jika kamu mencari peringatan dan pemicu otomatis, lihat untuk mengonfigurasi monit, monitor proaktif open-source untuk sistem Unix. Dengan monit kamu dapat membuat aturan untuk servermu dan memastikanmu diberitahu ketika beban naik sehingga kamu dapat menangkapnya ketika itu terjadi. Log Kueri Yang LambatMencatat semua kueri yang lambat membutuhkan waktu lebih dari satu detik untuk menyelesaikan dapat memberi tahu kita sesuatu, tetapi juga mengetahui pertanyaan mana yang mengeksekusi ratusan kali sama pentingnya. Bahkan jika permintaan tersebut singkat untuk dieksekusi, biaya overhead permintaan tinggi masih mengambil korban di server. Itulah mengapa tetap di sekitar ketika kamu memperbarui sesuatu dan menayangkannya secara langsung adalah waktu yang paling penting untuk setiap pekerjaan dan perubahan basis data baru. Kita selalu memiliki kebijakan di tim saya untuk tidak pernah menyinkronkan perubahan basis data fitur baru setelah hari Rabu pada proyek langsung. Itu harus dilakukan pada awal minggu, selambat-lambatnya Selasa, sehingga semua tim dapat memantau dan memberikan dukungan yang sesuai. Sebelum menayangkan dengan kueri baru, kamu harus membuat tolok ukur dengan tool pengujian beban seperti Untuk mengukur dengan #centos users $ sudo yum install ab #debian / ubuntu users $ sudo apt-get install ab Sekarang kamu dapat memulai dengan menguji aplikasimu, misalnya: $ ab -k -c 350 -n 20000 my-domain.com/
Jadi dengan menjalankan perintah di atas, kamu akan menekan http://my-domain.com/ dengan 350 koneksi simultan hingga 20.000 permintaan terpenuhi, dan ini akan dilakukan menggunakan header keep-alive. Setelah proses menyelesaikan 20.000 permintaan, kamu akan menerima umpan balik tentang statistik. Ini akan memberi tahumu seberapa baik situs dilakukan di bawah tekanan yang kamu masukkan saat menggunakan parameter di atas. Ini adalah cara yang baik untuk mengetahui dalam arti otomatis jika kuerimu telah mengubah apa pun. Benchmarking Hot vs. ColdJumlah permintaan dan beban server memiliki dampak besar pada kinerja, dan waktu kueri dapat terpengaruh karena hal ini. Dalam semua kamu harus mengaktifkan log kueri yang lambat untuk menangkap ini dalam produksi, dan sebagai aturan untuk pengembanganmu harus memastikan semua pertanyaan mengeksekusi dalam pecahan milidetik (0.0xx atau lebih cepat) pada server yang menganggur. Menerapkan Untuk menghindari keluar ke produksi dengan cache kosong, skrip pre-loader adalah cara yang baik untuk memastikan cache akan dibaca dan kamu tidak akan mendapatkan banyak sekali permintaan semua datang sekaligus ketika kembali dari downtime karena kegagalan kapasitas berlebih. Memperbaiki Query Yang LambatJadi setelah mengaktifkan pencatatan, kamu sekarang menemukan beberapa kueri yang lambat di aplikasimu. Mari memperbaikinya! Sebagai contoh, saya akan menunjukkan berbagai masalah umum yang akan kamu hadapi dan logika untuk memperbaikinya. Jika kamu belum menemukan kueri yang lambat, mungkin periksa apa setelanmu di mana untuk Masalah UmumBerikut ini enam masalah umum yang saya hadapi ketika mengoptimalkan query MySQL: 1. |