Lập chỉ mục MySQL VarChar


10

Tôi đang cố gắng lập chỉ mục blogentriescơ sở dữ liệu của mình để có hiệu suất tốt hơn nhưng tìm thấy một vấn đề.

Đây là cấu trúc:

CREATE TABLE IF NOT EXISTS `blogentries` (
  `id_id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `title_id` varchar(100) COLLATE latin1_german2_ci NOT NULL,
  `entry_id` varchar(5000) COLLATE latin1_german2_ci NOT NULL,
  `date_id` int(11) NOT NULL,
  PRIMARY KEY (`id_id`)
)
ENGINE=MyISAM
DEFAULT CHARSET=latin1
COLLATE=latin1_german2_ci
AUTO_INCREMENT=271;

Một truy vấn như sau sử dụng đúng chỉ mục:

EXPLAIN SELECT id_id,title_id FROM blogentries ORDER by id_id DESC
+ ---- + ------------- + ------------- + ------- + -------- ------- + --------- + --------- + ------ + ------ + -------- ----- +
| id | chọn_type | bàn | loại | có thể_key | chìa khóa | key_len | tham khảo | hàng | Thêm |
+ ---- + ------------- + ------------- + ------- + -------- ------- + --------- + --------- + ------ + ------ + -------- ----- +
| 1 | ĐƠN GIẢN | blog | chỉ số | NULL | CHÍNH HÃNG | 114 | NULL | 126 | Sử dụng chỉ mục |
+ ---- + ------------- + ------------- + ------- + -------- ------- + --------- + --------- + ------ + ------ + -------- ----- +

Tuy nhiên, khi tôi thêm entry_idvào SELECTtruy vấn nó sử dụng filesort

EXPLAIN SELECT id_id,title_id,entry_id FROM blogentries ORDER by id_id DESC
+ ---- + ------------- + ------------- + ------ + --------- ------ + ------ + --------- + ------ + ------ + ------------ ---- +
| id | chọn_type | bàn | loại | có thể_key | chìa khóa | key_len | tham khảo | hàng | Thêm |
+ ---- + ------------- + ------------- + ------ + --------- ------ + ------ + --------- + ------ + ------ + ------------ ---- +
| 1 | ĐƠN GIẢN | blog | TẤT CẢ | NULL | NULL | NULL | NULL | 126 | Sử dụng fileort |
+ ---- + ------------- + ------------- + ------ + --------- ------ + ------ + --------- + ------ + ------ + ------------ ---- +

Tôi đã tự hỏi tại sao điều này xảy ra và làm thế nào tôi có thể tránh nó? Có phải là do VarChar, và điều đó nên được thay đổi thành một cái gì đó khác?

Tôi cố gắng để có tất cả các truy vấn của tôi sử dụng các chỉ số như tôi đang chạy vào cao Handler_read_rndHandler_read_rnd_nextgiá trị.

Nếu bạn cần bất kỳ thông tin khác, tôi cũng có thể gửi nó.


filesort có nghĩa là nó đang thực hiện sắp xếp trên đĩa.
Kermit

Hãy thử thêm WHERE 1=1vào truy vấn thứ hai của bạn.
Kermit

Phiên bản nào của MySQL là đây? Kích thước bộ đệm sắp xếp của bạn ( SELECT @@sort_buffer_size) là gì?

@njk filesort là kết quả của phần 'ĐẶT HÀNG THEO' của truy vấn

1
@TashPemhiwa Không nhất thiết, hãy xem tuyên bố đầu tiên.
Kermit

Câu trả lời:


6

Vì bạn không có WHEREmệnh đề trong một trong hai truy vấn, nên bạn trả về tất cả các hàng trong cả hai trường hợp, vì vậy tôi nghĩ rằng việc sử dụng hoặc không sử dụng chỉ mục sẽ có rất ít ảnh hưởng đến hiệu suất trong các ví dụ này.


Chắc chắn MySQL nên sử dụng chỉ mục cho ORDER BY?
eggyal

@eggyal Không nếu nó quá lớn cho bộ nhớ.
Kermit

@njk: Điều đó không có ý nghĩa ... nó có thể đi qua chỉ mục, theo thứ tự, mà không cần phải tải toàn bộ vào bộ nhớ. Kết quả sẽ được sắp xếp mà không cần thực hiện fileort.
eggyal

@eggyal Tôi sẽ hỏi kích thước của varchar(5000).
Kermit

@njk: Nhưng cột đó không nằm trong chỉ mục và cũng không được sử dụng trong sắp xếp.
eggyal

2

Như tài liệu dưới ORDER BYTối ưu hóa :

Đối với các truy vấn chậm filesortkhông được sử dụng, hãy thử hạ max_length_for_sort_dataxuống một giá trị phù hợp để kích hoạt a filesort.

Trong bài viết trên blog của mình Chính xác là read_rnd_buffer_size , Peter Zaitsev giải thích:

Đối với tôi điều này có nghĩa là vì MySQL 4.1 tùy chọn này được sử dụng trong phạm vi hẹp - nếu bạn truy xuất một vài trường (ít hơn max_length_for_sort_data ) thì nên lưu trữ dữ liệu trong bộ đệm sắp xếp và sắp xếp tệp để không cần đọc read_rnd_buffer, nếu các cột được chọn dài nên chúng dài hơn max_length_for_sort_data, điều đó thường có nghĩa là có một số cột TEXT / BLOB trong số chúng. Tuy nhiên, nó sẽ được sử dụng nếu có số lượng cột lớn hoặc có các cột VARCHAR dài được sử dụng - chỉ mất vài UTF8 VARCHAR (255) để tạo một hàng dài hơn max_length_for_sort_data trong bản trình bày tĩnh của nó.

Điều này cho thấy rằng đó max_length_for_sort_datalà một giới hạn về tổng kích thước của các cột mà người ta đang chọn, trên đó một cột filesortsẽ được sử dụng thay vì sắp xếp dựa trên chỉ mục.

Trong trường hợp của bạn, việc chọn entry_id(5002 byte) sẽ lấy tổng kích thước trên giá trị mặc định 1KiB của biến này và do đó filesortđược sử dụng. Để tăng giới hạn lên 8KiB, bạn có thể làm:

SET SESSION max_length_for_sort_data = 8192;

Tôi có một bảng có thiết lập rất giống với bảng này và cài đặt này không xuất hiện để kích hoạt bất kỳ thay đổi nào trong việc sử dụng tệp.

@muffinista: Thật thú vị. Tôi cho rằng nó có thể liên quan đến một số cài đặt bộ đệm khác, theo câu trả lời của @ RolandoMySQLDBA ?
eggyal

2

Bạn đã nhận được rất nhiều câu trả lời thú vị ở đây, nhưng không ai trả lời chính xác câu hỏi - tại sao điều này lại xảy ra? Theo tôi hiểu, khi một truy vấn CHỌN chứa dữ liệu độ dài thay đổi trong MySQL và không có chỉ mục nào khớp với TẤT CẢ các cột được yêu cầu, nó sẽ luôn sử dụng một tệp. Kích thước của dữ liệu không liên quan khủng khiếp ở đây. Thật khó để tìm câu trả lời trực tiếp cho câu hỏi này trong tài liệu MySQL, nhưng đây là một bài đăng blog hay , nơi ai đó đang gặp vấn đề rất giống với bạn.

Xem thêm: 10 mẹo để tối ưu hóa các truy vấn MySQL (Điều đó không tệ) .

Vì vậy, nếu khả năng có một chỉ mục trên entry_id, thì bạn có thể thêm nó và được thiết lập. Nhưng tôi nghi ngờ rằng đó là một lựa chọn, vậy phải làm sao?

Cho dù bạn nên làm bất cứ điều gì về điều này là một câu hỏi riêng biệt. Điều quan trọng cần biết là 'filesort' được đặt tên kém trong MySQL - thực sự chỉ là tên của thuật toán được sử dụng để sắp xếp truy vấn cụ thể này và trong nhiều trường hợp, việc sắp xếp sẽ thực sự xảy ra trong bộ nhớ. Nếu bạn không mong đợi bảng này sẽ phát triển nhiều, nó có thể không phải là một vấn đề lớn.

Mặt khác, nếu bảng này sẽ có một triệu hàng trong đó, bạn có thể gặp vấn đề. Nếu bạn cần hỗ trợ phân trang các truy vấn trên bảng này, thì bạn có thể có một vấn đề hiệu suất thực sự nghiêm trọng ở đây. Trong trường hợp đó, phân vùng dữ liệu có độ dài thay đổi của bạn vào một bảng mới và thực hiện THAM GIA để truy xuất nó là một tối ưu hóa hợp lệ để xem xét.

Đây là một vài câu trả lời khác về SO nói về câu hỏi này:


Truy vấn đầu tiên của OP " chứa dữ liệu độ dài thay đổi trong MySQL và không có chỉ mục nào khớp với TẤT CẢ các cột được yêu cầu ", nhưng filesortdường như không được sử dụng trong trường hợp đó. Tôi cũng nghĩ rằng ngay cả việc sắp xếp một bảng nhỏ trong bộ nhớ cũng có thể chứng minh là một hiệu năng không thể chấp nhận được: ví dụ: nếu truy vấn được thực hiện nhiều (và bảng thay đổi để không thể sử dụng bộ đệm).
eggyal

Tôi không có thời gian để kiểm tra nó, nhưng tôi tự hỏi liệu điều này được kích hoạt bằng cách có VARCHAR yêu cầu 2 byte để lưu trữ độ dài như được chỉ định trong dev.mysql.com/doc/refman/5.1/en/char. html - vì vậy truy vấn đầu tiên phù hợp với giới hạn đó nhưng truy vấn thứ hai thì không.

0

Hãy thử thêm một WHEREmệnh đề vào các truy vấn của bạn.

Chỉ mục có thể được sử dụng ngay cả khi ORDER BY không khớp chính xác với chỉ mục, miễn là tất cả các phần không được sử dụng của chỉ mục và tất cả các cột ORDER BY bổ sung là các hằng số trong mệnh đề WHERE . Trong một số trường hợp, MySQL không thể sử dụng các chỉ mục để giải quyết ORDER BY , mặc dù nó vẫn sử dụng các chỉ mục để tìm các hàng khớp với mệnh đề WHERE .

http://dev.mysql.com/doc/refman/5.0/en/order-by-optimization.html


Nhưng trong trường hợp ORDER BY này không khớp chính xác với chỉ số, do đó không cần phải có WHEREmệnh đề.
eggyal

Tôi có một mệnh đề "where" trong truy vấn thực tế trên trang web, vì vậy tôi biết rằng đó không phải là nguyên nhân của việc sắp xếp tệp. Tôi đang tự hỏi nếu nó sử dụng varchar?

0

Trong phạm vi kiến ​​thức của tôi, varchar chỉ có thể chứa tối đa 8000 byte, tương đương 4000 ký tự. Do đó, 5000 dường như sẽ vượt quá giới hạn lưu trữ, và trong trường hợp này có lẽ là lý do tại sao việc sắp xếp đang bị rối tung.

"varchar [(n | max)] Dữ liệu ký tự không biến đổi, có độ dài biến đổi. n có thể là giá trị từ 1 đến 8.000. max cho biết kích thước lưu trữ tối đa là 2 ^ 31-1 byte. Kích thước lưu trữ là thực tế chiều dài của dữ liệu được nhập + 2 byte. Dữ liệu được nhập có thể có độ dài bằng 0 ký tự. Các từ đồng nghĩa SQL-2003 cho varchar là char khác nhau hoặc thay đổi ký tự. "

Hy vọng điều này trả lời câu hỏi của bạn


Như được ghi lại trong The CHARand VARCHARType : " Các giá trị trong các cột VARCHAR là các chuỗi có độ dài thay đổi. Độ dài có thể được chỉ định là một giá trị từ 0 đến 255 trước MySQL 5.0.3 và 0 đến 65.535 trong các phiên bản 5.0.3 trở lên. độ dài tối đa của VARCHARMySQL 5.0.3 trở lên tùy thuộc vào kích thước hàng tối đa (65.535 byte, được chia sẻ giữa tất cả các cột) và bộ ký tự được sử dụng. "
eggyal

0

Bạn chỉ có 126 hàng trong bảng của bạn. Ngay cả khi mỗi hàng có kích thước tối đa khoảng 5KB, điều đó có nghĩa là tổng kích thước để đọc từ đĩa chỉ khoảng 600KB - đây không phải là toàn bộ. Thành thật mà nói, nó là một lượng rất nhỏ, có thể ít hơn kích thước bộ đệm của hầu hết các ổ đĩa hiện đại.

Bây giờ, nếu máy chủ cần lấy dữ liệu của bạn để thực hiện truy vấn của bạn, hoạt động tốn kém nhất là đọc nó từ đĩa. Nhưng, đọc nó theo thứ tự chỉ mục KHÔNG phải luôn là cách nhanh nhất để làm điều đó, đặc biệt là khi lượng dữ liệu quá nhỏ.

Trong trường hợp của bạn, sẽ hiệu quả hơn nhiều khi đọc toàn bộ dữ liệu bảng từ đĩa dưới dạng một khối vào bộ nhớ (có thể chỉ trong một thao tác đọc hoặc tìm kiếm đĩa), sau đó sắp xếp nó vào RAM để đáp ứng ORDER BY, tức là so với đĩa đọc hoạt động. Nếu máy chủ đọc dữ liệu của bạn theo chỉ mục, nó sẽ phải phát hành tới 126 (rất tiếc!) Các hoạt động đọc, tìm kiếm qua lại trong cùng một tệp dữ liệu nhiều lần.

Nói cách khác, quét tuần tự KHÔNG phải lúc nào cũng là điều xấu và mysql không nhất thiết là ngu ngốc. Nếu bạn cố gắng buộc mysql sử dụng chỉ mục đó, rất có thể nó sẽ hoạt động chậm hơn so với quét tuần tự mà bạn hiện có.

Và lý do tại sao nó sử dụng chỉ mục khi không bao gồm trường 5KB là vì sau đó dữ liệu được truy xuất không cấu thành 99% dữ liệu trong bảng. Khi bạn bao gồm trường 5KB của mình, bây giờ truy vấn phải đọc 99% dữ liệu và sẽ rẻ hơn khi đọc toàn bộ nội dung và sắp xếp nó vào bộ nhớ sau đó.


Nghe có vẻ như bạn đang nhầm lẫn một số điều từ Cách tránh quét toàn bộ bảng , điều cần làm với việc sử dụng chỉ mục trong các JOINđiều kiện và WHEREmệnh đề thỏa mãn , không phải là ORDER BYmệnh đề.
eggyal

Chính xác thì ngược lại. Trong trường hợp cụ thể này, quét toàn bộ bảng là điều TỐT, đơn giản vì nó NHANH hơn đọc theo thứ tự chỉ mục.

0

Bạn đang sử dụng phiên bản nào của MySQL?

Ở 5.1, tôi đã cố gắng thiết lập kịch bản của bạn và điền một số dữ liệu giả. Sử dụng các SQL mà bạn cung cấp, tôi chỉ nhận được một lần quét bảng mỗi lần theo GIẢI THÍCH. Theo mặc định khi bạn sử dụng đơn đặt hàng của MYSQL resort để tập tin ngay cả khi chỉ mục chính được sử dụng theo thứ tự theo.

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.