PostgreSQL: Zero to Hero
Sê-ri 60 bài đi từ kiểu dữ liệu và chỉ mục tới MVCC, kế hoạch thực thi, sao chép và vận hành. Mọi khẳng định đều kèm số đo chạy thật trên PostgreSQL hiện hành.
60/60 phần đã đăng
Cơ sở dữ liệu
1
Ba lý do kinh điển để chọn PostgreSQL — tôi dựng nó cạnh MySQL 8 và SQLite rồi thử, một lý do đã hết đúng và chỗ Postgres thua tốn 68 MB cho 51 kết nối
So PostgreSQL với MySQL và SQLite bằng phép thử chạy thật: chặt chẽ kiểu đã hết là ưu thế riêng, DDL trong giao dịch vẫn đúng và đáng kể, và mỗi kết nối là một tiến trình tốn 1,34 MB.
20/10/2025
· 9 phút đọc
2
pg_isready báo khoẻ trong 208 ms cho một máy chủ PostgreSQL sắp bị tắt
Đo đường thời gian initdb bằng mốc trong log, xem nó tạo ra gì, và vì sao lần khởi động thứ hai nhanh hơn gần bốn lần.
21/10/2025
· 8 phút đọc
3
Cùng một triệu dòng, chỉ đổi thứ tự khai cột: bảng phình thêm 21% mà không đổi lấy một byte dữ liệu
Kích thước thật của từng kiểu, chi phí cố định mỗi dòng lớn hơn bạn nghĩ, và vì sao thứ tự khai cột đổi kích thước bảng tới một phần năm.
22/10/2025
· 9 phút đọc
4
char(5) bảo 'ab ' bằng 'ab', text bảo khác nhau — và đổi text sang varchar(15) khoá viết lại cả bảng một triệu dòng
Đo bốn kiểu chuỗi trên một triệu dòng: text/varchar/varchar(n) giống nhau tới từng byte, char(n) tốn thêm 19% và so sánh theo luật khác, nới giới hạn miễn phí còn thu hẹp thì khoá bảng.
23/10/2025
· 7 phút đọc
5
Cộng 0,01 một triệu lần: float lệch mất một khoản không cân được sổ, numeric thì không
Đo sai số tích luỹ, tốc độ và dung lượng của numeric so với float. Lời khuyên quen thuộc chỉ đúng một nửa.
24/10/2025
· 8 phút đọc
6
Cùng khoá 16 byte, chỉ khác thứ tự sinh: chỉ mục UUID ngẫu nhiên phình thêm 8 MB trên một triệu dòng
bigserial, UUID ngẫu nhiên và UUID tăng dần trên một triệu dòng. Đo dung lượng chỉ mục, mật độ trang lá và tốc độ tra cứu.
25/10/2025
· 8 phút đọc
7
Một dòng NULL làm NOT IN trả về rỗng — không lỗi, không cảnh báo, và còn chạy nhanh hơn
NULL không phải một giá trị, nó là 'chỗ chưa biết', và SQL xử lý nó bằng logic ba trạng thái. Hệ quả: một dòng NULL trong bảng con làm NOT IN xoá sạch kết quả, phong <> 'Ky thuat' bỏ sót người phòng trống, avg() chia nhầm mẫu số — tất cả im lặng.
26/10/2025
· 8 phút đọc
8
Nút ghi 0,003 ms nên bỏ qua? Nó chạy 49 lần — ba con số trong EXPLAIN ai cũng đọc sai
SQL nói bạn MUỐN gì, không nói LÀM THẾ NÀO — bộ lập lịch tự nghĩ ra cách làm, và EXPLAIN là cửa sổ nhìn vào đó. Mổ từng con số cost, rows, loops, buffers; ba chỗ hay đọc sai; và một ước lượng lệch 83 lần vì bộ tối ưu tưởng hai cột độc lập.
27/10/2025
· 8 phút đọc
9
Bộ tối ưu tin có 3 dòng, thực tế có 500.001 — lệch 166.667 lần, và vì sao con số đó quyết định tất cả
Gần như mọi truy vấn chậm bất thường đều bắt đầu từ một con số: chênh lệch giữa rows dự đoán và rows thật. Đo ba nguồn làm bộ tối ưu PostgreSQL đoán lệch, nặng nhất 166.667 lần — và một điều nó vẫn làm đúng dù thống kê rỗng hoàn toàn.
28/10/2025
· 8 phút đọc
10
Quét toàn bảng nhanh hơn dùng chỉ mục khi lấy quá 45% số dòng — và bộ tối ưu chuyển sớm ở 32%
Thấy Seq Scan là thêm chỉ mục — nhưng có một ngưỡng mà quét toàn bảng thật sự nhanh hơn. Đo ngưỡng đó trên 2 triệu dòng, và hai thứ dịch chuyển nó: random_page_cost (mặc định từ thời đĩa quay) và thứ tự vật lý của dữ liệu.
29/10/2025
· 8 phút đọc
11
Tìm 1 dòng trong 10 triệu chỉ tốn 3 lần đọc trang — và vì sao thêm dữ liệu gần như không làm chậm
Chỉ mục B-tree lớn hơn 5.350 lần mà thời gian tìm chỉ tăng 7%, nhờ mỗi nút chứa ~366 khoá nên cây chỉ cao 3 tầng. Đo cái giá phải trả khi ghi (bốn chỉ mục làm chèn chậm 3,5 lần) và chỗ UPDATE mất cơ chế HOT khi cột có chỉ mục.
30/10/2025
· 8 phút đọc
12
Cùng hai cột, đổi thứ tự khai làm truy vấn chậm 472 lần
CREATE INDEX ON don (a, b) và (b, a) chiếm cùng dung lượng, chứa cùng dữ liệu, nhưng phục vụ hai tập truy vấn khác hẳn. Ba quy tắc chọn thứ tự cột trong PostgreSQL, mỗi quy tắc kèm số đo trên 2 triệu dòng — kèm quy tắc tiền tố trái giúp bỏ chỉ mục thừa.
31/10/2025
· 8 phút đọc
13
Thêm một mệnh đề WHERE vào chỉ mục làm nó nhỏ đi 20 lần, đọc nhanh 2,9 lần và ghi nhanh 2,7 lần — cùng lúc
Phần lớn bảng nghiệp vụ đều lệch nặng: 95% đơn đã xong, 90% bản ghi đã xoá mềm. Chỉ mục một phần chỉ đánh chỉ mục đúng phần dữ liệu người ta thật sự truy vấn, và trên dữ liệu lệch nó thắng cả ba mặt cùng lúc — kèm ràng buộc unique có điều kiện và cái bẫy truy vấn quên vị từ.
01/11/2025
· 8 phút đọc
14
Bọc cột trong lower() làm truy vấn chậm 900 lần — và vì sao chỉ mục có sẵn không cứu được
Chỉ mục lưu giá trị nguyên của cột, nên hỏi về một hàm áp lên cột là mất chỉ mục và quét toàn bảng. Chỉ mục biểu thức chữa được, nhưng nó đòi khớp biểu thức chính xác tới từng ký tự và hàm phải IMMUTABLE — kèm cột sinh là lối thay thế dễ đọc hơn.
02/11/2025
· 8 phút đọc
15
Kế hoạch ghi 'Index Only Scan' mà vẫn chạm bảng 29.976 lần — con số thật nằm ở chỗ khác
Chỉ mục phủ làm truy vấn nhanh 6,6 lần, nhưng có một điều kiện thứ hai gần như không ai nhắc: bản đồ trang khả kiến. Kế hoạch vẫn ghi Index Only Scan mà thời gian tăng gấp đôi — và con số cần nhìn là Heap Fetches, thứ chỉ hiện khi bạn chạy EXPLAIN ANALYZE.
03/11/2025
· 7 phút đọc
16
Chỉ mục GIN tìm từ hiếm nhanh gấp 754 lần — nhưng tìm từ phổ biến còn chậm hơn cả không có chỉ mục
Đo trên 500.000 bài tiếng Việt: GIN nhanh 754 lần khi từ khoá hiếm, chậm hơn cả LIKE khi từ khoá phổ biến. Và cách xử lý ba chuyện riêng của tiếng Việt: không có bộ tách từ, tìm không dấu, và bẫy IMMUTABLE khi đánh chỉ mục unaccent.
04/11/2025
· 8 phút đọc
17
Chỉ mục nhỏ hơn B-tree 6.850 lần — nhưng xáo trộn thứ tự dòng một cái là nó chậm hơn cả không có chỉ mục
Đo B-tree, BRIN và GiST trên bảng sự kiện 10 triệu dòng trong PostgreSQL: BRIN chỉ 32 kB, gần như miễn phí lúc ghi, nhưng nó đánh chỉ mục bố cục vật lý chứ không phải dữ liệu — xáo trộn thứ tự dòng là nó chậm 234 lần, và chết hoàn toàn trong im lặng.
28/08/2026
· 12 phút đọc
18
Chỉ mục HASH nhỏ hơn B-tree 8,8 lần với khoá dài — nhưng chỉ cần một câu ORDER BY là nó thành đồ bỏ
Đo HASH, SP-GiST và bloom trên PostgreSQL 16: HASH nhỏ hơn B-tree 8,8 lần với khoá dài vì nó chỉ lưu vân tay 4 byte thay vì cả khoá, nhưng nó chỉ biết dấu bằng — mất thứ tự, mất ràng buộc duy nhất. Và vì sao B-tree vẫn là mặc định đúng sau mười tám phần đo.
28/08/2026
· 12 phút đọc
19
Cập nhật một cột không nằm trong chỉ mục nào — vẫn chậm gấp 5 lần vì sáu chỉ mục khác
Đo chi phí thật của chỉ mục thừa trong PostgreSQL: chèn chậm 4,6 lần, cập nhật chậm 5 lần dù cột được sửa chẳng nằm trong chỉ mục nào. Và ba lý do khiến idx_scan = 0 không đủ để kết luận nên xoá — kèm cách xoá an toàn bằng giao dịch thử.
28/08/2026
· 13 phút đọc
20
Thêm một điều kiện WHERE đúng, ước lượng tệ đi 66 lần — bộ lập lịch nhân hai xác suất như thể chúng không liên quan
Đo bộ lập lịch PostgreSQL làm việc với số liệu cũ: ước lượng 1 dòng trong khi thực tế 500.000, và một lớp bảo vệ bí mật thường cứu nó. Rồi tới kiểu sai mà ANALYZE không bao giờ chữa được — giả định các cột độc lập — và một câu lệnh sửa từ sai 66,7 lần xuống đúng.
28/08/2026
· 11 phút đọc
21
UPDATE một số dư — PostgreSQL ghi ra 500.000 dòng mới, và một phiên ngồi im làm bảng phình gấp 5 lần
Nhìn thẳng vào các byte trên đĩa để đo MVCC: 500.000 dòng sau năm lần cập nhật chiếm 361 MB thay vì 60 MB, DELETE không giải phóng gì, và chỉ một giao dịch mở là đủ ghim mọi phiên bản cũ khiến VACUUM bó tay trên toàn cơ sở dữ liệu.
28/08/2026
· 12 phút đọc
22
VACUUM chạy xong mà tệp không nhỏ đi một byte — và vì sao đó là điều hoàn toàn bình thường
Đo bảng PostgreSQL phình lên rồi ổn định: VACUUM thường thu hồi chỗ để tái dùng nhưng giữ nguyên kích thước tệp, VACUUM FULL viết lại toàn bảng và chặn mọi truy vấn, còn autovacuum chạy chậm 25 lần vì bị hãm tốc từ thời đĩa cứng quay. Kèm cách phân biệt bloat thật với trạng thái cân bằng bình thường.
28/08/2026
· 10 phút đọc
23
Hạ ngưỡng autovacuum theo lời khuyên phổ biến — nhưng bản chỉnh tay lại phình hơn bản mặc định 37%
Ba phép đo có kiểm soát trong PostgreSQL cho thấy hạ autovacuum_vacuum_scale_factor gần như không giảm được bloat khi tải ghi nặng — bản chỉnh tay còn phình hơn. Nút thắt thật là naptime và cost_delay, và mức phình ổn định do tốc độ ghi quyết định chứ không phải ngưỡng.
28/08/2026
· 11 phút đọc
24
Một bộ đếm 32 bit tiêu 5.053 số mỗi giây — 4,9 ngày là cạn, và khi cạn PostgreSQL từ chối mọi lệnh ghi
Đo tốc độ tiêu số hiệu giao dịch trong PostgreSQL: pgbench tám kết nối ăn 5.053 XID mỗi giây, đủ cạn 2,1 tỷ trong 4,9 ngày. Vì sao autovacuum chống-tràn-số không tắt được, và vì sao bảng lưu trữ tĩnh — chứ không phải bảng ghi nhiều — mới là bảng nguy hiểm.
28/08/2026
· 10 phút đọc
25
Đổi sang mức cô lập 'an toàn hơn' rồi mất 57% số ghi mà không một dòng lỗi — bốn mức, đo bằng tải thật
Dựng lại từng hiện tượng cô lập giao dịch bằng hai phiên chạy song song trong PostgreSQL: vì sao repeatable read không chặn được lệch ghi, và vì sao nâng lên mức cao mà không viết vòng thử lại làm biến mất 137 trong 480 lần ghi — im lặng.
28/08/2026
· 11 phút đọc
26
Hai giao dịch khoá hai dòng ngược thứ tự, cả hai đứng hình 2 giây — và một câu lệnh làm hàng đợi nhanh gấp 5,75 lần
Dựng lại deadlock có chủ đích trong PostgreSQL, đo thời gian phát hiện đúng bằng deadlock_timeout, và chỉ ra vì sao SKIP LOCKED làm hàng đợi công việc nhanh gấp 5,75 lần. Kèm cách chặn deadlock triệt để và cách tìm ai đang chặn ai.
28/08/2026
· 11 phút đọc
27
FOR UPDATE hứa 'mọi dòng tôi khoá đều khớp' — nhưng không hứa 'tôi khoá mọi dòng khớp', và cỡ lô đúng nhanh gấp 48 lần
Đo hàng đợi công việc trong PostgreSQL: FOR UPDATE đọc lại dòng sau khi chờ được khoá — và sự bất đối xứng của việc đọc lại đó là gốc của nhiều lỗi đua tranh. Kèm con số: cỡ lô 100 nhanh gấp 48 lần cỡ lô 1, chỉ mục một phần nhỏ hơn 375 lần, và một lời cảnh báo phổ biến tôi không dựng lại được.
28/08/2026
· 11 phút đọc
28
10 tiến trình cùng khởi động một cron, chỉ 1 chạy — nhưng hai cái bẫy khiến nó lặng lẽ ngừng hoạt động
Advisory lock là khoá không gắn với dòng nào: 10 bản sao ứng dụng cùng khởi động một công việc định kỳ, chỉ 1 chạy, không cần bảng phối hợp. Nó không rẻ hơn khoá hàng, và có hai cái bẫy — băm tên gây va chạm, và pool kết nối giữ khoá — mà cả hai đều hỏng trong im lặng.
28/08/2026
· 10 phút đọc
29
Bộ tối ưu có ba thuật toán nối đều đúng — và vẫn chọn sai 800 lần vì thống kê cũ
PostgreSQL có đúng ba cách nối hai bảng. Tôi ép chạy cả ba trên cùng truy vấn để xem chúng chênh nhau bao nhiêu, tìm ra một dải mà lựa chọn của bộ tối ưu không phải nhanh nhất, và một lần thiếu thống kê làm truy vấn chậm 800 lần mà không báo lỗi.
28/08/2026
· 11 phút đọc
30
Cùng một CTE: 226 ms trên PostgreSQL 11, 0,07 ms trên 16 — và bản 'sửa' đó lại làm vài mã cũ chậm đi
Nhiều năm liền, WITH của PostgreSQL là một hàng rào tối ưu không chuẩn SQL nào đòi hỏi: điều kiện lọc bên ngoài không được đẩy vào trong. PostgreSQL 12 đổi điều đó. Tôi dựng cả hai phiên bản với dữ liệu giống hệt để đo, và chỉ ra vì sao 'sửa' một hành vi tình cờ lại là một thay đổi phá vỡ.
28/08/2026
· 10 phút đọc
31
Cùng bài top-3 mỗi nhân viên: SQL mất 78 ms, kéo về ứng dụng tự tính mất 1.070 ms và 25 MB qua mạng
Window function giải lớp bài toán GROUP BY không giải nổi: xếp hạng trong nhóm, so với dòng trước, cộng dồn. Tôi đo chi phí thật của chúng, so với LATERAL và với cách kéo dữ liệu về ứng dụng tự tính — và bất ngờ là LATERAL nhanh gần gấp đôi window function.
29/08/2026
· 10 phút đọc
32
Gấp 64 lần bộ nhớ cho GROUP BY chỉ nhanh hơn 3% — vì chi phí nằm ở số nhóm, không ở số dòng
GROUP BY dễ viết nhất mà khó đoán chi phí nhất. Tôi đo lượng bộ nhớ nó thật sự dùng và tìm ra: chi phí phụ thuộc số nhóm đầu ra chứ không phải số dòng đầu vào, cho thêm bộ nhớ gần như vô ích, và một chỉ mục cho 2× tốc độ với 0 bộ nhớ.
29/08/2026
· 10 phút đọc
33
Trang cuối đọc đủ 5 triệu dòng để trả về 20 — và OFFSET còn âm thầm bỏ sót dữ liệu khi có người xoá bài
LIMIT 20 OFFSET 1000 là cách phân trang ai cũng viết đầu tiên: đúng, ngắn, và chậm dần theo số trang — nhưng chậm bao nhiêu thì ít ai đo. Tôi đo, và đo cả một lỗi nghiêm trọng hơn chuyện chậm: OFFSET neo vào vị trí, mà vị trí thì đổi khi dữ liệu đổi.
29/08/2026
· 10 phút đọc
34
JSONB tốn gấp đôi chỗ, đếm chậm 38 lần, ghi tốn 6,6 lần WAL — và vẫn có đúng một chỗ nó là lựa chọn duy nhất
JSONB rất tiện: không cần ALTER TABLE, thêm trường lúc nào cũng được. Tôi dựng hai bảng cùng dữ liệu — một cột thường, một JSONB — để đo cái giá của sự tiện đó, và tìm ra ranh giới rõ ràng giữa lúc nên dùng và lúc phải trả giá đắt.
29/08/2026
· 10 phút đọc
35
Cột mảng nhỏ hơn bảng phụ 4 lần — nhưng tra một thẻ chậm 57 lần, và không khoá ngoại nào chặn được thẻ ma
PostgreSQL cho một cột chứa cả mảng hoặc cả bản ghi con, tránh được một bảng phụ và một phép nối. Tôi đo cái giá của việc tránh đó bằng bài toán gắn thẻ cho bài viết — gọn hơn thật, nhưng mất Index Only Scan, mất khoá ngoại, và dữ liệu mồ côi tích lại mà không gì phát hiện.
29/08/2026
· 10 phút đọc
36
Phân mảnh để truy vấn nhanh hơn thường là lý do sai — chỗ nó thắng áp đảo là xoá dữ liệu cũ
Phân mảnh bảng hay được giới thiệu như cách tăng tốc truy vấn trên bảng lớn. Tôi đo thì thấy nó khi nhanh khi chậm; lợi ích thật nằm ở chỗ khác hẳn — DROP một phân vùng sinh 181 kB WAL thay vì 312 MB của DELETE, và không để lại một dòng chết nào.
29/08/2026
· 10 phút đọc
37
Nạp một triệu dòng: COPY nhanh gấp 112 lần chèn từng dòng — nhưng bước nhảy lớn nhất không nằm ở COPY
Nạp dữ liệu là việc ai cũng phải làm và ít ai đo. Tôi nạp cùng một triệu dòng bằng năm cách, và tìm ra rằng cách nhanh nhất không phải cách đáng khuyên nhất — cú tăng 40 lần nằm ở gộp lô, thứ chạy được ở mọi nơi COPY không dùng được.
29/08/2026
· 9 phút đọc
38
Cách 'kiểm tra rồi ghi' chạy đúng mọi lần tôi thử — rồi sáu tiến trình cùng chạy, nó sai cả 25/25 vòng
'Có thì cập nhật, chưa có thì chèn' là thao tác quen thuộc nhất mà SQL chuẩn không có cú pháp riêng. Cách viết tay chạy đúng khi bạn thử một luồng và hỏng khi có nhiều tiến trình. Tôi đo cả hai — cùng ba cái bẫy và ba tính năng ít dùng của ON CONFLICT.
29/08/2026
· 10 phút đọc
39
CHECK gần như miễn phí, nhưng xoá một dòng cha mà quên chỉ mục thì chậm 120 lần
Ràng buộc là cách rẻ nhất để dữ liệu không hỏng, nhưng 'rẻ' là từ định tính. Tôi gán số cho từng loại: CHECK và NOT NULL gần như miễn phí, khoá ngoại làm chèn chậm 6,2 lần — và cái bẫy lớn nhất là chi phí của nó hiện ra ở một thao tác bạn không ngờ tới.
29/08/2026
· 9 phút đọc
40
Một trigger có thân hàm rỗng làm chèn chậm 35% — và một trigger khác nuốt mất nửa dữ liệu, không báo lỗi
Trigger là đoạn mã chạy tự động mỗi khi dữ liệu thay đổi: tiện vì không thể quên, khó vì nó chạy vô hình. Tôi đo chi phí của nó và dựng lại bốn tình huống nó cho kết quả ngoài dự đoán — gồm một chỗ làm mất nửa dữ liệu mà lệnh vẫn báo thành công.
29/08/2026
· 9 phút đọc
41
Một hàm bạn tự viết lặng lẽ tắt chạy song song cho cả truy vấn — chậm gấp đôi tới gấp mười, không một lời cảnh báo
Đưa logic vào cơ sở dữ liệu là quyết định thiết kế, nhưng cũng là quyết định hiệu năng. Tôi đo cái giá của việc gọi hàm — PL/pgSQL chậm 7,2 lần so với viết thẳng — và tìm ra một mặc định làm chậm truy vấn gấp đôi mà không ai được cảnh báo.
29/08/2026
· 9 phút đọc
42
Trên máy 16 lõi, thông lượng đạt đỉnh ở đúng 16 kết nối — lên 256 thì độ trễ tăng 20 lần mà chẳng phục vụ thêm ai
max_connections = 500 là cấu hình phổ biến, và nó thường sai. Tôi đo xem một máy chủ thật sự phục vụ được bao nhiêu kết nối trước khi thêm nữa chỉ làm mọi thứ tệ đi — kèm ba chi phí của một kết nối mà ít ai cộng đủ.
29/08/2026
· 9 phút đọc
43
300 client dùng chung 17 kết nối — và work_mem của người này lặng lẽ rò sang truy vấn của người khác
Phần trước đo được thông lượng PostgreSQL đạt đỉnh ở đúng số lõi CPU. PgBouncer là câu trả lời khi ứng dụng có nhiều máy chủ — nó nhồi 300 client vào 17 kết nối. Nhưng chế độ transaction có một cái giá không phải hiệu năng: trạng thái phiên của một client rò sang client khác.
29/08/2026
· 9 phút đọc
44
Tỉ lệ trúng đệm 0% mà truy vấn chỉ chậm hơn 30% — vì sao con số ai cũng đuổi theo lại nói dối
'Tỉ lệ trúng đệm phải trên 99%' là một trong những lời khuyên được lặp nhiều nhất về PostgreSQL. Tôi đo xem nó thật sự nói lên điều gì: 19.184 trang bị đánh dấu 'read' chỉ tốn 27,75 ms — 1,4 micro giây mỗi trang, tức tốc độ bộ nhớ, vì có một lớp đệm mà con số đó không nhìn thấy.
29/08/2026
· 9 phút đọc
45
Sắp xếp 3 triệu dòng nhanh nhất ở work_mem 4 MB — cấp 256 MB để chạy hết trong RAM lại chậm hơn 36%
'Truy vấn tràn ra đĩa thì tăng work_mem' là lời khuyên có trong mọi bài tinh chỉnh PostgreSQL. Tôi đo nó trên ba loại thao tác và cả ba đều đi ngược — cấu hình có ghi tệp tạm lại nhanh hơn cấu hình chạy hết trong bộ nhớ, vì nút thắt không nằm ở dung lượng RAM.
29/08/2026
· 9 phút đọc
46
Sửa 4 byte mỗi dòng sinh ra 126 MB WAL — và đặt max_wal_size nhỏ để tiết kiệm đĩa lại ngốn thêm 29%
WAL là nhật ký ghi trước, lý do PostgreSQL sống sót sau mất điện. Tôi đo nó sinh ra bao nhiêu cho mỗi thao tác, và tìm ra một cấu hình phổ biến làm mọi thứ tệ đi theo cách ngược đời: hạ giới hạn để tiết kiệm đĩa lại đóng một vòng lặp khiến tốn nhiều đĩa hơn.
29/08/2026
· 9 phút đọc
47
Thông lượng tụt tám lần trong mười giây — mà con số trung bình vẫn đẹp như không có gì
Phần trước đo WAL sinh ra bao nhiêu; phần này đo chuyện xảy ra khi PostgreSQL đẩy tất cả xuống đĩa. Kết quả bất ngờ: cấu hình có nhiều checkpoint nhất lại mượt nhất — và con số trung bình mà mọi bảng theo dõi hiển thị giấu sạch mười giây người dùng thấy hệ thống đứng.
29/08/2026
· 8 phút đọc
48
Tham số nguy hiểm nhất của PostgreSQL hoá ra cũng là tham số vô dụng nhất
Tắt synchronous_commit cho 3,42 lần thông lượng. Tắt fsync không nhanh hơn chút nào — mà rủi ro thì thuộc một loại khác hẳn. Tôi đo cả bốn mức độ bền, và chỉ ra vì sao hai cái núm trông giống nhau thật ra là hai canh bạc rất khác nhau.
29/08/2026
· 9 phút đọc
49
Khôi phục xong, đủ mọi bảng mọi dòng — và không ai đăng nhập được: bốn thứ pg_dump lặng lẽ bỏ quên
Sao lưu là việc ai cũng làm và ít ai đo. Tôi đo năm cách trên cùng một cơ sở dữ liệu, và chỉ ra bốn thứ mà cách phổ biến nhất không hề lấy — trong đó có mật khẩu của mọi tài khoản, thứ chỉ lộ ra khi bạn khôi phục và không ai vào được.
29/08/2026
· 8 phút đọc
50
Xoá nhầm 900 dòng, hệ thống ghi tiếp 101 dòng — rồi tôi quay ngược về đúng một giây trước tai nạn, mất 2,3 giây
Bản sao lưu hàng đêm chỉ đưa bạn về nửa đêm hôm qua. Khôi phục theo thời điểm đưa bạn về một giây trước khi ai đó gõ nhầm — nhưng chỉ khi bạn đã chuẩn bị trước tai nạn. Tôi chạy đủ quy trình từ đầu đến cuối, và cho thấy đặt mốc sai thì nó im lặng đưa bạn về nhầm chỗ.
29/08/2026
· 9 phút đọc
51
Sao chép đồng bộ ăn mất 37% thông lượng — và một khe sao chép bỏ quên đủ làm sập cả máy chính
Máy dự phòng là thứ ai cũng dựng và ít ai đo. Tôi dựng một cặp máy chính – máy dự phòng thật, đo độ trễ dưới tải (dưới 3 ms), rồi cố tình làm nó hỏng theo hai cách hay gặp nhất trên máy chủ thật — trong đó có một khe sao chép âm thầm giữ WAL tới khi đầy đĩa.
29/08/2026
· 9 phút đọc
52
Dữ liệu khớp từng dòng, mọi kiểm tra đều xanh — rồi ngày chuyển đổi, dòng đầu trùng khoá chính ngay
Sao chép luồng sao chép byte; sao chép logic sao chép dòng. Khác biệt đó mở ra thứ cách kia không làm được — chọn từng bảng, chạy giữa hai phiên bản, máy đích ghi được — và mang theo ba chỗ hỏng cách kia không có, trong đó chỗ nguy hiểm nhất im lặng hoàn toàn cho tới đúng lúc bạn chuyển sang.
29/08/2026
· 9 phút đọc
53
Chuyển sang máy dự phòng mất 0,52 giây, không mất giao dịch nào — đưa máy cũ về lại mất 78,9 giây vì một dòng cấu hình quên bật
Máy dự phòng chỉ có giá trị nếu bạn chuyển sang được. Tôi giết một máy chính đang ghi dở, thăng cấp máy dự phòng, và đo mọi thứ — kể cả phần khó hơn nhiều: đưa máy cũ quay lại mà không tạo ra hai máy chính cùng nhận ghi.
29/08/2026
· 9 phút đọc
54
Câu 0,007 ms mỗi lần ngốn gấp ba lần thời gian máy chủ so với câu 118 ms — và vì sao bạn sẽ tối ưu nhầm
Sau năm mươi ba phần đo từng cơ chế, phần này đo cách tìm ra chỗ nào đáng đo. Kết quả đầu tiên đã đủ giật mình: cách xếp hạng phổ biến nhất — theo thời gian mỗi lần — dẫn thẳng tới việc tối ưu nhầm truy vấn, bỏ qua đúng câu đang chiếm nhiều tài nguyên nhất.
29/08/2026
· 9 phút đọc
55
Một lần mất điện xoá sạch mọi bộ đếm pg_stat — và vì sao con số tuyệt đối của chúng gần như vô nghĩa
pg_stat_statements trả lời 'truy vấn nào tốn nhất'. Các khung nhìn pg_stat_* trả lời câu khác: bảng nào, chỉ mục nào, bộ đệm đang làm gì. Tôi đo chúng nói gì, ba chỗ chúng im lặng — gồm một sự cố xoá sạch mọi bộ đếm — và một dự đoán của tôi bị bác bỏ.
29/08/2026
· 9 phút đọc
56
Chỉ mục tôi đinh ninh sẽ thắng lại về gần bét — quy trình 5 bước đưa truy vấn từ 684 ms xuống 208 ms
Năm mươi lăm phần trước đo từng cơ chế riêng lẻ; phần này ghép lại thành một quy trình gỡ truy vấn chậm và chạy trên một sự cố dựng sẵn từ đầu tới cuối. Bất ngờ lớn nhất: chỉ mục bao phủ mà ai cũng nghĩ là nhanh nhất lại tệ thứ nhì, còn chỉ mục nhanh nhất cũng là chỉ mục nhỏ nhất.
29/08/2026
· 8 phút đọc
57
pg_upgrade xong trong 2,4 giây — rồi bộ lập lịch ước sai 66 lần, vì nó cố tình bỏ lại một thứ
Nâng cấp phiên bản lớn là việc mỗi năm làm một lần nên chẳng ai thuộc. Tôi chạy pg_upgrade thật từ PostgreSQL 16 lên 17, đo từng bước — và tìm ra thứ nó mang theo đầy đủ lẫn thứ nó lặng lẽ để lại, đủ khiến máy chủ vừa nâng xong chậm bất thường vài giờ đầu.
29/08/2026
· 9 phút đọc
58
RLS giấu sạch dòng của phòng khác — nhưng một lỗi trùng khoá tố cáo đúng dòng đang bị giấu
Phân quyền PostgreSQL có bốn lớp, mỗi lớp chặn được một thứ khác nhau. Tôi đo từng lớp trên bảng nhân viên bốn dòng và tìm ra ba chỗ chúng không chặn — chỗ đáng nhớ nhất là một thông báo lỗi khẳng định sự tồn tại của đúng dòng mà chính sách đang cố giấu.
29/08/2026
· 9 phút đọc
59
Image PostgreSQL chính thức cho ai vào cũng khỏi cần mật khẩu — và đổi POSTGRES_PASSWORD rồi up lại chẳng thay đổi gì
Image postgres chính thức dùng mặc định gốc cho mọi tham số và 'trust' cho mọi kết nối cục bộ — tiện cho việc học, nguy hiểm cho máy chủ thật. Đây là danh sách phải đổi, kèm số đo, và một cái bẫy: nhiều thiết lập chỉ có tác dụng đúng một lần lúc khởi tạo.
29/08/2026
· 9 phút đọc
60
Một câu SQL chấm điểm máy chủ PostgreSQL của bạn — trên cấu hình Docker mặc định nó báo 10/13 mục cần sửa
Sáu mươi phần, mỗi phần một phép đo, gom lại thành một câu SQL chạy được và một bản tổng kết. Cả năm con số ấn tượng nhất, bốn lời khuyên phổ biến mà phép đo bác bỏ, và ba bài học về cách đo rút ra từ chính những lần tôi đo sai.
29/08/2026
· 11 phút đọc