- PostgreSQL là gì và khi nào nên chọn?
PostgreSQL là relational database (cơ sở dữ liệu quan hệ) mã nguồn mở — dữ liệu nằm trong các bảng có cột rõ ràng và ràng buộc chặt chẽ. Điểm mạnh chính: SQL chuẩn, transaction ACID (làm là đúng và đủ, không nửa vời), nhiều…
- Primary key, unique key và foreign key khác nhau thế nào?
Cả ba đều là ràng buộc nhưng phục vụ mục đích khác nhau: - Primary key: định danh duy nhất mỗi dòng (mỗi user một id). Đây là cột mà các bảng khác thường trỏ tới. - Unique key: đảm bảo một (hoặc vài) cột…
- `NULL` trong PostgreSQL cần hiểu thế nào?
NULL nghĩa là "không biết / chưa có", chứ không phải chuỗi rỗng, số 0 hay false. Vì là "không biết" nên so sánh = NULL luôn cho kết quả không xác định — phải dùng IS NULL / IS NOT NULL. Một bẫy hay…
- Transaction trong PostgreSQL giải quyết vấn đề gì?
Transaction gom nhiều câu lệnh thành một khối "được ăn cả, ngã về không": commit thì mọi thay đổi được ghi bền vững, rollback thì huỷ sạch như chưa từng chạy. Trong lúc chưa commit, thay đổi chưa hiện ra như dữ liệu hoàn chỉnh…
- Constraint và index khác nhau thế nào?
Dễ lẫn vì một số constraint tự tạo index, nhưng vai trò khác nhau: constraint là quy tắc dữ liệu (primary key, foreign key, unique, check, not null), còn index là cấu trúc giúp tìm/đọc nhanh hơn. Một unique constraint thường tạo unique index bên…
- Index trong PostgreSQL giúp gì và có trade-off gì?
Index giống mục lục cuối sách: thay vì lật từng trang để tìm một từ, database nhảy thẳng tới đúng dòng. Nhờ đó các query lọc (WHERE), join hay sắp xếp (ORDER BY) nhanh hơn hẳn, và việc tìm theo giá trị duy nhất gần…
- B-tree, GIN, GiST và BRIN index dùng khi nào?
PostgreSQL có nhiều loại index, mỗi loại hợp với một kiểu dữ liệu/truy vấn: - B-tree (mặc định): tốt cho so sánh bằng, khoảng (<, , BETWEEN) và sắp xếp trên giá trị thường (số, chuỗi, ngày). - GIN: cho dữ liệu "nhiều giá trị…
- Composite index thứ tự column quan trọng thế nào?
Trong composite index (index nhiều cột) kiểu B-tree, thứ tự cột rất quan trọng — giống danh bạ sắp theo "họ rồi tên": tra theo họ rất nhanh, nhưng tra chỉ theo tên thì gần như vô dụng. Index (tenantid, status, createdat) phục vụ tốt…
- Partial index dùng khi nào?
Partial index chỉ index những dòng thoả một điều kiện, thay vì cả bảng. Rất hợp khi query thường chỉ quan tâm một phần nhỏ: bản ghi đang active, job chưa xử lý, hay dòng chưa bị xoá mềm. Ví dụ ép email duy nhất,…
- `EXPLAIN` và `EXPLAIN ANALYZE` khác nhau thế nào?
- Isolation levels trong PostgreSQL khác nhau thế nào?
Isolation level quyết định một transaction "nhìn thấy" công việc đang chạy dở của transaction khác đến đâu. PostgreSQL có ba mức thực tế (Read Uncommitted bị xử lý như Read Committed): - Read Committed (mặc định): mỗi câu lệnh thấy snapshot dữ liệu mới…
- `SELECT FOR UPDATE` dùng khi nào?
SELECT ... FOR UPDATE khoá đúng các dòng vừa chọn, để transaction khác không thể update/delete chúng cho đến khi bạn commit hoặc rollback. Nó dành cho tình huống "đọc — sửa — ghi" cần an toàn, ví dụ trừ tồn kho hay lấy job…
- `SKIP LOCKED` dùng để build job queue như thế nào?
- Deadlock trong PostgreSQL xảy ra khi nào và xử lý ra sao?
- UUID, serial và identity column nên chọn thế nào?
Ba cách tạo khoá chính tự sinh: - serial: cách viết tắt cũ, ngầm tạo một sequence. Còn chạy được nhưng không còn là cách khuyến nghị. - GENERATED ... AS IDENTITY: cách chuẩn SQL, rõ ràng hơn, để tạo số tự tăng — nên…
- `json` và `jsonb` khác nhau thế nào?
json lưu nguyên văn bản JSON và phân tích lại mỗi lần dùng. jsonb lưu dạng nhị phân đã phân tích sẵn — mất định dạng và thứ tự key gốc, nhưng query và đánh index nhanh hơn nhiều. Hầu hết ứng dụng nên dùng…
- Khi nào nên normalize thay vì dùng JSONB?
Chọn normalize (tách thành cột/bảng riêng) khi field cần join, hay lọc, cần ràng buộc/foreign key, cần update độc lập hoặc dùng cho báo cáo. Chọn JSONB khi cấu trúc hay đổi, ít khi query sâu, metadata thay đổi liên tục, hoặc cần giữ nguyên…
- Generated columns trong PostgreSQL dùng khi nào?
Generated column là cột mà giá trị được tự tính từ các cột khác, nên bạn không phải lặp lại logic đó ở tầng app và có thể index/query trực tiếp trên nó. Hợp với công thức cố định, thuộc về dữ liệu: họ tên…
- `JOIN` types trong PostgreSQL khác nhau thế nào?
JOIN ghép dòng giữa hai bảng; khác nhau ở chỗ có giữ lại dòng không khớp hay không: - INNER JOIN: chỉ giữ dòng khớp ở cả hai bảng. - LEFT JOIN: giữ toàn bộ bảng trái, bên phải không khớp thì điền NULL. -…
- Window function trong PostgreSQL dùng khi nào?
Window function tính toán dựa trên một nhóm dòng liên quan nhưng không gộp các dòng lại như GROUP BY — bạn vẫn giữ từng dòng và có thêm cột tính toán. Hợp với xếp hạng, cộng dồn (running total), trung bình trượt, khử trùng…
- Index-only scan là gì và vì sao đôi khi không xảy ra?
- `ANALYZE` và statistics ảnh hưởng query plan như thế nào?
ANALYZE thu thập thống kê về phân bố dữ liệu (cột này có bao nhiêu giá trị khác nhau, giá trị nào hay gặp...). Planner dựa vào đó để đoán mỗi bước query trả về bao nhiêu dòng, rồi chọn cách chạy. Nếu thống kê…
- Connection pool trong PostgreSQL vì sao quan trọng?
Mỗi kết nối tới PostgreSQL là một process riêng khá nặng (tốn RAM). Nếu app mở quá nhiều kết nối, DB có thể cạn bộ nhớ, CPU bận chuyển ngữ cảnh và latency xấu đi. Connection pool giải quyết bằng cách giữ sẵn một số…
- Replication trong PostgreSQL gồm những kiểu nào?
- Backup và PITR trong PostgreSQL cần những gì?
- Migration schema PostgreSQL cần làm sao để ít downtime?
Migration ít downtime cốt ở chỗ tránh các thao tác khoá bảng lâu hoặc viết lại cả bảng vào giờ cao điểm. Pattern an toàn theo nhiều bước: thêm cột nullable → backfill dữ liệu theo từng lô nhỏ → deploy code app đọc/ghi tương…
- Soft delete trong PostgreSQL ảnh hưởng index và constraint thế nào?
Soft delete = không xoá thật mà đánh dấu, thường bằng cột deletedat. Hệ quả: gần như mọi query phải thêm WHERE deletedat IS NULL, và điều này ảnh hưởng tới index lẫn unique constraint — vì một email "đã xoá" về lý thuyết có…
- `UPSERT` trong PostgreSQL dùng như thế nào?
UPSERT = "update nếu đã có, insert nếu chưa". Trong PostgreSQL viết bằng INSERT ... ON CONFLICT: khi câu insert đụng một unique constraint/index, bạn bảo nó update thay vì báo lỗi (hoặc bỏ qua). EXCLUDED là dòng đáng lẽ được insert. Cần chỉ rõ…
- `RETURNING` trong PostgreSQL hữu ích khi nào?
RETURNING cho phép INSERT, UPDATE, DELETE, MERGE trả về luôn các dòng vừa bị tác động, khỏi cần query lại lần nữa. Rất tiện để lấy id vừa sinh, ghi log dòng vừa đổi, hoặc cập nhật xong trả thẳng response cho client. Lợi ích…
- Full-text search trong PostgreSQL khi nào đủ dùng?
- Partitioning trong PostgreSQL dùng khi nào?
- Advisory lock trong PostgreSQL dùng khi nào?
- Tại sao query có index nhưng PostgreSQL vẫn sequential scan?
- MVCC trong PostgreSQL là gì?
- CTE trong PostgreSQL dùng khi nào?
CTE (mệnh đề WITH) cho phép đặt tên cho một subquery, để chẻ một query phức tạp thành từng bước dễ đọc — hoặc dùng kèm câu lệnh sửa dữ liệu. PostgreSQL hiện đại (từ bản 12) thường tự "inline" CTE để tối ưu, nhưng…
- Enum type trong PostgreSQL có nên dùng không?
Enum trong PostgreSQL giới hạn một cột chỉ nhận một tập giá trị cố định và làm schema tự mô tả (nhìn là biết có những trạng thái nào). Hợp với tập trạng thái ổn định, ít đổi như order status. Nhưng enum khó "tiến…
- Read replica có giải quyết được mọi vấn đề scale đọc không?
- N+1 query với PostgreSQL thường xử lý thế nào?
- Thiết kế PostgreSQL multi-tenant có những lựa chọn nào?
- Viết báo cáo doanh thu theo tháng sao cho **tháng không có đơn vẫn hiện dòng 0**?
GROUP BY chỉ sinh ra dòng cho dữ liệu đã tồn tại — tháng không có đơn thì không có dòng nào để gom. Cách chuẩn là tự sinh dải thời gian rồi LEFT JOIN dữ liệu vào. Ba chi tiết quyết định đúng/sai: -…
- `DISTINCT ON` của PostgreSQL làm gì? So với `ROW_NUMBER()` khi lấy bản ghi mới nhất mỗi nhóm thì nên chọn cái nào?
- Truy vấn cây danh mục nhiều cấp bằng recursive CTE như thế nào? Chống vòng lặp vô hạn ra sao?
- CTE trong PostgreSQL có phải là "optimization fence" không? `MATERIALIZED` / `NOT MATERIALIZED` để làm gì?
- Cập nhật hàng loạt từ một bảng khác nên viết `UPDATE ... FROM` hay `MERGE`? Khác gì `INSERT ... ON CONFLICT`?
- Expression index là gì? Dùng nó để tìm email không phân biệt hoa thường thế nào?
Expression index (index trên biểu thức) đánh index lên kết quả của một biểu thức thay vì giá trị cột thô. Nó giải quyết đúng tình huống mà điều kiện bắt buộc phải bọc cột trong hàm. Quy tắc khớp: biểu thức trong WHERE phải…
- Đọc output `EXPLAIN ANALYZE` thế nào? `cost` và `actual time` khác nhau ra sao, và rows lệch nói lên điều gì?
Mỗi dòng trong plan là một node, thụt lề sâu hơn là node con chạy trước. Đọc từ trong ra ngoài. - cost=start..total là ước lượng của planner theo đơn vị nội bộ (không phải mili giây). Số đầu là chi phí tới khi trả…
- Seq Scan, Index Scan và Bitmap Heap Scan khác nhau thế nào? Khi nào planner chọn bitmap?
Ba node này là ba cách khác nhau để lấy dòng ra khỏi bảng, khác nhau ở cách truy cập đĩa. Seq Scan — đọc toàn bộ bảng tuần tự theo thứ tự trang. Không cần index. Nhanh nhất khi phải lấy phần lớn bảng,…
- Cần thêm index cho một bảng 200 triệu dòng đang chạy production. Làm thế nào để không khoá bảng?
CREATE INDEX thường lấy khoá SHARE trên bảng — chặn mọi INSERT/UPDATE/DELETE cho tới khi index dựng xong. Với bảng lớn việc này kéo dài hàng chục phút, tương đương ngừng ghi. Dùng CONCURRENTLY: Cách này quét bảng hai lượt và chờ các transaction cũ…
- Index bloat là gì? Vì sao index phình lên dù số dòng không đổi, và xử lý thế nào?
- Làm sao tìm ra index nào đang thừa hoặc không được dùng để xoá bớt?
- HOT update trong PostgreSQL là gì? Vì sao thêm index có thể làm sập throughput ghi của bảng update nhiều?
- PostgreSQL lưu nhiều phiên bản của một dòng ra sao? `xmin`/`xmax` là gì?
PostgreSQL không sửa dòng tại chỗ. Mỗi UPDATE ghi một phiên bản mới của dòng (tuple mới) và đánh dấu phiên bản cũ là hết hiệu lực. DELETE chỉ đánh dấu, không xoá byte ngay. Mỗi tuple mang hai cột hệ thống: - xmin —…
- Dead tuple là gì? Table bloat gây ảnh hưởng gì và phát hiện bằng cách nào?
Dead tuple là phiên bản dòng đã bị UPDATE thay thế hoặc DELETE đánh dấu, nhưng không transaction nào còn cần nhìn thấy nữa. Nó vẫn nằm trong file dữ liệu cho tới khi VACUUM đánh dấu vùng đó tái sử dụng được. Bloat là…
- Autovacuum làm những việc gì? Khi nào nó chạy không kịp?
Autovacuum là tiến trình nền tự động chạy hai việc trên từng bảng: 1. VACUUM — thu hồi không gian của dead tuple để tái sử dụng, cập nhật visibility map, và đẩy lùi ngưỡng transaction ID wraparound. 2. ANALYZE — cập nhật thống kê…
- Transaction ID wraparound là gì? Vì sao nó có thể làm database ngừng nhận ghi?
- `VACUUM FULL` khác `VACUUM` thường thế nào? Vì sao không nên chạy trên bảng đang phục vụ?
Hai lệnh giải quyết hai việc khác nhau. VACUUM (và autovacuum): đánh dấu không gian dead tuple là tái sử dụng được cho chính bảng đó. File trên đĩa hầu như không nhỏ lại. Chỉ lấy SHARE UPDATE EXCLUSIVE — SELECT, INSERT, UPDATE, DELETE vẫn…
- TOAST trong PostgreSQL là gì? Nó ảnh hưởng hiệu năng khi lưu giá trị lớn thế nào?
- Truy vấn `jsonb` bằng toán tử nào để dùng được index? Khi nào không nên nhét dữ liệu vào `jsonb`?
B-tree thường không giúp gì cho việc tìm bên trong document. jsonb cần GIN index, và chỉ một số toán tử mới dùng được nó. - @ (containment), ?, ?, ?& (tồn tại key) → dùng được GIN mặc định (jsonbops). - jsonbpathops nhỏ và…
- Bảng log 500 triệu dòng, chỉ query 30 ngày gần nhất — thiết kế partition theo thời gian thế nào? Partition pruning cần điều kiện gì?
Dùng declarative range partitioning theo cột thời gian, mỗi tháng (hoặc mỗi tuần nếu lượng ghi lớn) một partition. Lợi ích chính không phải là truy vấn nhanh hơn, mà là vòng đời dữ liệu: xoá dữ liệu cũ bằng drop table requestlogs202601 chạy tức…
- Vì sao mỗi kết nối tới PostgreSQL tốn tài nguyên? `max_connections` đặt cao có sao không?
PostgreSQL dùng mô hình process-per-connection: mỗi kết nối là một process hệ điều hành riêng, không phải thread. Chi phí đi kèm mỗi kết nối: - Bộ nhớ riêng của backend process: workmem cho mỗi thao tác sort/hash, tempbuffers, catalog cache, plan cache. Một kết…
- PgBouncer transaction mode hoạt động ra sao? Vì sao prepared statement và session state hay lỗi khi bật?
- Streaming replication hoạt động thế nào? Đo replication lag bằng gì?
Streaming replication là replication vật lý: primary ghi thay đổi vào WAL, standby mở một kết nối tới primary và nhận stream WAL liên tục, rồi replay từng record để giữ bản sao byte-level giống hệt. Các trạng thái vị trí WAL trên standby, theo…
- User tạo đơn xong, chuyển sang trang danh sách thì không thấy đơn vừa tạo vì hệ thống đọc từ replica. Bạn xử lý luồng này thế nào?
- Logical replication khác physical replication thế nào? Dùng trong tình huống nào?
Physical (streaming) replication copy WAL ở mức byte: standby là bản sao y hệt toàn bộ cluster, cùng phiên bản major, không ghi được, không chọn được bảng. Logical replication giải mã WAL thành thay đổi ở mức dòng (INSERT/UPDATE/DELETE) rồi gửi tới subscriber theo…
- Production chậm nhưng không biết truy vấn nào gây ra. Dùng `pg_stat_statements` thế nào?
pgstatstatements là extension gom thống kê tích luỹ theo từng dạng truy vấn — nó chuẩn hoá tham số nên where id = 1 và where id = 2 được tính chung. (Extension cần có trong sharedpreloadlibraries, tức phải restart một lần.) Tìm truy vấn…
- Một câu `ALTER TABLE` chạy trên production làm treo cả ứng dụng dù bảng nhỏ. Chẩn đoán bằng `pg_locks` ra sao?
- `statement_timeout` và `idle_in_transaction_session_timeout` khác nhau thế nào? Nên đặt bao nhiêu?
Hai tham số chặn hai kiểu sự cố khác nhau. statementtimeout — huỷ một câu lệnh chạy quá lâu. Bảo vệ khỏi truy vấn lỗi plan, quét toàn bảng ngoài ý muốn, hay báo cáo nặng lọt vào giờ cao điểm. idleintransactionsessiontimeout — ngắt session…
- `SELECT ... FOR UPDATE`, `FOR NO KEY UPDATE` và `FOR SHARE` khác nhau thế nào?
Cả ba đều là row-level lock lấy tường minh trong một SELECT, khác nhau ở mức độ chặt và ở việc chặn ai. - FOR UPDATE — chặt nhất. Giữ hàng để chính bạn sửa hoặc xoá; mọi transaction khác muốn FOR UPDATE, FOR SHARE,…
- Nhiều worker cùng lấy job từ một bảng hàng đợi — `SKIP LOCKED` và `NOWAIT` giải quyết thế nào?
Với SELECT ... FOR UPDATE không kèm tuỳ chọn, mọi worker cùng nhắm hàng cũ nhất nên chỉ một worker chạy, số còn lại xếp hàng chờ đúng hàng đó. Hai tuỳ chọn thay đổi hành vi chờ: - SKIP LOCKED — bỏ qua các…
- `lock_timeout`, `statement_timeout` và `idle_in_transaction_session_timeout` khác nhau thế nào? Đặt sao cho hợp lý?
Ba tham số cắt ba nguyên nhân treo khác nhau: - statementtimeout — huỷ một câu lệnh chạy quá lâu. Chặn truy vấn nặng ngoài dự kiến chiếm tài nguyên. - locktimeout — huỷ câu lệnh nếu chờ khoá quá lâu, còn thời gian chạy…
- Vì sao `REPEATABLE READ` của PostgreSQL ném lỗi "could not serialize access"? Ứng dụng phải xử lý ra sao?