Tại sao truy vấn sqlite này chậm hơn nhiều khi tôi lập chỉ mục các cột?


14

Tôi có một cơ sở dữ liệu sqlite với hai bảng, mỗi bảng có 50.000 hàng, chứa tên của những người (giả). Tôi đã tạo một truy vấn đơn giản để tìm hiểu có bao nhiêu tên (tên đã cho, tên đệm ban đầu, họ) chung cho cả hai bảng:

select count(*) from fakenames_uk inner join fakenames_usa on fakenames_uk.givenname=fakenames_usa.givenname and fakenames_uk.surname=fakenames_usa.surname and fakenames_uk.middleinitial=fakenames_usa.middleinitial;

Khi không có chỉ mục nào ngoại trừ các khóa chính (không liên quan đến truy vấn này), nó sẽ chạy nhanh:

[james@marlon Downloads] $ time sqlite3 generic_data_no_indexes.sqlite "select count(*) from fakenames_uk inner join fakenames_usa on fakenames_uk.givenname=fakenames_usa.givenname and fakenames_uk.surname=fakenames_usa.surname and fakenames_uk.middleinitial=fakenames_usa.middleinitial;"
131

real    0m0.115s
user    0m0.111s
sys     0m0.004s

Nhưng nếu tôi thêm chỉ mục vào ba cột trên mỗi bảng (tất cả sáu chỉ mục):

CREATE INDEX `idx_uk_givenname` ON `fakenames_uk` (`givenname` )
//etc.

sau đó nó chạy chậm một cách đau đớn:

[james@marlon Downloads] $ time sqlite3 generic_data.sqlite "select count(*) from fakenames_uk inner join fakenames_usa on fakenames_uk.givenname=fakenames_usa.givenname and fakenames_uk.surname=fakenames_usa.surname and fakenames_uk.middleinitial=fakenames_usa.middleinitial;"
131

real    1m43.102s
user    0m52.397s
sys     0m50.696s

Có bất kỳ vần điệu hoặc lý do cho điều này?

Đây là kết quả của EXPLAIN QUERY PLANphiên bản không có chỉ mục:

0|0|0|SCAN TABLE fakenames_uk
0|1|1|SEARCH TABLE fakenames_usa USING AUTOMATIC COVERING INDEX (middleinitial=? AND surname=? AND givenname=?)

Đây là với các chỉ mục:

0|0|0|SCAN TABLE fakenames_uk
0|1|1|SEARCH TABLE fakenames_usa USING INDEX idx_us_middleinitial (middleinitial=?)

1
Chỉ mục của bạn không bao gồm. Có vẻ như bạn đang lập chỉ mục cho từng cột riêng lẻ. Chuyện gì xảy ra khi bạn tạo một chỉ số bọc chứa cả ba cột trong một index ( middleinitial, surnamegivenname)?
Randolph West

Câu trả lời:


14

Trong SQLite, các phép nối được thực thi như các phép nối vòng lặp lồng nhau, nghĩa là cơ sở dữ liệu đi qua một bảng và với mỗi hàng, tìm kiếm các hàng khớp với các bảng khác.

Nếu có một chỉ mục, cơ sở dữ liệu có thể tìm kiếm bất kỳ kết quả khớp nào trong chỉ mục một cách nhanh chóng, sau đó đi đến hàng của bảng tương ứng để lấy các giá trị của bất kỳ cột nào khác cần thiết.

Trong trường hợp này, có ba chỉ số có thể. Nếu không có bất kỳ thông tin thống kê nào (sẽ được tạo bằng cách chạy ANALYZE ), cơ sở dữ liệu sẽ chọn cơ sở dữ liệu nhỏ nhất để giảm I / O. Tuy nhiên, middleinitialchỉ mục này là vô dụng vì nó không làm giảm đáng kể số lượng các hàng của bảng cần được tìm nạp; và bước bổ sung thông qua chỉ mục thực sự làm tăng I / O cần thiết vì các hàng của bảng không còn được đọc theo thứ tự, mà là ngẫu nhiên.

Nếu không có chỉ mục, việc tra cứu các hàng khớp sẽ yêu cầu quét bảng hoàn chỉnh của bảng thứ hai cho mỗi hàng của bảng đầu tiên. Điều này sẽ tệ đến mức cơ sở dữ liệu ước tính rằng nó đáng để tạo và sau đó bỏ chỉ mục tạm thời chỉ cho truy vấn này. Chỉ mục tạm thời ("TỰ ĐỘNG") này được tạo trên tất cả các colunms được sử dụng cho tìm kiếm. Hoạt động COUNT (*) không cần các giá trị từ bất kỳ cột nào khác, vì vậy chỉ mục này tình cờ là một chỉ số bao phủ , điều đó có nghĩa là không cần thiết phải tìm kiếm hàng bảng tương ứng với mục nhập chỉ mục, giúp tiết kiệm hơn nữa tôi / O.

Để tăng tốc truy vấn này, hãy tạo chỉ mục này vĩnh viễn, để không còn cần thiết phải xây dựng một truy vấn tạm thời:

CREATE INDEX uk_all_names ON fakenames_uk(surname, givenname, middleinitial);

EXPLAIN QUERY PLAN
SELECT count(*)
FROM fakenames_uk
JOIN fakenames_usa USING (givenname, middleinitial, surname);

0|0|1|SCAN TABLE fakenames_usa
0|1|0|SEARCH TABLE fakenames_uk USING COVERING INDEX uk_all_names (surname=? AND givenname=? AND middleinitial=?)

Chỉ mục trên surnamekhông còn cần thiết vì chỉ mục ba cột có thể được sử dụng cho mọi tra cứu trên cột này.
Chỉ mục trên givennamecó thể hữu ích nếu bạn chỉ thực hiện tra cứu trên cột này.
Chỉ mục trên middleinitialluôn không có giá trị: một truy vấn tìm kiếm một trong 26 giá trị có thể nhanh hơn nếu nó chỉ quét toàn bộ bả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.