Đây là những gì đang diễn ra.
The SELECT COUNT (...) icd_index where icd='25000'
sẽ sử dụng chỉ mục, là một BTree tách biệt với dữ liệu. Nhưng nó quét nó theo cách này:
- Tìm mục nhập đầu tiên có icd ='25000'. Điều này gần như ngay lập tức.
- Quét về phía trước cho đến khi tìm thấy thay đổi trong icd. Thao tác này sẽ chỉ quét trong chỉ mục, không chạm vào dữ liệu. Theo GIẢI THÍCH, sẽ có khoảng 910.104 mục nhập chỉ mục cần quét.
Bây giờ chúng ta hãy nhìn vào BTree cho chỉ số đó. Dựa trên các trường trong chỉ mục, mỗi hàng sẽ có chính xác 22 byte, cộng với một số chi phí (ước tính 40%). Khối chỉ mục MyISAM là 1KB (xem InnoDB's 16KB). Tôi ước tính 33 hàng cho mỗi khối. 910,104/33 cho biết khoảng 27K khối cần được đọc để thực hiện ĐẾM. (Lưu ý COUNT(core_id)
cần kiểm tra core_id
vì là null, COUNT(*)
không làm; Đây là một sự khác biệt nhỏ.) Đọc khối 27K trên ổ cứng đơn giản mất khoảng 270 giây. Bạn thật may mắn khi hoàn thành nó trong 60 giây.
Lần chạy thứ hai đã tìm thấy tất cả các khối đó trong key_buffer (giả sử key_buffer_size ít nhất là 27MB), vì vậy nó không phải đợi đĩa. Do đó, nó nhanh hơn nhiều. (Điều này bỏ qua bộ nhớ cache Truy vấn mà bạn có đủ khôn ngoan để xóa hoặc sử dụng SQL_NO_CACHE.)
5.6 xảy ra là không liên quan (nhưng cảm ơn bạn đã đề cập đến nó), vì quy trình này không thay đổi kể từ 4.0 trở về trước (ngoại trừ utf8 không tồn tại; thông tin thêm về điều đó bên dưới).
Chuyển sang InnoDB sẽ hữu ích theo một số cách. KHÓA CHÍNH sẽ được 'nhóm' với dữ liệu, không được lưu trữ như một BTree riêng biệt. Do đó, một khi dữ liệu hoặc PK được lưu vào bộ nhớ cache, dữ liệu khác sẽ ngay lập tức có sẵn. Số khối sẽ giống như 5K, nhưng chúng sẽ là khối 16KB. Các tệp này có thể tải nhanh hơn nếu bộ nhớ cache nguội.
Bạn hỏi "Tôi có cần chỉ mục trên icd không?" - Điều đó sẽ thu nhỏ kích thước MyISAM BTree xuống còn khoảng 21 byte mỗi hàng, vì vậy BTree sẽ có kích thước khoảng 21/27, không cải thiện nhiều (ít nhất là đối với tình trạng cache lạnh).
Một suy nghĩ khác là, nếu icd
luôn là số và luôn là số, để sử dụng MEDIUMINT UNSIGNED
và giải quyết ZEROFILL
nếu nó có thể có các số 0 ở đầu.
Rất tiếc, tôi không nhận thấy BỘ NHÂN VẬT. (Tôi đã sửa những con số ở trên, nhưng hãy để tôi giải thích thêm.)
- CHAR (5) cho phép 5 ký tự .
- ascii chiếm 1 byte per ký tự .
- utf8 chiếm tối đa 3 byte mỗi ký tự .
- Vì vậy, CHAR (5) CHARACTER SET utf8 mất 15 byte luôn luôn .
Thay đổi cột thành CHAR(5) CHARACTER SET ascii
sẽ thu nhỏ nó thành 5 byte.
Thay đổi nó thành MEDIUMINT UNSIGNED ZEROFILL sẽ thu nhỏ nó thành 3 byte.
Việc thu nhỏ dữ liệu sẽ tăng tốc I / O lên một lượng tương ứng gần đúng (sau khi cho phép thêm 6 byte nữa cho hai trường còn lại.