1. Database Normalization là gì?

Database Normalization (chuẩn hóa cơ sở dữ liệu) là quá trình tổ chức dữ liệu thành các bảng và thiết lập mối quan hệ phù hợp nhằm hạn chế dữ liệu trùng lặp không cần thiết, đồng thời giảm các vấn đề có thể xảy ra khi thêm, sửa hoặc xóa dữ liệu.

Trong một hệ thống Backend, dữ liệu thường liên quan đến nhiều thực thể như người dùng, sản phẩm, đơn hàng và danh mục. Nếu tất cả thông tin được lưu trong một bảng lớn, dữ liệu có thể bị lặp lại nhiều lần và trở nên khó quản lý.

Ví dụ, một bảng lưu đơn hàng có thể chứa các cột:

order_id customer_name product_name quantity
1001 An Keyboard 1
1001 An Mouse 2
1002 Bình Keyboard 1

Trong bảng trên, tên khách hàng An được lặp lại khi đơn hàng có nhiều sản phẩm. Tên sản phẩm Keyboard cũng xuất hiện nhiều lần.

Nếu tên sản phẩm thay đổi, hệ thống có thể phải cập nhật nhiều bản ghi. Nếu không cập nhật đầy đủ, dữ liệu sẽ không nhất quán.

Chuẩn hóa giúp tổ chức dữ liệu theo các thực thể và mối quan hệ phù hợp hơn.

2. Các vấn đề mà Normalization giải quyết

2.1. Data Redundancy — Dữ liệu trùng lặp

Dữ liệu trùng lặp xảy ra khi cùng một thông tin được lưu ở nhiều vị trí không cần thiết.

Ví dụ, nếu tên sản phẩm được lưu trong từng dòng đơn hàng, một sản phẩm xuất hiện trong nhiều đơn hàng sẽ khiến tên sản phẩm bị lặp lại.

Điều này làm tăng lượng dữ liệu cần quản lý và có thể khiến các bản ghi không đồng nhất.

2.2. Update Anomaly — Bất thường khi cập nhật

Update Anomaly xảy ra khi một thông tin được lưu ở nhiều vị trí và việc cập nhật không được thực hiện đồng bộ.

Ví dụ, tên sản phẩm Keyboard được lưu trong 100 dòng đơn hàng. Khi đổi tên sản phẩm, nếu chỉ sửa 99 dòng, dữ liệu sẽ tồn tại hai tên khác nhau cho cùng một sản phẩm.

2.3. Insertion Anomaly — Bất thường khi thêm dữ liệu

Insertion Anomaly xảy ra khi cấu trúc bảng khiến việc thêm một loại thông tin trở nên khó khăn hoặc buộc phải thêm những thông tin không cần thiết.

Ví dụ, nếu thông tin sản phẩm chỉ được lưu trong bảng đơn hàng, hệ thống có thể gặp khó khăn khi muốn thêm sản phẩm mới nhưng chưa có đơn hàng nào chứa sản phẩm đó.

2.4. Deletion Anomaly — Bất thường khi xóa dữ liệu

Deletion Anomaly xảy ra khi xóa một bản ghi làm mất luôn thông tin khác mà hệ thống vẫn cần giữ lại.

Ví dụ, nếu thông tin sản phẩm chỉ tồn tại trong các dòng đơn hàng, việc xóa dòng cuối cùng chứa sản phẩm đó có thể làm mất thông tin sản phẩm khỏi database.

Các bất thường trên là những lý do quan trọng khiến việc chuẩn hóa dữ liệu trở nên cần thiết.

3. First Normal Form (1NF) — Dạng chuẩn thứ nhất

Một bảng đạt First Normal Form (1NF) khi mỗi ô chứa một giá trị đơn lẻ theo mô hình dữ liệu đã chọn, thay vì lưu một danh sách các giá trị trong cùng một ô.

Xét bảng chưa đạt 1NF:

order_id product_names
1001 Keyboard, Mouse
1002 Keyboard, Monitor

Cột product_names chứa nhiều sản phẩm trong cùng một ô. Điều này khiến việc tìm kiếm, cập nhật hoặc thống kê từng sản phẩm trở nên khó khăn.

Có thể chuyển dữ liệu thành:

order_id product_name
1001 Keyboard
1001 Mouse
1002 Keyboard
1002 Monitor

Mỗi ô product_name giờ chỉ chứa một giá trị sản phẩm.

Ý nghĩa của 1NF: dữ liệu được tổ chức thành các giá trị riêng biệt, giúp việc truy vấn và xử lý từng giá trị dễ dàng hơn.

Lưu ý, 1NF không có nghĩa là mọi giá trị phải là một chuỗi đơn giản hoặc không được chứa dấu phẩy. Điều quan trọng là mỗi cột lưu một giá trị theo ý nghĩa dữ liệu của nó, thay vì gộp nhiều phần tử cần quản lý riêng thành một danh sách.

4. Second Normal Form (2NF) — Dạng chuẩn thứ hai

Một bảng đạt Second Normal Form (2NF) khi đã đạt 1NF và mọi thuộc tính không thuộc khóa đều phụ thuộc đầy đủ vào toàn bộ khóa ứng viên, không chỉ phụ thuộc vào một phần của khóa ghép.

Để hiểu điều này, xét bảng order_items:

order_id product_id product_name quantity
1001 101 Keyboard 1
1001 102 Mouse 2
1002 101 Keyboard 1

Giả sử khóa chính của bảng là khóa ghép (order_id, product_id).

Trong bảng này:

  • quantity phụ thuộc vào cả đơn hàng và sản phẩm trong đơn hàng.
  • product_name chỉ phụ thuộc vào product_id, không phụ thuộc vào toàn bộ khóa ghép.

Đây là phụ thuộc một phần (Partial Dependency), khiến bảng không đạt 2NF.

Ta có thể tách bảng thành hai bảng.

Bảng products

product_id product_name
101 Keyboard
102 Mouse

Bảng order_items

order_id product_id quantity
1001 101 1
1001 102 2
1002 101 1

Sau khi tách:

  • Thông tin tên sản phẩm được quản lý trong products.
  • Bảng order_items lưu sản phẩm nào thuộc đơn hàng nào và số lượng tương ứng.

Ý nghĩa của 2NF: loại bỏ sự phụ thuộc của thuộc tính không thuộc khóa vào chỉ một phần của khóa ghép.

Nếu bảng chỉ có các khóa ứng viên gồm một cột, bảng đã đạt 1NF thì không có phụ thuộc một phần vào khóa ghép; vì vậy, 2NF thường được quan tâm nhiều hơn khi thiết kế bảng có khóa ghép.

5. Third Normal Form (3NF) — Dạng chuẩn thứ ba

Một bảng đạt Third Normal Form (3NF) khi đã đạt 2NF và không có phụ thuộc bắc cầu không phù hợp từ khóa đến thuộc tính không thuộc khóa.

Xét bảng customers:

customer_id customer_name city_id city_name
1 An 10 Ho Chi Minh City
2 Bình 20 Da Nang
3 Chi 10 Ho Chi Minh City

Giả sử:

  • customer_id xác định duy nhất một khách hàng.
  • Mỗi khách hàng có một city_id.
  • city_id xác định city_name.

Khi đó, city_name phụ thuộc vào customer_id thông qua city_id. Đây là phụ thuộc bắc cầu.

Nếu tên thành phố thay đổi, nhiều bản ghi khách hàng có thể cần được cập nhật.

Ta có thể tách thành hai bảng.

Bảng customers

customer_id customer_name city_id
1 An 10
2 Bình 20
3 Chi 10

Bảng cities

city_id city_name
10 Ho Chi Minh City
20 Da Nang

Bảng customers chỉ lưu city_id, còn tên thành phố được quản lý tại bảng cities.

Ý nghĩa của 3NF: tránh lưu thuộc tính mô tả một thực thể khác theo cách tạo ra phụ thuộc bắc cầu không phù hợp, từ đó giảm trùng lặp và các bất thường dữ liệu.

Theo định nghĩa hình thức, với mọi phụ thuộc hàm không tầm thường (X \to A), bảng đạt 3NF nếu (X) là siêu khóa hoặc (A) là thuộc tính khóa. Vì vậy, không phải mọi phụ thuộc bắc cầu đều tự động vi phạm 3NF; cần xét các khóa và phụ thuộc hàm thực tế.

6. Denormalization — Phi chuẩn hóa

Denormalization là việc chủ động đưa dữ liệu trùng lặp hoặc dữ liệu đã tổng hợp vào thiết kế database để phục vụ một nhu cầu cụ thể, thường nhằm giảm chi phí truy vấn hoặc đơn giản hóa việc đọc dữ liệu.

Denormalization không đơn giản là thiết kế database sai chuẩn. Đây có thể là một quyết định có chủ đích khi đã hiểu rõ các đánh đổi.

Ví dụ, với thiết kế chuẩn hóa, để lấy tên khách hàng cùng thông tin đơn hàng, ứng dụng có thể cần JOIN:

SELECT
    orders.id,
    customers.customer_name,
    orders.total
FROM orders
JOIN customers
    ON orders.customer_id = customers.customer_id;

Trong một số hệ thống, người ta có thể lưu thêm customer_name vào bảng orders để phục vụ việc hiển thị thông tin đơn hàng mà không cần JOIN với bảng khách hàng trong mỗi truy vấn.

order_id customer_id customer_name
1001 1 An
1002 2 Bình

Tuy nhiên, cần phân biệt hai trường hợp:

  • Nếu customer_name được lưu để phản ánh tên hiện tại của khách hàng, dữ liệu có thể trở nên không đồng nhất khi tên khách hàng thay đổi.
  • Nếu customer_name được lưu để ghi nhận tên tại thời điểm đặt hàng, đây có thể là một yêu cầu nghiệp vụ hợp lý nhằm bảo tồn lịch sử đơn hàng.

Denormalization có thể hữu ích trong các hệ thống có nhiều truy vấn đọc, báo cáo hoặc dữ liệu tổng hợp. Tuy nhiên, nó cũng làm tăng dung lượng lưu trữ và độ phức tạp khi cập nhật dữ liệu.

Không nên phi chuẩn hóa chỉ vì nghĩ rằng ít JOIN hơn luôn đồng nghĩa với hiệu năng tốt hơn. Quyết định này nên dựa trên cách ứng dụng sử dụng dữ liệu và kết quả đo hiệu năng thực tế.

7. So sánh Normalization và Denormalization

Tiêu chí Normalization Denormalization
Mục tiêu chính Tổ chức dữ liệu và giảm trùng lặp không cần thiết Phục vụ nhu cầu truy vấn cụ thể
Cách tổ chức Tách dữ liệu thành các bảng có quan hệ phù hợp Có thể lưu trùng lặp hoặc dữ liệu tổng hợp
Truy vấn Có thể cần JOIN giữa nhiều bảng Một số truy vấn có thể ít JOIN hơn
Cập nhật Thường dễ duy trì một nguồn dữ liệu chính xác cho mỗi thuộc tính Có thể cần đồng bộ các bản sao dữ liệu
Trường hợp sử dụng Thiết kế dữ liệu nghiệp vụ thông thường Báo cáo, dữ liệu tổng hợp hoặc tối ưu đã được đánh giá

Normalization và Denormalization không nhất thiết loại trừ nhau. Một hệ thống có thể sử dụng thiết kế chuẩn hóa làm nền tảng và phi chuẩn hóa một số phần khi có nhu cầu cụ thể.

8. Database Normalization trong Backend

Khi xây dựng Backend bằng Go + Gin và PostgreSQL/MySQL, thiết kế database ảnh hưởng trực tiếp đến cách ứng dụng lưu và truy vấn dữ liệu.

Một kiến trúc phân lớp phổ biến:

Client
  |
  v
Handler
  |
  v
Service
  |
  v
Repository
  |
  v
Database
  • Handler: nhận HTTP request và trả HTTP response.
  • Service: xử lý các quy tắc nghiệp vụ.
  • Repository: thực hiện các truy vấn và thao tác dữ liệu.
  • Database: lưu dữ liệu và thực thi các ràng buộc được khai báo.

Normalization giúp tổ chức dữ liệu sao cho thông tin về người dùng, sản phẩm và đơn hàng được lưu tại những nơi phù hợp. Nhờ đó, Repository có thể truy vấn các bảng thông qua khóa và quan hệ đã thiết kế.

Ví dụ, thay vì lưu toàn bộ tên khách hàng và thông tin sản phẩm lặp lại trong mỗi dòng đơn hàng, hệ thống có thể sử dụng các bảng customers, products, orders và order_items.

Tuy nhiên, Normalization không thay thế việc kiểm tra nghiệp vụ trong Service, cũng không tự động bảo đảm mọi dữ liệu đều hợp lệ. Thiết kế database và logic Backend cần phối hợp với nhau để đáp ứng yêu cầu của ứng dụng.

9. Kết luận

Database Normalization là một phương pháp quan trọng để tổ chức dữ liệu có cấu trúc, giảm trùng lặp và hạn chế các bất thường khi thêm, sửa hoặc xóa dữ liệu.

  • 1NF: tổ chức dữ liệu thành các giá trị đơn lẻ theo mô hình đã chọn.
  • 2NF: loại bỏ phụ thuộc một phần vào khóa ghép.
  • 3NF: loại bỏ các phụ thuộc bắc cầu không phù hợp theo định nghĩa chuẩn hóa.
  • Denormalization: chủ động lưu dữ liệu trùng lặp hoặc tổng hợp khi có lý do cụ thể.

Đối với Backend Developer, hiểu Normalization giúp thiết kế database phù hợp với dữ liệu nghiệp vụ. Hiểu Denormalization giúp nhận biết khi nào có thể đánh đổi giữa sự đơn giản trong quản lý dữ liệu và nhu cầu truy vấn của hệ thống.

Nguồn tham khảo

Bài viết khác

ORM

ORM là gì? (Object-Relational Mapping) Trong quá trình phát triển Backend, ứng dụng thường xuyên cần tương tác với database để lưu trữ, truy vấn, cập nhật và xóa dữ liệu. Một cách phổ biến để thực hiện những thao tác này là viết SQL trực tiếp. Tuy nhiên, khi dự án phát triển, việc […]

N+1 Query Problem

N+1 Query Problem là gì? Trong quá trình phát triển Backend, ứng dụng thường cần truy xuất dữ liệu từ database để trả về cho client. Một API có thể cần lấy danh sách đơn hàng cùng thông tin khách hàng tương ứng. Tuy nhiên, nếu ứng dụng truy vấn dữ liệu liên quan riêng […]

Database Indexes

Database Indexes là gì? Trong Backend, database không chỉ cần lưu trữ dữ liệu mà còn phải tìm kiếm và truy xuất dữ liệu hiệu quả. Khi số lượng bản ghi tăng lên, những truy vấn đơn giản như tìm một người dùng theo email hoặc tìm sản phẩm theo tên cũng có thể mất […]

Transactions & ACID

Transactions & ACID là gì? Trong Backend, một request gửi đến API có thể yêu cầu hệ thống thực hiện nhiều thao tác trên database. Chẳng hạn, khi khách hàng đặt hàng, hệ thống có thể cần tạo đơn hàng, lưu chi tiết đơn hàng và cập nhật số lượng tồn kho. Nếu một thao […]

Database Design & Relationships là gì? Hướng dẫn thiết kế cơ sở dữ liệu cho Backend

1. Database Design là gì? Database Design (thiết kế cơ sở dữ liệu) là quá trình xác định cách tổ chức và liên kết dữ liệu để đáp ứng yêu cầu nghiệp vụ của một ứng dụng. Khi xây dựng Backend, chúng ta không chỉ cần viết API mà còn phải xác định dữ liệu […]

PostgreSQL và MySQL là gì? So sánh hai hệ quản trị cơ sở dữ liệu phổ biến

1. Tổng quan về cơ sở dữ liệu Khi xây dựng một ứng dụng phần mềm, dữ liệu cần được lưu trữ để có thể truy xuất, cập nhật và quản lý trong suốt quá trình hoạt động. Chẳng hạn, một ứng dụng bán hàng cần lưu thông tin sản phẩm, khách hàng, đơn hàng […]

Leave a Reply

Your email address will not be published. Required fields are marked *