Cơ Bản Về Cơ Sở Dữ Liệu Quan Hệ: Database Thực Sự Hoạt Động Như Thế Nào

Giới Thiệu
Bạn viết SELECT * FROM users WHERE id = 1 và nhận được kết quả. Nhưng thực sự điều gì xảy ra bên trong database giữa câu query và response? Database tìm một dòng đó giữa hàng triệu dòng bằng cách nào? Tại sao một số query chạy trong vài mili-giây trong khi những query khác mất vài phút?
Hầu hết backend developer tương tác với database thông qua ORM và không bao giờ tìm hiểu sâu hơn. Điều đó ổn — cho đến khi query bị chậm, dữ liệu bị hỏng, hoặc bạn cần thiết kế schema không sụp đổ khi scale.
Hướng dẫn này đưa bạn vào bên trong database engine. Bạn sẽ hiểu mô hình quan hệ, cách query thực sự thực thi, ACID thực sự có nghĩa là gì, và cách đưa ra quyết định đúng đắn về data types, constraints, và database engines.
Bạn Sẽ Học Được Gì
✅ Hiểu mô hình quan hệ — tables, rows, columns, và relationships
✅ Xem điều gì xảy ra bên trong database khi bạn chạy SQL query
✅ Nắm vững primary keys, foreign keys, và constraints
✅ Chọn đúng data types cho các columns
✅ Hiểu thuộc tính ACID với ví dụ thực tế
✅ Học transactions và isolation levels
✅ Biết khi nào dùng PostgreSQL vs MySQL vs SQLite
✅ Thiết kế schema e-commerce đơn giản từ đầu
Mô Hình Quan Hệ (Relational Model)
Năm 1970, Edgar F. Codd công bố bài báo thay đổi ngành khoa học máy tính. Ông đề xuất tổ chức dữ liệu thành quan hệ (tables) với tuples (rows) và attributes (columns). Đây trở thành mô hình quan hệ — nền tảng của gần như mọi database bạn sử dụng ngày nay.
Tables, Rows, và Columns
Một table (hay quan hệ) là tập hợp dữ liệu có cấu trúc về một thực thể cụ thể:
CREATE TABLE users (
id SERIAL PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
email VARCHAR(255) NOT NULL UNIQUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);| Khái niệm | Tên khác | Mô tả |
|---|---|---|
| Table | Relation | Tập hợp dữ liệu liên quan (vd: users, orders) |
| Row | Tuple, Record | Một bản ghi trong table (vd: một user) |
| Column | Attribute, Field | Thuộc tính của thực thể (vd: username, email) |
| Schema | — | Định nghĩa cấu trúc table (tên cột, kiểu dữ liệu, constraints) |
Quan Hệ Giữa Các Tables
Từ "quan hệ" trong cơ sở dữ liệu quan hệ đề cập đến cách các tables liên kết với nhau.
One-to-Many (phổ biến nhất):
-- Một user có nhiều orders
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
user_id INT NOT NULL REFERENCES users(id),
total DECIMAL(10, 2) NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'pending',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);One-to-One:
-- Một user có một profile
CREATE TABLE profiles (
id SERIAL PRIMARY KEY,
user_id INT NOT NULL UNIQUE REFERENCES users(id),
bio TEXT,
avatar VARCHAR(500)
);Many-to-Many:
-- Products có nhiều tags, tags có nhiều products
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name VARCHAR(200) NOT NULL
);
CREATE TABLE tags (
id SERIAL PRIMARY KEY,
name VARCHAR(50) NOT NULL UNIQUE
);
-- Bảng trung gian (junction table)
CREATE TABLE product_tags (
product_id INT NOT NULL REFERENCES products(id),
tag_id INT NOT NULL REFERENCES tags(id),
PRIMARY KEY (product_id, tag_id)
);Điều Gì Xảy Ra Khi Bạn Chạy SQL Query
Khi bạn thực thi một query, database không chỉ đơn giản "tìm" dữ liệu. Nó đi qua pipeline nhiều giai đoạn.
Pipeline Thực Thi Query
SQL Query
│
▼
┌──────────┐
│ Parser │ → Kiểm tra cú pháp, xây dựng parse tree
└──────────┘
│
▼
┌──────────┐
│ Analyzer │ → Xác nhận tên table/column, giải quyết kiểu dữ liệu
└──────────┘
│
▼
┌──────────┐
│ Planner │ → Tạo các execution plans, chọn plan rẻ nhất
└──────────┘
│
▼
┌──────────┐
│ Executor │ → Chạy plan đã chọn, trả về kết quả
└──────────┘
│
▼
┌──────────┐
│ Storage │ → Đọc/ghi data pages trên disk
└──────────┘Parser
Parser kiểm tra SQL có đúng cú pháp không và chuyển thành parse tree:
-- Parse thành công
SELECT username FROM users WHERE id = 1;
-- Lỗi tại parser (sai cú pháp)
SELEC username FORM users WHER id = 1;
-- ERROR: syntax error at or near "SELEC"Analyzer
Analyzer kiểm tra tables và columns có tồn tại không, giải quyết kiểu dữ liệu, và mở rộng wildcards như *.
Planner (Query Optimizer)
Đây là giai đoạn thú vị nhất. Planner tạo nhiều execution plans và chọn plan có chi phí ước tính thấp nhất.
Bạn có thể xem quyết định của planner bằng EXPLAIN:
EXPLAIN SELECT * FROM users WHERE id = 1;
-- Output:
-- Index Scan using users_pkey on users (cost=0.29..8.30 rows=1 width=100)
-- Index Cond: (id = 1)Executor và Storage Engine
Executor chạy plan đã chọn. Storage engine quản lý cách dữ liệu được lưu trữ vật lý trên disk trong các pages (thường là 8KB). Nó sử dụng buffer cache trong bộ nhớ để tăng tốc truy xuất.
Primary Keys, Foreign Keys, và Constraints
Constraints là các quy tắc được database thực thi để giữ dữ liệu nhất quán.
Primary Key
Mỗi table nên có primary key — cột (hoặc tổ hợp cột) xác định duy nhất mỗi row.
CREATE TABLE users (
id SERIAL PRIMARY KEY,
username VARCHAR(50) NOT NULL
);Foreign Key
Foreign key tạo liên kết giữa hai tables và thực thi referential integrity:
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
user_id INT NOT NULL REFERENCES users(id),
total DECIMAL(10, 2) NOT NULL
);
-- Thành công (user 1 tồn tại)
INSERT INTO orders (user_id, total) VALUES (1, 99.99);
-- Thất bại (user 999 không tồn tại)
INSERT INTO orders (user_id, total) VALUES (999, 99.99);
-- ERROR: violates foreign key constraintCASCADE behavior:
-- ON DELETE CASCADE → Xóa user sẽ xóa luôn orders
-- ON DELETE RESTRICT → Ngăn xóa user nếu có orders (mặc định)
-- ON DELETE SET NULL → Đặt user_id thành NULL khi user bị xóaCác Constraints Khác
- NOT NULL: Ngăn dữ liệu bị thiếu — mặc định nên
NOT NULL, chỉ cho phépNULLkhi có lý do - UNIQUE: Đảm bảo không trùng lặp (vd: email, username)
- CHECK: Xác thực giá trị (
CHECK (price > 0)) - DEFAULT: Cung cấp giá trị mặc định (
DEFAULT NOW())
Chọn Đúng Data Types
Numeric Types
| Kiểu | Dung lượng | Phạm vi | Use Case |
|---|---|---|---|
SMALLINT | 2 bytes | -32,768 đến 32,767 | Status codes |
INTEGER | 4 bytes | -2.1 tỷ đến 2.1 tỷ | Hầu hết IDs |
BIGINT | 8 bytes | ±9.2 quintillion | IDs bảng lớn |
DECIMAL(p,s) | Thay đổi | Tùy chỉnh | Tiền, tính chính xác |
Quan trọng: Không bao giờ dùng REAL hoặc DOUBLE PRECISION cho tiền:
-- ❌ SAI: Floating point cho tiền
SELECT 0.1 + 0.2; -- 0.30000000000000004
-- ✅ ĐÚNG: DECIMAL cho tiền
SELECT 0.1::DECIMAL(10,2) + 0.2::DECIMAL(10,2); -- 0.30String Types
VARCHAR(n): Chuỗi có giới hạn — dùng cho username, emailTEXT: Chuỗi không giới hạn — dùng cho blog content, descriptions
Trong PostgreSQL, VARCHAR và TEXT có hiệu suất giống nhau.
Date và Time Types
Luôn dùng TIMESTAMPTZ (timestamp with time zone):
-- ❌ SAI: Không có timezone
created_at TIMESTAMP NOT NULL DEFAULT NOW()
-- ✅ ĐÚNG: Có timezone
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()JSON/JSONB
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name VARCHAR(200) NOT NULL,
metadata JSONB DEFAULT '{}'::JSONB
);Khi nào dùng JSONB: Thuộc tính linh hoạt khác nhau mỗi row, dữ liệu từ API bên ngoài.
Khi nào KHÔNG dùng JSONB: Dữ liệu bạn thường xuyên filter, sort, hoặc join — dùng columns thông thường.
Thuộc Tính ACID
ACID đảm bảo transactions trong database đáng tin cậy.
A — Atomicity (Tính Nguyên Tử)
Transaction là all-or-nothing. Tất cả thành công, hoặc tất cả thất bại.
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE user_id = 1; -- Alice
UPDATE accounts SET balance = balance + 100 WHERE user_id = 2; -- Bob
COMMIT;
-- Cả hai update thành công, hoặc không cái nào thực hiệnC — Consistency (Tính Nhất Quán)
Database chuyển từ trạng thái hợp lệ này sang trạng thái hợp lệ khác. Constraints không bao giờ bị vi phạm.
I — Isolation (Tính Cô Lập)
Các transactions đồng thời không ảnh hưởng lẫn nhau. Mỗi transaction thấy snapshot nhất quán.
D — Durability (Tính Bền Vững)
Khi transaction đã commit, dữ liệu tồn tại qua mọi sự cố — mất điện, crash, hỏng phần cứng. Database dùng Write-Ahead Log (WAL) để đảm bảo điều này.
Transactions và Isolation Levels
Transaction Là Gì?
Transaction là nhóm operations thực thi như một đơn vị duy nhất:
BEGIN;
INSERT INTO orders (user_id, total) VALUES (1, 150.00);
INSERT INTO order_items (order_id, product_id, quantity) VALUES (1, 42, 2);
UPDATE products SET stock = stock - 2 WHERE id = 42;
COMMIT;Isolation Levels
| Level | Dirty Reads | Non-repeatable Reads | Performance |
|---|---|---|---|
| Read Uncommitted | Có thể | Có thể | Nhanh nhất |
| Read Committed | Không | Có thể | Tốt (mặc định) |
| Repeatable Read | Không | Không | Trung bình |
| Serializable | Không | Không | Chậm nhất |
Khuyến nghị: Dùng Read Committed (mặc định) cho hầu hết ứng dụng. Dùng Serializable chỉ cho giao dịch tài chính quan trọng.
PostgreSQL vs MySQL vs SQLite
| Tính năng | PostgreSQL | MySQL | SQLite |
|---|---|---|---|
| Loại | Server-based | Server-based | Embedded (file) |
| Concurrent writes | Xuất sắc | Tốt | Yếu |
| Advanced SQL | Đầy đủ | Hầu hết | Cơ bản |
| JSONB | Native, indexable | JSON (yếu hơn) | JSON functions |
| Full-text search | Built-in | Built-in | Extension |
| Tốt nhất cho | Production chung | Web apps đọc nhiều | Dev/test/embedded |
PostgreSQL: Lựa chọn mặc định cho mọi ứng dụng production. Nhiều tính năng nâng cao nhất.
MySQL: Rất nhanh cho read queries đơn giản. Phổ biến trong hệ sinh thái PHP/WordPress.
SQLite: Không cần server, chỉ là một file. Hoàn hảo cho development, testing, mobile apps.
Thực Hành: Thiết Kế Schema E-Commerce
Áp dụng mọi thứ đã học bằng cách thiết kế schema cho ứng dụng e-commerce đơn giản.
-- Users
CREATE TABLE users (
id SERIAL PRIMARY KEY,
email VARCHAR(255) NOT NULL UNIQUE,
username VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Categories (phân cấp)
CREATE TABLE categories (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
parent_id INT REFERENCES categories(id),
UNIQUE (name, parent_id)
);
-- Products
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name VARCHAR(200) NOT NULL,
description TEXT,
price DECIMAL(10, 2) NOT NULL CHECK (price > 0),
stock INT NOT NULL DEFAULT 0 CHECK (stock >= 0),
category_id INT REFERENCES categories(id),
is_active BOOLEAN NOT NULL DEFAULT TRUE,
metadata JSONB DEFAULT '{}'::JSONB,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Tags (many-to-many)
CREATE TABLE tags (
id SERIAL PRIMARY KEY,
name VARCHAR(50) NOT NULL UNIQUE
);
CREATE TABLE product_tags (
product_id INT NOT NULL REFERENCES products(id) ON DELETE CASCADE,
tag_id INT NOT NULL REFERENCES tags(id) ON DELETE CASCADE,
PRIMARY KEY (product_id, tag_id)
);
-- Orders
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
user_id INT NOT NULL REFERENCES users(id),
status VARCHAR(20) NOT NULL DEFAULT 'pending'
CHECK (status IN ('pending', 'confirmed', 'shipped', 'delivered', 'cancelled')),
total DECIMAL(10, 2) NOT NULL CHECK (total >= 0),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Order Items
CREATE TABLE order_items (
id SERIAL PRIMARY KEY,
order_id INT NOT NULL REFERENCES orders(id) ON DELETE CASCADE,
product_id INT NOT NULL REFERENCES products(id),
quantity INT NOT NULL CHECK (quantity > 0),
unit_price DECIMAL(10, 2) NOT NULL CHECK (unit_price > 0),
UNIQUE (order_id, product_id)
);Quyết Định Thiết Kế Quan Trọng
unit_pricetrongorder_items: Lưu giá tại thời điểm mua, không tham chiếu giá hiện tại- Status dùng CHECK constraint: Database ngăn giá trị không hợp lệ
- Categories tự tham chiếu:
parent_idcho phân cấp - JSONB cho metadata: Thuộc tính biến đổi mà không cần thêm nhiều nullable columns
Lỗi Thường Gặp Cần Tránh
- Dùng sai kiểu ID — Dùng
BIGSERIALcho tables có thể lớn - Thiếu NOT NULL — Mặc định nên
NOT NULL, chỉ cho phépNULLkhi cần - Lưu tiền dạng float — Luôn dùng
DECIMALcho tiền - Không có foreign keys — Database nên thực thi referential integrity
- TIMESTAMP không có timezone — Luôn dùng
TIMESTAMPTZ
Tóm Tắt và Điểm Chính
✅ Mô hình quan hệ tổ chức dữ liệu thành tables với rows và columns, liên kết bằng relationships
✅ SQL queries đi qua 5 giai đoạn: parser → analyzer → planner → executor → storage
✅ Query planner là chìa khóa hiệu suất — nó chọn cách truy cập dữ liệu
✅ Constraints bảo vệ tính toàn vẹn dữ liệu ở tầng database
✅ Chọn data types cẩn thận: DECIMAL cho tiền, TIMESTAMPTZ cho ngày giờ
✅ ACID đảm bảo transactions đáng tin cậy
✅ Read Committed là mặc định phù hợp cho hầu hết ứng dụng
✅ PostgreSQL là lựa chọn mặc định tốt nhất cho production
✅ Dùng transactions cho operations nhiều bước phải thành công hoặc thất bại cùng nhau
Tiếp Theo
Bây giờ bạn đã hiểu cách database hoạt động, tiếp tục với các bài tiếp theo trong series:
📚 Database Schema Design Guide — Học normalization, denormalization, và design patterns
📚 SQL Indexing Deep Dive — Làm queries nhanh với indexes đúng
📚 SQL Query Optimization Guide — Viết queries hiệu quả và tránh pitfalls
Kết Luận
Database không phải hộp đen. Hiểu cách chúng hoạt động bên trong — từ query execution pipeline đến ACID — giúp bạn trở thành backend developer tốt hơn. Bạn sẽ viết schema tốt hơn, debug vấn đề hiệu suất nhanh hơn, và đưa ra quyết định đúng đắn.
Mô hình quan hệ đã tồn tại hơn 50 năm vì nó hoạt động. Tables, relationships, constraints, và transactions cung cấp nền tảng đáng tin cậy cho ứng dụng ở mọi quy mô. Nắm vững những kiến thức nền tảng này, và mọi thứ khác — ORMs, migrations, optimization — sẽ dễ hiểu hơn nhiều.
Bắt đầu với PostgreSQL, dùng constraints thoải mái, và luôn bọc operations nhiều bước trong transactions.
📬 Đăng ký nhận bản tin
Nhận những bài viết mới nhất qua email mỗi tuần. Không spam, hủy đăng ký bất cứ lúc nào.
Chúng tôi tôn trọng quyền riêng tư của bạn. Hủy đăng ký bất cứ lúc nào.
💬 Comments
Đăng nhập để bình luận
Chúng tôi sẽ không đăng bất cứ điều gì mà không có sự cho phép của bạn.