Đây là điểm chuẩn MariaDB (10.0.19) với 10 triệu hàng (sử dụng plugin trình tự ):
drop table if exists test;
CREATE TABLE `test` (
`id` MEDIUMINT UNSIGNED NOT NULL,
`is_active` TINYINT UNSIGNED NOT NULL,
`deleted_at` TIMESTAMP NULL,
PRIMARY KEY (`id`),
INDEX `is_active` (`is_active`),
INDEX `deleted_at` (`deleted_at`)
) ENGINE=InnoDB
select seq id
, rand(1)<0.5 as is_active
, case when rand(1)<0.5
then null
else '2017-03-18' - interval floor(rand(2)*1000000) second
end as deleted_at
from seq_1_to_10000000;
Để đo thời gian, tôi sử dụng set profiling=1
và chạy show profile
sau khi thực hiện một truy vấn. Từ kết quả cấu hình, tôi lấy giá trị của Sending data
vì mọi thứ khác hoàn toàn nhỏ hơn một mili giây.
TINYINT chỉ mục:
SELECT COUNT(*) FROM test WHERE is_active = 1;
Thời gian chạy:~ 738 mili giây
TIMESTAMP chỉ mục:
SELECT COUNT(*) FROM test WHERE deleted_at is null;
Thời gian chạy:~ 748 mili giây
Kích thước chỉ mục:
select database_name, table_name, index_name, stat_value*@@innodb_page_size
from mysql.innodb_index_stats
where database_name = 'tmp'
and table_name = 'test'
and stat_name = 'size'
Kết quả:
database_name | table_name | index_name | stat_value*@@innodb_page_size
-----------------------------------------------------------------------
tmp | test | PRIMARY | 275513344
tmp | test | deleted_at | 170639360
tmp | test | is_active | 97107968
Lưu ý rằng mặc dù TIMESTAMP (4 Byte) dài gấp 4 lần TYNYINT (1 Byte), kích thước chỉ mục thậm chí không lớn gấp đôi. Nhưng kích thước chỉ mục có thể đáng kể nếu nó không vừa với bộ nhớ. Vì vậy, khi tôi thay đổi innodb_buffer_pool_size
từ 1G
đến 50M
tôi nhận được những con số sau:
- TINYINT:~ 960 mili giây
- TIMESTAMP:~ 1500 mili giây
Cập nhật
Để giải quyết câu hỏi trực tiếp hơn, tôi đã thực hiện một số thay đổi đối với dữ liệu:
- Thay vì TIMESTAMP, tôi sử dụng DATETIME
- Vì các mục nhập thường hiếm khi bị xóa nên tôi sử dụng
rand(1)<0.99
(1% đã bị xóa) thay vìrand(1)<0.5
(50% đã bị xóa) - Kích thước bảng đã thay đổi từ 10 triệu thành 1 triệu hàng.
-
SELECT COUNT(*)
đã thay đổi thànhSELECT *
Kích thước chỉ mục:
index_name | stat_value*@@innodb_page_size
------------------------------------------
PRIMARY | 25739264
deleted_at | 12075008
is_active | 11026432
Vì 99% deleted_at
giá trị là NULL, không có sự khác biệt đáng kể về kích thước chỉ mục, mặc dù DATETIME không trống yêu cầu 8 Byte (MariaDB).
SELECT * FROM test WHERE is_active = 1; -- 782 msec
SELECT * FROM test WHERE deleted_at is null; -- 829 msec
Bỏ cả hai chỉ mục cả hai truy vấn sẽ thực thi trong khoảng 350 mili giây. Và bỏ is_active
cột deleted_at is null
truy vấn thực thi trong 280 mili giây.
Lưu ý rằng đây vẫn không phải là một kịch bản thực tế. Bạn sẽ không muốn chọn 990 nghìn hàng trong số 1 triệu hàng và phân phối nó cho người dùng. Bạn cũng có thể sẽ có nhiều cột hơn (có thể bao gồm cả văn bản) trong bảng. Nhưng nó cho thấy rằng bạn có thể không cần is_active
(nếu nó không thêm thông tin bổ sung) và bất kỳ chỉ mục nào trong trường hợp tốt nhất là vô dụng để chọn các mục nhập không bị xóa.
Tuy nhiên, một chỉ mục có thể hữu ích để chọn các hàng đã xóa:
SELECT * FROM test WHERE is_active = 0;
Thực thi trong 10 mili giây có chỉ mục và trong 170 mili giây không có chỉ mục.
SELECT * FROM test WHERE deleted_at is not null;
Thực thi trong 11 mili giây có chỉ mục và trong 167 mili giây không có chỉ mục.
Bỏ is_active
cột nó thực thi trong 4 mili giây có chỉ mục và trong 150 mili giây không có chỉ mục.
Vì vậy, nếu tình huống này bằng cách nào đó phù hợp với dữ liệu của bạn, kết luận sẽ là:Bỏ is_active
và không tạo chỉ mục trên deleted_at
nếu bạn hiếm khi chọn các mục đã xóa. Hoặc điều chỉnh điểm chuẩn theo nhu cầu của bạn và đưa ra kết luận của riêng bạn.