Giao diện
Chuẩn hoá & phá chuẩn
Chia dữ liệu thành bảng và quan hệ sao cho mỗi sự thật chỉ lưu một chỗ. Phá chuẩn khi có lý do đo được, và luôn kèm cách giữ các bản sao đồng bộ.
Bắt đầu từ cách dữ liệu được dùng
- Thực thể và quan hệ: khách hàng, đơn hàng, sản phẩm; một khách có nhiều đơn, một đơn có nhiều sản phẩm.
- Bất biến nghiệp vụ: email không trùng, tồn kho không âm, đơn đã giao không quay về “chờ xác nhận”. Những điều này nên được database giữ bằng ràng buộc, không chỉ dựa vào code.
- Mẫu truy cập: màn hình nào đọc gì, ghi gì, bao nhiêu lần mỗi giây. Schema và index phục vụ các query thật, không phải sơ đồ đẹp.
- Vòng đời dữ liệu: giữ bao lâu, có phải lưu lịch sử thay đổi không (Versioning & lịch sử), có cần xoá theo yêu cầu người dùng không (Soft delete).
Chuẩn hoá: mỗi sự thật lưu một chỗ
Khi cùng một thông tin nằm ở nhiều dòng, sớm muộn các bản sao sẽ lệch nhau. Chuẩn hoá chia dữ liệu để tránh ba loại bất thường: sửa (phải sửa nhiều chỗ, sót một chỗ là sai), xoá (xoá một thứ làm mất luôn thông tin khác), thêm (không thêm được thứ này nếu chưa có thứ kia).
- 1NF: mỗi ô một giá trị nguyên tử. Không lưu
'1,5,9'trong một cột, không đặtphone1,phone2,phone3: tách thành bảng con. - 2NF: với khoá ghép, mọi cột phải phụ thuộc vào toàn bộ khoá. Trong
order_items(order_id, product_id, …), cộtproduct_namechỉ phụ thuộcproduct_id, nên thuộc về bảngproducts. - 3NF: cột không phụ thuộc bắc cầu qua cột khác.
orders.customer_phonephụ thuộc vàocustomer_idchứ không phải vào đơn hàng, nên thuộc vềcustomers. - BCNF: mọi phụ thuộc đều phải bắt nguồn từ một khoá ứng viên. Hiếm gặp khác biệt với 3NF trong thực tế; biết tên là đủ.
Thử với một bảng “phẳng” gộp mọi thứ vào một chỗ và bản đã chuẩn hoá:
Thao tác
4 dòng phải sửabảng phẳng
1 dòngđã chuẩn hoá
Bảng phẳng lặp số điện thoại của An ở mọi đơn: phải sửa 4 dòng, sót một dòng là An có hai số khác nhau (bất thường khi sửa). Bản chuẩn hoá chỉ sửa một dòng trong customers.
Bảng phẳng: orders_flat
| id | khách | SĐT | sản phẩm | giá | SL |
|---|---|---|---|---|---|
| 1 | An | 0999 | Áo thun | 150.000 | 2 |
| 2 | An | 0999 | Quần jean | 400.000 | 1 |
| 3 | Bình | 0902 | Áo thun | 150.000 | 1 |
| 4 | An | 0999 | Giày | 900.000 | 1 |
| 5 | Chi | 0903 | Quần jean | 400.000 | 2 |
| 6 | Bình | 0902 | Giày | 900.000 | 1 |
| 7 | Chi | 0903 | Áo thun | 150.000 | 3 |
| 8 | Dũng | 0904 | Mũ | 120.000 | 1 |
| 9 | An | 0999 | Áo thun | 150.000 | 1 |
Đã chuẩn hoá
customers
| id | tên | SĐT |
|---|---|---|
| 1 | An | 0999 |
| 2 | Bình | 0902 |
| 3 | Chi | 0903 |
| 4 | Dũng | 0904 |
products
| id | tên | giá |
|---|---|---|
| 1 | Áo thun | 150.000 |
| 2 | Quần jean | 400.000 |
| 3 | Giày | 900.000 |
| 4 | Mũ | 120.000 |
orders (rút gọn: mỗi đơn một sản phẩm)
| id | customer_id | product_id | unit_price | SL |
|---|---|---|---|---|
| 1 | 1 | 1 | 150.000 | 2 |
| 2 | 1 | 2 | 400.000 | 1 |
| 3 | 2 | 1 | 150.000 | 1 |
| 4 | 1 | 3 | 900.000 | 1 |
| 5 | 3 | 2 | 400.000 | 2 |
| 6 | 2 | 3 | 900.000 | 1 |
| 7 | 3 | 1 | 150.000 | 3 |
| 8 | 4 | 4 | 120.000 | 1 |
| 9 | 1 | 1 | 150.000 | 1 |
Phá chuẩn có chủ đích
Chuẩn hoá trước, phá chuẩn khi đo được rằng đường đọc nóng đang trả giá cho JOIN hoặc phép đếm. Mỗi bản sao là một thứ phải giữ đồng bộ.
| Kỹ thuật | Ví dụ | Giữ đồng bộ bằng |
|---|---|---|
| Cột đếm | posts.comment_count thay cho COUNT(*) mỗi lần hiển thị | Cập nhật trong cùng transaction với lệnh ghi comment, hoặc qua CDC |
| Bảng tổng hợp | Doanh thu theo ngày tính sẵn | Job định kỳ, hoặc cập nhật tăng dần qua CDC |
| Chép cột để hiển thị | products.seller_name để trang danh sách không phải join | Cập nhật khi tên đổi + job đối soát định kỳ |
| Read model riêng | Bảng hoặc index search dựng sẵn cho một màn hình | CDC / event (CQRS) |
Snapshot không phải phá chuẩn
order_items.unit_price trông giống bản sao của products.price, nhưng thực chất là một sự thật khác: giá tại thời điểm đặt hàng. Đổi giá sản phẩm không được làm đổi giá trong các đơn cũ. Tương tự với địa chỉ giao hàng, tên sản phẩm trên hoá đơn, tỷ giá áp dụng.
Câu hỏi để phân biệt: khi bản gốc đổi, giá trị này có phải đổi theo không? Có thì đó là bản sao (phá chuẩn, phải đồng bộ). Không thì đó là snapshot (dữ liệu lịch sử, không bao giờ cập nhật).
Ghi nhớ
- Chuẩn hoá tới 3NF trước; phá chuẩn khi đo được, kèm cơ chế đồng bộ.
- Ba bất thường: sửa, xoá, thêm.
- Giá, địa chỉ trong đơn là snapshot, không phải dữ liệu thừa.