Databasepostgresdatabase

Thiết kế database schema bắt đầu từ đâu?

Hầu hết lập trình viên đều mắc sai lầm khi vội vàng tạo bảng mà chưa nghĩ đến hậu quả dài hạn. Bài viết này chỉ ra những lỗi phổ biến trong thiết kế schema và cách khắc phục trước khi đưa lên production.

Dong Nguyen

2 tháng 3, 2026

Thiết kế database schema bắt đầu từ đâu?

Hãy tưởng tượng: đội ngũ ra mắt sản phẩm mới, lượng truy cập tăng dần, mọi thứ ổn. Rồi đột nhiên một truy vấn mất 10 giây thay vì 100ms. Mọi người lao vào thêm index, layer cache, read replica… Nhưng chẳng cải thiện bao nhiêu.

Vấn đề gốc rễ không phải hiệu suất – mà là thiết kế database kém. Đến lúc nhận ra thì bảng đã có hàng trăm triệu dòng, và rồi các kênh chat nội bộ dành riêng cho “sự cố database” hoạt động 24/7. =))

Quyết định về database là một cuộc đánh cược dài hạn. Sai từ đầu → bạn sẽ phải chữa cháy suốt nhiều năm. Khi làm đúng → hệ thống tự động scale êm ru trong khi bạn tập trung build feature.

Schema sống lâu hơn code của bạn – hãy lập kế hoạch cho điều đó

Một cột chọn sai kiểu dữ liệu, ví dụ thiếu ràng buộc foreign key, hoặc denormalize sớm vì “có thể nhanh hơn”… Những lựa chọn nhỏ này khi build v1 trông vô hại. Nhưng database “vĩnh cửu” theo cách code không bao giờ có.

Bạn refactor service trong một cuối tuần. Nhưng migrate schema cho bảng 500 triệu dòng trên PostgreSQL? Đó là dự án kéo dài nhiều tháng: lock bảng, theo dõi lag replica, phối hợp chặt chẽ với team ops,...

Các đội ngũ thành công lâu dài đối xử với thiết kế bảng giống như kiến trúc code – thậm chí còn cẩn trọng hơn, vì bạn không thể rollback schema khi hàng chục triệu bản ghi người dùng phụ thuộc vào nó.

Bắt đầu từ thực tế, đừng bắt đầu từ CREATE TABLE

Hầu hết dev lao thẳng vào viết CREATE TABLE mà chưa hiểu rõ mình đang mô hình hóa cái gì.

Đừng làm vậy.

Hãy dành thời gian thực sự để map domain kinh doanh:

  • Entity thực tế là gì?
  • Mối quan hệ nào quan trọng với business?
  • Ràng buộc thực tế nào cần database phản ánh?

Ví dụ xây job board: Một job posting không chỉ là một dòng. Nó có vòng đời: draft → published → filled → expired. Nó thuộc về một công ty. Nó nhận application từ candidate. Mỗi application liên kết candidate với posting.

Database phải enforce những gì domain yêu cầu. Nếu schema cho phép trạng thái không tồn tại trong thực tế → sớm muộn dữ liệu sẽ bị đẩy vào trạng thái đó.

Normalization là bạn – cho đến khi không còn là bạn nữa

Normalization giúp loại bỏ dư thừa và vấn đề phụ thuộc: Mục tiêu là đạt 3NF – mọi cột phụ thuộc vào primary key, toàn bộ key, và chỉ key đó.

Thực tế: dữ liệu user ở một bảng, address ở bảng khác, order ở bảng thứ ba → join khi cần. Lợi ích:

  • Update email user chỉ thay đổi một chỗ.
  • Xóa order không làm mất thông tin customer.

Nhưng normalize quá mức sẽ giết hiệu suất đọc. Nếu trang profile load “user + 10 order gần nhất” mà phải join 5 bảng → bạn trả giá đắt mỗi request.

Quy tắc vàng: Bắt đầu normalize. Denormalize có chủ đích khi có dữ liệu hiệu suất thực tế.

Giải pháp thay thế:

  • Materialized view để flatten query, refresh khi write.
  • Hoặc tùy business case: duplicate cột cần thiết, dùng trigger để sync.

Denormalization là công cụ – dùng khi tradeoff rõ ràng.

Index: hợp đồng bạn ký với tương lai

Index giúp lookup nhanh bằng cách tránh full table scan. Không index trên users.email → query WHERE email = '...' phải quét hết bảng. Có index → nhảy thẳng đến dòng khớp.

Nhưng index không miễn phí:

  • Mỗi INSERT/UPDATE/DELETE phải update index → write chậm hơn.
  • Tốn disk space.
  • Index kém chọn lọc sẽ phình to theo thời gian, cần maintenance.

Chiến lược áp dụng:

  • Index foreign key và cột dùng trong WHERE, JOIN, ORDER BY.
  • Tránh index cột low cardinality (boolean, gender…).
  • Dùng composite index khi filter nhiều cột cùng lúc.
  • Theo dõi và rebuild index định kỳ (PostgreSQL: pg_stat_user_indexes).

Index có chủ đích. Dùng EXPLAIN ANALYZE để đo. Xóa index không dùng.

Constraints – tài liệu tốt nhất bạn từng viết

Foreign key, NOT NULL, UNIQUE, CHECK… không phải tùy chọn. Chúng encode business rule ngay trong database.

Ví dụ:

  • Foreign key đảm bảo referential integrity → không có order mồ côi.
  • NOT NULL → không cần defensive code check null.
  • CHECK (age > 0) hoặc CHECK (status IN ('active', 'inactive')) → validate tại write time.

Constraints tốn ít overhead write (thường <2%), nhưng mang lại độ đúng đảm bảo. Nếu database cho phép dữ liệu sai → sớm muộn bạn sẽ có dữ liệu sai.

Hiệu suất là lựa chọn thiết kế, không phải tuning

Nhiều team tốn hàng tuần chase query plan, thêm cache, tune pool. Thường thì vấn đề nằm ở schema.

Một redesign tốt có thể fix vĩnh viễn:

  • Di chuyển cột hay join vào bảng chính.
  • Tách bảng lớn thành hot/cold.
  • Covering index.
  • Partition theo thời gian.

Thiết kế database khó vì: bạn không biết hết context business từ đầu. Requirement thay đổi. Query pattern evolve.

Giải pháp không phải hoàn hảo ngay từ đầu – mà là thiết kế cho sự thay đổi:

  • Giữ đơn giản, đặt tên rõ ràng.
  • Tránh optimize sớm.
  • Không ngại migrate khi học được điều mới về domain hoặc access pattern.

Schema tốt chịu được tăng trưởng 10x với chỉnh sửa nhỏ. Schema kém → phải rewrite toàn bộ.

Điều tôi ước ai đó nói với mình sớm hơn

Nếu được khuyên bản thân trẻ:

  • Thực hành thiết kế schema cho hệ thống thật: LinkedIn, Twitter, Airbnb… Vẽ entity, relationship, nghĩ query trên giấy trước khi chạm database.
  • Học SQL sâu: không chỉ SELECT/JOIN, mà window function, CTE, query plan, execution order.
  • Bắt đầu đơn giản nhưng extensible: đừng build cho scale chưa có, nhưng để chỗ cho mở rộng.
  • Đo lường mọi thứ: query time, index usage, table size, slow log.

Các kỹ sư giỏi nhất coi thiết kế database như kiến trúc hệ thống: Họ nghĩ về hậu quả của quyết định. Lập kế hoạch cho failure mode. Xây hệ thống bền vững, không chỉ chạy tạm.

Ủng hộ tác giả

Nếu bạn thấy bài viết này hữu ích, hãy cân nhắc ủng hộ công việc của tôi. Điều đó giúp tôi tiếp tục tạo nội dung miễn phí cho cộng đồng lập trình viên.

Mua cho tôi ly cà phê

Cần hỗ trợ cho dự án của bạn?

Tôi sẵn sàng nhận việc freelance. Dù bạn cần ứng dụng full-stack, phát triển API hay tư vấn kỹ thuật, tôi rất vui được giúp đỡ.

Xem dịch vụ của tôi