Quick Start: Cấu hình Histogram trong 5 phút
Bạn đã bao giờ gặp cảnh một câu SQL chạy lúc nhanh (0.5s) lúc chậm (10s) dù đã đánh Index? Có thể MySQL Optimizer đang “đoán mò” vì thiếu thông tin về mật độ dữ liệu. Hãy thử tạo Histogram để cung cấp cho nó một cái nhìn toàn cảnh mà không làm nặng DB như Index.
Ví dụ, với bảng orders có hàng triệu dòng, bạn muốn MySQL hiểu rõ phân phối của cột order_status:
-- Tạo Histogram cho cột order_status với 100 buckets
ANALYZE TABLE orders UPDATE HISTOGRAM ON order_status WITH 100 BUCKETS;
-- Kiểm tra kết quả trong Data Dictionary
SELECT * FROM information_schema.column_statistics
WHERE table_name = 'orders' AND column_name = 'order_status';
Chỉ với một dòng lệnh, bạn đã tạo ra một “bản đồ mật độ” dữ liệu. Nhờ đó, Optimizer sẽ biết chính xác khi nào nên dùng Index, khi nào nên quét toàn bảng (Full Table Scan) để đạt tốc độ tốt nhất.
Tại sao Index đôi khi lại “bất lực”?
Trong một dự án E-commerce mình từng tham gia, bảng products có 15 triệu record. Cột category_id đã được đánh Index. Tuy nhiên, khi query các danh mục phổ biến (chiếm 40% dữ liệu), hệ thống bỗng dưng treo vì MySQL cố tình dùng Index thay vì quét bảng, gây ra hàng triệu lần tìm kiếm ngẫu nhiên (Random I/O).
Nguyên nhân rất đơn giản: Index chỉ cho MySQL biết dữ liệu nằm ở đâu, chứ không cho biết dữ liệu phân bổ thế nào.
MySQL Histogram (từ bản 8.0) giải quyết triệt để vấn đề này. Nó lưu trữ thống kê tần suất xuất hiện của các giá trị dưới dạng snapshot cực nhẹ. Khác với Index phải cập nhật liên tục mỗi khi INSERT/UPDATE, Histogram chỉ tốn tài nguyên khi bạn chủ động chạy lệnh ANALYZE.
So sánh nhanh: Index vs Histogram
- Index: Tự động cập nhật, tốt cho việc tìm kiếm chính xác từng dòng nhưng làm chậm tốc độ ghi (INSERT/UPDATE).
- Histogram: Cập nhật thủ công, không hỗ trợ tìm kiếm dòng cụ thể nhưng giúp Optimizer ước tính số dòng (Selectivity) cực chuẩn. Nó là lựa chọn hoàn hảo cho các cột có dữ liệu bị lệch (skewed data).
Hai loại Histogram bạn cần biết
MySQL sẽ tự động chọn loại Histogram phù hợp dựa trên đặc điểm cột dữ liệu của bạn:
1. Singleton Histogram
Loại này dành cho các cột có ít giá trị phân biệt (Low Cardinality). Ví dụ: gender chỉ có 3 giá trị hoặc order_status có 5 trạng thái. Mỗi “bucket” sẽ đại diện cho một giá trị cụ thể kèm tỷ lệ phần trăm chính xác của nó.
2. Equi-Height Histogram
Nếu cột có dải giá trị rộng như price hoặc created_at, MySQL sẽ dùng Equi-Height. Dữ liệu được chia thành các nhóm sao cho số lượng dòng trong mỗi nhóm xấp xỉ nhau. Điều này giúp xử lý tốt các câu lệnh WHERE price BETWEEN 100 AND 500.
Quản lý Histogram như một chuyên gia
Đừng tạo Histogram cho mọi cột một cách bừa bãi. Hãy tập trung vào những cột thường xuyên nằm trong điều kiện lọc nhưng lại có độ lệch dữ liệu cao.
Tinh chỉnh độ chi tiết
Số lượng Buckets mặc định là 100. Nếu dữ liệu của bạn cực kỳ phức tạp (như tọa độ địa lý hoặc mã lỗi hệ thống), hãy tăng con số này lên tối đa 1024 để tăng độ chính xác:
ANALYZE TABLE users UPDATE HISTOGRAM ON age WITH 256 BUCKETS;
Dọn dẹp khi không còn giá trị
Nếu cấu trúc dữ liệu thay đổi hoàn toàn, Histogram cũ có thể khiến Optimizer đưa ra quyết định sai lầm. Lúc này, hãy xóa nó đi:
ANALYZE TABLE users DROP HISTOGRAM ON age;
Kinh nghiệm thực tế từ dự án lớn
Sau nhiều năm tối ưu các hệ thống Database lớn, mình rút ra 3 quy tắc vàng khi dùng Histogram:
- Ưu tiên dữ liệu bị lệch (Skewed): Nếu 80% khách hàng tập trung ở TP.HCM và 20% còn lại chia cho 62 tỉnh, Histogram sẽ giúp MySQL không áp dụng cùng một chiến lược query cho mọi tỉnh thành.
- Dùng cho cột ít biến động: Vì không tự cập nhật, Histogram lý tưởng cho các cột phân loại sản phẩm hoặc dữ liệu lịch sử. Tránh dùng cho các cột thay đổi hàng nghìn lần mỗi giây.
- Thay thế Index cho báo cáo: Với các truy vấn BI/Reporting cuối ngày, thay vì đánh 10 Index làm nặng file
.ibd, mình dùng Histogram. Hiệu quả query tương đương nhưng tốc độ INSERT đơn hàng trong ngày không hề bị ảnh hưởng.
Mình từng xử lý một bảng log 100 triệu dòng. Việc đánh Index vào error_code khiến dung lượng file Index tăng thêm 4GB. Sau khi xóa Index và thay bằng Histogram với 512 buckets, các câu lệnh thống kê lỗi chạy nhanh hơn 5 lần, trong khi file dữ liệu lại nhẹ đi đáng kể.
Lưu ý kỹ thuật
Histogram hiện chỉ hỗ trợ các kiểu dữ liệu cơ bản như Number, String, Date. Nó không hoạt động với kiểu JSON hay Spatial. Đừng quên thiết lập một Cron job hàng tuần để chạy UPDATE HISTOGRAM, đảm bảo số liệu thống kê luôn bám sát thực tế dữ liệu trong bảng.
Hiểu và vận dụng tốt Histogram sẽ giúp bạn kiểm soát MySQL Optimizer hiệu quả hơn, thay vì chỉ biết phụ thuộc vào việc đánh Index bừa bãi.

