Database Design Mistakes I Made So You Don't Have To \u2014 TXT1.ai

March 2026 · 15 min read · 3,636 words · Last Updated: March 31, 2026Advanced

💡 Key Takeaways

  • The "I'll Just Use UUIDs Everywhere" Disaster
  • Premature Normalization: When Third Normal Form Becomes Your Enemy
  • The NULL Nightmare: When Optional Becomes Impossible
  • Index Overload: When More Isn't Better
Tôi sẽ viết bài blog chuyên gia này cho bạn dưới dạng một bài viết HTML toàn diện từ góc nhìn của một nhân vật cụ thể.

Ba năm trước, tôi đã chứng kiến cơ sở dữ liệu của công ty khởi nghiệp của chúng tôi dừng lại vào lúc 2:47 sáng vào Black Friday. Chúng tôi có 50,000 người dùng đồng thời, 2 triệu đô la trong các giao dịch chờ xử lý, và một truy vấn mất 45 giây để trả về tính khả dụng của sản phẩm. CTO của chúng tôi đang la hét trên Slack. Các nhà đầu tư của chúng tôi đang gọi điện. Và tôi đang nhìn chằm chằm vào một lược đồ mà tôi đã thiết kế sáu tháng trước, nhận ra rằng mọi quyết định "khôn ngoan" mà tôi đã đưa ra giờ đây đang tốn cho chúng tôi khoảng 8,000 đô la mỗi phút trong doanh thu bị mất.

💡 Những Điều Cần Nhớ

  • Thảm Họa "Tôi Chỉ Sử Dụng UUID Ở Mọi Nơi"
  • Chuẩn Hóa Sớm: Khi Hình Thức Chuẩn Thứ Ba Trở Thành Kẻ Thù Của Bạn
  • Cơn Ác Mộng NULL: Khi Tùy Chọn Trở Nên Không Thể
  • Quá Tải Chỉ Mục: Khi Nhiều Hơn Không Tốt Hơn

Tôi là Marcus Chen, và tôi đã dành mười hai năm qua làm kiến trúc sư cơ sở dữ liệu, làm việc với tất cả mọi người từ các công ty khởi nghiệp SaaS đến các doanh nghiệp Fortune 500. Tôi đã thiết kế các lược đồ cho các hệ thống xử lý 500 triệu giao dịch hàng ngày, tối ưu hóa các truy vấn đã giảm 200ms trên các đường đi quan trọng, và vâng—tôi đã mắc gần như mọi sai lầm trong thiết kế cơ sở dữ liệu trong sách. Sự cố Black Friday đó? Nó đã dạy tôi nhiều về thiết kế cơ sở dữ liệu hơn cả bằng cấp công nghệ thông tin của tôi.

Ngày nay, tôi là Kiến Trúc Sư Cơ Sở Dữ Liệu Chính tại TXT1.ai, nơi chúng tôi xử lý hơn 3 tỷ tin nhắn văn bản mỗi năm thông qua nền tảng giao tiếp được hỗ trợ bởi AI của chúng tôi. Nhưng tôi đã đến đây bằng cách tiến bộ qua thất bại, và tôi muốn chia sẻ những bài học tốn kém mà tôi đã học được để bạn có thể tránh các cuộc tấn công lo âu vào lúc 2 giờ sáng và những cuộc gọi giận dữ của nhà đầu tư.

Thảm Họa "Tôi Chỉ Sử Dụng UUID Ở Mọi Nơi"

Hãy để tôi bắt đầu với cái mà tôi gọi là sai lầm 40,000 đô la của tôi. Vào năm 2019, tôi đang thiết kế một hệ thống quản lý quan hệ khách hàng cho một công ty thương mại điện tử vừa. Tôi vừa đọc một bài viết blog về cách UUID là cách "hiện đại" để xử lý các khóa chính—không còn số nguyên tự tăng, không còn lộ ID tuần tự, hoàn hảo cho các hệ thống phân phối. Tôi đã bị thuyết phục.

Vì vậy, tôi đã biến mọi khóa chính trong hệ thống thành UUID. Bảng người dùng? UUID. Bảng đơn hàng? UUID. Các mặt hàng trong đơn hàng? Bạn đoán đúng—UUID. Tôi cảm thấy như một thiên tài. Lược đồ trông sạch sẽ, không có điểm yếu ID tuần tự, và tôi có thể tạo ID từ phía khách hàng nếu cần. Có thể có gì sai?

Tất cả mọi thứ. Hoàn toàn mọi thứ đã sai.

Chỉ trong sáu tháng, kích thước cơ sở dữ liệu của chúng tôi đã tăng vọt lên 340GB khi lẽ ra nó đã ở khoảng 180GB. Hiệu suất truy vấn đang giảm dần từng tuần. Kích thước chỉ mục của chúng tôi rất lớn—chỉ mục bảng đơn hàng một mình đã là 12GB. Các phép nối giữa đơn hàng và các mặt hàng trong đơn hàng lẽ ra mất 50ms nhưng giờ mất 800ms. Cơ sở dữ liệu đã dành một số lượng lớn thời gian cho I/O đĩa, và chi phí AWS RDS của chúng tôi gần như đã gấp đôi.

Dưới đây là điều tôi đã học được theo cách khó khăn: UUID có kích thước 128 bit (16 byte) so với số nguyên 4 byte hoặc bigint 8 byte. Điều đó có nghĩa là gấp 4 lần dung lượng lưu trữ cho mỗi khóa chính. Nhưng điều thực sự gây tử vong không phải là dung lượng lưu trữ—mà là sự phân mảnh chỉ mục. UUID là ngẫu nhiên, có nghĩa là mỗi lần chèn gây ra các ghi ngẫu nhiên trên các chỉ mục B-tree của bạn. Với các số nguyên tuần tự, các hàng mới sẽ được thêm vào cuối chỉ mục. Với UUID, cơ sở dữ liệu liên tục cân bằng lại toàn bộ cấu trúc chỉ mục.

Chúng tôi đã đo lường tác động: chèn 100,000 hàng với ID số nguyên mất 8 giây. Cùng một thao tác với UUID mất 34 giây. Đó là hình phạt hiệu suất 4.25x chỉ từ lựa chọn khóa chính. Khi bạn xử lý 50,000 đơn hàng mỗi ngày, điều đó cộng dồn rất nhanh.

Việc sửa chữa đã tiêu tốn của chúng tôi ba tuần phát triển và yêu cầu một di động được tổ chức cẩn thận trong suốt một khoảng thời gian bảo trì. Chúng tôi đã chuyển sang các khóa chính bigint cho các bảng có khối lượng lớn và chỉ giữ UUID cho những bảng mà chúng tôi thực sự cần các định danh toàn cầu duy nhất trong các hệ thống phân phối—mà cuối cùng chỉ có chính xác hai bảng trong bốn mươi bảy bảng.

Quy tắc của tôi bây giờ: Sử dụng các số nguyên tự động hoặc bigint cho các khóa chính trừ khi bạn có lý do cụ thể, được tài liệu hóa để sử dụng UUID. Và "có vẻ hiện đại hơn" không phải là một lý do được tài liệu hóa.

Chuẩn Hóa Sớm: Khi Hình Thức Chuẩn Thứ Ba Trở Thành Kẻ Thù Của Bạn

Mới ra trường, tôi đã cuồng nhiệt với việc chuẩn hóa. Tôi đã ghi nhớ tất cả các hình thức chuẩn, có thể đọc thuộc lòng các quy tắc của Codd ngay cả trong giấc ngủ, và tin rằng một cơ sở dữ liệu được chuẩn hóa đúng cách là đỉnh cao của xuất sắc trong thiết kế. Vì vậy, khi tôi thiết kế hệ thống sản xuất đầu tiên của mình—một nền tảng quản lý nội dung—tôi đã chuẩn hóa mọi thứ lên hình thức chuẩn thứ ba và hơn thế nữa.

"Mọi quyết định 'khôn ngoan' về cơ sở dữ liệu mà bạn đưa ra hôm nay là một cuộc khủng hoảng tiềm ẩn vào lúc 2 giờ sáng trong sáu tháng tới. Thiết kế cho hệ thống mà bạn sẽ có, không phải hệ thống mà bạn muốn."

Tôi có bảng người dùng, một bảng user_addresses (bởi vì người dùng có thể có nhiều địa chỉ), một bảng user_phone_numbers (nhiều điện thoại!), một bảng user_preferences, một bảng user_settings và một bảng user_metadata. Tải một hồ sơ người dùng duy nhất cần phải nối bốn bảng. Tôi rất tự hào về sự "sạch sẽ" của tất cả những điều đó.

Rồi chúng tôi ra mắt. Trang hồ sơ người dùng—trang được truy cập nhiều nhất trong toàn bộ ứng dụng—đã mất 1.2 giây để tải. Chúng tôi đang thực hiện sáu phép nối cho mỗi lần xem trang, và với 10,000 người dùng hoạt động hàng ngày, điều đó có nghĩa là 60,000 phép nối mỗi ngày chỉ cho việc xem hồ sơ. CPU của cơ sở dữ liệu liên tục vượt quá 70%.

Cú điện thoại cảnh tỉnh đến khi nhà phát triển chính của chúng tôi đã kéo tôi sang bên và cho tôi thấy kế hoạch thực thi truy vấn. "Marcus," anh ấy nói, "chúng ta đang nối sáu bảng chỉ để hiển thị tên, email và số điện thoại của một người dùng. Điều này thật điên rồ." Anh ấy đã đúng. Tôi đã tối ưu hóa cho sự tinh khiết lý thuyết thay vì hiệu suất thực tế.

Chúng tôi đã tiến hành chuẩn hóa một cách chiến lược. Địa chỉ chính của người dùng quay trở lại bảng người dùng. Số điện thoại chính của họ? Cũng vậy. Chúng tôi đã giữ lại các bảng riêng cho các địa chỉ và số điện thoại bổ sung, nhưng 94% người dùng của chúng tôi chỉ có một trong mỗi loại. Thay đổi đơn lẻ đó đã giảm thời gian trung bình của truy vấn trang hồ sơ từ 1.2 giây xuống còn 180ms—một cải thiện 85%.

Dưới đây là điều tôi đã học được: Chuẩn hóa là một công cụ, không phải một tôn giáo. Hình thức chuẩn thứ ba là một điểm khởi đầu tuyệt vời, nhưng hiệu suất thực tế thường yêu cầu chuẩn hóa chiến lược. Bây giờ tôi theo một quy tắc mà tôi gọi là "quy tắc chuẩn hóa 80/20"—nếu 80% các truy vấn cần dữ liệu từ nhiều bảng, thì dữ liệu đó có lẽ nên nằm trong một bảng. Tôi đo lường các mẫu truy vấn trong sản xuất và chuẩn hóa dựa trên việc sử dụng thực tế, không phải sự tinh khiết lý thuyết.

Điều quan trọng là biết khi nào nên chuẩn hóa. Các bảng có nhiều lượt đọc, ít ghi là những ứng viên hoàn hảo. Hồ sơ người dùng, danh mục sản phẩm, dữ liệu cấu hình—tất cả đều là những nơi tuyệt vời để chuẩn hóa. Các bảng giao dịch với khối lượng viết cao? Giữ cho chúng được chuẩn hóa để tránh những bất thường trong việc cập nhật.

Cơn Ác Mộng NULL: Khi Tùy Chọn Trở Nên Không Thể

Tôi đã từng yêu các cột có thể NULL. Chúng có vẻ rất linh hoạt, rất thoải mái. Một người dùng có thể không có tên đệm? Hãy để nó NULL. Một đơn hàng có thể không có mã giảm giá? NULL. Một sản phẩm có thể không có trọng lượng? Bạn hiểu rồi đấy.

Loại Khóa ChínhKích Thước Lưu TrữHiệu Suất Chỉ MụcTrường Hợp Sử Dụng Tốt Nhất
Số Nguyên Tự Tăng4 byteTuyệt vời (tuần tự)Các hệ thống máy chủ đơn, bảng có khối lượng lớn
BIGINT Tự Tăng8 byteTuyệt vời (tuần tự)Các hệ thống máy chủ đơn quy mô lớn
UUID (v4)16 byteKém (ngẫu nhiên)Các hệ thống phân phối, ID nhạy cảm với bảo mật
ULID/UUID (v7)16 byteTốt (thời gian được sắp xếp)Các hệ thống phân phối cần có khả năng sắp xếp
Khóa Tổng HợpKhác nhauKhá đến TốtCác mối quan hệ tự nhiên, hệ thống đa người thuê

Trong một dự án thất bại đặc biệt, tôi đã thiết kế một hệ thống quản lý tồn kho mà khoảng 60% các cột trên tất cả các bảng đều có thể NULL. Điều đó có vẻ hợp lý—không phải mọi trường đều sẽ luôn có dữ liệu đúng không? Tại sao phải buộc các giá trị mặc định khi NULL rõ ràng truyền đạt "không có giá trị"?

Các vấn đề bắt đầu ngay lập tức. Các truy vấn trở thành một mảnh đất minefield của các kiểm tra NULL. Bạn có muốn tìm tất cả các sản phẩm không có trọng lượng? Bạn có thể nghĩ rằng "WHERE weight IS NULL" sẽ hoạt động, nhưng còn các sản phẩm mà trọng lượng được đặt rõ ràng ở 0 thì sao? Bây giờ bạn cần "WHERE weight IS NULL OR weight = 0". Bạn có muốn cộng tổng số đơn hàng không? Tốt hơn hãy sử dụng COALESCE hoặc SUM của bạn có thể trả về NULL nếu bất kỳ giá trị đơn lẻ nào là NULL.

🛠 Khám Phá Các Công Cụ Của Chúng Tôi

Trình Nén JavaScript - Nén Mã JS Miễn Phí → Nhật Ký Thay Đổi — txt1.ai → Trình Nén CSS - Nén CSS Trực Tuyến Miễn Phí →
T

Written by the Txt1.ai Team

Our editorial team specializes in writing, grammar, and language technology. We research, test, and write in-depth guides to help you work smarter with the right tools.

Share This Article

Twitter LinkedIn Reddit HN

Related Tools

Code Diff Checker - Compare Two Files Side by Side Free Use Cases - TXT1 HTML to PDF Converter — Free, Accurate Rendering

Related Articles

AI Coding Tools in 2026: An Honest Assessment — txt1.ai TypeScript vs JavaScript in 2026: Which Should You Learn? — txt1.ai Clean Code: 10 Principles That Make You a Better Developer — txt1.ai

Put this into practice

Try Our Free Tools →

🔧 Explore More Tools

Ai Code AssistantGrammar CheckerUrl EncoderJwt DecoderDiff ViewerEssay Outliner

📬 Stay Updated

Get notified about new tools and features. No spam.