Làm cách nào tôi có thể buộc MySQL phải IGNORE TẤT CẢ các chỉ mục?


12

Tôi đã đọc các bài viết về FORCEchỉ mục, nhưng làm cách nào để buộc MySQL IGNORE ALLlập chỉ mục?

Tôi đã thử SELECT * FROM tbl IGNORE INDEX(*), nhưng tôi đã không thành công.

Về lý do tại sao tôi (và những người khác) cần phải làm điều này: Ví dụ: tôi cần tóm tắt số liệu thống kê người giới thiệu bằng tld như thế này:

SELECT 
    count(*) as c, 
    SUBSTRING
    (
        domain_name, 
        LENGTH(domain_name) - LOCATE('.', REVERSE(domain_name)) + 2
    ) as tld
FROM `domains_import` 
    IGNORE INDEX(domain_name)
GROUP BY tld
ORDER BY c desc
LIMIT 100

... nhưng tôi luôn phải xem xét các chỉ mục nào được xác định hoặc xác định chỉ mục nào sẽ được sử dụng thông qua Giải thích. Nó sẽ rất tiện dụng chỉ đơn giản là viết IGNORE INDEX ALLvà đơn giản là không quan tâm.

Có ai biết cú pháp hay hack không? (Hàng chục dòng thông qua các bảng định nghĩa MySQL thực sự không phải là một phím tắt).

Đã thêm từ thảo luận trò chuyện :

Bechmark:

  • không có chỉ số = 148,5 giây

  • với chỉ số = 180 giây và vẫn đang chạy với Gửi dữ liệu Mảng SSD rất mạnh, đến nỗi bạn gần như không quan tâm đến bộ đệm dữ liệu ...

Định nghĩa cho điểm chuẩn:

CREATE TABLE IF NOT EXISTS `domains_import` (
`domain_id` bigint(20) unsigned NOT NULL,
`domain_name` varchar(253) CHARACTER SET ascii COLLATE ascii_bin NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

ALTER TABLE `domains_import`
ADD PRIMARY KEY (`domain_id`),
ADD UNIQUE KEY `domain_name` (`domain_name`);

ALTER TABLE `domains_import`
MODIFY `domain_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT;

InnoDB, thử nghiệm với chỉ mục (không có USE INDEX () hoặc tương tự) vẫn đang chạy, 250 giây, tôi vừa giết nó.

Câu trả lời:


24

Hoàn toàn không rõ lý do tại sao bạn muốn điều này nhưng bạn có thể sử dụng gợi ý USE INDEX ()để nói với trình tối ưu hóa không sử dụng bất kỳ chỉ mục nào. Từ tài liệu MySQL: gợi ý chỉ mục

Đó là cú pháp hợp lệ để bỏ qua index_listchoUSE INDEX , mà có nghĩa là “sử dụng không chỉ mục.” Bỏ qua index_list cho FORCE INDEXhoặc IGNORE INDEXlà một lỗi cú pháp.

Truy vấn của bạn trở thành:

SELECT count(*) AS c, 
       substring_index(domain_name, '.', -1) AS tld
FROM domains_import 
       USE INDEX ()        -- use no indexes
GROUP BY tld
ORDER BY c DESC
LIMIT 100 ;

Lưu ý bên: biểu thức phức tạp:

SUBSTRING(domain_name, LENGTH(domain_name) - LOCATE('.', REVERSE(domain_name)) + 2) 

có thể được đơn giản hóa từ 4 lệnh gọi hàm đến 1:

SUBSTRING_INDEX(domain_name, '.', -1)

1
Nó rất hữu ích cho tôi khi trình tối ưu hóa MySQL 5.7.10 thay đổi kế hoạch truy vấn của nó cho một điều tồi tệ nhất khi loại bỏ một số trong số LEFT JOINtôi có. `USE INDEX ()` khiến MySQL thực hiện quét bảng trên bảng hàng 20K và 1 đến 1 JOINthay vì vượt qua 500 hàng giữa hai chỉ mục. Nhanh hơn gấp 20 lần.
Xenos

2

Bạn cũng có thể nhúng WHERE 1=1

SELECT 
    count(*) as c, 
    SUBSTRING
    (
        domain_name, 
        LENGTH(domain_name) - LOCATE('.', REVERSE(domain_name)) + 2
    ) as tld
FROM `domains_import` 
WHERE 1=1
GROUP BY tld
ORDER BY c desc
LIMIT 100

ypercube chỉ hỏi tôi

Rolando, trình tối ưu hóa của MySQL có quá ngu ngốc đến nỗi một điều kiện đơn giản luôn luôn đúng sẽ cấm sử dụng các chỉ mục?

Có, nhưng bạn đã cung cấp cho MySQL một truy vấn thực sự ngu ngốc. 1=1sẽ trở lại Chỉ số cụm. Mặc dù vậy, vẫn còn một cách khác, nhưng nó đòi hỏi một chút độc hại đối với Trình tối ưu hóa.

SELECT 
    count(*) as c, 
    SUBSTRING
    (
        domain_name, 
        LENGTH(domain_name) - LOCATE('.', REVERSE(domain_name)) + 2
    ) as tld
FROM `domains_import` 
WHERE domain_name = domain_name
GROUP BY tld
ORDER BY c desc
LIMIT 100

Điều này sẽ ném mọi chỉ số dưới xe buýt chắc chắn bởi vì giá trị của mỗi hàng đối với domain_namenhiều thứ được kiểm tra. Nếu domain_nameđược lập chỉ mục, bạn phải chọn một cột cho cái WHERE column_name=column_namekhông được lập chỉ mục nào cả.

Tôi vừa thử cái này trên một cái bàn lớn trong máy chủ Staging

mysql > explain SELECT COUNT(1) FROM VIDEO WHERE EMBEDDED_FLG=EMBEDDED_FLG;
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows   | Extra       |
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------+
|  1 | SIMPLE      | VIDEO | ALL  | NULL          | NULL | NULL    | NULL | 354327 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------+
1 row in set (0.00 sec)

Không có chỉ số nào được chọn


Rolando, trình tối ưu hóa của MySQL thật ngu ngốc đến nỗi một điều kiện đơn giản luôn luôn đúng sẽ cấm sử dụng các chỉ mục?
ypercubeᵀᴹ

@ypercube có, nhưng bạn phải giả mạo truy vấn đủ để điều đó xảy ra.
RolandoMySQLDBA

1
Này, tôi tự nâng cao câu trả lời của yercube. Câu trả lời của tôi chỉ là một cách khác và giải thích kẽ hở của Trình tối ưu hóa.
RolandoMySQLDBA

1
Rolando, không đúng: Chỉ mục sẽ được sử dụng: SQLfiddle . Ngay cả khi bạn làm một cái gì đó phức tạp hơn, như WHERE id+0 = id*1chỉ mục vẫn sẽ được sử dụng, và một phần phụ Using wheresẽ xuất hiện.
ypercubeᵀᴹ

4
@PaulWhite nó sẽ. (thật ngu ngốc nhưng không phải là ngu ngốc;) Và đó có thể là lý do tại sao truy vấn của Roalndo không sử dụng chỉ mục, cột phải được xác định là NULL.
ypercubeᵀᴹ

0

Giả sử bạn có hai chỉ số này:

ADD PRIMARY KEY (`domain_id`),
ADD UNIQUE KEY `domain_name` (`domain_name`);

Sau đó, nó không quan trọng những gì trình tối ưu hóa làm; về cơ bản nó phải quét một lượng thứ giống hệt nhau.

Trường hợp 1: Nó thực hiện quét bảng (hoặc sử dụng domain_id): Nó sẽ quét các cặp (id, name), định vị tất cả các tên, thực hiện SUBSTRING..LOCATE, GROUP BY và cuối cùng là OR BY BY. Mỗi nhóm BY BY và ORDER BY có lẽ mỗi bảng cần một bảng tmp và tệportort. Kiểm tra EXPLAIN SELECT ...xem nếu nó làm.

Trường hợp 2: Nó thực hiện quét chỉ mục (của domain_name): Chỉ mục đó thực sự chứa các cặp (tên, id) - bởi vì InnoDB ngầm đặt PK ở cuối bất kỳ khóa phụ nào. Phần còn lại của xử lý song song Trường hợp 1.

Một điều thể khác - kích thước của hai BTrees. Làm SHOW TABLE STATUS LIKE domains_importđể xem Data_length (cho trường hợp 1) và Index_length (cho trường hợp 2). BTree lớn hơn sẽ chậm hơn.

Một điều khác có thể khác - bộ nhớ đệm. Giá trị của là innodb_buffer_pool_sizegì? Bạn có bao nhiêu RAM? Dữ liệu (hoặc Chỉ mục) có thể được chứa trong nhóm bộ đệm. (Hoặc nó sẽ là 37% của nó, do đây là quét bảng / chỉ mục?) Nếu phù hợp, sau đó chạy truy vấn hai lần. Lần thứ hai sẽ nhanh gấp khoảng 10 lần do không nhấn vào đĩa (bộ nhớ đệm).

Nếu đây là nhiệm vụ một lần, SSD sẽ giúp ích. Nếu không, và bạn có thể lưu trữ toàn bộ bảng, thì nó sẽ không giúp ích gì sau khi bộ đệm được tải.

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.