Cơ sở dữ liệu 15/11/2026 8 phút

SQLite đo thật — kiến trúc và định dạng tệp một file: cả cơ sở dữ liệu nằm gọn trong một file, header 100 byte mở đầu bằng chuỗi SQLite format 3, và kích thước tệp luôn đúng bằng page_size nhân page_count

Bài mở đầu sê-ri SQLite đo thật: SQLite không phải tiến trình máy chủ mà là một thư viện nhúng, cả CSDL là một file duy nhất. Đo trong container sqlite 3.45.3: file rỗng 0 byte cho tới lần ghi đầu, sau một CREATE TABLE là đúng 8192 byte (2 trang 4096), 16 byte đầu là chuỗi ma thuật SQLite format 3, page_size nằm ở offset 16 (0x1000 = 4096), bật WAL đổi hai byte phiên bản từ 01/01 sang 02/02, và bảng lược đồ sqlite_master chính là một cây B-tree nằm trên trang 1.

Cơ sở dữ liệu 15/11/2026 7 phút

SQLite đo thật — cây B-tree: vì sao một triệu dòng chỉ sâu ba tầng nên tìm theo khóa chính tốn tối đa ba lần đọc trang, và index là một cây B-tree riêng nhỏ hơn nằm cạnh cây của bảng

Phần 2 sê-ri SQLite đo thật: mọi bảng và index trong SQLite đều là một cây B-tree. Đo trong container sqlite 3.45.3 bằng bảng ảo dbstat: bảng một triệu dòng dùng 7125 trang nhưng cây chỉ sâu 3 tầng vì mỗi trang trong (internal) trỏ tới trung bình 373 trang con — hệ số phân nhánh khổng lồ khiến chiều cao cây tăng theo log cơ số lớn. Trang lá chứa trọn dòng dữ liệu (trung bình 140 dòng/trang), trang trong chỉ chứa khóa và con trỏ để điều hướng. Tạo index trên một cột sinh ra một cây B-tree thứ hai riêng biệt, nhỏ hơn vì chỉ lưu cột đó cộng rowid.

Cơ sở dữ liệu 15/11/2026 6 phút

SQLite đo thật — rowid, INTEGER PRIMARY KEY và WITHOUT ROWID: vì sao mỗi bảng thường có một rowid ẩn, một khóa chính không phải số nguyên sinh thêm cả một cây B-tree, và WITHOUT ROWID cắt một nửa dung lượng

Phần 3 sê-ri SQLite đo thật: mỗi bảng thường (rowid table) có một khóa 64-bit ẩn tên rowid, và khai INTEGER PRIMARY KEY chỉ là đặt bí danh cho rowid chứ không lưu thêm cột. Đo trong container sqlite 3.45.3: một khóa chính TEXT trên bảng rowid tạo thêm một cây B-tree index tự động (sqlite_autoindex) gần như nhân đôi dung lượng — 500 nghìn dòng chiếm 24 MB. Khai cùng bảng đó WITHOUT ROWID khiến chính khóa chính thành khóa của cây bảng, bỏ hẳn index phụ, còn 12 MB — đúng một nửa. Đổi lại WITHOUT ROWID có cái giá của nó với index thứ cấp.

Cơ sở dữ liệu 15/11/2026 5 phút

SQLite đo thật — index và EXPLAIN QUERY PLAN: đọc một chữ SCAN hay SEARCH là biết truy vấn quét hai triệu dòng hay đi thẳng qua cây B-tree, và chênh lệch đo được là ba nghìn lần

Phần 4 sê-ri SQLite đo thật: EXPLAIN QUERY PLAN cho biết SQLite sẽ quét toàn bảng (SCAN) hay tìm theo index (SEARCH) trước khi chạy một dòng nào. Đo trên bảng hai triệu dòng trong container sqlite 3.45.3: tìm theo cột chưa có index là SCAN mất 43 mili-giây mỗi truy vấn; thêm index rồi thì kế hoạch đổi thành SEARCH USING INDEX chỉ còn 0,014 mili-giây — nhanh hơn khoảng ba nghìn lần; tìm theo khóa chính INTEGER PRIMARY KEY còn nhanh hơn ở 0,003 mili-giây; và khi mọi cột cần lấy đều nằm trong index, kế hoạch thành COVERING INDEX, khỏi phải tra về cây bảng.