Lý do đôi khi truy vấn chậm?


16

Chúng tôi đang chạy MySQL 5.1 trên Windows Server 2008 R2.

Chúng tôi đã thực hiện một số chẩn đoán trên cơ sở dữ liệu muộn và đã tìm thấy một số hiện vật đáng lo ngại mà chúng tôi không thể giải thích . Chúng tôi đã thêm một số mã để đăng nhập khi chúng tôi có các truy vấn mất nhiều thời gian (> 2000ms). Kết quả thật đáng ngạc nhiên (và có thể là một lời giải thích cho những bế tắc của chúng tôi).

Thỉnh thoảng truy vấn, thường mất rất ít thời gian (<10ms), mất từ ​​4 đến 13 giây. Để rõ ràng, đây là những truy vấn đang chạy liên tục (vài lần một giây) và không bị các đột biến thời gian truy vấn này.

Chúng tôi đã trải qua các chỉ số của mình để tìm kiếm bất kỳ sai lầm rõ ràng và không gặp nhiều may mắn.

Cập nhật

Bảng người:

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

Chỉ mục:

+--------+------------+------------------+--------------+------------------+-----------+-------------+----------+--------+------+------------+---------+
| 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      |         |
+--------+------------+------------------+--------------+------------------+-----------+-------------+----------+--------+------+------------+---------+

chúng tôi có ~ 5000 hàng trong bảng trên máy chủ gây rắc rối cho chúng tôi.


1
Có một cái gì đó bạn chưa thể hiện trong hai câu hỏi trước. Vui lòng thêm vào câu hỏi này ba (3) bài thi: 1) HIỂN THỊ TẠO BẢNG người \ G 2) HIỂN THỊ CHỈ SỐ TỪ người; 3) CHỌN QUỐC GIA (1) TỪ người;
RolandoMySQLDBA

@RolandoMySQLDBA Tôi sẽ làm điều đó ngay khi tôi đi làm vào ngày mai. Chúc mừng :)
RedBlueTìm

Tôi cập nhật câu trả lời của tôi. Xin vui lòng đọc !!!
RolandoMySQLDBA

@RolandoMySQLDBA Cảm ơn :). Vẫn phân tích thứ này. Tôi sẽ cho bạn biết làm thế nào chúng ta đi.
RedBlueThing

Câu trả lời:


14

Các truy vấn CẬP NHẬT trong hai câu hỏi trước của bạn ( Câu hỏi 1 , Câu hỏi 2 ) đang đánh vào bảng 'người' bằng khóa CHÍNH HÃNG với khóa cấp hàng. Đây là những gì tôi đã nêu lại trong Câu hỏi 1 vào ngày 6 tháng 6 năm 2011 10:03 sáng

Tất cả các giao dịch đang đi qua khóa CHÍNH. Vì PRIMARY là một chỉ mục được nhóm trong InnoDB, khóa PRIMARY và chính hàng được đặt cùng nhau. Do đó, đi qua một hàng và KEY PRIMary là một và giống nhau. Do đó, bất kỳ khóa chỉ mục nào trên KEY PRIMARY cũng là khóa cấp hàng.

Một cái gì đó khác chưa được xem xét có thể quy kết chậm cho các chỉ mục: Việc sử dụng các chỉ mục NON-UNIQUE trong InnoDB. Mỗi tra cứu được lập chỉ mục trong InnoDB bằng cách sử dụng các chỉ mục không duy nhất cũng có hàngID của mỗi hàng được gắn vào khóa không duy nhất. RowID về cơ bản phát ra từ Chỉ mục cụm . Cập nhật các chỉ mục không duy nhất PHẢI LUÔN LUÔN tương tác với chỉ mục được nhóm NGAY LẬP TỨC NẾU BẢNG KHÔNG CÓ KHÓA CHÍNH.

Một điều khác cần suy nghĩ là quá trình quản lý các nút BTREE trong một chỉ mục. Đôi khi, nó yêu cầu chia trang của các nút. Tất cả các mục trong nút BTREE của các chỉ mục không duy nhất chứa các trường không duy nhất PLUS các rowID trong chỉ mục được nhóm. Để giảm thiểu chính xác việc chia các trang BTREE như vậy mà không làm ảnh hưởng đến tính toàn vẹn dữ liệu, hàng được liên kết với rowID phải trải qua khóa cấp hàng trong nội bộ.

Nếu bảng 'người' có nhiều chỉ mục không duy nhất, hãy chuẩn bị để có một số lượng lớn các trang chỉ mục trong không gian bảng cũng như thỉnh thoảng có các hàng nhỏ xíu lén lút theo dõi bạn.

Có một yếu tố khác không rõ ràng: Dân số chủ chốt

Đôi khi, khi một chỉ mục được điền, các giá trị chính tạo nên các chỉ mục có thể bị mất dần theo thời gian và khiến Trình tối ưu hóa truy vấn MySQL chuyển từ tra cứu có khóa, sang quét chỉ mục và cuối cùng là quét toàn bộ bảng. Rằng bạn không thể kiểm soát trừ khi bạn thiết kế lại bảng với các chỉ mục mới để bù cho các phím ot bị lệch. Vui lòng cung cấp cấu trúc bảng cho bảng 'người', số lượng bảng 'người' và đầu ra chỉ mục hiển thị cho bảng 'người' .

Ngay cả khi các truy vấn chỉ sử dụng KHÓA CHÍNH, sự thiếu sót của các khóa trong các chỉ mục không duy nhất vẫn cần cân bằng BTREE và phân tách trang xảy ra. Việc quản lý BTREE như vậy sẽ tạo ra sự chậm lại đáng chú ý do các khóa cấp hàng không liên tục mà bạn không có ý định xảy ra.

CẬP NHẬT 2011-06-14 22:19

Truy vấn từ câu hỏi 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

Hình ảnh trình tự trong các sự kiện

  1. Tìm hàng theo KEY PRIMARY
  2. Khóa hàng và chỉ mục cụm
  3. Tạo dữ liệu MVCC cho tất cả các cột đang được cập nhật
  4. Bốn cột được lập chỉ mục (email, company_id, iphone_device_id, image_blob_id)
  5. Mỗi chỉ số yêu cầu quản lý BTREE
  6. Trong cùng một không gian giao dịch, các bước 1-5 đang cố gắng được lặp lại trên cùng một hàng, cập nhật các cột giống nhau (gửi email giống nhau trong cả hai truy vấn, company_id giống nhau trong cả hai truy vấn, hình ảnh_blob_id giống nhau trong cả hai truy vấn, iphone_device_id khác nhau)

Truy vấn từ câu hỏi 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;

Hai truy vấn này thậm chí còn khó hiểu hơn vì truy vấn đầu tiên đang cập nhật mọi thứ trừ people_id 666. Hàng trăm hàng đang bị khóa một cách đau đớn chỉ bằng truy vấn đầu tiên. Truy vấn thứ hai đang cập nhật people_id 666 chạy 5 chuỗi sự kiện. Truy vấn đầu tiên đang chạy 5 chuỗi sự kiện tương tự trên mỗi hàng liên quan ngoại trừ people_id 666 nhưng chỉ mục cho iphone_device_id nằm trong một khóa học liên tục với hai truy vấn khác nhau. Ai đó phải khóa trong các trang BTREE trên cơ sở đầu tiên đến trước phục vụ trước.

Đối mặt với hai cặp truy vấn này trong khóa học xung đột để có thể khóa các trang BTREE giống nhau trong một chỉ mục có thể là một trải nghiệm đau đớn cho InnoDB hoặc bất kỳ RDBMS tuân thủ ACID nào. Do đó, làm chậm chỉ mục là số mệnh của các cặp truy vấn này trừ khi bạn có thể đảm bảo rằng các truy vấn chạy với AUTOCOMMIT = 1 hoặc bằng cách cho phép đọc bẩn (mặc dù các va chạm như thế này khiến READ-CAM KẾT và READ-UNCOMMITED trở thành cơn ác mộng đối với MVCC).

CẬP NHẬT 2011-06-15 10:29

@RedBlueThing: Trong các truy vấn từ câu hỏi 2, truy vấn đầu tiên là một truy vấn phạm vi, do đó, rất nhiều khóa hàng đang được đạt được. Cũng lưu ý rằng cả hai truy vấn đang cố gắng khóa cùng một không gian id 0 trang không có 4611 n bit 152 đang bị khóa trong PRIMARY KEY, còn gọi là chỉ mục được nhóm.

Để đảm bảo ứng dụng của bạn, ít nhất, chạy dựa trên chuỗi sự kiện bạn mong đợi, có hai tùy chọn khác nhau bạn có thể thử:

Tùy chọn 1) Chuyển đổi bảng này sang MyISAM (ít nhất là trên máy chủ phát triển). Mỗi CẬP NHẬT, XÁC NHẬN và XÓA sẽ áp đặt khóa bảng đầy đủ trên cơ sở đến trước phục vụ trước.

Tùy chọn 2) Thử sử dụng mức cách ly SERIALIZABLE . Điều đó sẽ khóa tất cả các hàng dự định trong chế độ CHIA SẺ.

Chuỗi sự kiện bạn mong đợi sẽ phá vỡ hoặc thành công khi sử dụng hai tùy chọn thay thế này. Nếu cả hai tùy chọn này đều thất bại, thì bạn sẽ cần xem qua ứng dụng của mình và ưu tiên thứ tự thực hiện các truy vấn của bạn. Khi bạn thiết lập mức ưu tiên đó, bạn có thể hoàn tác các tùy chọn này (Đối với tùy chọn 1, quay lại InnoDB, Đối với tùy chọn 2, quay lại mức cô lập mặc định [ngừng sử dụng SERIALIZABLE]).


@RolandoMySQLDBA Tôi đã cập nhật câu hỏi của chúng tôi với các chi tiết bạn yêu cầu.
RedBlueThing

@RolandoMySQLDBA Cảm ơn bạn đã xem xét lại điều này. Tôi đã tự hỏi, bạn nhận xét cho câu hỏi 2, tại sao truy vấn đầu tiên sẽ khóa hàng trăm hàng? Nó sẽ không chỉ khóa 666 hàng không khớp với id thiết bị chứ? (tức là một hàng đơn)
RedBlueThing

@RolandoMySQLDBA Dựa trên đề xuất của bạn từ Câu hỏi 1, chúng tôi đã kiểm tra cài đặt tự động của chúng tôi và xác nhận rằng nó đã được bật.
RedBlueT thở

@RolandoMySQLDBA Có vấn đề cụ thể với các truy vấn từ câu hỏi đầu tiên (ngoài việc cập nhật tất cả các trường trong hàng). Một cái gì đó sẽ giải thích thời gian thực hiện 13 giây cho truy vấn? Tôi có cảm giác rằng việc lập chỉ mục bốn cột không phải là thứ bạn muốn giới thiệu, nhưng điều này có thực sự dẫn đến hiệu suất kém như vậy không?
RedBlueT thở

@RolandoMySQLDBA +1 và cảm ơn tất cả các đề xuất của bạn. Chúng tôi đã không kết thúc việc thay đổi mức cô lập để giải quyết vấn đề. Thay vào đó, chúng tôi đã thực hiện cập nhật một phần cho câu hỏi 2 và tối ưu hóa một truy vấn trong đường dẫn cập nhật. Voila! không còn bế tắc. :)
RedBlueThing

3

HIỂN THỊ BIỂU TƯỢNG THÍCH 'innodb%'; - Đặc biệt, nếu dữ liệu và chỉ mục chưa đạt đến kích thước của vùng đệm, bạn có thể đánh đĩa mạnh hơn trước rất nhiều. I / O là kẻ giết người hiệu suất lớn.

Hầu hết các lĩnh vực của bạn là lớn gấp đôi khi cần thiết. BIGINT (8 byte) là cách quá mức cần thiết cho hầu hết các id. 5000 hàng chỉ cần một SMALLINT KHÔNG ĐƯỢC KÝ (giới hạn 65K, chỉ có 2 byte). Hoặc sử dụng MEDIUMINT cho một biên độ an toàn.

NHÂN ĐÔI cung cấp cho bạn 16 chữ số có nghĩa với chi phí 8 byte. Pin_level có nhiều hơn 2 chữ số chính xác không? FLOAT mất 4 byte.

Quan điểm của tôi ở đây là "nhỏ hơn -> dễ nhớ hơn -> nhanh hơn".

Vui lòng cho chúng tôi xem các truy vấn chậm; ít nhất một vài trong số đó đột nhiên trở nên chậm hơn. Chúng tôi chỉ có thể đoán mà không có chúng. Bật Slowlog và đặt long_query_time = 1; những điều này sẽ giúp tìm ra các truy vấn chậm nhất.

Bạn có hiểu lợi ích của chỉ số "hợp chất" không?

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.