Lập trình 23/10/2026 7 phút

Secondary index đo thật: lấy 100 dòng qua index phụ nhưng cần cột ngoài index phải tra ngược về khóa chính 100 lần, chạm 410 trang — còn truy vấn phủ chỉ chạm 10 trang, và quá ~10% số dòng thì MySQL bỏ index quét cả bảng

Phần 4 sê-ri MySQL và InnoDB: secondary index lưu giá trị khóa chính chứ không lưu địa chỉ hàng, nên tra theo index phụ mà cần cột ngoài index phải đi hai cây — index phụ rồi tra ngược về clustered index từng dòng (bookmark lookup). Đo thật trong mysql:8: lấy 100 dòng theo cust=5000 có kèm payload chạm 410 trang, còn truy vấn phủ chỉ cần id thì Using index, không tra ngược, chỉ chạm 10 trang — ít hơn 41 lần. Vì mỗi dòng thêm một lần tra ngược, qua ngưỡng ~10–15% số dòng MySQL bỏ hẳn index mà quét cả bảng: cust<1000 vẫn dùng index, cust<1500 chuyển full scan.

Lập trình 22/10/2026 7 phút

B+tree đo thật: tra một khóa trên bảng 10 triệu dòng chỉ đọc 3 trang, vì cây cao đúng 3 tầng — và từ 1.000 dòng lên 10 triệu dòng (gấp 10.000 lần) chiều cao cây chỉ tăng từ 2 lên 3

Phần 3 sê-ri MySQL và InnoDB: vì sao một index trên bảng hàng triệu dòng chỉ sâu vài tầng, nên tra một khóa chỉ đọc vài trang. Đo thật chiều cao cây B+ trong mysql:8 bằng số trang một lần tra khóa phải đọc: bảng 1.000 dòng cao 2 tầng, 100.000 dòng vẫn 2 tầng, 1 triệu dòng lên 3 tầng, 10 triệu dòng vẫn 3 tầng. Gấp 10.000 lần số dòng chỉ thêm đúng một tầng, vì mỗi nút là một trang 16 KB chứa cả nghìn khóa định tuyến (fanout lớn): cây cao 2 tầng chứa tới ~110 nghìn dòng, cao 3 tầng chứa tới ~110 triệu dòng. Nhờ vậy tra điểm theo khóa chính đọc 2–3 trang bất kể bảng to cỡ nào.

Lập trình 21/10/2026 7 phút

Buffer pool đo thật: cùng một truy vấn quét bảng 131 MB chạm đĩa 577 trang mỗi lần (185 ms) khi pool 128 MB không đủ chứa, rồi tụt về 0 trang chạm đĩa (128 ms) ngay khi nới pool lên 256 MB để bảng vừa khít

Phần 2 sê-ri MySQL và InnoDB: buffer pool là vùng RAM InnoDB giữ các trang 16 KB của bảng và index, và nó quyết định một truy vấn chạm đĩa bao nhiêu lần. Đo thật trong mysql:8: bảng 46 MB vừa pool thì quét ấm chạm đĩa 0 trang (hit ratio ~100%); bảng 131 MB lớn hơn pool 128 MB thì mọi lần quét vẫn chạm đĩa 577 trang và mất 185 ms, nhưng nới innodb_buffer_pool_size lên 256 MB để bảng vừa khít thì lần quét sau chạm đĩa 0 trang, còn 128 ms. Phân biệt đọc logic (read_requests) và đọc vật lý (reads), hit ratio ~99,9%, và vì sao một lần quét lớn không đẩy tập dữ liệu nóng khỏi pool nhờ LRU hai vùng.

Lập trình 20/10/2026 7 phút

Kiến trúc InnoDB đo thật: bảng CHÍNH LÀ clustered index — tra theo khóa chính chạm đúng 1 dòng trong 0,015 ms, còn lọc theo cột không index phải duyệt cả 1.000.001 dòng lá mất 161 ms

Bài mở đầu sê-ri MySQL và InnoDB: vì sao trong InnoDB, bảng không nằm cạnh index mà bảng CHÍNH LÀ clustered index — một cây B+ khóa theo khóa chính, lá chứa trọn hàng. Đo thật trong container mysql:8 trên bảng một triệu dòng: tra theo khóa chính là một lần đi xuống cây (Handler_read_key=1, type=const, 0,015 ms), còn lọc theo cột không index phải duyệt sạch 1.000.001 dòng lá (Handler_read_rnd_next=1.000.001, 161 ms) vì không có heap riêng để nhảy tới. Hàng luôn nằm vật lý theo thứ tự khóa chính: bảng chèn khóa ngẫu nhiên vẫn trả về theo thứ tự PK, và quét một dải PK là đi tuần tự trên lá.