Làm cách nào để thiết kế các chỉ mục cho các cột có giá trị NULL trong MySQL?


11

Tôi có một cơ sở dữ liệu với 40 triệu mục và muốn chạy truy vấn với WHEREmệnh đề sau

...
WHERE
  `POP1` IS NOT NULL 
  && `VT`='ABC'
  && (`SOURCE`='HOME')
  && (`alt` RLIKE '^[AaCcGgTt]$')
  && (`ref` RLIKE '^[AaCcGgTt]$')
  && (`AA` RLIKE '^[AaCcGgTt]$')
  && (`ref` = `AA` || `alt` = `AA`)
LIMIT 10 ;

POP1là một cột float cũng có thể là NULL. POP1 IS NOT NULLnên loại trừ khoảng 50% các mục, đó là lý do tại sao tôi đặt nó ở đầu. Tất cả các điều khoản khác giảm số lượng chỉ một chút.

Trong số những người khác, tôi đã thiết kế một chỉ mục pop1_vt_source, dường như không được sử dụng, trong khi một chỉ mục với vtcột đầu tiên được sử dụng. GIẢI THÍCH-đầu ra:

| id | select_type | table | type | possible_keys                          | key                 | key_len | ref         | rows     | Extra       |
|  1 | SIMPLE      | myTab | ref  | vt_source_pop1_pop2,pop1_vt_source,... | vt_source_pop1_pop2 | 206     | const,const | 20040021 | Using where |

Tại sao chỉ mục với pop1cột đầu tiên không được sử dụng? Vì NOThay vì NULLnói chung. Làm cách nào tôi có thể cải thiện thiết kế các chỉ số của mình và mệnh đề WHERE? Ngay cả khi giới hạn ở 10 mục, truy vấn mất hơn 30 giây, mặc dù 100 mục đầu tiên trong bảng phải chứa 10 kết quả khớp.

Câu trả lời:


10

Đó là NOT NULL:

CREATE TEMPORARY TABLE `myTab` (`notnul` FLOAT, `nul` FLOAT);
INSERT INTO `myTab` VALUES (1, NULL), (1, 2), (1, NULL), (1, 2), (1, NULL), (1, 2), (1, NULL), (1, 2), (1, NULL), (1, 2), (1, NULL), (1, 2);
SELECT * FROM `myTab`;

cho:

+--------+------+
| notnul | nul  |
+--------+------+
|      1 | NULL |
|      1 |    2 |
|      1 | NULL |
|      1 |    2 |
|      1 | NULL |
|      1 |    2 |
|      1 | NULL |
|      1 |    2 |
|      1 | NULL |
|      1 |    2 |
|      1 | NULL |
|      1 |    2 |
+--------+------+

Tạo chỉ mục:

CREATE INDEX `notnul_nul` ON `myTab` (`notnul`, `nul`);
CREATE INDEX `nul_notnul` ON `myTab` (`nul`, `notnul`);

SHOW INDEX FROM `myTab`;

cho:

+-------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name   | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| myTab |          1 | notnul_nul |            1 | notnul      | A         |          12 |     NULL | NULL   | YES  | BTREE      |         |               |
| myTab |          1 | notnul_nul |            2 | nul         | A         |          12 |     NULL | NULL   | YES  | BTREE      |         |               |
| myTab |          1 | nul_notnul |            1 | nul         | A         |          12 |     NULL | NULL   | YES  | BTREE      |         |               |
| myTab |          1 | nul_notnul |            2 | notnul      | A         |          12 |     NULL | NULL   | YES  | BTREE      |         |               |
+-------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

Bây giờ giải thích các lựa chọn. Có vẻ như MySQL sử dụng chỉ mục, ngay cả khi bạn sử dụng NOT NULL:

EXPLAIN SELECT * FROM `myTab` WHERE `notnul` IS NOT NULL;
+----+-------------+-------+-------+---------------+------------+---------+------+------+--------------------------+ 
| id | select_type | table | type  | possible_keys | key        | key_len | ref  | rows | Extra                    |
+----+-------------+-------+-------+---------------+------------+---------+------+------+--------------------------+ 
|  1 | SIMPLE      | myTab | index | notnul_nul    | notnul_nul | 10      | NULL |   12 | Using where; Using index |
+----+-------------+-------+-------+---------------+------------+---------+------+------+--------------------------+


EXPLAIN SELECT * FROM `myTab` WHERE `nul` IS NOT NULL;
+----+-------------+-------+-------+---------------+------------+---------+------+------+--------------------------+
| id | select_type | table | type  | possible_keys | key        | key_len | ref  | rows | Extra                    |
+----+-------------+-------+-------+---------------+------------+---------+------+------+--------------------------+
|  1 | SIMPLE      | myTab | range | nul_notnul    | nul_notnul | 5       | NULL |    6 | Using where; Using index |
+----+-------------+-------+-------+---------------+------------+---------+------+------+--------------------------+

Nhưng, khi so sánh NOT NULLNULL, có vẻ như MySQL thích các chỉ mục khác khi sử dụng NOT NULL. Mặc dù điều này rõ ràng không thêm bất kỳ thông tin. Điều này là do MySQL diễn giải NOT NULLnhư một phạm vi như bạn có thể thấy trong cột kiểu. Tôi không chắc chắn nếu có một cách giải quyết:

EXPLAIN SELECT * FROM `myTab` WHERE `nul` IS NULL && notnul=2;
+----+-------------+-------+------+-----------------------+------------+---------+-------------+------+--------------------------+
| id | select_type | table | type | possible_keys         | key        | key_len | ref         | rows | Extra                    |
+----+-------------+-------+------+-----------------------+------------+---------+-------------+------+--------------------------+
|  1 | SIMPLE      | myTab | ref  | notnul_nul,nul_notnul | notnul_nul | 10      | const,const |    1 | Using where; Using index |
+----+-------------+-------+------+-----------------------+------------+---------+-------------+------+--------------------------+


EXPLAIN SELECT * FROM `myTab` WHERE `nul` IS NOT NULL && notnul=2;
+----+-------------+-------+-------+-----------------------+------------+---------+------+------+--------------------------+
| id | select_type | table | type  | possible_keys         | key        | key_len | ref  | rows | Extra                    |
+----+-------------+-------+-------+-----------------------+------------+---------+------+------+--------------------------+
|  1 | SIMPLE      | myTab | range | notnul_nul,nul_notnul | notnul_nul | 10      | NULL |    1 | Using where; Using index |
+----+-------------+-------+-------+-----------------------+------------+---------+------+------+--------------------------+

Tôi nghĩ rằng có thể có một triển khai tốt hơn trong MySQL, bởi vì đó NULLlà một giá trị đặc biệt. Có lẽ hầu hết mọi người quan tâm đến NOT NULLcác giá trị.


3

Vấn đề không phải là các giá trị NULL. Đó là sự chọn lọc của chỉ số. Trong ví dụ của bạn, độ chọn lọc source, pop1tốt hơn độ chọn lọc của chỉ pop1. Nó bao gồm nhiều điều kiện hơn trong wheremệnh đề, do đó có nhiều khả năng giảm lượt truy cập trang.

Bạn có thể nghĩ rằng giảm 50% số hàng là đủ, nhưng thực sự không phải vậy. Lợi ích của các chỉ mục trong một wheremệnh đề là giảm số lượng trang được đọc. Nếu trung bình, một trang có ít nhất một bản ghi có giá trị không phải là NULL, thì không có lợi ích gì khi sử dụng chỉ mục. Và, nếu có 10 bản ghi trên mỗi trang, thì hầu như mọi trang sẽ có một trong những bản ghi đó.

Bạn có thể thử một chỉ mục trên (pop1, vt, source). Trình tối ưu hóa nên chọn cái đó.

Tuy nhiên, cuối cùng, nếu wheređiều khoản bị mất hồ sơ - không có quy tắc nhưng hãy nói 20% - thì chỉ mục có thể sẽ không giúp ích. Một ngoại lệ sẽ là khi chỉ mục chứa tất cả các cột cần thiết cho truy vấn. Sau đó, nó có thể đáp ứng truy vấn mà không cần đưa vào trang dữ liệu cho mỗi bản ghi.

Và, nếu một chỉ mục được sử dụng và độ chọn lọc cao, thì hiệu suất với chỉ mục có thể kém hơn hiệu suất mà không có nó.


Tôi nghĩ rằng nó thực sự là phạm vi gây ra sự khác biệt (xem câu trả lời của tôi). Mặc dù tôi nghĩ rằng nó có thể được triển khai tốt hơn trong MySQL, vì hầu hết mọi người đều quan tâm đến NOT NULLcác cột.
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.