Lưu trữ dạng cột thực chất là một dạng Chuẩn hóa dữ liệu
Bài viết phân tích mối liên hệ sâu sắc giữa lưu trữ dạng cột (columnar storage) và quá trình chuẩn hóa dữ liệu trong cơ sở dữ liệu quan hệ. Tác giả lập luận rằng việc tái tạo một hàng dữ liệu từ lưu trữ dạng cột thực chất là thực hiện một phép nối (join), cung cấp một mô hình tư duy mới về tối ưu hóa hiệu suất truy vấn.

Trong một thời gian dài, tôi đã lầm tưởng rằng việc chuyển đổi dữ liệu từ định dạng hướng hàng (row-oriented) sang định dạng hướng cột (column-oriented) là một khái niệm hoàn toàn xa lạ và tách biệt với thế giới cơ sở dữ liệu truyền thống. Thực tế, nó vẫn nằm trong phạm trù của trừu tượng quan hệ (relational abstraction). Hoặc ít nhất là nó có thể như vậy.
Để minh họa, hãy xem xét một bộ dữ liệu đơn giản về thú cưng:
data = [
{ "name": "Smudge", "colour": "black" },
{ "name": "Sissel", "colour": "grey" },
{ "name": "Hamlet", "colour": "black" }
]
Đây là đại diện cho một bảng trong cơ sở dữ liệu quan hệ. Cách biểu diễn dạng hàng này có những ưu điểm riêng. Nó rất dễ dàng để thêm một hàng mới: chúng ta chỉ cần xây dựng một đối tượng mới và thêm nó vào cuối danh sách. Trên đĩa, chúng ta có thể chỉ cần thao tác trên một vài trang dữ liệu để thực hiện việc này. Điều này cũng đúng với việc tra cứu một hàng. Vì tất cả các cột của một hàng được lưu kế nhau, việc lấy ra toàn bộ hàng đó diễn ra rất nhanh.
Tuy nhiên, nhược điểm xuất hiện khi chúng ta muốn thực hiện các tác vụ phân tích, ví dụ như tính toán biểu đồ phân phối màu sắc của các thú cưng. Để làm điều đó, chúng ta buộc phải đọc một lượng lớn dữ liệu không cần thiết (tên của chúng) mới có thể lấy được thông tin màu sắc.
So sánh cấu trúc dữ liệu dạng hàng và dạng cột
Ngược lại, một biểu diễn dạng cột sẽ trông như sau:
data = {
"name": ["Smudge", "Sissel", "Hamlet"],
"colour": ["black", "grey", "black"],
}
Đây là sự đánh đổi ngược lại hoàn toàn so với thiết kế dạng hàng. Nếu chúng ta chỉ quan tâm đến màu sắc, chúng ta có thể đọc rất hiệu quả chỉ dữ liệu đó mà không cần chạm đến tên của chúng. Tuy nhiên, việc sửa đổi dữ liệu hoặc đọc một hàng cụ thể trở nên khó khăn hơn. Chúng ta phải đi đến nhiều vị trí khác nhau để thực hiện cả hai thao tác này.
Một cách để suy nghĩ về việc định hình dữ liệu này là coi nó ở mức độ mã hóa (encoding level). Nó sống ở một mức độ trừu tượng thấp hơn nhiều so với mô hình dữ liệu: một công cụ SQL chạy trên nó không thể phân biệt được sự khác biệt giữa hai dạng này, ngoại trừ thông qua các đặc điểm hiệu suất của các truy vấn khác nhau.
Lưu trữ dạng cột là Chuẩn hóa cực đoan
Có một cách nhìn khác thú vị hơn: coi việc chuyển đổi sang dạng cột (columnarization) giống như một loại cực đoan của việc chuẩn hóa cơ sở dữ liệu (database normalization).
Thay vì một bảng rộng được đại diện bởi một loạt các vector dữ liệu, bạn có thể nghĩ rằng dữ liệu dạng cột là một tập hợp các bảng mà trong đó mỗi bảng chỉ có một khóa chính (primary key) và một thuộc tính bổ sung.
Hãy xem ví dụ về một bảng chưa chuẩn hóa:
Bảng chưa chuẩn hóa: +----+------+-----+ | id | name | age | +----+------+-----+ | 12 | Bob | 30 | | 93 | Tom | 35 | | 27 | Kim | 28 | +----+------+-----+
Sau khi áp dụng tư duy chuẩn hóa, chúng ta tách nó thành các bảng riêng biệt:
Các bảng đã chuẩn hóa: Bảng Name: +----+------+ | id | name | +----+------+ | 12 | Bob | | 93 | Tom | | 27 | Kim | +----+------+
Bảng Age: +----+-----+ | id | age | +----+-----+ | 12 | 30 | | 93 | 35 | | 27 | 28 | +----+-----+
Chúng ta có thể dễ dàng tái tạo lại bảng ban đầu bằng cách thực hiện phép nối (join) trên cột id.
Trong bối cảnh của một bảng được lưu trữ dạng cột, bạn có thể nghĩ rằng khóa chính chính là vị trí thứ tự (ordinal position) của một mảnh dữ liệu nhất định.
Dữ liệu gốc của chúng ta:
data = {
"name": ["Smudge", "Sissel", "Hamlet"],
"colour": ["black", "grey", "black"],
}
Sẽ trông như thế này nếu thêm cột id:
Bảng Name: +----+--------+ | id | name | +----+--------+ | 0 | Smudge | | 1 | Sissel | | 2 | Hamlet | +----+--------+
Bảng Colour: +----+--------+ | id | colour | +----+--------+ | 0 | black | | 1 | grey | | 2 | black | +----+--------+
Tuy nhiên, trong lưu trữ dạng cột, cột id chỉ được ngầm định bởi vị trí trong các mảng dữ liệu:
+--------+ | name | +--------+ | Smudge | | Sissel | | Hamlet | +--------+
+--------+ | colour | +--------+ | black | | grey | | black | +--------+
Kết luận
Giá trị của góc nhìn này là nó thống nhất nhiều thao tác xử lý truy vấn truyền thống, như phép chiếu (projections) và phép nối (joins), với việc thao tác trên định dạng dữ liệu. Đôi khi, bạn nên coi các định dạng dữ liệu này là một chi tiết triển khai mà các truy vấn về mặt logic không "thấy" được.
Tuy nhiên, đây là một mô hình tư duy hữu ích để nhận ra rằng "tái tạo một hàng từ lưu trữ dạng cột" không chỉ trông giống như đang thực hiện phép nối, mà nó chính là một phép nối.
Bài viết liên quan

Công nghệ
Sự bùng nổ của "Vibe-coded": Tại sao các dự án Show HN ngày càng giống nhau?
22 tháng 4, 2026

Phần mềm
DuckDB 1.5.2 ra mắt: Nâng cấp hiệu suất, hỗ trợ DuckLake v1.0 và Shell trực tuyến mới
22 tháng 4, 2026

Phần mềm
OpenAI hợp tác với Infosys để đưa công cụ AI đến nhiều doanh nghiệp hơn
22 tháng 4, 2026
