Alasan terkadang permintaan lambat?

16

Kami menjalankan MySQL 5.1 pada Windows Server 2008 R2.

Kami telah melakukan beberapa diagnosa pada database kami akhir-akhir ini dan telah menemukan beberapa artefak mengganggu yang tidak dapat kami jelaskan . Kami menambahkan beberapa kode untuk dicatat ketika kami memiliki pertanyaan yang membutuhkan waktu lama (> 2000 ms). Hasilnya mengejutkan (dan mungkin penjelasan untuk kebuntuan kami).

Kadang-kadang pertanyaan, yang biasanya memakan waktu sangat sedikit (<10 ms), membutuhkan 4 hingga 13 detik. Agar jelas, ini adalah kueri yang berjalan terus-menerus (beberapa kali per detik) dan tidak menderita lonjakan waktu kueri ini.

Kami telah melalui indeks kami untuk mencari kesalahan yang jelas dan tidak memiliki banyak keberuntungan.

Memperbarui

Tabel orang:

| people | CREATE TABLE `people` (
`people_id` bigint(20) NOT NULL AUTO_INCREMENT,
`company_id` bigint(20) NOT NULL,
`name` varchar(255) DEFAULT NULL,
`password` varchar(255) DEFAULT NULL,
`temp_password` varchar(10) DEFAULT NULL,
`reset_password_hash` varchar(255) DEFAULT NULL,
`email` varchar(255) DEFAULT NULL,
`phone` varchar(32) DEFAULT NULL,
`mobile` varchar(32) DEFAULT NULL,
`iphone_device_id` varchar(160) DEFAULT NULL,
`iphone_device_time` datetime DEFAULT NULL,
`last_checkin` datetime DEFAULT NULL,
`location_lat` double DEFAULT NULL,
`location_long` double DEFAULT NULL,
`gps_strength` smallint(6) DEFAULT NULL,
`picture_blob_id` bigint(20) DEFAULT NULL,
`authority` int(11) NOT NULL DEFAULT '0',
`active` tinyint(1) NOT NULL DEFAULT '1',
`date_created` datetime NOT NULL,
`last_login` datetime NOT NULL,
`panic_mode` tinyint(1) NOT NULL DEFAULT '0',
`battery_level` double DEFAULT NULL,
`battery_state` varchar(32) DEFAULT NULL,
PRIMARY KEY (`people_id`),
KEY `email` (`email`),
KEY `company_id` (`company_id`),
KEY `iphone_device_id` (`iphone_device_id`),
KEY `picture_blob_id` (`picture_blob_id`),
CONSTRAINT `people_ibfk_1` FOREIGN KEY (`company_id`) REFERENCES `companies` (`company_id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `people_ibfk_2` FOREIGN KEY (`picture_blob_id`) REFERENCES `blobs` (`blob_id`) ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=4658 DEFAULT CHARSET=utf8 |

Indeks:

+--------+------------+------------------+--------------+------------------+-----------+-------------+----------+--------+------+------------+---------+
| Table  | Non_unique | Key_name         | Seq_in_index | Column_name      | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+--------+------------+------------------+--------------+------------------+-----------+-------------+----------+--------+------+------------+---------+
| people |          0 | PRIMARY          |            1 | people_id        | A         |        3502 |     NULL | NULL   |      | BTREE      |         |
| people |          1 | email            |            1 | email            | A         |        3502 |     NULL | NULL   | YES  | BTREE      |         |
| people |          1 | company_id       |            1 | company_id       | A         |        3502 |     NULL | NULL   |      | BTREE      |         |
| people |          1 | iphone_device_id |            1 | iphone_device_id | A         |        3502 |     NULL | NULL   | YES  | BTREE      |         |
| people |          1 | picture_blob_id  |            1 | picture_blob_id  | A         |        3502 |     NULL | NULL   | YES  | BTREE      |         |
+--------+------------+------------------+--------------+------------------+-----------+-------------+----------+--------+------+------------+---------+

kami memiliki ~ 5.000 baris dalam tabel di server yang memberi kami masalah.

RedBlueThing
sumber
1
Ada sesuatu yang belum Anda tunjukkan dalam dua pertanyaan sebelumnya. Silakan tambahkan ke pertanyaan ini tiga (3) thiings: 1) SHOW CREATE TABLE people \ G 2) SHOW INDEXES FROM people; 3) PILIH COUNT (1) DARI orang;
RolandoMySQLDBA
@RolandoMySQLDBA Saya akan melakukan itu segera setelah saya mulai bekerja besok. Cheers :)
RedBlueThing
Saya memperbarui jawaban saya. Silakan baca !!!
RolandoMySQLDBA
@RolandoMySQLDBA Terima kasih :). Masih mengurai hal ini. Saya akan memberi tahu Anda bagaimana kami melangkah.
RedBlueThing

Jawaban:

14

Kueri UPDATE dalam dua pertanyaan Anda sebelumnya ( Pertanyaan1 , Pertanyaan2 ) mengenai tabel 'orang' oleh PRIMARY KEY dengan penguncian level baris. Inilah yang saya nyatakan kembali di Pertanyaan1 pada 6 Juni 2011 10:03

Semua transaksi melintasi kunci UTAMA. Karena PRIMARY adalah indeks berkerumun di InnoDB, kunci PRIMARY dan baris itu sendiri bersama-sama. Dengan demikian, melintasi baris dan dan KUNCI UTAMA adalah satu dan sama. Oleh karena itu, setiap kunci indeks pada KUNCI UTAMA juga merupakan kunci tingkat baris.

Sesuatu yang lain belum dipertimbangkan yang dapat menghubungkan kelambatan ke indeks: Penggunaan indeks NON-UNIK di InnoDB. Setiap pencarian yang diindeks di InnoDB menggunakan indeks tidak unik juga memiliki rowID setiap baris yang dilampirkan pada kunci tidak unik. RowID pada dasarnya berasal dari Clustered Index . Memperbarui indeks non-unik HARUS SELALU berinteraksi dengan indeks berkerumun BAHKAN JIKA TABEL TIDAK PUNYA KUNCI UTAMA.

Hal lain yang perlu dipikirkan adalah proses mengelola node BTREE dalam indeks. Kadang-kadang, itu membutuhkan pemisahan halaman node. Semua entri dalam node BTREE indeks non-unik berisi bidang non-unik DITAMBAH rowID dalam indeks berkerumun. Untuk mengurangi pemisahan halaman BTREE tersebut tanpa mengganggu integritas data, baris yang terkait dengan rowID harus mengalami kunci tingkat baris secara internal.

Jika tabel 'people' memiliki banyak indeks non-unik, bersiaplah untuk memiliki sejumlah besar halaman indeks di tablespace serta memiliki kunci baris kecil kecil menyelinap pada Anda dari waktu ke waktu.

Ada faktor lain yang tidak begitu jelas: Populasi Kunci

Kadang-kadang ketika suatu indeks diisi, nilai-nilai kunci yang menyusun indeks dapat menjadi miring dari waktu ke waktu dan menyebabkan MySQL Query Optimizer untuk beralih dari pencarian kunci, untuk mengindeks scan, dan akhirnya ke scan tabel penuh. Bahwa Anda tidak dapat mengontrol kecuali Anda mendesain ulang tabel dengan indeks baru untuk mengimbangi kunci miring. Harap berikan struktur tabel untuk tabel 'orang', jumlah tabel 'orang', dan output indeks acara untuk tabel 'orang' .

Sekalipun kueri hanya menggunakan KUNCI UTAMA, ketidakseimbangan kunci dalam indeks non-unik masih membutuhkan penyeimbangan BTREE dan pemisahan halaman agar terjadi. Manajemen BTREE tersebut akan menghasilkan pelambatan penting karena penguncian level baris terputus-putus yang tidak Anda inginkan.

UPDATE 2011-06-14 22:19

Pertanyaan Dari Pertanyaan 1

UPDATE people SET company_id = 1610, name = '<name>', password = '<hash>',
temp_password = NULL, reset_password_hash = NULL, email = '<redacted>@yahoo.com',
phone = NULL, mobile = '<phone>', iphone_device_id = 'android:<id>-<id>',
iphone_device_time = '2011-06-06 05:35:09', last_checkin = '2011-06-06 05:24:42',
location_lat = <lat>, location_long = -<lng>, gps_strength = 3296,
picture_blob_id = 1190,
authority = 1, active = 1, date_created = '2011-04-13 20:21:20',
last_login = '2011-06-06 05:35:09', panic_mode = 0,
battery_level = NULL, battery_state = NULL WHERE people_id = 3125

UPDATE people SET company_id = 1610, name = '<name>', password = '<hash>',
temp_password = NULL, reset_password_hash = NULL, email = '<redacted>@yahoo.com',
phone = NULL, mobile = '<phone>', iphone_device_id = 'android:<id>-<id>-<id>-<id>',
iphone_device_time = '2011-06-06 05:24:42', last_checkin = '2011-06-06 05:35:07',
location_lat = <lat>, location_long = -<lng>, gps_strength = 3296,
picture_blob_id = 1190,
authority = 1, active = 1, date_created = '2011-04-13 20:21:20',
last_login = '2011-06-06 05:35:09', panic_mode = 0,
battery_level = NULL, battery_state = NULL WHERE people_id = 3125

Bayangkan urutan dalam peristiwa

  1. Temukan baris dengan KUNCI UTAMA
  2. Kunci baris dan indeks berkerumun
  3. Buat Data MVCC untuk semua kolom yang diperbarui
  4. Empat kolom diindeks (email, company_id, iphone_device_id, picture_blob_id)
  5. Setiap indeks membutuhkan manajemen BTREE
  6. Dalam ruang transaksi yang sama, langkah 1-5 mencoba diulangi pada baris yang sama, memperbarui kolom yang sama (email sama di kedua kueri, company_id sama di kedua kueri, picture_blob_id sama di kedua kueri, iphone_device_id berbeda)

Pertanyaan Dari Pertanyaan 2

UPDATE people SET iphone_device_id=NULL
WHERE iphone_device_id='iphone:<device_id_blah>' AND people_id<>666;

UPDATE people SET company_id = 444, name = 'Dad', password = '<pass>',
temp_password = NULL, reset_password_hash = NULL, email = '<redacted>@gmail.com',
phone = NULL, mobile = NULL, iphone_device_id = 'iphone:<device_id_blah>',
iphone_device_time = '2011-06-06 19:12:29', last_checkin = '2011-06-07 02:49:47',
location_lat = <lat>, location_long = <lng>, gps_strength = 66,
picture_blob_id = 1661,
authority = 1, active = 1, date_created = '2011-03-20 19:18:34',
last_login = '2011-06-07 11:15:01', panic_mode = 0, battery_level = 0.55,
battery_state = 'unplugged' WHERE people_id = 666;

Dua kueri ini bahkan lebih membingungkan karena kueri pertama memperbarui semuanya kecuali people_id 666. Ratusan baris dikunci dengan menyakitkan hanya dengan kueri pertama. Kueri kedua memperbarui people_id 666 menjalankan 5 urutan peristiwa. Kueri pertama menjalankan 5 urutan kejadian yang sama di setiap baris yang terlibat kecuali people_id 666 tetapi indeks untuk iphone_device_id berada di jalur interecept dengan dua kueri yang berbeda. Seseorang harus mengunci halaman BTREE dengan dasar siapa cepat dia dapat.

Menghadapi dua pasang kueri ini di jalur tabrakan untuk mungkin mengunci halaman BTREE yang sama dalam satu indeks bisa menjadi pengalaman yang menyayat hati untuk InnoDB atau RDBMS yang sesuai dengan ACID. Dengan demikian, pelambatan indeks adalah takdir dari pasangan kueri ini kecuali jika Anda dapat menjamin bahwa kueri dijalankan dengan AUTOCOMMIT = 1 atau dengan memungkinkan pembacaan kotor (meskipun tabrakan seperti ini membuat READ-COMMITTED dan READ-UNCOMMITED menjadi mimpi buruk untuk MVCC).

UPDATE 2011-06-15 10:29

@RedBlueThing: Dalam kueri dari pertanyaan 2, kueri pertama adalah kueri rentang, jadi banyak kunci baris diperoleh. Juga perhatikan kedua query mencoba untuk mengunci ruang id yang sama 0 halaman no 4611 n bit 152 sedang dikunci dalam KUNCI UTAMA, alias indeks berkerumun.

Untuk memastikan bahwa aplikasi Anda, paling tidak, berjalan berdasarkan rangkaian acara yang Anda harapkan, ada dua opsi yang bisa Anda coba:

Opsi 1) Konversi tabel ini ke MyISAM (setidaknya di server pengembangan). Setiap UPDATE, INSERT, dan DELETE akan memaksakan kunci tabel penuh atas dasar pertama datang, pertama dilayani.

Opsi 2) Coba gunakan tingkat isolasi SERIALIZABLE . Itu akan mengunci semua baris yang dimaksud dalam mode SHARED.

Urutan peristiwa yang Anda harapkan akan pecah atau berhasil menggunakan dua opsi alternatif ini. Jika kedua opsi ini gagal, maka Anda perlu memeriksa aplikasi Anda dan memprioritaskan urutan eksekusi permintaan Anda. Setelah Anda menetapkan prioritas itu, Anda dapat dengan mudah membatalkan opsi-opsi ini (Untuk opsi 1, kembali ke InnoDB, Untuk opsi 2, kembali ke tingkat isolasi default [berhenti menggunakan SERIALIZABLE]).

RolandoMySQLDBA
sumber
@RolandoMySQLDBA Saya memperbarui pertanyaan kami dengan detail yang Anda minta.
RedBlueThing
@RolandoMySQLDBA Terima kasih telah melihat ini lagi. Saya ingin tahu, Anda berkomentar untuk pertanyaan 2, mengapa kueri pertama mengunci ratusan baris? Bukankah hanya mengunci 666 baris yang cocok dengan id perangkat? (Yaitu satu baris)
RedBlueThing
@RolandoMySQLDBA Berdasarkan saran Anda dari Pertanyaan 1, kami memeriksa pengaturan autocommit kami dan mengonfirmasi bahwa ini telah dihidupkan.
RedBlueThing
@RolandoMySQLDBA Apakah ada masalah khusus dengan pertanyaan dari pertanyaan pertama (selain memperbarui semua bidang di baris). Sesuatu yang akan menjelaskan waktu eksekusi 13 detik untuk kueri? Saya mendapatkan pengertian bahwa pengindeksan empat kolom bukanlah sesuatu yang akan Anda rekomendasikan, tetapi apakah ini benar-benar menghasilkan kinerja yang buruk?
RedBlueThing
@RolandoMySQLDBA +1 dan terima kasih atas semua saran Anda. Kami tidak akhirnya mengubah level isolasi untuk menyelesaikan masalah. Alih-alih, kami melakukan pembaruan lapangan sebagian untuk pertanyaan 2 & mengoptimalkan kueri di jalur pembaruan. Voila! tidak ada lagi kebuntuan. :)
RedBlueThing
3

TAMPILKAN VARIABEL SEPERTI 'innodb%'; - Secara khusus, jika data dan indeks tidak mencapai ukuran buffer pool, Anda bisa memukul hard disk lebih keras dari sebelumnya. I / O adalah pembunuh kinerja besar.

Sebagian besar bidang Anda dua kali lebih besar dari yang dibutuhkan. BIGINT (8 byte) adalah cara yang berlebihan bagi sebagian besar id. 5000 baris hanya membutuhkan tanda SMALLINT UNSIGNED (batas 65K, hanya 2 byte). Atau gunakan MEDIUMINT untuk batas keamanan.

DOUBLE memberi Anda 16 digit signifikan dengan biaya 8 byte. Apakah battery_level memiliki presisi lebih dari 2 digit? FLOAT membutuhkan 4 byte.

Maksud saya di sini adalah bahwa "lebih kecil -> lebih mudah disimpan -> lebih cepat".

Tolong tunjukkan kepada kami pertanyaan lambat; setidaknya beberapa dari mereka yang tiba-tiba menjadi lebih lambat. Kita hanya bisa membuat tebakan tanpa itu. Nyalakan slowlog dan atur long_query_time = 1; ini akan membantu menemukan kueri paling lambat.

Apakah Anda memahami manfaat dari indeks "gabungan"?

Rick James
sumber