2015-12-15 14 views
6

Tôi muốn chạy các truy vấn này:Sử dụng một chỉ số bao gồm để chọn hồ sơ cho một ngày nhất định

select url from weixin_kol_status where created_at>'2015-12-11 00:00:00' and created_at<'2015-12-11 23:59:59';

select url from weixin_kol_status where userid in ('...') and created_at>'2015-12-11 00:00:00' and created_at<'2015-12-11 23:59:59';

... sử dụng định nghĩa bảng này:

CREATE TABLE `weixin_kol_status` (
    `id` bigint(20) NOT NULL AUTO_INCREMENT, 
    `url` varchar(512) NOT NULL, 
    `created_at` datetime NOT NULL, 
    `title` varchar(512) NOT NULL DEFAULT '', 
    `text` text, 
    `attitudes_count` int(11) NOT NULL DEFAULT '0', 
    `readcount` int(11) NOT NULL DEFAULT '0', 
    `reposts_count` int(11) NOT NULL DEFAULT '0', 
    `comments_count` int(11) NOT NULL DEFAULT '0', 
    `userid` varchar(32) NOT NULL, 
    `screen_name` varchar(32) NOT NULL, 
    `type` tinyint(4) NOT NULL DEFAULT '0', 
    `ext_data` text, 
    `is_topline` tinyint(4) NOT NULL DEFAULT '0', 
    `is_business` tinyint(4) NOT NULL DEFAULT '0', 
    PRIMARY KEY (`id`), 
    UNIQUE KEY `idx_url` (`url`(255)), 
    KEY `idx_userid` (`userid`), 
    KEY `idx_name` (`screen_name`), 
    KEY `idx_created_at` (`created_at`) 
) ENGINE=InnoDB AUTO_INCREMENT=328727437 DEFAULT CHARSET=utf8 | 


rows = 328727437; 

Các truy vấn mất tích ral phút. Làm cách nào để tối ưu hóa các truy vấn? Làm cách nào tôi có thể sử dụng chỉ mục bao gồm?

Các kế hoạch thực hiện như sau:

explain select id from weixin_kol_status where created_at>='2015-12-11 00:00:00' and created_at<='2015-12-11 23:59:59'\G; 
    *************************** 1. row *************************** 
       id: 1 
     select_type: SIMPLE 
      table: weixin_kol_status 
      type: range 
    possible_keys: idx_created_at 
       key: idx_created_at 
      key_len: 5 
       ref: NULL 
      rows: 1433704 
      Extra: Using where; Using index 
    1 row in set (0.00 sec) 

explain select id from weixin_kol_status where created_at='2015-12-11 00:00:00'\G; 
*************************** 1. row *************************** 
      id: 1 
    select_type: SIMPLE 
     table: weixin_kol_status 
     type: ref 
possible_keys: idx_created_at 
      key: idx_created_at 
     key_len: 5 
      ref: const 
     rows: 1 
     Extra: Using index 
1 row in set (0.00 sec) 

nhưng tại sao truy vấn đầu tiên Extra: Using where; Using index, và truy vấn thứ hai Extra: Using index. Truy vấn đầu tiên không sử dụng chỉ mục bao gồm?

+0

Vì điều này bây giờ đã trở thành một câu hỏi cụ thể về cách sử dụng các chỉ mục bao gồm, nó không còn là yêu cầu để mã của bạn được xem xét. –

Trả lời

2

Tôi có thể sử dụng chỉ mục bao gồm bằng cách nào?

Bạn có biết covering index là gì không? Đó là một chỉ mục chứa tất cả các cột mà bạn cần cho truy vấn của bạn. Vì vậy, đối

select url from weixin_kol_status where created_at>'2015-12-11 00:00:00' and created_at<'2015-12-11 23:59:59'; 

chỉ số tối thiểu bao gồm sẽ là một cái gì đó giống như

 KEY `idx_created_url` (`created_at`, `url`) 

Và đối với

select url from weixin_kol_status where userid in ('...') and created_at>'2015-12-11 00:00:00' and created_at<'2015-12-11 23:59:59'; 

chỉ số bao gồm tối thiểu có thể là

 KEY `idx_created_user_url` (`created_at`, `userid`, `url`) 

mà cũng sẽ bao gồm truy vấn đầu tiên hoặc

 KEY `idx_user_created_url` (`userid`, `created_at`, `url`) 

đó sẽ không làm việc cho truy vấn đầu tiên nhưng có thể tối ưu hóa tốt hơn thứ hai.

Bạn có thể phải viết ra url(512) thay vì chỉ url. VARCHAR các cột không lập chỉ mục tốt. Nếu bạn gặp lỗi về các giá trị được lập chỉ mục quá rộng, bạn có thể không sử dụng được chỉ mục bao phủ với truy vấn này.

Chỉ mục bao gồm rất hữu ích vì nó có thể trả lời mọi thứ từ chỉ mục trong bộ nhớ mà không cần chuyển sang bảng trên đĩa. Vì bộ nhớ nhanh hơn đĩa, nó có tác dụng tăng tốc truy vấn. Tất nhiên, nếu chỉ mục của bạn được phân trang, bạn sẽ vẫn phải tải nó từ đĩa. Vì vậy, nếu bạn đang bị ràng buộc bộ nhớ, điều này có thể không giúp đỡ.

Lưu ý rằng truy vấn sẽ chỉ sử dụng một chỉ mục cho mỗi bảng, do đó, các chỉ mục riêng biệt trên mỗi cột sẽ không bao gồm truy vấn.Bạn cần một chỉ mục phức hợp để bao gồm tất cả các cột cần thiết cùng một lúc.

Lưu ý phụ, tôi cho rằng >< của bạn phải là >=<= tương ứng. Có lẽ sẽ không tạo ra nhiều khác biệt, nhưng bạn dường như bỏ qua hai giây một ngày.

+0

Chỉ mục phụ được phân trang vào/ra khỏi bộ nhớ giống như dữ liệu. Đó là, chỉ số sẽ không nhất thiết phải "trả lời mọi thứ từ chỉ mục trong bộ nhớ". –

+0

Chỉ số "bao phủ" sẽ tốt đẹp. Nhưng 'url' quá lớn để nằm trong bất kỳ chỉ mục nào. –

0

Một số vấn đề

  • UNIQUE(url(255)) chế các đầu tiên 255 nhân vật phải là duy nhất; điều này có lẽ không mong muốn.

  • Nếu bạn cần buộc đơn nhất của một chuỗi dài (url), hãy thêm cột khác với MD5(url) và đặt cột đó UNIQUE. (Hoặc một cái gì đó như thế.)

  • Có một giới hạn của 767 byte cho mỗi cột trong một chỉ số, vì vậy nếu bạn cố gắng tạo ra INDEX(created_at, url), bạn sẽ có được INDEX(created_at, url(255)), đó là không tráng, vì không phải tất cả các url là trong chỉ mục.

  • Cả hai EXPLAINs đều vô ích cho cuộc thảo luận này vì chúng không sử dụng số SELECTs mà bạn đang hỏi. Đầu tiên, nó nói Using index bởi vì bạn nói SELECT id; truy vấn thực tế là SELECT url. Điều này tạo ra sự khác biệt lớn về hiệu suất lớn.

  • Bạn có một bảng rất lớn. Tôi thấy không có cách nào cho PARTITIONing để giúp với tốc độ.

  • Đây là một cách tốt hơn để diễn tả 1 ngày WHERE:

     created_at >= '2015-12-11' 
    AND created_at < '2015-12-11' + INTERVAL 1 DAY 
    

Đẩy nhanh tiến độ nó lên

Dưới đây là một kỹ thuật off-the-tường mà nên giúp cả hai truy vấn. Thay vì

PRIMARY KEY (`id`), 
KEY `idx_created_at` (`created_at`) 

Hãy làm theo cách này:

PRIMARY KEY(created_at, id), 
INDEX(id) 

Điều đó sẽ "cụm" trên created_at, qua đó cắt giảm I/O đáng kể, đặc biệt đối với SELECT đầu tiên. Có, nó là OK cho một số AUTO_INCREMENT chỉ là INDEXed, không phải UNIQUE cũng không phải PRIMARY KEY.

Thận trọng: Để thực hiện thay đổi sẽ mất nhiều giờ và nhiều dung lượng đĩa.

Các vấn đề liên quan