Ba thứ chạy ngon trên máy bạn rồi vỡ ở production: schema, thời gian, và tải.Three things work fine on your machine and break in production: schema, time, and load.
Mỗi nhóm hỏng theo một nhịp riêng. Schema hỏng chậm, qua nhiều năm. Thời gian hỏng theo lịch, vào những đêm đổi giờ. Tải hỏng trong một đêm cao điểm. Tài liệu này gom 30 khái niệm từ ba infographic, đã kiểm lại và bổ sung những gì còn thiếu.Each group fails on its own rhythm. Schema fails slowly, over years. Time fails on a calendar, on the nights clocks change. Load fails on one peak night. This guide gathers 30 concepts from three infographics, checked and filled in where they fell short.
Schema: chi phí sửa tăng dần theo nămSchema: the cost of change grows every year
Thời gian: hỏng vào hai đêm đổi giờ mỗi nămTime: breaks on two clock-change nights a year
Tải: hỏng trong một đêm cao điểmLoad: breaks on one peak night
Năm 1Year 1Năm 2Year 2Năm 3Year 3
Chọn cái nàoWhich one do I need
Trước hết: có cần gì không?First: do you need any of it?
Phần lớn dự án chỉ cần phương án rẻ nhất dưới đây. Thêm khái niệm khi có một vấn đề thật, không phải để phòng xa.Most projects only need the cheapest option below. Add a concept when a real problem shows up, not just in case.
Mô hình dữ liệuData modeling
Không cần xoá mềm, liên kết đa hình hay bảng tra cứu khi: ứng dụng nhỏ, ít bảng, không ai cần khôi phục dữ liệu, và danh sách giá trị gần như không đổi.You don't need soft deletes, polymorphic associations or lookup tables when the app is small, has few tables, nobody needs to restore data, and value lists barely change.
Phương án rẻ nhất: khoá chính bigint, khoá ngoại có index, created_at/updated_at kiểu timestamptz, kiểu dữ liệu đúng, text cộng CHECK cho giá trị cố định, và một công cụ migration ngay từ file đầu tiên. Phần còn lại thêm khi có nhu cầu thật.The cheapest option: bigint primary keys, indexed foreign keys, created_at/updated_at as timestamptz, correct data types, text with CHECK for fixed values, and a migration tool from the very first file. Add the rest when a real need appears.
Thời gian và múi giờTime and time zones
Không cần nghĩ tới giây nhuận, PTP hay logical clock khi: hệ thống chỉ chạy trên một cụm máy, không cần độ chính xác dưới giây, và không so sánh timestamp giữa các máy để quyết định thứ tự.You don't need to think about leap seconds, PTP or logical clocks when the system runs on one cluster, needs no sub-second accuracy, and never orders writes by comparing timestamps across machines.
Phương án rẻ nhất: server ở UTC, cột timestamptz, lưu tên múi giờ IANA của người dùng, dùng thư viện ngày giờ chuẩn của ngôn ngữ, bật chrony. Chừng đó đã loại được phần lớn lỗi thời gian.The cheapest option: servers in UTC, timestamptz columns, the user's IANA zone name stored, your language's standard date library, chrony enabled. That alone removes most time bugs.
Kiểm thử hiệu năngPerformance testing
Không cần load test bài bản khi: traffic thấp và ổn định (công cụ nội bộ vài chục người dùng), không có sự kiện cao điểm, và chậm một chút cũng không mất tiền.You don't need a formal load test when traffic is low and steady (an internal tool with a few dozen users), there are no peak events, and being a bit slow costs nothing.
Phương án rẻ nhất: đo P95 và P99 thật trên production bằng APM hoặc log, đặt cảnh báo, và chạy một smoke test k6 khoảng 5 phút trước các release lớn.The cheapest option: measure real P95 and P99 in production through APM or logs, set alerts, and run a five-minute k6 smoke test before major releases.
Cây quyết địnhDecision tree
Bạn đang làm gì? Tìm dòng khớp với câu hỏi của bạn; mã trong ngoặc dẫn tới thẻ chi tiết.What are you doing? Find the line that matches your question; the code in brackets points to the detailed card.
Thiết kế bảng
Một X có nhiều Y, mỗi Y thuộc một Xmột–nhiều (D2)
Cả hai phía đều là "nhiều"bảng nối (D3)
Y gắn vào nhiều loại Xcột FK riêng + CHECK trước, đa hình là lựa chọn cuối (D6)
Tập giá trị cố định
code rẽ nhánh theo từng giá trịtext + CHECK hoặc enum (D7)
người vận hành tự thêmbảng tra cứu (D7)
Cần khôi phục thứ đã xoáxoá mềm + partial unique (D5)
Tiền, id, thời điểmNUMERIC/BIGINT, BIGINT, timestamptz (D10)
Lưu hoặc tính thời gian
Sự kiện đã xảy raUTC, timestamptz (T1)
Hẹn tương lai theo giờ người dùnggiờ địa phương + tên IANA (T1, T6)
Đo "bao lâu"đồng hồ monotonic (T8)
Cộng "ngày" hoặc "tháng"phép tính theo lịch (T9)
So timestamp giữa các máyNTP + sai số cho phép (T7, T10)
Kiểm tra hiệu năng
"Chịu được bao nhiêu?"ramp-up / stress (P3)
"Chạy lâu có hỏng không?"soak (P4)
"Cả triệu người vào cùng lúc?"spike (P5)
"Chậm ở đâu?"tìm nút thắt (P6)
"Bản mới có tệ hơn không?"baseline + budget (P9, P10)
Designing tables
One X has many Y, each Y has one Xone-to-many (D2)
Both sides are "many"join table (D3)
Y attaches to several kinds of Xseparate FK columns + CHECK first, polymorphic as a last resort (D6)
Fixed set of values
code branches on each valuetext + CHECK or enum (D7)
Something that already happenedUTC, timestamptz (T1)
Future event on the user's clocklocal time + IANA name (T1, T6)
Measuring "how long"monotonic clock (T8)
Adding "days" or "months"calendar arithmetic (T9)
Comparing timestamps across machinesNTP + tolerance (T7, T10)
Checking performance
"How much can it take?"ramp-up / stress (P3)
"Does it break over time?"soak (P4)
"A million people at once?"spike (P5)
"Where is it slow?"bottleneck hunt (P6)
"Is the new version worse?"baseline + budget (P9, P10)
Mô hình dữ liệuData modeling
Schema là quyết định bạn phải sống cùng trong nhiều năm.Your schema is a decision you'll live with for years.
Mười quyết định định hình cách dữ liệu được lưu. Chúng rẻ khi làm đúng ở ngày đầu và rất đắt khi phải sửa trên một bảng đã có trăm triệu dòng.Ten decisions that shape how data is stored. They're cheap to get right on day one and very expensive to fix on a table with a hundred million rows.
D1Mô hình dữ liệuData modeling
Khoá chính và khoá ngoạiPrimary vs foreign keys
Khoá chínhPrimary keyKhoá ngoại trỏ tớiForeign key points to it
Khoá chính định danh duy nhất một dòng; khoá ngoại trỏ tới nó và được database kiểm tra.A primary key uniquely identifies a row; a foreign key points to it and the database enforces the link.
Dùng choUse for
Đơn hàng thuộc về khách hàng; bình luận thuộc về bài viết; nhân viên thuộc về phòng banOrders belonging to customers; comments belonging to posts; employees belonging to departments
ĐượcGain
Chặn dữ liệu mồ côi ngay ở tầng lưu trữ, không phụ thuộc vào việc code ứng dụng có nhớ kiểm tra hay không.Blocks orphaned rows at the storage layer, regardless of whether application code remembers to check.
MấtLose
Mỗi lần ghi phải kiểm tra thêm một lần, bulk insert chậm đi thấy rõ.Every write pays for an extra lookup; bulk inserts get noticeably slower.
Đánh đổiTrade-offChọn ràng buộc khoá ngoại là đổi một phần tốc độ ghi và sự linh hoạt khi tách database lấy bảo đảm không bao giờ có dữ liệu mồ côi.Choosing foreign key constraints trades some write speed and freedom to split databases for a guarantee that orphaned rows never exist.
Dấu hiệu nhận biếtHow to recognize it:Nếu một dòng mất ý nghĩa khi dòng kia biến mất, bạn cần khoá ngoại.If a row stops making sense once another row disappears, you need a foreign key.
Xem chi tiết: cách hoạt động, lợi ích, hạn chế, mẹoDetails: how it works, benefits, limits, tips
users orders
+----+------+ +----+---------+-------+
| id | name |<------| id | user_id | total |
+----+------+ FK +----+---------+-------+
PK PK
Nó là gìWhat it is
Khoá chính (primary key) là cột hoặc nhóm cột xác định duy nhất mỗi dòng: không trùng, không NULL. Khoá ngoại (foreign key) là cột ở bảng khác chứa giá trị của khoá chính đó, kèm một ràng buộc khiến database từ chối mọi giá trị không tồn tại.A primary key is the column or set of columns that uniquely identifies each row: no duplicates, no NULLs. A foreign key is a column in another table that holds that primary key's value, plus a constraint that makes the database reject any value that doesn't exist.
Hoạt động thế nàoHow it works
Mỗi lần ghi orders.user_id, database tra index của users.id; không thấy thì báo lỗi. Khi xoá một user, mệnh đề ON DELETE quyết định chuyện gì xảy ra với các đơn hàng: chặn lại (RESTRICT), xoá theo (CASCADE) hay đặt về NULL (SET NULL).Every write to orders.user_id looks up the index on users.id; if the value is missing, the write fails. When a user is deleted, the ON DELETE clause decides what happens to their orders: block the delete (RESTRICT), delete them too (CASCADE), or set the column to NULL (SET NULL).
Lợi íchBenefits
Chặn dữ liệu mồ côi ngay ở tầng lưu trữ, không phụ thuộc vào việc code ứng dụng có nhớ kiểm tra hay không.Blocks orphaned rows at the storage layer, regardless of whether application code remembers to check.
Quan hệ được ghi thẳng vào schema, nên công cụ vẽ ERD và ORM đọc được.The relationship lives in the schema, so ERD tools and ORMs can read it.
ON DELETE CASCADE dọn dữ liệu con trong cùng một transaction.ON DELETE CASCADE cleans up child rows inside the same transaction.
Hạn chếLimitations
Mỗi lần ghi phải kiểm tra thêm một lần, bulk insert chậm đi thấy rõ.Every write pays for an extra lookup; bulk inserts get noticeably slower.
Khoá ngoại không vượt được ranh giới giữa hai database, nên cản đường khi shard hoặc tách service.Foreign keys can't cross database boundaries, which gets in the way of sharding or splitting services.
CASCADE dùng bất cẩn có thể xoá hàng triệu dòng chỉ bằng một lệnh.A careless CASCADE can delete millions of rows with one statement.
Mẹo triển khaiImplementation tips
Luôn tạo index cho cột khoá ngoại. PostgreSQL không tự tạo, và thiếu nó thì mỗi lần xoá ở bảng cha sẽ quét toàn bộ bảng con.Always index foreign key columns. PostgreSQL doesn't create one for you, and without it every delete on the parent scans the whole child table.
Mặc định dùng khoá nhân tạo (bigint identity hoặc UUIDv7) làm khoá chính; khoá tự nhiên như email thì đặt UNIQUE.Default to a surrogate key (bigint identity or UUIDv7) as the primary key; make natural keys like email UNIQUE.
Chọn ON DELETE có chủ đích cho từng khoá ngoại, đừng để mặc định rồi quên.Pick ON DELETE deliberately for every foreign key instead of leaving the default and forgetting it.
D2Mô hình dữ liệuData modeling
Quan hệ một–nhiềuOne-to-many
Một dòngOne rowNhiều dòng tham chiếuMany referencing rows
Một dòng ở bảng cha được nhiều dòng ở bảng con tham chiếu; khoá ngoại đặt ở phía nhiều.One parent row is referenced by many child rows; the foreign key lives on the many side.
Dùng choUse for
Khách hàng và đơn hàng; bài viết và bình luận; hoá đơn và các dòng hàngCustomers and orders; posts and comments; invoices and line items
ĐượcGain
Quan hệ phổ biến và đơn giản nhất: một cột, một index.The most common and simplest relationship: one column, one index.
MấtLose
Tải danh sách cha kèm con qua ORM rất dễ sinh lỗi N+1 query.Loading parents with their children through an ORM is the classic source of N+1 queries.
Đánh đổiTrade-offChọn một–nhiều là đổi khả năng để một Y thuộc nhiều X lấy schema đơn giản nhất có thể.Choosing one-to-many trades the ability for a Y to belong to several X for the simplest possible schema.
Dấu hiệu nhận biếtHow to recognize it:Hỏi hai câu: một X có nhiều Y không, và một Y có chỉ thuộc một X không. Cả hai đều có thì đó là một–nhiều.Ask two questions: can one X have many Y, and does each Y belong to only one X? Two yeses mean one-to-many.
Xem chi tiết: cách hoạt động, lợi ích, hạn chế, mẹoDetails: how it works, benefits, limits, tips
customers 1 ─────────< N orders
(orders.customer_id → customers.id)
Nó là gìWhat it is
Một dòng cha có thể có nhiều dòng con, nhưng mỗi dòng con chỉ thuộc về đúng một cha. Cài đặt bằng một cột khoá ngoại ở bảng con.A parent row can have many children, but each child belongs to exactly one parent. You implement it with a single foreign key column on the child table.
Hoạt động thế nàoHow it works
Khoá ngoại luôn nằm ở phía nhiều. Câu hỏi "các đơn của khách X" chỉ là WHERE customer_id = X, nhanh nếu cột đó có index.The foreign key always sits on the many side. "Orders for customer X" is just WHERE customer_id = X, fast as long as that column is indexed.
Lợi íchBenefits
Quan hệ phổ biến và đơn giản nhất: một cột, một index.The most common and simplest relationship: one column, one index.
Truy vấn theo cả hai chiều đều rẻ.Queries in both directions are cheap.
JOIN dễ đọc, dễ tối ưu.JOINs stay readable and easy to optimize.
Hạn chếLimitations
Tải danh sách cha kèm con qua ORM rất dễ sinh lỗi N+1 query.Loading parents with their children through an ORM is the classic source of N+1 queries.
Nếu sau này một Y cần thuộc nhiều X, bạn phải migrate sang nhiều–nhiều.If a Y later needs to belong to several X, you have to migrate to many-to-many.
Bảng con lớn buộc phải phân trang, không thể tải hết.Large child tables force pagination; you can't load everything.
Mẹo triển khaiImplementation tips
Đặt tên cột theo vai trò, không theo tên bảng (author_id thay vì user_id), nhất là khi một bảng có nhiều khoá ngoại tới cùng một bảng.Name the column after its role, not the table (author_id rather than user_id), especially when a table has several foreign keys to the same table.
Bật log truy vấn khi phát triển để bắt N+1 sớm; dùng eager loading hoặc JOIN.Turn on query logging in development to catch N+1 early; use eager loading or a JOIN.
Nếu phía nhiều luôn nhỏ và luôn đọc cùng cha (vài địa chỉ giao hàng), có thể dùng cột JSONB, nhưng chỉ khi không bao giờ cần truy vấn riêng các phần tử.If the many side is always small and always read with its parent (a few shipping addresses), a JSONB column can work, but only if you never need to query the items on their own.
D3Mô hình dữ liệuData modeling
Quan hệ nhiều–nhiềuMany-to-many
Bảng ATable ABảng nốiJoin tableBảng BTable B
Không bên nào tự giữ được liên kết, nên cần một bảng nối chứa từng cặp.Neither side can hold the link on its own, so a join table stores each pair.
Dùng choUse for
Sinh viên và môn học; sản phẩm và tag; người dùng và vai tròStudents and courses; products and tags; users and roles
ĐượcGain
Mô hình đúng quan hệ thật, không lặp dữ liệu.Models the real relationship without duplicating data.
MấtLose
Mọi truy vấn qua quan hệ cần hai JOIN.Every query across the relationship needs two JOINs.
Đánh đổiTrade-offChọn bảng nối là đổi thêm một bảng và hai JOIN mỗi truy vấn lấy quan hệ hai chiều chính xác, không trùng lặp.Choosing a join table trades an extra table and two JOINs per query for an exact, duplicate-free two-way relationship.
Dấu hiệu nhận biếtHow to recognize it:Nếu đặt khoá ngoại ở bên nào cũng buộc phải nhân bản dòng, đó là nhiều–nhiều.If putting the foreign key on either side forces you to duplicate rows, it's many-to-many.
Xem chi tiết: cách hoạt động, lợi ích, hạn chế, mẹoDetails: how it works, benefits, limits, tips
students enrollments courses
+----+ +------------+-----------+ +----+
| id |<------| student_id | course_id |->| id |
+----+ +------------+-----------+ +----+
PK (student_id, course_id)
Nó là gìWhat it is
Mỗi dòng A liên kết với nhiều dòng B và ngược lại. Đặt khoá ngoại ở bên nào cũng phải lặp dữ liệu, nên liên kết được tách ra một bảng trung gian chứa cặp (a_id, b_id).Each A links to many B and vice versa. Putting the foreign key on either side would duplicate rows, so the link moves to an intermediate table holding (a_id, b_id) pairs.
Hoạt động thế nàoHow it works
Mỗi liên kết là một dòng trong bảng nối. Khoá chính ghép trên hai cột chặn việc cùng một cặp xuất hiện hai lần.Every link is one row in the join table. A composite primary key over both columns stops the same pair from appearing twice.
Lợi íchBenefits
Mô hình đúng quan hệ thật, không lặp dữ liệu.Models the real relationship without duplicating data.
Bảng nối là chỗ tự nhiên để lưu thuộc tính của chính quan hệ, như ngày đăng ký hay điểm số.The join table is the natural home for attributes of the relationship itself, such as enrollment date or grade.
Thêm hay bỏ một liên kết chỉ là insert hoặc delete một dòng.Adding or removing a link is a single insert or delete.
Hạn chếLimitations
Mọi truy vấn qua quan hệ cần hai JOIN.Every query across the relationship needs two JOINs.
Bảng nối thường trở thành bảng lớn nhất database.Join tables often become the largest tables in the database.
ORM giấu bảng nối đi, đến khi cần thêm cột thì phải viết lại mô hình.ORMs hide the join table, so adding a column later means rewriting the model.
Mẹo triển khaiImplementation tips
Tạo thêm index theo chiều ngược (course_id, student_id); khoá chính chỉ phục vụ truy vấn bắt đầu từ cột đầu tiên.Add the reverse index (course_id, student_id); the primary key only serves queries that start from its first column.
Đặt tên bảng nối theo nghiệp vụ (enrollments, memberships) vì sớm muộn nó cũng có thêm cột.Name the join table after the business concept (enrollments, memberships); sooner or later it will grow columns.
Đừng lưu mảng id trong một cột: mất khoá ngoại và khó truy vấn.Don't store an array of ids in one column: you lose the foreign key and querying gets painful.
D4Mô hình dữ liệuData modeling
Khoá ghépComposite keys
Nhiều cộtMultiple columnsMột dòng duy nhấtOne unique row
Chỉ tổ hợp của nhiều cột mới duy nhất; từng cột riêng thì có thể trùng.Only the combination of several columns is unique; each column alone may repeat.
Dùng choUse for
Bảng nối nhiều–nhiều; dòng hàng trong đơn (order_id, line_no); dữ liệu nhiều tenant (tenant_id, id); chuỗi thời gian (sensor_id, ts)Many-to-many join tables; order lines (order_id, line_no); multi-tenant data (tenant_id, id); time series (sensor_id, ts)
ĐượcGain
Ràng buộc nghiệp vụ nằm ngay trong schema, không cần cột id thừa.The business rule lives in the schema with no redundant id column.
MấtLose
Khoá ngoại trỏ tới phải mang đủ mọi cột, và gánh nặng đó lan sang các bảng con.Foreign keys pointing at it must carry every column, and that weight spreads to child tables.
Đánh đổiTrade-offChọn khoá ghép làm khoá chính là đổi sự tiện lợi khi tham chiếu lấy ràng buộc duy nhất nằm ngay trong khoá.Choosing a composite primary key trades convenient references for a uniqueness rule built into the key itself.
Dấu hiệu nhận biếtHow to recognize it:Nếu câu trả lời cho "điều gì làm dòng này là duy nhất" cần chữ "và", đó là khoá ghép.If the answer to "what makes this row unique" needs the word "and", it's a composite key.
Xem chi tiết: cách hoạt động, lợi ích, hạn chế, mẹoDetails: how it works, benefits, limits, tips
Khoá ghép (composite key) gồm hai cột trở lên. Từng cột có thể lặp lại, nhưng tổ hợp giá trị của chúng thì không.A composite key spans two or more columns. Each column may repeat on its own, but their combined values may not.
Hoạt động thế nàoHow it works
Database tạo một index nhiều cột. Thứ tự cột quyết định index phục vụ được truy vấn nào: index (a, b) dùng được cho điều kiện trên a, nhưng không dùng được cho điều kiện chỉ trên b.The database builds a multi-column index. Column order decides which queries it can serve: an index on (a, b) helps filters on a, but not filters on b alone.
Lợi íchBenefits
Ràng buộc nghiệp vụ nằm ngay trong schema, không cần cột id thừa.The business rule lives in the schema with no redundant id column.
Index ghép phục vụ luôn các truy vấn lọc theo cột đầu.The composite index doubles as the index for filters on its leading column.
Với multi-tenant, dữ liệu của cùng tenant nằm cạnh nhau trên đĩa.In multi-tenant systems, one tenant's rows sit next to each other on disk.
Hạn chếLimitations
Khoá ngoại trỏ tới phải mang đủ mọi cột, và gánh nặng đó lan sang các bảng con.Foreign keys pointing at it must carry every column, and that weight spreads to child tables.
URL, API và ORM làm việc với khoá nhiều cột kém tiện hơn hẳn.URLs, APIs and ORMs are far clumsier with multi-column keys.
Sai thứ tự cột thì index vô dụng cho truy vấn chính.Get the column order wrong and the index is useless for your main query.
Mẹo triển khaiImplementation tips
Cách thực dụng hay gặp: id nhân tạo làm khoá chính, cộng UNIQUE (cot_a, cot_b) để giữ ràng buộc.The common pragmatic setup: a surrogate id as primary key, plus UNIQUE (col_a, col_b) to keep the rule.
Đặt cột lọc thường xuyên nhất lên đầu, thường là tenant_id.Put the most frequently filtered column first, usually tenant_id.
Với bảng nối, cứ dùng khoá ghép làm khoá chính; ở đó nó gần như luôn đúng.For join tables, use the composite key as the primary key; there it's almost always right.
D5Mô hình dữ liệuData modeling
Xoá mềmSoft deletes
Dòng bị đánh dấu xoáRow marked deletedDữ liệu vẫn cònData still there
Đánh dấu một dòng là đã xoá thay vì xoá thật; mọi truy vấn đọc phải tự lọc nó ra.Mark a row as deleted instead of removing it; every read has to filter it out.
Dùng choUse for
Thùng rác để khôi phục thứ người dùng lỡ xoá; dữ liệu có nghĩa vụ lưu trữ theo quy định; giữ dữ liệu cho báo cáo lịch sửA trash bin so users can restore mistakes; records you're required to retain; keeping rows for historical reporting
ĐượcGain
Hoàn tác tức thì, không cần backup.Instant undo without touching backups.
MấtLose
Quên một điều kiện WHERE là dữ liệu đã xoá hiện ra lại, và lỗi này rất im lặng.Forget one WHERE and deleted data reappears, silently.
Đánh đổiTrade-offChọn xoá mềm là đổi sự đơn giản của mọi truy vấn và cam kết xoá thật lấy khả năng hoàn tác.Choosing soft deletes trades simple queries and a real deletion guarantee for the ability to undo.
Dấu hiệu nhận biếtHow to recognize it:Nếu sẽ có người hỏi "khôi phục lại được không?", hãy cân nhắc xoá mềm.If someone will ask "can we get that back?", consider soft deletes.
Xem chi tiết: cách hoạt động, lợi ích, hạn chế, mẹoDetails: how it works, benefits, limits, tips
Nó là gìWhat it is
Thay vì DELETE, ứng dụng ghi thời điểm xoá vào một cột như deleted_at. Dòng vẫn nằm trong bảng, chỉ bị ẩn khỏi người dùng.Instead of DELETE, the application writes a timestamp into a column like deleted_at. The row stays in the table and is simply hidden from users.
Hoạt động thế nàoHow it works
"Xoá" là UPDATE ... SET deleted_at = now(). Mọi truy vấn đọc phải thêm WHERE deleted_at IS NULL, thường qua scope của ORM hoặc một view."Delete" becomes UPDATE ... SET deleted_at = now(). Every read must add WHERE deleted_at IS NULL, usually through an ORM scope or a view.
Lợi íchBenefits
Hoàn tác tức thì, không cần backup.Instant undo without touching backups.
Không làm gãy khoá ngoại hay dữ liệu lịch sử đang tham chiếu tới dòng đó.Doesn't break foreign keys or historical data that reference the row.
Điều tra sự cố dễ hơn vì dấu vết còn nguyên.Incident investigation is easier because the evidence is still there.
Hạn chếLimitations
Quên một điều kiện WHERE là dữ liệu đã xoá hiện ra lại, và lỗi này rất im lặng.Forget one WHERE and deleted data reappears, silently.
Phá ràng buộc UNIQUE: không đăng ký lại được email đã "xoá" nếu không dùng partial index.It breaks UNIQUE: a "deleted" email can't be registered again without a partial index.
Không phải xoá thật, nên không đáp ứng yêu cầu xoá dữ liệu cá nhân theo GDPR hay luật bảo vệ dữ liệu cá nhân của Việt Nam.It isn't real deletion, so it doesn't satisfy erasure requirements under GDPR or similar personal data laws.
Mẹo triển khaiImplementation tips
Dùng partial unique index: UNIQUE (email) WHERE deleted_at IS NULL.Use a partial unique index: UNIQUE (email) WHERE deleted_at IS NULL.
Đọc qua view active_users hoặc Row-Level Security thay vì tin rằng lập trình viên nào cũng nhớ thêm WHERE.Read through an active_users view or Row-Level Security instead of trusting every developer to remember the WHERE.
Nếu chỉ cần lịch sử cho audit, bảng archive hoặc bảng lịch sử thường sạch hơn.If you only need history for auditing, an archive or history table is usually cleaner.
Lên lịch xoá thật sau N ngày.Schedule a hard delete after N days.
D6Mô hình dữ liệuData modeling
Liên kết đa hìnhPolymorphic associations
Cặp cột type + idA type + id pairNhiều loại bảngSeveral table types
Một cặp cột (type, id) trỏ tới nhiều loại bảng khác nhau; database không kiểm tra được liên kết.A (type, id) column pair points at several different tables; the database can't enforce the link.
Dùng choUse for
Bình luận, lượt thích, tag dùng chung cho nhiều loại nội dung; thông báo trỏ tới nhiều loại sự kiện; tệp đính kèm cho nhiều loại đối tượngComments, likes or tags shared across content types; notifications pointing at different kinds of events; attachments for many kinds of objects
ĐượcGain
Một bảng dùng chung cho mọi loại cha; thêm loại mới không cần migration.One table serves every parent type; adding a type needs no migration.
MấtLose
Không có khoá ngoại, nên dữ liệu mồ côi chỉ là chuyện sớm hay muộn.No foreign key, so orphaned rows are a matter of when, not if.
Đánh đổiTrade-offChọn liên kết đa hình là đổi toàn vẹn tham chiếu do database bảo đảm lấy sự linh hoạt thêm loại cha mà không đổi schema.Choosing polymorphic associations trades database-enforced referential integrity for adding parent types without schema changes.
Dấu hiệu nhận biếtHow to recognize it:Nếu bạn sắp viết "thuộc về bài viết hoặc ảnh hoặc video", bạn đang nghĩ tới liên kết đa hình.If you're about to write "belongs to a post or a photo or a video", you're thinking of a polymorphic association.
Đã hiệu chỉnh so với ảnhCorrected from the image.Ảnh ghi "một cột". Thực tế cần một cặp cột (loại và id), và điểm quan trọng nhất ảnh không nhắc: database không thể dùng khoá ngoại để kiểm tra liên kết này.The image says "one column". In practice it's a pair of columns (type and id), and the most important point is missing: the database can't enforce this link with a foreign key.
Xem chi tiết: cách hoạt động, lợi ích, hạn chế, mẹoDetails: how it works, benefits, limits, tips
comments
+----+-------------+-----------+
| id | target_type | target_id |
+----+-------------+-----------+
| 1 | 'post' | 42 | --> posts.id ?
| 2 | 'photo' | 42 | --> photos.id ?
+----+-------------+-----------+
(no real FK: the database cannot check it)
Nó là gìWhat it is
Một bảng như comments có thể gắn vào nhiều loại bảng cha (posts, photos, videos) bằng hai cột: target_type ghi tên loại và target_id ghi id bên trong loại đó.A table like comments attaches to several kinds of parent (posts, photos, videos) through two columns: target_type names the kind and target_id holds the id within it.
Hoạt động thế nàoHow it works
Ứng dụng đọc target_type để biết tra bảng nào, rồi tìm target_id trong bảng đó. Với database, đó chỉ là một chuỗi và một con số.The application reads target_type to choose a table, then looks up target_id in it. To the database it's just a string and a number.
Lợi íchBenefits
Một bảng dùng chung cho mọi loại cha; thêm loại mới không cần migration.One table serves every parent type; adding a type needs no migration.
Code gọn, nhiều ORM hỗ trợ sẵn (Rails, Laravel).Compact code, with built-in support in several ORMs (Rails, Laravel).
Truy vấn kiểu "mọi bình luận của một user" chỉ chạm một bảng.Queries like "all comments by a user" touch a single table.
Hạn chếLimitations
Không có khoá ngoại, nên dữ liệu mồ côi chỉ là chuyện sớm hay muộn.No foreign key, so orphaned rows are a matter of when, not if.
Không JOIN bình thường được; phải JOIN có điều kiện theo type.No plain JOINs; every JOIN has to branch on type.
Đổi tên class hay bảng là phải cập nhật chuỗi type trong dữ liệu.Renaming a class or table means rewriting the type strings in your data.
Mẹo triển khaiImplementation tips
Phương án giữ được khoá ngoại: nhiều cột khoá ngoại nullable (post_id, photo_id) cộng một CHECK bảo đảm đúng một cột khác NULL.An alternative that keeps foreign keys: several nullable FK columns (post_id, photo_id) plus a CHECK that exactly one is non-NULL.
Hoặc tạo một bảng cha chung (commentables) mà mọi loại nội dung chia sẻ id.Or create a shared parent table (commentables) whose id every content type shares.
Nếu vẫn dùng: index (target_type, target_id) và chạy job định kỳ quét dòng mồ côi.If you use it anyway: index (target_type, target_id) and run a periodic orphan sweep.
D7Mô hình dữ liệuData modeling
Enum và bảng tra cứuEnum vs lookup table
Enum: đổi cần migrationEnum: change needs a migrationBảng: thêm dòng tự doTable: grows freely
Enum khoá tập giá trị trong schema; bảng tra cứu cho thêm giá trị bằng một lệnh INSERT.An enum locks the allowed values into the schema; a lookup table lets you add values with an INSERT.
Dùng choUse for
Enum: trạng thái mà code phải xử lý từng nhánh (pending, paid, shipped); bảng tra cứu: danh mục do bộ phận vận hành quản lý (tỉnh thành, loại sản phẩm); bảng tra cứu: giá trị cần nhãn nhiều ngôn ngữEnum: states that code branches on (pending, paid, shipped); lookup table: catalogs managed by operations staff (regions, product categories); lookup table: values that need labels in several languages
ĐượcGain
Enum gọn, nhanh, không cần JOIN, và ánh xạ an toàn sang kiểu trong ngôn ngữ lập trình.Enums are compact and fast, need no JOIN, and map cleanly to types in your language.
MấtLose
Xoá hay sắp xếp lại giá trị enum rất khó; PostgreSQL không có lệnh xoá giá trị enum.Removing or reordering enum values is hard; PostgreSQL has no command to drop an enum value.
Đánh đổiTrade-offChọn enum là đổi khả năng thay đổi không cần migration lấy sự chặt chẽ và gọn nhẹ.Choosing an enum trades changes without migrations for strictness and compactness.
Dấu hiệu nhận biếtHow to recognize it:Thêm một giá trị mới có buộc phải sửa code không? Có thì enum, không thì bảng tra cứu.Does adding a new value force a code change? Yes means enum; no means lookup table.
Đã hiệu chỉnh so với ảnhCorrected from the image.Ảnh nói enum "cần deploy để đổi". Chính xác hơn là cần một migration schema. PostgreSQL thêm được giá trị bằng ALTER TYPE ... ADD VALUE và đổi tên được, nhưng không xoá được giá trị.The image says an enum "needs a deploy to change". More precisely it needs a schema migration. PostgreSQL can add values with ALTER TYPE ... ADD VALUE and rename them, but it can't remove one.
Xem chi tiết: cách hoạt động, lợi ích, hạn chế, mẹoDetails: how it works, benefits, limits, tips
Hai cách giới hạn một cột vào tập giá trị cho phép. Với enum, tập giá trị là một phần của định nghĩa kiểu. Với bảng tra cứu (lookup table), mỗi giá trị là một dòng trong bảng riêng, và cột tham chiếu tới nó bằng khoá ngoại.Two ways to restrict a column to a set of allowed values. With an enum, the set is part of the type definition. With a lookup table, each value is a row in its own table and the column references it through a foreign key.
Hoạt động thế nàoHow it works
Enum được kiểm tra bởi chính kiểu dữ liệu, nên đổi tập giá trị là một migration. Bảng tra cứu thêm giá trị bằng INSERT, và mỗi giá trị mang được thêm nhãn, thứ tự, cờ ngừng sử dụng.An enum is checked by the type itself, so changing the set is a migration. A lookup table adds values with an INSERT, and each value can carry a label, sort order or an active flag.
Lợi íchBenefits
Enum gọn, nhanh, không cần JOIN, và ánh xạ an toàn sang kiểu trong ngôn ngữ lập trình.Enums are compact and fast, need no JOIN, and map cleanly to types in your language.
Bảng tra cứu cho người vận hành tự thêm giá trị mà không cần lập trình viên.Lookup tables let operations add values without a developer.
Bảng tra cứu chứa được metadata: nhãn, màu, thứ tự, trạng thái ngừng dùng.Lookup tables hold metadata: labels, colors, ordering, retirement status.
Hạn chếLimitations
Xoá hay sắp xếp lại giá trị enum rất khó; PostgreSQL không có lệnh xoá giá trị enum.Removing or reordering enum values is hard; PostgreSQL has no command to drop an enum value.
Bảng tra cứu thêm một JOIN, và nếu code vẫn hardcode id thì mất hết lợi thế.Lookup tables add a JOIN, and if code still hardcodes ids the benefit is gone.
Với cả hai, giá trị mới xuất hiện trong database trước khi code cũ biết tới nó sẽ gây lỗi khi đọc.With either, a new value that reaches the database before old code knows about it breaks reads.
Mẹo triển khaiImplementation tips
Lựa chọn thứ ba ít rủi ro nhất: cột text cộng CHECK (status IN (...)); đổi tập giá trị chỉ là một migration nhẹ.The lowest-risk third option: a text column with CHECK (status IN (...)); changing the set is a light migration.
Trong bảng tra cứu, để code tham chiếu qua cột code dạng chuỗi ổn định, không qua id tự tăng.In a lookup table, have code reference a stable string code column, not the auto-increment id.
Khi thêm giá trị enum, deploy code hiểu giá trị mới trước, rồi mới bắt đầu ghi nó.When adding an enum value, deploy code that understands it first, and only then start writing it.
D8Mô hình dữ liệuData modeling
Cột auditAudit columns
Một dòng thay đổiA row changescreated_at / updated_atcreated_at / updated_at
Hai cột ghi thời điểm tạo và lần sửa gần nhất; trả lời "khi nào", không trả lời "ai" hay "sửa gì".Two columns record creation time and the latest change; they answer "when", not "who" or "what".
Dùng choUse for
Đồng bộ tăng dần: lấy các dòng có updated_at sau lần đồng bộ trước; điều tra "dòng này đổi lúc nào"; sắp xếp theo mới nhấtIncremental sync: fetch rows with updated_at after the last sync; investigating "when did this row change"; sorting by most recent
ĐượcGain
Gần như miễn phí nếu thêm từ đầu, và không thể bù lại dữ liệu cũ nếu thêm muộn.Nearly free if added up front, and impossible to backfill if added late.
MấtLose
Chỉ biết lần sửa cuối, không biết đã sửa gì hay giá trị trước đó.Only knows the last change, not what changed or the previous value.
Đánh đổiTrade-offChọn cột audit là đổi vài byte mỗi dòng lấy câu trả lời cho "khi nào", nhưng không phải cho "ai" hay "đã đổi gì".Choosing audit columns trades a few bytes per row for an answer to "when", but not to "who" or "what changed".
Dấu hiệu nhận biếtHow to recognize it:Bảng nào người dùng sửa được thì nên có hai cột này ngay từ ngày đầu.Any table users can edit should have these two columns from day one.
Đã hiệu chỉnh so với ảnhCorrected from the image.Ảnh gợi ý cột audit trả lời "khi nào dòng này thật sự thay đổi". Thực tế updated_at chỉ giữ lần thay đổi cuối cùng, không có lịch sử.The image suggests audit columns answer "when did this actually change". In reality updated_at keeps only the most recent change, with no history.
Xem chi tiết: cách hoạt động, lợi ích, hạn chế, mẹoDetails: how it works, benefits, limits, tips
Nó là gìWhat it is
Các cột ghi metadata vòng đời của một dòng: created_at, updated_at, đôi khi thêm created_by, updated_by.Columns that record a row's lifecycle metadata: created_at, updated_at, sometimes created_by and updated_by.
Hoạt động thế nàoHow it works
created_at có DEFAULT now(). updated_at được cập nhật mỗi lần UPDATE, lý tưởng nhất là bằng trigger trong database.created_at gets DEFAULT now(). updated_at is refreshed on every UPDATE, ideally by a trigger in the database.
Lợi íchBenefits
Gần như miễn phí nếu thêm từ đầu, và không thể bù lại dữ liệu cũ nếu thêm muộn.Nearly free if added up front, and impossible to backfill if added late.
Là nền cho đồng bộ tăng dần và ETL.The foundation for incremental sync and ETL.
Trả lời câu hỏi đầu tiên của mọi cuộc điều tra.Answers the first question of every investigation.
Hạn chếLimitations
Chỉ biết lần sửa cuối, không biết đã sửa gì hay giá trị trước đó.Only knows the last change, not what changed or the previous value.
Nếu dựa vào ORM, các lệnh UPDATE viết tay sẽ bỏ qua cột này.If you rely on the ORM, hand-written UPDATE statements skip it.
Dùng timestamp không có múi giờ, hoặc lấy giờ từ máy lệch, sẽ sai một cách im lặng.Using timestamps without a zone, or taking time from a skewed machine, goes wrong silently.
Mẹo triển khaiImplementation tips
Dùng timestamptz và để database đặt giờ, đừng lấy giờ từ client.Use timestamptz and let the database set the time; never take it from the client.
Cập nhật updated_at bằng trigger để bắt cả những lệnh UPDATE viết tay.Maintain updated_at with a trigger so hand-written UPDATEs are covered too.
Cần lịch sử đầy đủ thì dùng bảng audit log hoặc CDC (như Debezium), đừng nhồi thêm cột.For full history, use an audit log table or CDC (such as Debezium) rather than more columns.
Đồng bộ theo updated_at có thể sót dòng khi transaction commit muộn; lùi cửa sổ vài giây hoặc chuyển sang CDC.Syncing on updated_at can miss rows from late-committing transactions; overlap the window by a few seconds or move to CDC.
D9Mô hình dữ liệuData modeling
Schema migrationSchema migrations
Thay đổi cấu trúcStructural changeÁp dụng có phiên bảnApplied, versioned
Mỗi thay đổi schema là một file có phiên bản, chạy theo thứ tự và được ghi lại, giống như code.Every schema change is a versioned file, applied in order and recorded, just like code.
Dùng choUse for
Mọi thay đổi schema ở mọi môi trường; đổi tên cột mà không downtime; backfill dữ liệu theo đợtEvery schema change in every environment; renaming a column without downtime; backfilling data in batches
ĐượcGain
Schema được review như code và có lịch sử.The schema is reviewed like code and has history.
MấtLose
Thay đổi phá huỷ không thật sự đảo ngược được: rollback một DROP COLUMN chỉ trả lại cột rỗng.Destructive changes aren't truly reversible: rolling back a DROP COLUMN gives you back an empty column.
Đánh đổiTrade-offChọn migration là đổi tốc độ sửa tay lấy khả năng tái lập và kiểm soát mọi thay đổi.Choosing migrations trades the speed of hand edits for reproducibility and control over every change.
Dấu hiệu nhận biếtHow to recognize it:Nếu bạn từng chạy ALTER TABLE bằng tay trên production, bạn cần migration.If you've ever run ALTER TABLE by hand in production, you need migrations.
Đã hiệu chỉnh so với ảnhCorrected from the image.Ảnh gọi migration là "có thể đảo ngược". Chỉ đúng với thay đổi không phá huỷ; dữ liệu đã xoá thì migration down không mang lại được.The image calls migrations "reversible". That only holds for non-destructive changes; a down migration can't bring back deleted data.
Xem chi tiết: cách hoạt động, lợi ích, hạn chế, mẹoDetails: how it works, benefits, limits, tips
Các file đánh số phiên bản mô tả thay đổi schema (thêm bảng, đổi cột, tạo index), được áp dụng theo thứ tự và ghi vào một bảng lịch sử để mọi môi trường ở cùng trạng thái.Numbered files that describe schema changes (new tables, altered columns, new indexes), applied in order and recorded in a history table so every environment ends up in the same state.
Hoạt động thế nàoHow it works
Công cụ (Flyway, Liquibase, Alembic, Rails, Prisma) so bảng lịch sử với thư mục migration và chạy những file chưa áp dụng, trong transaction nếu database hỗ trợ DDL trong transaction.A tool (Flyway, Liquibase, Alembic, Rails, Prisma) compares the history table with the migrations folder and runs whatever hasn't been applied, inside a transaction when the database supports transactional DDL.
Lợi íchBenefits
Schema được review như code và có lịch sử.The schema is reviewed like code and has history.
Dev, staging và production đồng nhất.Dev, staging and production stay identical.
Dựng lại database từ con số không bằng một lệnh.Rebuild the database from scratch with one command.
Hạn chếLimitations
Thay đổi phá huỷ không thật sự đảo ngược được: rollback một DROP COLUMN chỉ trả lại cột rỗng.Destructive changes aren't truly reversible: rolling back a DROP COLUMN gives you back an empty column.
ALTER trên bảng lớn có thể khoá bảng nhiều phút.An ALTER on a large table can lock it for minutes.
Code và schema deploy lệch nhau sẽ gây lỗi trong khoảng giữa.Code and schema deployed out of step break things in the gap.
Mẹo triển khaiImplementation tips
Làm theo expand, migrate, contract: thêm cột mới, ghi cả hai cột, backfill, chuyển sang đọc cột mới, và chỉ xoá cột cũ ở release sau.Follow expand, migrate, contract: add the new column, write to both, backfill, switch reads, and drop the old column only in a later release.
Trên PostgreSQL, đặt lock_timeout trước mỗi ALTER để nó không treo cả hệ thống khi chờ khoá.On PostgreSQL, set lock_timeout before each ALTER so a lock wait can't freeze the whole system.
Tạo index trên bảng lớn bằng CREATE INDEX CONCURRENTLY.Build indexes on large tables with CREATE INDEX CONCURRENTLY.
Khi có sự cố, ưu tiên viết migration sửa tiếp (roll-forward) hơn là chạy down.When something goes wrong, prefer a new corrective migration (roll forward) over running down.
D10Mô hình dữ liệuData modeling
Độ chính xác và kiểu dữ liệuPrecision & data types
Một con số hay ngày giờA number or dateLưu đúng như ý địnhStored exactly right
Kiểu dữ liệu quyết định giá trị đọc ra có đúng bằng giá trị ghi vào hay không.The data type decides whether the value you read back equals the value you wrote.
Dùng choUse for
Tiền tệ: NUMERIC hoặc số nguyên theo đơn vị nhỏ nhất; toạ độ, số đo khoa học: float là ổn; thời điểm: timestamptz; ngày sinh: date, không phải timestampMoney: NUMERIC or an integer in the smallest unit; coordinates and scientific measurements: float is fine; instants: timestamptz; birthdays: date, not a timestamp
ĐượcGain
Chọn đúng từ đầu loại bỏ cả một lớp lỗi làm tròn.Choosing right up front eliminates a whole class of rounding bugs.
MấtLose
NUMERIC chậm hơn số nguyên và float khi tính toán khối lượng lớn.NUMERIC is slower than integers and floats in heavy computation.
Đánh đổiTrade-offChọn kiểu chính xác là đổi một ít tốc độ và dung lượng lấy việc con số đọc ra đúng bằng con số ghi vào.Choosing exact types trades a little speed and space for reading back exactly what you wrote.
Dấu hiệu nhận biếtHow to recognize it:Nếu bộ phận kế toán sẽ đối chiếu con số này, đừng dùng float.If accounting will reconcile this number, don't use a float.
Xem chi tiết: cách hoạt động, lợi ích, hạn chế, mẹoDetails: how it works, benefits, limits, tips
Chọn kiểu dữ liệu quyết định giá trị có được lưu chính xác không, chiếm bao nhiêu chỗ, và so sánh ra sao.The data type determines whether a value is stored exactly, how much space it takes, and how it compares.
Hoạt động thế nàoHow it works
float và double lưu số nhị phân gần đúng theo IEEE 754. NUMERIC/DECIMAL lưu số thập phân chính xác. timestamp và timestamptz khác nhau về ngữ nghĩa, không chỉ về cách hiển thị.float and double store approximate binary values per IEEE 754. NUMERIC/DECIMAL store exact decimals. timestamp and timestamptz differ in meaning, not just display.
Lợi íchBenefits
Chọn đúng từ đầu loại bỏ cả một lớp lỗi làm tròn.Choosing right up front eliminates a whole class of rounding bugs.
Kiểu hẹp tiết kiệm bộ nhớ và làm index nhỏ hơn.Narrow types save memory and shrink indexes.
Database tự chặn giá trị vô lý, như kiểu date từ chối ngày 30/2.The database rejects nonsense values, like a date refusing 30 February.
Hạn chếLimitations
NUMERIC chậm hơn số nguyên và float khi tính toán khối lượng lớn.NUMERIC is slower than integers and floats in heavy computation.
Đổi kiểu cột trên bảng lớn thường phải viết lại toàn bộ bảng.Changing a column type on a big table usually rewrites the whole table.
INT 32-bit tràn ở khoảng 2,1 tỷ; id tự tăng dùng INT đã làm sập không ít hệ thống.A 32-bit INT overflows around 2.1 billion; INT auto-increment ids have taken down plenty of systems.
Mẹo triển khaiImplementation tips
Tiền: NUMERIC hoặc số nguyên theo đơn vị nhỏ nhất, luôn lưu kèm mã tiền tệ. Với VND, BIGINT theo đồng là đủ.Money: NUMERIC or an integer in the smallest unit, always stored with a currency code. For currencies without minor units, a BIGINT of whole units is enough.
Id: dùng BIGINT ngay từ đầu.Ids: use BIGINT from the start.
Thời điểm dùng timestamptz; ngày trên lịch dùng date.Instants get timestamptz; calendar days get date.
Đừng viết VARCHAR(255) theo thói quen; đặt giới hạn theo nghiệp vụ hoặc dùng text cộng CHECK.Don't write VARCHAR(255) out of habit; size it by the business rule or use text with a CHECK.
Thời gian và múi giờTime and time zones
Lưu thời điểm bằng UTC, lưu ý định bằng giờ địa phương cộng tên múi giờ IANA, đo thời lượng bằng đồng hồ monotonic.Store instants in UTC, store intent as local time plus an IANA zone name, measure durations with a monotonic clock.
Lỗi thời gian hiếm khi làm sập hệ thống ngay. Chúng làm sai dữ liệu một cách im lặng, thường vào đúng những ngày không ai test.Time bugs rarely crash a system outright. They corrupt data silently, usually on exactly the days nobody tested.
T1Thời gian và múi giờTime and time zones
UTC và giờ địa phươngUTC vs local time
UTCUTCGiờ địa phươngLocal time
UTC là thang thời gian chung không lệch, không đổi giờ; giờ địa phương là UTC cộng một độ lệch do luật của từng vùng quyết định.UTC is the shared time scale with no offset and no DST; local time is UTC plus an offset set by each region's rules.
Dùng choUse for
Lưu mọi thời điểm đã xảy ra (log, giao dịch) bằng UTC; hiển thị theo giờ địa phương của người xem; lịch hẹn tương lai: lưu giờ địa phương kèm tên múi giờStore every past instant (logs, transactions) in UTC; display in the viewer's local time; future appointments: store local time plus the zone name
ĐượcGain
Thời điểm UTC so sánh và sắp xếp trực tiếp được giữa mọi server.UTC instants compare and sort directly across every server.
MấtLose
UTC không giữ được ý định "9 giờ sáng giờ Hà Nội".UTC can't capture the intent "9 a.m. Hanoi time".
Đánh đổiTrade-offChọn lưu bằng UTC là đổi ý định giờ địa phương ban đầu lấy khả năng so sánh tuyệt đối giữa mọi hệ thống.Choosing to store UTC trades the original local-time intent for absolute comparability across systems.
Dấu hiệu nhận biếtHow to recognize it:Sự kiện đã xảy ra thì dùng UTC. Sự kiện hẹn trong tương lai theo giờ của người dùng thì dùng giờ địa phương cộng tên múi giờ.Something that already happened goes in UTC. Something scheduled in the future on a user's clock goes in local time plus a zone name.
Đã hiệu chỉnh so với ảnhCorrected from the image.Ảnh gọi UTC là "một điểm cố định". UTC không phải một điểm mà là một thang thời gian: không có độ lệch và không đổi giờ mùa hè.The image calls UTC "one fixed point". UTC isn't a point but a time scale: zero offset and no daylight saving.
Xem chi tiết: cách hoạt động, lợi ích, hạn chế, mẹoDetails: how it works, benefits, limits, tips
UTC 2026-03-29T01:30Z
Ha Noi +07:00 -> 08:30
London +01:00 -> 02:30 (BST began at 01:00Z)
New York -04:00 -> 21:30 the previous day
Nó là gìWhat it is
UTC là thang thời gian chuẩn mà mọi múi giờ quy chiếu vào; nó không có độ lệch và không đổi giờ mùa hè. Giờ địa phương là UTC cộng một độ lệch (offset), và độ lệch này phụ thuộc vào cả nơi chốn lẫn thời điểm.UTC is the reference scale every time zone is defined against; it has no offset and no daylight saving. Local time is UTC plus an offset, and that offset depends on both place and date.
Hoạt động thế nàoHow it works
Độ lệch không gắn cố định với một vùng. London là +00:00 vào mùa đông và +01:00 vào mùa hè. Vì vậy múi giờ (Europe/London) và độ lệch (+01:00) là hai thứ khác nhau.An offset isn't fixed to a region. London is +00:00 in winter and +01:00 in summer. That's why a time zone (Europe/London) and an offset (+01:00) are different things.
Lợi íchBenefits
Thời điểm UTC so sánh và sắp xếp trực tiếp được giữa mọi server.UTC instants compare and sort directly across every server.
Không có giờ lặp hay giờ biến mất.No repeated or missing hours.
Log từ nhiều vùng ghép lại được ngay.Logs from different regions merge immediately.
Hạn chếLimitations
UTC không giữ được ý định "9 giờ sáng giờ Hà Nội".UTC can't capture the intent "9 a.m. Hanoi time".
Người dùng không đọc UTC, nên luôn phải chuyển đổi khi hiển thị.Users don't read UTC, so display always needs a conversion.
Lịch tương lai đã đổi sẵn sang UTC sẽ sai nếu vùng đó đổi luật giờ mùa hè.A future event pre-converted to UTC goes wrong if that region changes its DST rules.
Mẹo triển khaiImplementation tips
Đặt server, database và container ở UTC; chỉ chuyển sang giờ địa phương ở tầng hiển thị.Run servers, databases and containers in UTC; convert to local time only at the display layer.
Lưu tên vùng IANA (Asia/Ho_Chi_Minh), không chỉ lưu độ lệch +07:00.Store the IANA zone name (Asia/Ho_Chi_Minh), not just the +07:00 offset.
Với cuộc họp định kỳ, lưu giờ địa phương cộng múi giờ rồi tính ra UTC cho từng lần diễn ra.For recurring meetings, store local time plus zone and compute UTC for each occurrence.
T2Thời gian và múi giờTime and time zones
Unix timestampUnix timestamp
Một thời điểmA momentSố giây từ epochSeconds since epoch
Một con số đếm từ 1970-01-01T00:00Z, không kèm múi giờ, và không tính giây nhuận.A number counted from 1970-01-01T00:00Z, with no time zone and no leap seconds.
Dùng choUse for
Trường exp và iat trong JWT; TTL của cache; trao đổi thời điểm gọn nhẹ giữa các hệ thốngThe exp and iat claims in JWTs; cache TTLs; compact timestamps between systems
ĐượcGain
Gọn: 8 byte, so sánh nhanh.Compact: 8 bytes, fast to compare.
MấtLose
Không đọc được bằng mắt khi debug.Unreadable to humans while debugging.
Đánh đổiTrade-offChọn Unix timestamp là đổi khả năng đọc hiểu của con người lấy sự gọn nhẹ và không mơ hồ.Choosing a Unix timestamp trades human readability for compactness and zero ambiguity.
Dấu hiệu nhận biếtHow to recognize it:Máy ghi, máy đọc, máy so sánh, không ai phải đọc bằng mắt: Unix timestamp là lựa chọn ổn.Machines write it, read it and compare it, and no human has to read it: a Unix timestamp is fine.
Đã hiệu chỉnh so với ảnhCorrected from the image.Ảnh nói Unix timestamp "đếm số giây kể từ epoch". Nó bỏ qua giây nhuận, nên không phải số giây thật đã trôi qua.The image says a Unix timestamp "counts seconds since epoch". It ignores leap seconds, so it isn't the real number of elapsed seconds.
Xem chi tiết: cách hoạt động, lợi ích, hạn chế, mẹoDetails: how it works, benefits, limits, tips
Số giây (hoặc mili giây) tính từ 1970-01-01T00:00:00Z. Chính xác hơn, Unix time giả định mỗi ngày dài đúng 86.400 giây và bỏ qua giây nhuận, nên nó không phải số giây vật lý thật sự đã trôi qua.The number of seconds (or milliseconds) since 1970-01-01T00:00:00Z. More precisely, Unix time assumes every day is exactly 86,400 seconds and ignores leap seconds, so it isn't the true count of elapsed physical seconds.
Hoạt động thế nàoHow it works
Một số nguyên thì so sánh và trừ ra khoảng cách trực tiếp. Muốn biết đó là mấy giờ ở đâu thì vẫn cần quy tắc múi giờ.An integer compares and subtracts directly. Turning it into a wall-clock time somewhere still needs time zone rules.
Lợi íchBenefits
Gọn: 8 byte, so sánh nhanh.Compact: 8 bytes, fast to compare.
Không mơ hồ về múi giờ.No time zone ambiguity.
Mọi ngôn ngữ và nền tảng đều hỗ trợ.Every language and platform supports it.
Hạn chếLimitations
Không đọc được bằng mắt khi debug.Unreadable to humans while debugging.
Nhầm giây với mili giây là lỗi kinh điển: JavaScript dùng mili giây, nhiều API dùng giây.Mixing up seconds and milliseconds is a classic bug: JavaScript uses milliseconds, many APIs use seconds.
Số nguyên 32-bit có dấu tràn vào 2038-01-19.Signed 32-bit integers overflow on 2038-01-19.
Mẹo triển khaiImplementation tips
Ghi đơn vị vào tên trường: expires_at_ms, created_s.Put the unit in the field name: expires_at_ms, created_s.
Luôn dùng số nguyên 64-bit.Always use 64-bit integers.
Hiện nay, số có 13 chữ số gần như chắc là mili giây, 10 chữ số là giây.Today a 13-digit value is almost certainly milliseconds and a 10-digit value is seconds.
Trong database, ưu tiên kiểu timestamptz thay vì cột số: truy vấn dễ hơn mà dung lượng tương đương.In a database, prefer timestamptz over a numeric column: easier to query at the same size.
T3Thời gian và múi giờTime and time zones
ISO 8601ISO 8601
Một ngày giờA date-timeChuỗi sắp xếp đượcSortable text
Định dạng văn bản đi từ đơn vị lớn tới nhỏ; sắp xếp theo chuỗi chỉ đúng khi mọi giá trị cùng định dạng và cùng độ lệch.A text format ordered from largest unit to smallest; string sorting only works when every value shares the format and the offset.
Dùng choUse for
Thời gian trong JSON API; tên file log và backup; CSV xuất ra để trao đổiTimestamps in JSON APIs; log and backup file names; CSV exports shared with others
ĐượcGain
Người và máy đều đọc được.Readable by both humans and machines.
MấtLose
Sắp xếp theo chuỗi sai khi trộn độ lệch: 10:00+07:00 (tức 03:00Z) bị xếp sau 05:00Z.String sorting breaks with mixed offsets: 10:00+07:00 (03:00Z) sorts after 05:00Z.
Đánh đổiTrade-offChọn ISO 8601 là đổi dung lượng lấy một định dạng vừa đọc được vừa không mơ hồ.Choosing ISO 8601 trades space for a format that is both readable and unambiguous.
Dấu hiệu nhận biếtHow to recognize it:Khi thời gian phải đi qua ranh giới hệ thống dưới dạng văn bản, dùng ISO 8601 hoặc RFC 3339.When time crosses a system boundary as text, use ISO 8601 or RFC 3339.
Đã hiệu chỉnh so với ảnhCorrected from the image.Ảnh nói ISO 8601 "sắp xếp đúng như văn bản thuần". Chỉ đúng khi mọi giá trị cùng định dạng, cùng độ chính xác và cùng độ lệch, tốt nhất là tất cả ở UTC.The image says ISO 8601 "sorts correctly as plain text". That's only true when every value shares the format, precision and offset, ideally all in UTC.
Xem chi tiết: cách hoạt động, lợi ích, hạn chế, mẹoDetails: how it works, benefits, limits, tips
2026-09-29T14:05:00.123+07:00
|-- date --||- time -||ms||offset|
Nó là gìWhat it is
Chuẩn quốc tế biểu diễn ngày giờ bằng văn bản, ví dụ 2026-09-29T14:05:00+07:00. RFC 3339 là một tập con chặt hơn, là định dạng thường dùng trong API.The international standard for writing dates and times as text, for example 2026-09-29T14:05:00+07:00. RFC 3339 is a stricter subset and the usual choice in APIs.
Hoạt động thế nàoHow it works
Vì thứ tự là năm, tháng, ngày, giờ, so sánh chuỗi trùng với so sánh thời gian, nhưng chỉ khi mọi chuỗi cùng định dạng, cùng độ chính xác và cùng độ lệch.Because the order is year, month, day, hour, string comparison matches time comparison, but only when every string has the same format, the same precision and the same offset.
Lợi íchBenefits
Người và máy đều đọc được.Readable by both humans and machines.
Hết mơ hồ kiểu 03/04 là mùng 3 tháng 4 hay mùng 4 tháng 3.No more guessing whether 03/04 is 3 April or 4 March.
Mọi thư viện ngày giờ đều hỗ trợ.Every date library supports it.
Hạn chếLimitations
Sắp xếp theo chuỗi sai khi trộn độ lệch: 10:00+07:00 (tức 03:00Z) bị xếp sau 05:00Z.String sorting breaks with mixed offsets: 10:00+07:00 (03:00Z) sorts after 05:00Z.
Chuẩn rất rộng (tuần, ngày thứ trong năm, khoảng thời gian), không parser nào hỗ trợ hết.The standard is broad (week dates, ordinal dates, intervals), and no parser supports all of it.
Dài hơn số nguyên, tốn chỗ hơn.Longer than an integer, so it takes more space.
Mẹo triển khaiImplementation tips
Trong API, chuẩn hoá về RFC 3339 ở UTC (hậu tố Z) với độ chính xác cố định.In APIs, normalize to RFC 3339 in UTC (the Z suffix) with fixed precision.
Tên file dạng 2026-09-29T0705Z: bỏ dấu : vì Windows không cho phép.Name files like 2026-09-29T0705Z: drop the : because Windows forbids it.
Đừng lưu thời gian dạng chuỗi trong database khi đã có kiểu timestamp.Don't store times as strings in a database that has a timestamp type.
T4Thời gian và múi giờTime and time zones
Chuyển giờ mùa hè (DST)DST transitions
Ngày 23 giờ23-hour dayNgày 25 giờ25-hour day
Ở vùng có giờ mùa hè, mỗi năm có một ngày 23 giờ và một ngày 25 giờ.Where daylight saving applies, each year has one 23-hour day and one 25-hour day.
Dùng choUse for
Cron chạy lúc 2:30 sáng; tính lương theo giờ cho ca đêm; báo cáo tổng hợp theo ngàyCron jobs at 2:30 a.m.; hourly pay for night shifts; daily aggregate reports
ĐượcGain
Xử lý đúng sẽ loại bỏ loại lỗi chỉ xuất hiện hai lần mỗi năm, cũng là loại khó tái hiện nhất.Handling it correctly removes bugs that appear twice a year, the hardest kind to reproduce.
MấtLose
Việt Nam không dùng giờ mùa hè, nên lập trình viên dễ không bao giờ gặp lỗi này khi test tại chỗ.Vietnam doesn't observe DST, so developers there may never hit these bugs in local testing.
Đánh đổiTrade-offChọn xử lý giờ mùa hè tường minh là đổi sự đơn giản của phép cộng giờ lấy lịch chạy đúng cả hai đêm đổi giờ.Choosing to handle DST explicitly trades the simplicity of adding hours for schedules that stay correct on both transition nights.
Dấu hiệu nhận biếtHow to recognize it:Nếu người dùng hoặc server của bạn ở châu Âu, Bắc Mỹ hay phần lớn nước Úc, hãy test hai ngày đổi giờ.If your users or servers are in Europe, North America or most of Australia, test both transition days.
Xem chi tiết: cách hoạt động, lợi ích, hạn chế, mẹoDetails: how it works, benefits, limits, tips
Spring (US): 01:59 -> 03:00 02:30 never happens
Autumn (US): 01:59 -> 01:00 01:30 happens twice
Nó là gìWhat it is
Vào mùa xuân, đồng hồ nhảy tới một giờ: ngày đó chỉ có 23 giờ và có một khoảng giờ không tồn tại. Vào mùa thu, đồng hồ lùi lại một giờ: ngày đó có 25 giờ và một khoảng giờ xảy ra hai lần.In spring the clock jumps forward an hour: that day has 23 hours and a stretch of time that never exists. In autumn it falls back an hour: that day has 25 hours and a stretch that happens twice.
Hoạt động thế nàoHow it works
Thư viện xử lý giờ không tồn tại và giờ lặp theo quy tắc riêng: dời tới, chọn lần đầu, chọn lần sau, hoặc báo lỗi. Bạn cần biết thư viện của mình chọn cách nào.Libraries resolve missing and repeated times with their own rule: shift forward, pick the earlier, pick the later, or throw. You need to know which one yours uses.
Lợi íchBenefits
Xử lý đúng sẽ loại bỏ loại lỗi chỉ xuất hiện hai lần mỗi năm, cũng là loại khó tái hiện nhất.Handling it correctly removes bugs that appear twice a year, the hardest kind to reproduce.
Báo cáo theo ngày chính xác cả với ngày 23 và 25 giờ.Daily reports stay correct on 23- and 25-hour days.
Lịch hẹn không bị lệch sau khi đổi giờ.Appointments don't drift after the change.
Hạn chếLimitations
Việt Nam không dùng giờ mùa hè, nên lập trình viên dễ không bao giờ gặp lỗi này khi test tại chỗ.Vietnam doesn't observe DST, so developers there may never hit these bugs in local testing.
Luật giờ mùa hè do chính phủ quyết, đôi khi chỉ báo trước vài tuần.DST rules are set by governments, sometimes with only weeks of notice.
Không phải lúc nào cũng lệch một giờ: đảo Lord Howe của Úc chỉ đổi 30 phút.The shift isn't always an hour: Australia's Lord Howe Island moves by 30 minutes.
Mẹo triển khaiImplementation tips
Cho cron trên server chạy theo UTC; job buộc phải theo giờ địa phương thì tránh khung 01:00 đến 03:00.Run server cron in UTC; if a job must follow local time, avoid the 01:00 to 03:00 window.
Viết test cố định vào ngày đổi giờ, ví dụ 2026-03-08 và 2026-11-01 ở America/New_York.Write tests pinned to transition dates, such as 2026-03-08 and 2026-11-01 in America/New_York.
Cộng "một ngày" bằng hàm lịch, không cộng 86.400 giây.Add "one day" with a calendar function, not by adding 86,400 seconds.
T5Thời gian và múi giờTime and time zones
Giây nhuậnLeap seconds
Vòng quay Trái ĐấtEarth's rotationUTC được chỉnhUTC adjusted
Giây được chèn vào UTC để bám theo vòng quay Trái Đất; lần gần nhất là cuối năm 2016 và sẽ bị bãi bỏ chậm nhất vào 2035.A second inserted into UTC to track Earth's rotation; the last one was at the end of 2016 and they will be abolished by 2035.
Dùng choUse for
Hệ thống tài chính, viễn thông, định vị cần giây chính xác; log phân tán cần thứ tự nghiêm ngặt; hiểu vì sao Unix time không phải số giây thậtFinance, telecom and positioning systems that need exact seconds; distributed logs that need strict ordering; understanding why Unix time isn't a true second count
ĐượcGain
Giữ UTC khớp với mặt trời cho thiên văn và hàng hải.Keeps UTC aligned with the sun for astronomy and navigation.
MấtLose
Giây nhuận năm 2012 từng khiến nhiều server Linux treo CPU vì lỗi kernel, ảnh hưởng tới Reddit, Mozilla và nhiều dịch vụ khác.The 2012 leap second pegged CPUs on many Linux servers through a kernel bug, affecting Reddit, Mozilla and others.
Đánh đổiTrade-offChọn leap smear là đổi độ chính xác tuyệt đối trong khoảng 24 giờ lấy việc ứng dụng không bao giờ thấy giây 23:59:60.Choosing a leap smear trades absolute accuracy for about 24 hours for applications never seeing 23:59:60.
Dấu hiệu nhận biếtHow to recognize it:Nếu hệ thống chỉ cần độ chính xác cỡ mili giây theo giờ tường, bạn gần như không cần quan tâm, ngoài việc chọn nguồn NTP cho đúng.If you only need millisecond-level wall-clock accuracy, you can mostly ignore this, apart from choosing the right NTP source.
Đã hiệu chỉnh so với ảnhCorrected from the image.Ảnh chưa nhắc hai điểm quan trọng: giây nhuận sắp bị bãi bỏ (chậm nhất 2035), và Unix time không đếm chúng.The image leaves out two key points: leap seconds are being abolished (by 2035 at the latest), and Unix time doesn't count them.
Xem chi tiết: cách hoạt động, lợi ích, hạn chế, mẹoDetails: how it works, benefits, limits, tips
Một giây được chèn vào UTC (thành 23:59:60) để UTC không lệch quá 0,9 giây so với thời gian theo vòng quay thực của Trái Đất (UT1). Từ 1972 đã có 27 lần chèn; lần gần nhất vào 31/12/2016.A second added to UTC (as 23:59:60) to keep UTC within 0.9 seconds of time based on Earth's actual rotation (UT1). There have been 27 since 1972; the latest was on 31 December 2016.
Hoạt động thế nàoHow it works
IERS thông báo trước khoảng sáu tháng. Unix time không có 23:59:60 nên giây đó bị lặp lại. Google và AWS dùng leap smear: trải giây thừa ra suốt khoảng 24 giờ để đồng hồ không bao giờ nhảy.IERS announces them about six months ahead. Unix time has no 23:59:60, so that second repeats. Google and AWS use a leap smear: they spread the extra second over roughly 24 hours so clocks never jump.
Lợi íchBenefits
Giữ UTC khớp với mặt trời cho thiên văn và hàng hải.Keeps UTC aligned with the sun for astronomy and navigation.
Hiểu nó giải thích vì sao phải đo thời lượng bằng đồng hồ monotonic.Understanding it explains why durations belong on a monotonic clock.
Hạn chếLimitations
Giây nhuận năm 2012 từng khiến nhiều server Linux treo CPU vì lỗi kernel, ảnh hưởng tới Reddit, Mozilla và nhiều dịch vụ khác.The 2012 leap second pegged CPUs on many Linux servers through a kernel bug, affecting Reddit, Mozilla and others.
Trộn nguồn NTP có smear với nguồn không smear có thể làm các máy lệch nhau tới nửa giây.Mixing smeared and unsmeared NTP sources can put machines up to half a second apart.
Không dự đoán được lâu dài; về lý thuyết còn có thể có giây nhuận âm.They can't be predicted far ahead; in theory a negative leap second is possible.
Mẹo triển khaiImplementation tips
Đừng tự xử lý. Dùng nguồn NTP có smear (Google Public NTP, Amazon Time Sync) và đừng trộn với nguồn khác.Don't handle it yourself. Use a smeared NTP source (Google Public NTP, Amazon Time Sync) and don't mix it with others.
Đo thời lượng bằng đồng hồ monotonic, không bằng hiệu của hai timestamp.Measure durations with a monotonic clock, not by subtracting two timestamps.
Theo nghị quyết CGPM năm 2022, giây nhuận sẽ bị bãi bỏ chậm nhất vào 2035, nhưng code vẫn phải chịu được nó cho tới lúc đó.Under the 2022 CGPM resolution, leap seconds end no later than 2035, but code still has to tolerate them until then.
T6Thời gian và múi giờTime and time zones
Cơ sở dữ liệu múi giờTime zone database
Luật của vùng thay đổiRegional rules changetzdata được cập nhậttz database updated
IANA tz database là nguồn sự thật cho luật giờ của mọi vùng, quá khứ lẫn hiện tại, và được cập nhật vài lần mỗi năm.The IANA tz database is the source of truth for every region's time rules, past and present, updated several times a year.
Dùng choUse for
Chuyển đổi UTC và giờ địa phương cho bất kỳ ngày nào trong quá khứ hay tương lai; hiển thị giờ đúng cho người dùng toàn cầu; lập lịch sự kiện định kỳConverting between UTC and local time for any past or future date; showing correct local times to a global audience; scheduling recurring events
ĐượcGain
Lịch sử đầy đủ: ví dụ biết miền Nam Việt Nam từng dùng +08:00 trước năm 1975.Complete history: for example, it knows southern Vietnam used +08:00 before 1975.
MấtLose
Mỗi thành phần giữ một bản riêng, nên database, JVM, container và điện thoại có thể lệch phiên bản nhau.Each component keeps its own copy, so the database, JVM, containers and phones can disagree on version.
Đánh đổiTrade-offChọn dựa vào tzdata là đổi gánh nặng cập nhật định kỳ lấy luật múi giờ đúng mà không ai tự viết nổi.Choosing to rely on tzdata trades a recurring update chore for time zone rules nobody could write correctly alone.
Dấu hiệu nhận biếtHow to recognize it:Nếu bạn đang hardcode +7 hay GMT+8, bạn đang tự viết lại tzdata, và bản của bạn kém hơn.If you're hardcoding +7 or GMT+8, you're rewriting tzdata yourself, and doing it worse.
Xem chi tiết: cách hoạt động, lợi ích, hạn chế, mẹoDetails: how it works, benefits, limits, tips
Nó là gìWhat it is
IANA tz database (tzdata, còn gọi là Olson database) là bộ dữ liệu công cộng chứa lịch sử và quy tắc hiện hành của mọi múi giờ, định danh theo dạng Khu_vực/Thành_phố như Asia/Ho_Chi_Minh.The IANA tz database (tzdata, also called the Olson database) is a public dataset holding the history and current rules of every time zone, identified as Area/City, such as Asia/Ho_Chi_Minh.
Hoạt động thế nàoHow it works
Hệ điều hành, JVM, ICU, Python zoneinfo và trình duyệt đều mang theo một bản tzdata. Khi một quốc gia đổi luật, IANA phát hành bản mới, và từng thành phần phải tự cập nhật.Operating systems, the JVM, ICU, Python's zoneinfo and browsers each ship a copy of tzdata. When a country changes its rules, IANA releases a new version, and each component has to update on its own.
Lợi íchBenefits
Lịch sử đầy đủ: ví dụ biết miền Nam Việt Nam từng dùng +08:00 trước năm 1975.Complete history: for example, it knows southern Vietnam used +08:00 before 1975.
Miễn phí và được duy trì liên tục.Free and continuously maintained.
Định danh ổn định, dùng chung trên mọi nền tảng.Stable identifiers shared by every platform.
Hạn chếLimitations
Mỗi thành phần giữ một bản riêng, nên database, JVM, container và điện thoại có thể lệch phiên bản nhau.Each component keeps its own copy, so the database, JVM, containers and phones can disagree on version.
Luật đổi gấp làm bản cũ tính sai cho tới khi được cập nhật.Rushed rule changes make old copies compute wrong times until updated.
Tên viết tắt như CST hay IST mơ hồ (IST có thể là Ấn Độ, Ireland hoặc Israel), nên không dùng làm khoá được.Abbreviations like CST or IST are ambiguous (IST can mean India, Ireland or Israel), so they can't be used as keys.
Mẹo triển khaiImplementation tips
Đưa việc cập nhật tzdata vào quy trình vá lỗi: base image, JDK, gói tzdata của hệ điều hành.Make tzdata updates part of patching: base images, the JDK, the OS tzdata package.
Chỉ lưu tên IANA trong dữ liệu, không lưu viết tắt như ICT hay EST.Store only IANA names in data, never abbreviations like ICT or EST.
Asia/Ho_Chi_Minh là tên chuẩn; Asia/Saigon chỉ là bí danh.Asia/Ho_Chi_Minh is the canonical name; Asia/Saigon is only an alias.
T7Thời gian và múi giờTime and time zones
Lệch đồng hồ (clock skew)Clock skew
Đồng hồ máy AMachine A's clockĐồng hồ máy BMachine B's clock
Hai máy không bao giờ đồng ý tuyệt đối về giờ hiện tại; so sánh timestamp của hai máy khác nhau là so sánh cả độ lệch.Two machines never agree exactly on the current time; comparing their timestamps compares their skew too.
Dùng choUse for
Sắp xếp sự kiện từ nhiều service; kiểm tra token hết hạn giữa các máy (JWT exp); lease và khoá phân tánOrdering events from several services; checking token expiry across machines (JWT exp); leases and distributed locks
ĐượcGain
Xử lý đúng thì log không còn cảnh "kết quả xảy ra trước nguyên nhân".Handled well, logs stop showing effects before their causes.
MấtLose
Không loại bỏ được hoàn toàn, chỉ giới hạn được.It can be bounded, never eliminated.
Đánh đổiTrade-offChọn chấp nhận một độ lệch có giới hạn là đổi thứ tự tuyệt đối lấy một hệ thống không đòi hỏi đồng hồ hoàn hảo.Choosing to tolerate bounded skew trades absolute ordering for a system that doesn't need perfect clocks.
Dấu hiệu nhận biếtHow to recognize it:Nếu bạn đang so sánh timestamp do hai máy khác nhau tạo ra, độ lệch đồng hồ đang nằm trong phép so sánh đó.If you compare timestamps produced by two different machines, clock skew is inside that comparison.
Xem chi tiết: cách hoạt động, lợi ích, hạn chế, mẹoDetails: how it works, benefits, limits, tips
real time: 10:00:00.000
machine A: 10:00:00.120
machine B: 09:59:59.950 skew A-B = 170 ms
Nó là gìWhat it is
Chênh lệch giữa đồng hồ của hai máy tại cùng một khoảnh khắc thật.The difference between two machines' clocks at the same real instant.
Hoạt động thế nàoHow it works
Tinh thể thạch anh trôi theo nhiệt độ, thường vài chục phần triệu, tức khoảng một đến vài giây mỗi ngày nếu không đồng bộ. NTP kéo đồng hồ về gần nhau nhưng không bao giờ về đúng 0.Quartz crystals drift with temperature, typically tens of parts per million, roughly one to a few seconds a day without syncing. NTP pulls clocks close but never to exactly zero.
Lợi íchBenefits
Xử lý đúng thì log không còn cảnh "kết quả xảy ra trước nguyên nhân".Handled well, logs stop showing effects before their causes.
Token không bị từ chối oan chỉ vì lệch vài giây.Tokens aren't wrongly rejected over a few seconds of drift.
Khoá phân tán an toàn hơn.Distributed locks become safer.
Hạn chếLimitations
Không loại bỏ được hoàn toàn, chỉ giới hạn được.It can be bounded, never eliminated.
Máy ảo bị tạm dừng hoặc di chuyển có thể lệch đột ngột.Paused or migrated VMs can jump suddenly.
Đồng hồ trên điện thoại hay trình duyệt có thể lệch nhiều phút, hoặc bị người dùng cố ý chỉnh sai.Phone and browser clocks can be minutes off, or deliberately set wrong by the user.
Mẹo triển khaiImplementation tips
Cho phép sai số 30 đến 60 giây khi kiểm tra exp và nbf của JWT.Allow 30 to 60 seconds of leeway when checking JWT exp and nbf.
Đừng dùng timestamp để quyết định bản ghi nào mới hơn giữa các máy; dùng version, sequence hoặc logical clock (Lamport, HLC).Don't use timestamps to decide which write is newer across machines; use versions, sequences or logical clocks (Lamport, HLC).
Đừng tin giờ của client. Giám sát độ lệch NTP (chronyc tracking) và cảnh báo khi vượt ngưỡng.Never trust client time. Monitor NTP offset (chronyc tracking) and alert past a threshold.
T8Thời gian và múi giờTime and time zones
Đồng hồ monotonic và đồng hồ tườngMonotonic vs wall clock
Monotonic: chỉ tiếnMonotonic: only forwardĐồng hồ tường: có thể nhảyWall clock: can jump
Muốn hỏi "bao lâu" thì dùng monotonic; muốn hỏi "lúc mấy giờ" thì dùng đồng hồ tường.Ask "how long" with a monotonic clock; ask "what time" with the wall clock.
Dùng choUse for
Đo latency, timeout, backoff khi retry: monotonic; timestamp ghi vào database và log: đồng hồ tường; rate limiter: monotonicLatency, timeouts, retry backoff: monotonic; timestamps written to databases and logs: wall clock; rate limiters: monotonic
ĐượcGain
Monotonic không bao giờ cho kết quả âm và không bị NTP nhảy.Monotonic never goes negative and isn't affected by NTP steps.
MấtLose
Giá trị monotonic vô nghĩa khi mang sang máy khác hoặc sau khi khởi động lại.Monotonic values mean nothing on another machine or after a reboot.
Đánh đổiTrade-offChọn đồng hồ monotonic là đổi ý nghĩa lịch lấy khoảng thời gian luôn đúng.Choosing a monotonic clock trades calendar meaning for intervals that are always correct.
Dấu hiệu nhận biếtHow to recognize it:Câu hỏi là "bao lâu?" thì dùng monotonic. Câu hỏi là "lúc mấy giờ?" thì dùng đồng hồ tường.If the question is "how long?", use monotonic. If it's "what time?", use the wall clock.
Xem chi tiết: cách hoạt động, lợi ích, hạn chế, mẹoDetails: how it works, benefits, limits, tips
wall: 10:00:05 -> [NTP steps -2 s] -> 10:00:03 elapsed = -2 s wrong
monotonic: 5003 ms -> 5004 ms elapsed = +1 ms right
Nó là gìWhat it is
Đồng hồ tường (wall clock) là giờ theo lịch, có thể bị NTP hoặc người dùng chỉnh nhảy tới hay lùi. Đồng hồ monotonic là bộ đếm chỉ tăng, không mang nghĩa lịch, dùng để đo khoảng thời gian.The wall clock is calendar time, and NTP or a user can step it forward or back. A monotonic clock is a counter that only increases, has no calendar meaning, and exists to measure intervals.
Hoạt động thế nàoHow it works
Mỗi nền tảng có cặp riêng: Linux CLOCK_REALTIME và CLOCK_MONOTONIC; Java System.currentTimeMillis() và System.nanoTime(); JavaScript Date.now() và performance.now(); Python time.time() và time.monotonic(). Go gói cả hai vào time.Now().Each platform has its pair: Linux CLOCK_REALTIME and CLOCK_MONOTONIC; Java System.currentTimeMillis() and System.nanoTime(); JavaScript Date.now() and performance.now(); Python time.time() and time.monotonic(). Go bundles both into time.Now().
Lợi íchBenefits
Monotonic không bao giờ cho kết quả âm và không bị NTP nhảy.Monotonic never goes negative and isn't affected by NTP steps.
Timeout vẫn đáng tin kể cả khi giờ hệ thống bị chỉnh.Timeouts stay reliable even when system time is changed.
Đồng hồ tường so sánh được giữa các máy, sau khi đã đồng bộ NTP.Wall clocks can be compared across machines once NTP-synced.
Hạn chếLimitations
Giá trị monotonic vô nghĩa khi mang sang máy khác hoặc sau khi khởi động lại.Monotonic values mean nothing on another machine or after a reboot.
Đồng hồ tường có thể lùi, nên đo thời lượng bằng nó có thể ra số âm.The wall clock can go backwards, so durations measured with it can be negative.
Trên Linux, CLOCK_MONOTONIC dừng khi máy ngủ; muốn tính cả thời gian ngủ thì cần CLOCK_BOOTTIME.On Linux, CLOCK_MONOTONIC stops during suspend; use CLOCK_BOOTTIME if sleep time should count.
Mẹo triển khaiImplementation tips
Rà code: mọi phép end - start dùng Date.now() hay time.time() đều nên đổi sang monotonic.Audit your code: every end - start built on Date.now() or time.time() should move to a monotonic clock.
Đừng bao giờ lưu giá trị monotonic vào database.Never persist monotonic values to a database.
Trong Go, time.Since tự dùng phần monotonic, nhưng Round(0) hay serialize sẽ bỏ phần đó đi.In Go, time.Since uses the monotonic reading automatically, but Round(0) or serialization strips it.
T9Thời gian và múi giờTime and time zones
Bẫy khi tính toán ngày giờDate arithmetic pitfalls
Một ngày không luôn dài 24 giờ, một tháng không có độ dài cố định, và 31/1 cộng một tháng không có đáp án duy nhất.A day isn't always 24 hours, a month has no fixed length, and 31 January plus a month has no single answer.
Dùng choUse for
Hạn thanh toán "sau 30 ngày"; gói đăng ký theo tháng; xác định "hôm nay" cho báo cáo theo ngàyPayment due "in 30 days"; monthly subscriptions; defining "today" for daily reports
ĐượcGain
Hạn chót và ngày gia hạn không bị lệch.Deadlines and renewal dates don't drift.
MấtLose
Mỗi thư viện xử lý cuối tháng một kiểu.Every library handles month ends its own way.
Đánh đổiTrade-offChọn phép tính theo lịch là đổi sự đơn giản của phép cộng giây lấy kết quả khớp với cách con người hiểu "ngày" và "tháng".Choosing calendar arithmetic trades the simplicity of adding seconds for results that match how people understand "day" and "month".
Dấu hiệu nhận biếtHow to recognize it:Nếu yêu cầu được viết bằng chữ "ngày", "tháng", "năm", hãy dùng phép tính theo lịch, đừng nhân 86.400.If the requirement says "days", "months" or "years", use calendar arithmetic, not multiples of 86,400.
Xem chi tiết: cách hoạt động, lợi ích, hạn chế, mẹoDetails: how it works, benefits, limits, tips
2026-03-08 00:00 America/New_York
+ 24 hours -> 2026-03-09 01:00 (drifts an hour)
+ 1 day -> 2026-03-09 00:00 (right)
2026-01-31 + 1 month -> 2026-02-28 in most libraries, 03-03 in some
Nó là gìWhat it is
Những phép cộng trừ ngày giờ trông hiển nhiên nhưng cho kết quả sai hoặc mơ hồ, vì thời gian vật lý và thời gian theo lịch là hai thứ khác nhau.Date and time arithmetic that looks obvious but gives wrong or ambiguous results, because physical time and calendar time are different things.
Hoạt động thế nàoHow it works
Có hai loại thời lượng: vật lý (giây, giờ tuyệt đối) và lịch (ngày, tháng theo lịch địa phương). Thư viện tốt tách bạch hai loại này, như Duration và Period trong Java, hay Temporal trong JavaScript.There are two kinds of duration: physical (seconds, absolute hours) and calendar (days, months on a local calendar). Good libraries keep them apart, like Duration and Period in Java, or Temporal in JavaScript.
Lợi íchBenefits
Hạn chót và ngày gia hạn không bị lệch.Deadlines and renewal dates don't drift.
Báo cáo theo ngày cắt đúng theo ranh giới giờ địa phương.Daily reports cut on local-time boundaries.
Ít lỗi vào ngày cuối tháng và năm nhuận.Fewer bugs at month ends and in leap years.
Hạn chếLimitations
Mỗi thư viện xử lý cuối tháng một kiểu.Every library handles month ends its own way.
"Hôm nay" phụ thuộc múi giờ của người hỏi: hai người ở hai vùng có hai "hôm nay" khác nhau."Today" depends on who's asking: two people in two zones have two different todays.
Năm nhuận: 29/2 cộng một năm là ngày nào phụ thuộc vào quy tắc bạn chọn.Leap years: 29 February plus a year depends on the rule you pick.
Mẹo triển khaiImplementation tips
Dùng kiểu riêng cho ngày không có giờ (LocalDate, PlainDate) trong Java java.time, Python datetime với zoneinfo, JavaScript Temporal (hoặc polyfill), Luxon.Use a dedicated date-only type (LocalDate, PlainDate) from Java java.time, Python datetime with zoneinfo, JavaScript Temporal (or its polyfill), Luxon.
Chốt quy tắc nghiệp vụ cho ngày 31 và 29/2 bằng văn bản, và viết test cho chúng.Write down the business rule for the 31st and 29 February, and test it.
Khi nhóm theo ngày trong SQL, chuyển sang múi giờ người dùng trước rồi mới cắt ngày (AT TIME ZONE).When grouping by day in SQL, convert to the user's zone before truncating (AT TIME ZONE).
T10Thời gian và múi giờTime and time zones
Đồng bộ NTPNTP sync
Đồng hồ của máyMachine clockKhớp giờ chuẩnSynced to real time
Giao thức giữ đồng hồ máy khớp với nguồn giờ chuẩn, sai số cỡ vài mili giây qua internet.The protocol that keeps a machine's clock close to reference time, within a few milliseconds over the internet.
Dùng choUse for
Mọi server production; máy chạy database phân tán; hệ thống xác thực: Kerberos mặc định từ chối khi lệch quá 5 phútEvery production server; hosts running distributed databases; authentication: Kerberos rejects more than 5 minutes of skew by default
ĐượcGain
Chính xác cỡ vài mili giây qua internet, dưới 1 mili giây trong mạng LAN tốt.Accurate to a few milliseconds over the internet and under a millisecond on a good LAN.
MấtLose
Không đủ cho yêu cầu micro giây như giao dịch tần suất cao hay viễn thông; khi đó cần PTP (IEEE 1588).Not enough for microsecond needs like high-frequency trading or telecom; those need PTP (IEEE 1588).
Đánh đổiTrade-offChọn NTP là đổi độ chính xác micro giây lấy đồng bộ đủ tốt, miễn phí, ở mọi nơi.Choosing NTP trades microsecond precision for good-enough sync that's free and everywhere.
Dấu hiệu nhận biếtHow to recognize it:Máy nào ghi timestamp mà nơi khác sẽ đem ra so sánh thì máy đó cần NTP.Any machine whose timestamps get compared elsewhere needs NTP.
Xem chi tiết: cách hoạt động, lợi ích, hạn chế, mẹoDetails: how it works, benefits, limits, tips
stratum 0 : atomic clock / GPS
stratum 1 : server wired to stratum 0
stratum 2+: public and internal servers
your host : chrony / systemd-timesyncd
Nó là gìWhat it is
Network Time Protocol đồng bộ đồng hồ máy với các nguồn giờ chuẩn xếp theo tầng (stratum), có bù trừ độ trễ mạng.Network Time Protocol syncs a machine's clock to reference sources arranged in tiers (strata), compensating for network delay.
Hoạt động thế nàoHow it works
Client hỏi nhiều server, ước lượng độ lệch và độ trễ, rồi chỉnh dần tốc độ đồng hồ (slew). Chỉ khi lệch lớn, thường lúc khởi động, nó mới nhảy thẳng (step).The client polls several servers, estimates offset and delay, and gradually adjusts the clock rate (slew). It only steps the clock directly when the offset is large, usually at boot.
Lợi íchBenefits
Chính xác cỡ vài mili giây qua internet, dưới 1 mili giây trong mạng LAN tốt.Accurate to a few milliseconds over the internet and under a millisecond on a good LAN.
Miễn phí, có sẵn trong mọi hệ điều hành.Free and built into every operating system.
Cơ chế slew giữ đồng hồ không lùi trong lúc chạy.Slewing keeps the clock from going backwards at runtime.
Hạn chếLimitations
Không đủ cho yêu cầu micro giây như giao dịch tần suất cao hay viễn thông; khi đó cần PTP (IEEE 1588).Not enough for microsecond needs like high-frequency trading or telecom; those need PTP (IEEE 1588).
Đường mạng bất đối xứng tạo sai số hệ thống mà NTP không tự phát hiện được.Asymmetric network paths create systematic error NTP can't detect on its own.
Một nguồn giờ sai hoặc bị tấn công có thể kéo lệch cả cụm máy.A wrong or compromised time source can drag a whole fleet off.
Mẹo triển khaiImplementation tips
Dùng chrony và cấu hình ít nhất bốn nguồn, để loại được một nguồn nói sai.Use chrony with at least four sources so a single lying source can be outvoted.
Trên cloud, dùng nguồn giờ của nhà cung cấp (Amazon Time Sync, metadata.google.internal).On cloud, use the provider's time source (Amazon Time Sync, metadata.google.internal).
Giám sát độ lệch NTP như một metric và cảnh báo khi vượt khoảng 100 ms.Track NTP offset as a metric and alert beyond roughly 100 ms.
Kiểm thử hiệu năngPerformance testing
"Chạy ngon trên máy tôi" không phải là một bài load test."It works on my machine" isn't a load test.
Mười khái niệm để trả lời bốn câu hỏi: chịu được bao nhiêu, chạy lâu có hỏng không, chậm ở đâu, và bản mới có tệ hơn bản cũ không.Ten concepts for answering four questions: how much can it take, does it break over time, where is it slow, and is the new version worse than the old one.
P1Kiểm thử hiệu năngPerformance testing
Throughput và latencyThroughput vs latency
Xử lý được bao nhiêuHow much is handledMỗi request mất bao lâuHow long each takes
Throughput là khối lượng xử lý mỗi giây; latency là thời gian của từng request. Một con số không mô tả được hệ thống.Throughput is work handled per second; latency is the time for each request. One number alone can't describe a system.
Dùng choUse for
Lập kế hoạch capacity; đặt SLO cho API; batch job tối ưu throughput, API tương tác tối ưu latencyCapacity planning; setting API SLOs; batch jobs optimize throughput; interactive APIs optimize latency
ĐượcGain
Hai con số đi cùng nhau mới mô tả được hệ thống.Together, the two numbers actually describe the system.
MấtLose
Tối ưu cái này thường hại cái kia: gom batch tăng throughput nhưng tăng latency.Optimizing one usually hurts the other: batching raises throughput and latency together.
Đánh đổiTrade-offChọn tối đa hoá throughput là đổi latency của từng request lấy tổng khối lượng xử lý.Choosing to maximize throughput trades per-request latency for total volume.
Dấu hiệu nhận biếtHow to recognize it:Có người đang ngồi chờ thì tối ưu latency. Job chạy ban đêm thì tối ưu throughput.Someone is waiting: optimize latency. A job runs overnight: optimize throughput.
Xem chi tiết: cách hoạt động, lợi ích, hạn chế, mẹoDetails: how it works, benefits, limits, tips
Little's Law: concurrency (L) = throughput (lambda) x latency (W)
200 in flight = 1000 req/s x 0.2 s
Nó là gìWhat it is
Throughput là lượng công việc xử lý được trong một đơn vị thời gian, ví dụ số request mỗi giây. Latency là thời gian một request đi từ lúc gửi tới lúc nhận xong phản hồi.Throughput is the amount of work completed per unit of time, such as requests per second. Latency is the time from sending a request to receiving its full response.
Hoạt động thế nàoHow it works
Khi tải còn thấp, tăng tải thì throughput tăng còn latency gần như đứng yên. Khi hệ thống gần bão hoà, hàng đợi bắt đầu hình thành: throughput chững lại và latency tăng vọt.At low load, more traffic raises throughput while latency barely moves. Near saturation, queues form: throughput flattens and latency shoots up.
Lợi íchBenefits
Hai con số đi cùng nhau mới mô tả được hệ thống.Together, the two numbers actually describe the system.
Little's Law cho phép suy ra số kết nối hay worker cần có.Little's Law lets you derive how many connections or workers you need.
Tối ưu cái này thường hại cái kia: gom batch tăng throughput nhưng tăng latency.Optimizing one usually hurts the other: batching raises throughput and latency together.
Throughput cao đo ở mức latency không chấp nhận được là con số vô nghĩa.High throughput measured at unacceptable latency is a meaningless number.
Latency đo phía server bỏ qua mạng và các hàng đợi phía trước.Server-side latency ignores the network and queues in front of the server.
Mẹo triển khaiImplementation tips
Luôn báo cáo throughput kèm latency ở một percentile: "1.200 rps với p99 dưới 300 ms".Always report throughput with latency at a percentile: "1,200 rps at p99 under 300 ms".
Đo latency từ phía client.Measure latency from the client side.
Dùng Little's Law để kiểm tra xem kết quả test có hợp lý không.Use Little's Law to sanity-check test results.
P2Kiểm thử hiệu năngPerformance testing
P50 / P95 / P99P50 / P95 / P99
Giá trị trung bìnhThe averageĐiển hình, tệ, rất tệTypical, bad, very bad
Percentile cho thấy trải nghiệm điển hình và phần đuôi chậm mà trung bình che mất. P99 không phải trường hợp tệ nhất.Percentiles show the typical experience and the slow tail that an average hides. P99 is not the worst case.
Dùng choUse for
SLO kiểu "P99 dưới 500 ms"; so sánh trước và sau một release; tìm đuôi chậm do GC hoặc tranh chấp khoáSLOs like "P99 under 500 ms"; before-and-after release comparisons; finding slow tails caused by GC or lock contention
ĐượcGain
Thấy được phần đuôi chậm mà trung bình che mất.Reveals the slow tail an average hides.
MấtLose
Không cộng và không lấy trung bình percentile được: trung bình P99 của 10 server không phải P99 của hệ thống.Percentiles can't be added or averaged: the mean of 10 servers' P99 isn't the system P99.
Đánh đổiTrade-offChọn percentile là đổi sự đơn giản của một con số trung bình lấy bức tranh thật về những trải nghiệm tệ.Choosing percentiles trades the simplicity of one average for a true picture of the bad experiences.
Dấu hiệu nhận biếtHow to recognize it:Một trang gọi song song 20 API thì khoảng 18% lượt tải trang sẽ gặp ít nhất một request chậm hơn P99 của API đó.A page that calls 20 APIs in parallel will hit at least one request slower than P99 on about 18% of loads.
Đã hiệu chỉnh so với ảnhCorrected from the image.Ảnh gọi P99 là "worst-case". P99 chỉ là ngưỡng của 1% chậm nhất; trường hợp tệ nhất thật sự là max, và nó có thể tệ hơn nhiều.The image calls P99 "worst-case". P99 is only the threshold of the slowest 1%; the true worst case is the max, and it can be far worse.
Xem chi tiết: cách hoạt động, lợi ích, hạn chế, mẹoDetails: how it works, benefits, limits, tips
100 requests: 95 x 50 ms, 5 x 2,000 ms
mean = 147.5 ms <- no request took this long
P50 = 50 ms P99 = 2,000 ms
Nó là gìWhat it is
P95 bằng 300 ms nghĩa là 95% request mất tối đa 300 ms và 5% chậm hơn. P50 là trung vị. P99 là ngưỡng mà 1% request chậm nhất vượt qua; trường hợp tệ nhất là max.A P95 of 300 ms means 95% of requests take at most 300 ms and 5% take longer. P50 is the median. P99 is the threshold the slowest 1% exceed; the worst case is the max.
Hoạt động thế nàoHow it works
Về lý thuyết: sắp xếp mọi latency và lấy giá trị ở vị trí tương ứng. Trong thực tế, công cụ dùng histogram (như HdrHistogram) để ước lượng mà không phải lưu từng mẫu.In theory you sort every latency and read the value at the matching position. In practice tools use histograms (such as HdrHistogram) to estimate it without storing every sample.
Lợi íchBenefits
Thấy được phần đuôi chậm mà trung bình che mất.Reveals the slow tail an average hides.
Khớp tự nhiên với SLO.Maps naturally onto SLOs.
So sánh các release một cách công bằng.Makes release comparisons fair.
Hạn chếLimitations
Không cộng và không lấy trung bình percentile được: trung bình P99 của 10 server không phải P99 của hệ thống.Percentiles can't be added or averaged: the mean of 10 servers' P99 isn't the system P99.
Cần đủ mẫu: P99 tính từ 100 request chỉ dựa vào đúng một điểm.You need enough samples: a P99 from 100 requests rests on a single point.
Coordinated omission: công cụ chờ request chậm xong rồi mới gửi tiếp sẽ đánh giá thấp latency đuôi.Coordinated omission: tools that wait for a slow request before sending the next one underreport tail latency.
Mẹo triển khaiImplementation tips
Gộp histogram trước rồi mới tính percentile; đừng gộp các percentile.Merge histograms first, then compute percentiles; never merge percentiles.
Dùng công cụ tránh được coordinated omission: wrk2, hoặc k6 với executor theo arrival rate.Use tools that avoid coordinated omission: wrk2, or k6 with an arrival-rate executor.
Báo cáo cả giá trị max.Report the max as well.
P3Kiểm thử hiệu năngPerformance testing
Ramp-up và stress testRamp-up and stress testing
Tải tăng dầnLoad increases slowlyTìm điểm gãyBreaking point found
Tăng tải dần để tìm vùng hệ thống bắt đầu suy giảm; đẩy tiếp tới lúc vỡ là stress test.Increase load gradually to find where the system starts to degrade; pushing on until it breaks is a stress test.
Dùng choUse for
Xác định capacity trước một sự kiện lớn; đặt ngưỡng autoscale; so sánh hai kiến trúcEstablishing capacity before a big event; setting autoscaling thresholds; comparing two architectures
ĐượcGain
Cho ra một con số capacity dùng được để lập kế hoạch.Produces a capacity number you can plan with.
MấtLose
Điểm gãy chỉ đúng với môi trường, dữ liệu và kịch bản đã dùng; đó là ước lượng, không phải con số chính xác.The breaking point only holds for that environment, data and scenario; it's an estimate, not an exact figure.
Đánh đổiTrade-offChọn ramp-up là đổi thời gian chạy test lấy việc thấy hệ thống bắt đầu gãy ở đâu và gãy như thế nào.Choosing a ramp-up trades test time for seeing where the system starts to break and how.
Dấu hiệu nhận biếtHow to recognize it:Câu hỏi "hệ thống chịu được bao nhiêu?" là câu hỏi của ramp-up và stress test."How much can the system take?" is a question for ramp-up and stress tests.
Đã hiệu chỉnh so với ảnhCorrected from the image.Ảnh nói ramp-up tìm "chính xác" chỗ hiệu năng bắt đầu suy giảm. Ramp-up là một kiểu tăng tải; việc tìm điểm vỡ thường gọi là stress test, và kết quả luôn là ước lượng gắn với môi trường test.The image says ramp-up finds "exactly" where performance degrades. Ramp-up is a load profile; finding the breaking point is usually called stress testing, and the result is always an estimate tied to the test environment.
Xem chi tiết: cách hoạt động, lợi ích, hạn chế, mẹoDetails: how it works, benefits, limits, tips
Tăng tải theo bậc hoặc tuyến tính, ví dụ từ 50 lên 2.000 người dùng ảo trong 30 phút, và quan sát lúc latency bắt đầu tăng vọt hoặc lỗi xuất hiện. Khi đẩy tới mức hệ thống vỡ, ta gọi là stress test hay breakpoint test.Raise load in steps or linearly, for example from 50 to 2,000 virtual users over 30 minutes, and watch for the point where latency spikes or errors appear. Pushing until the system breaks is called a stress or breakpoint test.
Hoạt động thế nàoHow it works
Mỗi bậc giữ đủ lâu để hệ thống ổn định, thường vài phút, và ghi lại throughput, percentile, tỉ lệ lỗi, CPU, bộ nhớ. Điểm gãy là nơi throughput ngừng tăng trong khi latency vẫn tăng.Hold each step long enough to stabilize, usually a few minutes, and record throughput, percentiles, error rate, CPU and memory. The knee is where throughput stops rising while latency keeps climbing.
Lợi íchBenefits
Cho ra một con số capacity dùng được để lập kế hoạch.Produces a capacity number you can plan with.
Cho thấy hệ thống suy giảm ra sao: từ từ hay sập đột ngột.Shows how the system degrades: gracefully or all at once.
Tăng dần cho cache, JIT và connection pool kịp khởi động.Gradual ramps let caches, JIT and connection pools warm up.
Hạn chếLimitations
Điểm gãy chỉ đúng với môi trường, dữ liệu và kịch bản đã dùng; đó là ước lượng, không phải con số chính xác.The breaking point only holds for that environment, data and scenario; it's an estimate, not an exact figure.
Tăng quá nhanh thì bỏ lỡ autoscale, quá chậm thì tốn thời gian.Ramp too fast and you miss autoscaling; too slow and you waste time.
Không phát hiện được rò rỉ chậm, đó là việc của soak test.It won't catch slow leaks; that's the soak test's job.
Mẹo triển khaiImplementation tips
Dùng mô hình tải mở (theo arrival rate) thay vì số người dùng cố định: người dùng ảo cố định tự giảm tải khi hệ thống chậm, che mất điểm gãy.Use an open model (arrival rate) instead of a fixed user count: fixed virtual users back off when the system slows, hiding the knee.
Lập kế hoạch capacity ở khoảng 70% điểm gãy, không phải 100%.Plan capacity at about 70% of the breaking point, not 100%.
Đẩy qua điểm gãy rồi hạ tải, để xem hệ thống có tự hồi phục không.Push past the knee and then back off, to see whether the system recovers on its own.
Giữ tải bình thường trong nhiều giờ để lộ những lỗi chỉ xuất hiện theo thời gian.Hold normal load for hours to expose failures that only appear over time.
Dùng choUse for
Rò rỉ bộ nhớ; connection không được trả về pool; log hoặc file tạm làm đầy đĩa; token hoặc chứng chỉ hết hạn sau N giờMemory leaks; connections never returned to the pool; logs or temp files filling the disk; tokens or certificates expiring after N hours
ĐượcGain
Tìm ra lỗi mà test 15 phút không bao giờ thấy.Finds failures a 15-minute test never sees.
MấtLose
Tốn thời gian và chi phí hạ tầng.Costs time and infrastructure.
Đánh đổiTrade-offChọn soak test là đổi nhiều giờ máy và thời gian chờ lấy khả năng bắt những lỗi chỉ lộ ra sau thời gian dài.Choosing a soak test trades hours of compute and waiting for catching failures that only emerge over time.
Dấu hiệu nhận biếtHow to recognize it:Nếu một service phải restart định kỳ "cho khoẻ", bạn cần soak test.If a service needs a routine restart "to stay healthy", you need a soak test.
Xem chi tiết: cách hoạt động, lợi ích, hạn chế, mẹoDetails: how it works, benefits, limits, tips
memory
| /// <- leak: never levels off
| ///////
| /////// (load held flat for 12 h)
+------------------------ hours
Nó là gìWhat it is
Chạy tải ở mức bình thường hoặc hơi cao liên tục trong nhiều giờ, thường 4 đến 24 giờ, đôi khi vài ngày.Run normal or slightly elevated load continuously for hours, typically 4 to 24, sometimes days.
Hoạt động thế nàoHow it works
Giữ tải phẳng và theo dõi xu hướng: bộ nhớ, file descriptor, connection pool, dung lượng đĩa và log, latency theo từng giờ.Keep load flat and watch the trends: memory, file descriptors, connection pools, disk and log usage, latency hour by hour.
Lợi íchBenefits
Tìm ra lỗi mà test 15 phút không bao giờ thấy.Finds failures a 15-minute test never sees.
Kiểm tra được job định kỳ, xoay vòng log, gia hạn token và chứng chỉ.Exercises scheduled jobs, log rotation, token and certificate renewal.
Xác nhận hệ thống ổn định khi chạy lâu dài.Confirms the system is stable over long runs.
Hạn chếLimitations
Tốn thời gian và chi phí hạ tầng.Costs time and infrastructure.
Khó chạy sau mỗi lần deploy.Hard to run on every deploy.
Rò rỉ rất chậm vẫn có thể lọt qua 12 giờ test.Very slow leaks can still slip through a 12-hour run.
Mẹo triển khaiImplementation tips
Nhìn độ dốc chứ không nhìn giá trị: bộ nhớ tăng đều mỗi giờ là tín hiệu xấu kể cả khi còn xa giới hạn.Watch the slope, not the level: memory rising steadily every hour is a bad sign even far from the limit.
Chạy qua đêm theo lịch, đừng để nó chặn pipeline.Run it overnight on a schedule, not as a pipeline gate.
Chọn cửa sổ test bao trùm cả các sự kiện định kỳ như cron và xoay vòng log.Make the window cover periodic events such as cron runs and log rotation.
P5Kiểm thử hiệu năngPerformance testing
Spike testSpike testing
Tải cực lớn đột ngộtSudden extreme loadXem hệ thống phản ứngSystem response seen
Tăng tải gấp nhiều lần chỉ trong vài giây để xem hệ thống chịu, suy giảm có kiểm soát, và hồi phục ra sao.Multiply load within seconds to see whether the system holds, degrades in a controlled way, and recovers.
Dùng choUse for
Flash sale; push notification gửi tới hàng triệu người; mở bán vé; bot và crawler tràn vàoFlash sales; push notifications to millions of users; ticket releases; bot and crawler floods
ĐượcGain
Kiểm chứng autoscale, rate limit và load shedding.Validates autoscaling, rate limiting and load shedding.
MấtLose
Autoscale thường chậm hơn đợt tăng tải, vì máy mới cần vài phút; kết quả hay gây bất ngờ.Autoscaling is usually slower than the spike, since new instances take minutes; results often surprise.
Đánh đổiTrade-offChọn thiết kế chịu được spike là đổi chi phí công suất dự phòng, hoặc việc từ chối bớt request, lấy việc không sập toàn bộ.Choosing to withstand spikes trades spare capacity, or rejecting some requests, for not going down entirely.
Dấu hiệu nhận biếtHow to recognize it:Nếu bộ phận marketing có thể bấm một nút khiến cả triệu người vào cùng lúc, bạn cần spike test.If marketing can press one button that sends a million people in at once, you need a spike test.
Xem chi tiết: cách hoạt động, lợi ích, hạn chế, mẹoDetails: how it works, benefits, limits, tips
load
| +--+
| | |
| ------+ +-------- x10 for 30 seconds
+----------------------- time
Nó là gìWhat it is
Tăng tải đột ngột lên nhiều lần mức bình thường trong vài giây, giữ trong thời gian ngắn rồi hạ xuống.Raise load to several times normal within seconds, hold briefly, then drop it.
Hoạt động thế nàoHow it works
Quan sát autoscale phản ứng mất bao lâu, phần request bị từ chối có được trả lỗi rõ ràng (429, 503) không, hàng đợi có xả kịp không, và hệ thống có về trạng thái bình thường sau đợt tăng không.Watch how long autoscaling takes, whether rejected requests get clear errors (429, 503), whether queues drain, and whether the system returns to normal afterwards.
Lợi íchBenefits
Kiểm chứng autoscale, rate limit và load shedding.Validates autoscaling, rate limiting and load shedding.
Lộ ra hiệu ứng dây chuyền như thundering herd hay bão retry.Exposes cascades like thundering herds and retry storms.
Chứng minh hệ thống hồi phục được chứ không kẹt lại.Proves the system recovers instead of staying stuck.
Hạn chếLimitations
Autoscale thường chậm hơn đợt tăng tải, vì máy mới cần vài phút; kết quả hay gây bất ngờ.Autoscaling is usually slower than the spike, since new instances take minutes; results often surprise.
Dễ ảnh hưởng tới hệ thống bên thứ ba dùng chung.Easily hurts shared third-party systems.
Khó tái tạo đúng hình dạng đợt tăng tải thật.Hard to reproduce the real spike's shape.
Mẹo triển khaiImplementation tips
Mục tiêu thực tế thường là suy giảm có kiểm soát: trả nhanh 429 hoặc 503 cho phần vượt ngưỡng thay vì làm chậm tất cả mọi người.The realistic goal is usually controlled degradation: fail the excess fast with 429 or 503 instead of slowing everyone down.
Retry phải có exponential backoff và jitter, nếu không chính retry sẽ tạo ra đợt spike thứ hai.Retries need exponential backoff with jitter, or they create a second spike.
Khi biết trước giờ sự kiện, scale sẵn thay vì trông vào autoscale.When you know the event time, pre-scale instead of relying on autoscaling.
P6Kiểm thử hiệu năngPerformance testing
Tìm nút thắt cổ chaiBottleneck identification
Cả hệ thống chậmWhole system slowNút thắt hiện tạiThe current limiter
Tìm thành phần đang giới hạn hiệu năng; sửa xong thì nút thắt dời sang chỗ khác.Find the component currently limiting performance; fix it and the bottleneck moves elsewhere.
Dùng choUse for
API chậm dần khi tải tăng; CPU thấp mà vẫn chậm, do chờ I/O hoặc khoá; nâng cấp phần cứng mà không nhanh hơnAPIs slowing as load grows; low CPU but still slow, from I/O or lock waits; hardware upgrades that didn't help
ĐượcGain
Tối ưu đúng một chỗ hiệu quả hơn tối ưu mười chỗ sai.Fixing the right spot beats fixing ten wrong ones.
MấtLose
Nút thắt dời chỗ: sửa xong cái này thì cái tiếp theo lộ ra.Bottlenecks move: fix one and the next appears.
Đánh đổiTrade-offChọn đo trước khi tối ưu là đổi thời gian điều tra lấy việc không tối ưu nhầm chỗ.Choosing to measure before optimizing trades investigation time for not optimizing the wrong thing.
Dấu hiệu nhận biếtHow to recognize it:Nếu thêm tài nguyên mà hệ thống không nhanh hơn, bạn đã thêm nhầm chỗ.If adding resources didn't make things faster, you added them in the wrong place.
Đã hiệu chỉnh so với ảnhCorrected from the image.Ảnh gọi đó là "thành phần duy nhất thật sự giới hạn". Tại một thời điểm thường có một nút thắt chính, nhưng nó dời chỗ ngay khi bạn sửa xong.The image calls it "the one real limiter". At any moment there is usually one dominant bottleneck, but it moves as soon as you fix it.
Xem chi tiết: cách hoạt động, lợi ích, hạn chế, mẹoDetails: how it works, benefits, limits, tips
request 800 ms
|- app code 40 ms
|- DB pool wait 610 ms <- bottleneck
'- DB query 150 ms
Nó là gìWhat it is
Tìm thành phần giới hạn hiệu năng của toàn hệ thống, tức nơi tài nguyên bão hoà trước tiên: CPU, bộ nhớ, I/O, khoá, connection pool, hay một service phụ thuộc.Find the component that limits the whole system, the resource that saturates first: CPU, memory, I/O, locks, a connection pool, or a downstream service.
Hoạt động thế nàoHow it works
Phương pháp USE của Brendan Gregg: với mỗi tài nguyên, kiểm tra Utilization (mức sử dụng), Saturation (mức bão hoà, hàng chờ) và Errors (lỗi). Kết hợp tracing để thấy request tiêu thời gian ở đâu.Brendan Gregg's USE method: for each resource, check Utilization, Saturation (queueing) and Errors. Combine it with tracing to see where requests spend their time.
Lợi íchBenefits
Tối ưu đúng một chỗ hiệu quả hơn tối ưu mười chỗ sai.Fixing the right spot beats fixing ten wrong ones.
Tiết kiệm chi phí scale.Saves scaling costs.
Chỉ ra bước tối ưu tiếp theo.Shows the next optimization to make.
Hạn chếLimitations
Nút thắt dời chỗ: sửa xong cái này thì cái tiếp theo lộ ra.Bottlenecks move: fix one and the next appears.
Tài nguyên ẩn như connection pool, thread pool hay rate limit bên ngoài không hiện trên dashboard CPU.Hidden resources like connection pools, thread pools or external rate limits don't show on a CPU dashboard.
Profiling trên production có chi phí và rủi ro.Profiling in production has cost and risk.
Mẹo triển khaiImplementation tips
Kiểm tra các pool (connection database, thread) trước tiên; đây là nút thắt phổ biến nhất mà CPU không phản ánh.Check pools (database connections, threads) first; they're the most common bottleneck that CPU graphs don't show.
Dùng distributed tracing (OpenTelemetry) và flame graph.Use distributed tracing (OpenTelemetry) and flame graphs.
Sau mỗi lần sửa, chạy lại test để tìm nút thắt mới.Rerun the test after every fix to find the new bottleneck.
P7Kiểm thử hiệu năngPerformance testing
Synthetic và người dùng thậtSynthetic vs real user
Mô phỏng theo lịchSimulated on scheduleĐo lượt truy cập thậtActual visitors measured
Synthetic monitoring chạy kịch bản giả lập theo lịch; RUM đo trải nghiệm thật từ thiết bị của người dùng.Synthetic monitoring runs scripted checks on a schedule; RUM measures real experience from users' devices.
Dùng choUse for
Synthetic: cảnh báo sự cố lúc 3 giờ sáng khi chưa có ai truy cập; synthetic: kiểm tra luồng đăng nhập và thanh toán sau mỗi deploy; RUM: biết người dùng mạng yếu ở xa thật sự thấy gìSynthetic: alerting at 3 a.m. before anyone visits; synthetic: checking login and checkout after each deploy; RUM: seeing what users on slow networks actually get
ĐượcGain
Synthetic ổn định, lặp lại được, và có dữ liệu cả khi không có traffic.Synthetic is stable, repeatable, and has data even without traffic.
MấtLose
Synthetic chạy từ datacenter có mạng tốt nên lạc quan hơn thực tế.Synthetic runs from well-connected datacenters, so it's optimistic.
Đánh đổiTrade-offChọn synthetic là đổi độ chân thực lấy sự nhất quán và cảnh báo sớm.Choosing synthetic trades realism for consistency and early warning.
Dấu hiệu nhận biếtHow to recognize it:Muốn biết "có hỏng không" thì dùng synthetic. Muốn biết "người dùng thấy gì" thì dùng RUM.To know "is it broken?", use synthetic. To know "what do users see?", use RUM.
Xem chi tiết: cách hoạt động, lợi ích, hạn chế, mẹoDetails: how it works, benefits, limits, tips
Nó là gìWhat it is
Synthetic monitoring chạy script mô phỏng người dùng theo lịch cố định từ các vị trí được chọn. Real User Monitoring (RUM) thu số đo trải nghiệm thật từ trình duyệt hoặc app của người dùng.Synthetic monitoring runs scripts that imitate users on a fixed schedule from chosen locations. Real User Monitoring (RUM) collects real experience measurements from users' browsers or apps.
Hoạt động thế nàoHow it works
Synthetic: bot mở trang hoặc gọi API vài phút một lần, ghi lại thời gian và lỗi. RUM: một đoạn JavaScript hoặc SDK gửi số liệu như Core Web Vitals (LCP, INP, CLS) từ thiết bị thật.Synthetic: a bot loads a page or calls an API every few minutes and records timing and errors. RUM: a JavaScript snippet or SDK sends metrics such as Core Web Vitals (LCP, INP, CLS) from real devices.
Lợi íchBenefits
Synthetic ổn định, lặp lại được, và có dữ liệu cả khi không có traffic.Synthetic is stable, repeatable, and has data even without traffic.
RUM phản ánh đúng thiết bị, mạng và vị trí địa lý thật.RUM reflects real devices, networks and geography.
Kết hợp cả hai vừa phát hiện sự cố sớm vừa hiểu được tác động.Together they catch problems early and show their real impact.
Hạn chếLimitations
Synthetic chạy từ datacenter có mạng tốt nên lạc quan hơn thực tế.Synthetic runs from well-connected datacenters, so it's optimistic.
RUM cần traffic, dữ liệu nhiễu, và vướng vấn đề quyền riêng tư, đồng ý cookie.RUM needs traffic, is noisy, and raises privacy and cookie-consent issues.
RUM không ghi nhận được người dùng không tải nổi trang.RUM can't record users who never managed to load the page.
Mẹo triển khaiImplementation tips
Dùng synthetic cho vài luồng quan trọng nhất, không phải cho mọi trang.Use synthetic for a handful of critical flows, not every page.
Phân đoạn RUM theo thiết bị, quốc gia, loại mạng; con số toàn cục che mất nhóm chịu thiệt.Segment RUM by device, country and network type; global numbers hide the group that suffers.
INP đã thay FID trong bộ Core Web Vitals từ tháng 3/2024.INP replaced FID in Core Web Vitals in March 2024.
P8Kiểm thử hiệu năngPerformance testing
Môi trường load testLoad test environments
Môi trường testTest environmentGiống production ở điểm quyết địnhMatches prod where it matters
Kết quả chỉ có nghĩa khi môi trường giống production ở những điểm quyết định hiệu năng, không cần giống hệt mọi thứ.Results only mean something when the environment matches production where performance is decided, not in every detail.
Dùng choUse for
Trước khi go-live; trước khi đổi database hoặc loại instance; kiểm chứng autoscaleBefore go-live; before changing databases or instance types; validating autoscaling
ĐượcGain
Kết quả chuyển sang production được.Results transfer to production.
MấtLose
Bản sao đầy đủ rất đắt.A full replica is expensive.
Đánh đổiTrade-offChọn môi trường giống production là đổi chi phí hạ tầng lấy kết quả tin được.Choosing a production-like environment trades infrastructure cost for results you can trust.
Dấu hiệu nhận biếtHow to recognize it:Nếu database test có 1.000 dòng còn production có 100 triệu, kết quả test không nói gì về production.If the test database has 1,000 rows and production has 100 million, the test says nothing about production.
Đã hiệu chỉnh so với ảnhCorrected from the image.Ảnh nói môi trường test "phải giống production". Giống hệt thường không khả thi về chi phí; điều cần là giống ở những điểm quyết định hiệu năng, và hiểu rõ phần nào khác.The image says the test environment "must resemble prod". An identical copy is rarely affordable; what matters is matching where performance is decided, and knowing what differs.
Xem chi tiết: cách hoạt động, lợi ích, hạn chế, mẹoDetails: how it works, benefits, limits, tips
Nó là gìWhat it is
Môi trường dùng để chạy load test. Nó cần giống production ở những điểm quyết định hiệu năng: kiến trúc, cấu hình, khối lượng và phân bố dữ liệu, các phụ thuộc.The environment where load tests run. It must match production where performance is decided: architecture, configuration, data volume and distribution, dependencies.
Hoạt động thế nàoHow it works
Có ba cách: dựng bản sao đủ lớn; thu nhỏ theo tỉ lệ và hiểu rõ giới hạn của việc thu nhỏ; hoặc test có kiểm soát ngay trên production (giờ thấp điểm, shadow traffic).Three options: build a large enough replica; scale down proportionally and understand what scaling down hides; or test carefully in production (off-peak, shadow traffic).
Lợi íchBenefits
Kết quả chuyển sang production được.Results transfer to production.
Bắt lỗi cấu hình (pool, timeout, limit) trước khi người dùng gặp.Catches configuration problems (pools, timeouts, limits) before users do.
Có chỗ an toàn để đẩy hệ thống tới lúc vỡ.Gives you a safe place to push until things break.
Hạn chếLimitations
Bản sao đầy đủ rất đắt.A full replica is expensive.
Kết quả trên môi trường nhỏ không nhân tuyến tính lên được.Results from a small environment don't scale linearly.
Phụ thuộc bên thứ ba (thanh toán, SMS) phải giả lập, và bản giả luôn nhanh hơn bản thật.Third-party dependencies (payments, SMS) have to be mocked, and mocks are always faster than the real thing.
Mẹo triển khaiImplementation tips
Ưu tiên giống về dữ liệu (khối lượng, phân bố, index) hơn giống về số máy.Prioritize matching data (volume, distribution, indexes) over matching machine count.
Giả lập bên thứ ba với độ trễ và tỉ lệ lỗi giống thật, gồm cả latency P99.Mock third parties with realistic latency and error rates, including their P99.
Máy tạo tải cũng có thể là nút thắt; theo dõi CPU và mạng của nó, và đừng chạy test từ laptop.The load generator can be the bottleneck too; watch its CPU and network, and never run the test from a laptop.
P9Kiểm thử hiệu năngPerformance testing
So sánh với baselineBaseline comparison
Kết quả test mớiNew test resultBaseline đã biết là tốtKnown-good baseline
So kết quả mới với một kết quả tham chiếu tốt đã biết, cùng kịch bản và cùng môi trường.Compare new results against a known-good reference run with the same scenario and environment.
Dùng choUse for
Kiểm tra mỗi release; so sánh trước và sau khi nâng cấp thư viện hoặc database; xác nhận một tối ưu có hiệu quả thậtChecking each release; before and after library or database upgrades; confirming an optimization actually worked
ĐượcGain
Biến con số tuyệt đối vô nghĩa thành thông tin so sánh được.Turns meaningless absolute numbers into comparable information.
MấtLose
Nhiễu môi trường (noisy neighbor trên cloud) có thể lớn hơn thay đổi bạn định đo.Environmental noise (noisy neighbors on cloud) can exceed the change you're measuring.
Đánh đổiTrade-offChọn so với baseline là đổi công sức duy trì một kết quả tham chiếu lấy khả năng phát hiện suy giảm.Choosing baseline comparison trades the effort of maintaining a reference for the ability to detect regressions.
Dấu hiệu nhận biếtHow to recognize it:Một con số không có baseline không trả lời được câu hỏi "tốt hay xấu?".A number without a baseline can't answer "good or bad?".
Xem chi tiết: cách hoạt động, lợi ích, hạn chế, mẹoDetails: how it works, benefits, limits, tips
baseline new delta
p95 180 ms 240 ms +33% fail
rps 1,200 1,190 -1% ok
errors 0.1% 0.1% 0 ok
Nó là gìWhat it is
So sánh kết quả test mới với một kết quả tham chiếu đã biết là tốt, chạy cùng kịch bản trong cùng môi trường.Compare a new test result with a reference result known to be good, from the same scenario in the same environment.
Hoạt động thế nàoHow it works
Lưu kết quả của một phiên bản ổn định. Mỗi thay đổi chạy lại cùng kịch bản và tính độ lệch. Chỉ cập nhật baseline khi có chủ đích.Store the results of a stable version. Rerun the same scenario for every change and compute the difference. Update the baseline only on purpose.
Lợi íchBenefits
Biến con số tuyệt đối vô nghĩa thành thông tin so sánh được.Turns meaningless absolute numbers into comparable information.
Bắt được suy giảm tích luỹ qua nhiều release.Catches regressions that accumulate across releases.
Có bằng chứng cho các quyết định tối ưu.Gives evidence for optimization decisions.
Hạn chếLimitations
Nhiễu môi trường (noisy neighbor trên cloud) có thể lớn hơn thay đổi bạn định đo.Environmental noise (noisy neighbors on cloud) can exceed the change you're measuring.
Baseline cũ lỗi thời khi dữ liệu và traffic thay đổi.Old baselines go stale as data and traffic change.
Cập nhật baseline tuỳ tiện sẽ hợp thức hoá suy giảm.Updating the baseline casually legitimizes regressions.
Mẹo triển khaiImplementation tips
Chạy mỗi test vài lần và so sánh phân phối, không so một lần chạy.Run each test several times and compare distributions, not single runs.
Đặt ngưỡng lệch cho phép lớn hơn mức nhiễu đã đo, ví dụ trên 10%.Set the allowed deviation above measured noise, for example over 10%.
Ghi kèm phiên bản code, cấu hình và dữ liệu vào baseline.Record code version, configuration and dataset with the baseline.
P10Kiểm thử hiệu năngPerformance testing
Performance budgetPerformance budgets
Một giới hạn cứngA hard limitBắt suy giảm sớmRegression caught early
Giới hạn cứng cho các chỉ số hiệu năng, được kiểm tra trong CI, để suy giảm bị chặn ngay ở pull request gây ra nó.Hard limits on performance metrics, checked in CI, so regressions are stopped at the pull request that causes them.
Dùng choUse for
Frontend: kích thước bundle, LCP; các API trọng yếu; ứng dụng di động: thời gian khởi động, dung lượngFrontend: bundle size, LCP; critical APIs; mobile apps: startup time, download size
ĐượcGain
Bắt suy giảm ngay ở pull request gây ra nó, không phải sáu tháng sau.Catches regressions in the pull request that caused them, not six months later.
MấtLose
Budget đặt quá chặt gây fail giả, rồi bị tắt đi.Budgets set too tight cause false failures and get switched off.
Đánh đổiTrade-offChọn performance budget là đổi sự tự do thêm tính năng lấy hiệu năng không bị bào mòn từng chút một.Choosing performance budgets trades the freedom to add features for performance that doesn't erode bit by bit.
Dấu hiệu nhận biếtHow to recognize it:Nếu hiệu năng chỉ được nhìn tới khi có người phàn nàn, bạn cần budget.If performance only gets attention when someone complains, you need budgets.
Xem chi tiết: cách hoạt động, lợi ích, hạn chế, mẹoDetails: how it works, benefits, limits, tips
budget: JS <= 170 KB gzip | LCP <= 2.5 s | API p95 <= 300 ms
PR #482: JS 185 KB -> CI fails, merge blocked
Nó là gìWhat it is
Giới hạn mà mọi thay đổi không được vượt qua: kích thước bundle JavaScript, LCP, P95 của API, số query mỗi request.Limits no change is allowed to exceed: JavaScript bundle size, LCP, API P95, queries per request.
Hoạt động thế nàoHow it works
Budget được đo trong CI bằng các công cụ như Lighthouse CI, size-limit, hoặc threshold của k6. Vượt ngưỡng thì build fail, giống như một test fail.Budgets are measured in CI with tools like Lighthouse CI, size-limit or k6 thresholds. Exceeding one fails the build, just like a failing test.
Lợi íchBenefits
Bắt suy giảm ngay ở pull request gây ra nó, không phải sáu tháng sau.Catches regressions in the pull request that caused them, not six months later.
Biến hiệu năng thành yêu cầu kiểm được, không còn là mong muốn.Turns performance into a checkable requirement instead of a wish.
Buộc phải bàn về đánh đổi mỗi khi thêm thư viện.Forces a trade-off discussion whenever a library is added.
Hạn chếLimitations
Budget đặt quá chặt gây fail giả, rồi bị tắt đi.Budgets set too tight cause false failures and get switched off.
Đo latency trong CI nhiễu, dễ thành test chập chờn.Latency measured in CI is noisy and turns flaky.
Budget chỉ bảo vệ những chỉ số được đo; mọi thứ khác vẫn có thể xấu đi.Budgets only protect what they measure; everything else can still get worse.
Mẹo triển khaiImplementation tips
Đặt budget từ hiện trạng cộng một biên nhỏ, rồi siết dần.Set budgets at current values plus a small margin, then tighten.
Chỉ số tất định (kích thước bundle, số query) hợp để chặn merge; chỉ số nhiễu (latency) nên chạy lặp hoặc chỉ cảnh báo.Deterministic metrics (bundle size, query count) suit merge gates; noisy ones (latency) should be repeated or only warn.
Ngưỡng "tốt" của Google cho LCP là tối đa 2,5 giây, tính ở P75 của lượt tải trang.Google's "good" threshold for LCP is 2.5 seconds or less, measured at the 75th percentile of page loads.
Bảng đánh đổiTrade-off tables
Chỉ ba chỗ có lựa chọn thật giữa các phương án. Các khái niệm còn lại không thay thế được cho nhau, nên không đặt chung một bảng.Only three places offer a real choice between options. The other concepts aren't substitutes for one another, so they don't share a table.
Giới hạn giá trị của một cộtRestricting a column's values
EnumEnum
text + CHECKtext + CHECK
Bảng tra cứuLookup table
Thêm giá trịAdding a value
MigrationMigration
Migration nhẹLight migration
Một lệnh INSERTOne INSERT
Xoá giá trịRemoving a value
Rất khóVery hard
Migration nhẹLight migration
Cờ ngừng dùngRetire flag
Chi phí đọcRead cost
Không JOINNo JOIN
Không JOINNo JOIN
Thêm một JOINOne extra JOIN
Metadata (nhãn, thứ tự)Metadata (labels, order)
KhôngNo
KhôngNo
CóYes
Ai được thay đổiWho can change it
Lập trình viênDevelopers
Lập trình viênDevelopers
Người vận hànhOperations staff
Cách lưu một thời điểmHow to store an instant
timestamptztimestamptz
Unix int64Unix int64
Chuỗi ISO 8601ISO 8601 string
Giờ địa phương + tên IANALocal time + IANA name
Mơ hồAmbiguity
KhôngNone
KhôngNone
Có, nếu trộn độ lệchYes, with mixed offsets
Có, ở giờ lặp khi đổi giờYes, at repeated DST hours
Báo cáo latency trung bìnhReporting average latency
Trung bình che mất phần đuôi chậm mà người dùng thật sự cảm thấy.Averages hide the slow tail users actually feel.
Báo cáo P50, P95, P99 và max.Report P50, P95, P99 and max.
Lấy trung bình P99 của nhiều serverAveraging P99 across servers
Percentile không cộng hay chia trung bình được.Percentiles can't be added or averaged.
Gộp histogram rồi mới tính percentile.Merge histograms, then compute percentiles.
Load test từ laptop vào database rỗngLoad testing from a laptop against an empty database
Máy tạo tải thành nút thắt; dữ liệu nhỏ nằm gọn trong cache.The load generator becomes the bottleneck; tiny data fits in cache.
Máy tạo tải riêng, dữ liệu có khối lượng và phân bố giống thật.Dedicated load generators, data with realistic volume and distribution.
Retry không có backoff và jitterRetries without backoff and jitter
Mỗi lần hệ thống chậm, retry nhân tải lên và tạo spike thứ hai.Every slowdown multiplies load through retries and creates a second spike.
Exponential backoff, jitter, giới hạn số lần retry.Exponential backoff, jitter, capped retries.
Checklist trước khi lên productionPre-production checklist
Ba câu cần nhớThree things to remember
Ràng buộc đặt trong database sống lâu hơn code của bạn: khoá ngoại, kiểu dữ liệu đúng, CHECK, và mọi thay đổi đi qua migration.Constraints in the database outlive your code: foreign keys, correct types, CHECK, and every change through a migration.
Lưu thời điểm bằng UTC, lưu ý định bằng giờ địa phương cộng tên IANA, và đo thời lượng bằng đồng hồ monotonic.Store instants in UTC, store intent as local time plus an IANA name, and measure durations with a monotonic clock.
Một con số hiệu năng chỉ có nghĩa khi đi kèm percentile, một môi trường giống thật ở điểm quyết định, và một baseline để so sánh.A performance number only means something with a percentile, an environment that's realistic where it matters, and a baseline to compare against.