TL;DR
Phân vùng dữ liệu PostgreSQL của bạn đã đầy. Giải phóng dung lượng đĩa ngay lập tức, sau đó khởi động lại PostgreSQL nếu nó bị crash.
# Kiểm tra phân vùng nào đang đầy
df -h
# Kiểm tra kích thước thư mục dữ liệu PostgreSQL
du -sh /var/lib/postgresql/*/main
# Dọn nhanh — xóa log cũ và file tạm
sudo find /var/log/postgresql -name '*.log' -mtime +7 -delete
sudo find /tmp -name 'pgsql_tmp*' -delete
# Reload/restart PostgreSQL nếu bị treo
sudo systemctl restart postgresql
Chuyện Gì Đang Xảy Ra
PostgreSQL ghi mọi thứ — hàng dữ liệu, index, WAL segment, file sort tạm — vào filesystem. Khi phân vùng chứa thư mục dữ liệu (/var/lib/postgresql theo mặc định) đạt 100%, mọi thao tác ghi sẽ thất bại ngay lập tức:
ERROR: could not write to file "base/pgsql_tmp/pgsqltmp12345.0": No space left on device
# hoặc
ERROR: could not write to file "base/16384/1259": No space left on device
# hoặc
PANIC: could not write to file "pg_wal/000000010000000000000001": No space left on device
Đường dẫn sau base/ cho biết chính xác điều gì đã thất bại: file tạm cho sort/hash join, heap file, hoặc WAL segment. Lỗi WAL là nghiêm trọng nhất trong số này. PostgreSQL sẽ panic và tắt hoàn toàn để bảo vệ tính toàn vẹn dữ liệu — vì vậy hãy hành động nhanh.
Bước 1 — Chẩn Đoán Thứ Gì Đang Chiếm Dung Lượng
# Xem tất cả phân vùng và mức sử dụng hiện tại
df -h
# Tìm các thư mục lớn nhất trong thư mục dữ liệu postgres
sudo du -sh /var/lib/postgresql/*/main/* | sort -rh | head -20
# Kiểm tra riêng phần tích lũy WAL
sudo du -sh /var/lib/postgresql/*/main/pg_wal
# File tạm (có thể phình to nhanh khi có query xấu)
sudo du -sh /var/lib/postgresql/*/main/base/pgsql_tmp 2>/dev/null || echo "no temp dir"
# Kiểm tra thư mục log PostgreSQL
du -sh /var/log/postgresql/
Các nguyên nhân phổ biến, theo thứ tự tần suất:
- WAL tích lũy — một replication slot cũ hoặc
archive_commandbị lỗi âm thầm giữ lại hàng trăm WAL segment 16 MB - File tạm — một query nào đó đang thực hiện sort hoặc hash join khổng lồ trên đĩa
- Table bloat — dead tuple chưa được vacuum, hoặc TOAST overflow trên các bảng có cột rộng
- File log — logging chi tiết vô tình bị bật trong môi trường production
- File cấp OS — ứng dụng khác trên cùng phân vùng đang âm thầm lấp đầy dung lượng
Cách Khắc Phục
A. Xóa WAL Bloat (Nguyên Nhân Phổ Biến Nhất)
Replication slot cũ là nguyên nhân tinh vi nhất. PostgreSQL giữ lại từng WAL segment (16 MB mỗi cái) cho đến khi tất cả slot đã tiêu thụ nó — kể cả khi replica đã biến mất từ nhiều tuần trước. Một slot chết có thể âm thầm tích lũy hàng gigabyte. Kiểm tra trước:
-- Kiểm tra replication slot và mức độ tụt hậu của chúng
SELECT slot_name, active, restart_lsn,
pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS lag
FROM pg_replication_slots;
Một slot với hàng gigabyte lag và active = false chính là vấn đề của bạn. Xóa nó đi:
-- Xóa slot này cho phép PostgreSQL tái sử dụng các WAL segment đang bị giữ lại
SELECT pg_drop_replication_slot('slot_name');
PostgreSQL tái sử dụng WAL tại checkpoint tiếp theo. Buộc chạy checkpoint ngay nếu bạn đang cần gấp:
CHECKPOINT;
B. Xóa Log Cũ
# Xóa log cũ hơn 3 ngày
sudo find /var/log/postgresql -name '*.log' -mtime +3 -delete
# Truncate log hiện tại nếu nó quá lớn
# (đừng xóa — postgres vẫn đang giữ file handle mở)
sudo truncate -s 0 /var/log/postgresql/postgresql-$(date +%Y-%m-%d)*.log
C. Dừng Query Đang Tạo File Tạm
Thư mục pgsql_tmp phình to hầu như luôn có nghĩa là một query đang thực hiện sort hoặc hash join khổng lồ trên đĩa. Dừng nó lại — PostgreSQL tự động dọn sạch file tạm khi query kết thúc:
-- Tìm các query đang chạy lâu nhất
SELECT pid, now() - query_start AS duration, state, query
FROM pg_stat_activity
WHERE state != 'idle'
ORDER BY duration DESC
LIMIT 10;
-- Kết thúc query có vấn đề theo pid của nó
SELECT pg_terminate_backend(12345);
D. Giải Phóng Dung Lượng Ở Cấp OS
# Tìm file lớn ở bất kỳ đâu trên phân vùng
sudo find / -xdev -size +100M -printf '%s %p\n' 2>/dev/null | sort -rn | head -20
# Dọn cache package manager
sudo apt-get clean # Debian/Ubuntu
sudo yum clean all # CentOS/RHEL
# Dọn systemd journal (cái này âm thầm tăng lên gigabyte trên server bận)
sudo journalctl --vacuum-size=200M
E. Khẩn Cấp: Mở Rộng Đĩa hoặc Di Chuyển Thư Mục WAL
Đôi khi bạn không thể giải phóng đủ dung lượng dù đã xóa gì đi nữa. Thêm dung lượng là cách duy nhất thoát khỏi tình trạng này:
# Tùy chọn 1: resize đĩa cloud (AWS EBS, GCP PD, v.v.) rồi mở rộng filesystem
# Sau khi resize trong cloud console:
sudo resize2fs /dev/xvda1 # ext4
sudo xfs_growfs / # xfs
# Tùy chọn 2: chuyển pg_wal sang phân vùng khác có đủ dung lượng
sudo systemctl stop postgresql
sudo mv /var/lib/postgresql/14/main/pg_wal /mnt/extra_disk/pg_wal
sudo ln -s /mnt/extra_disk/pg_wal /var/lib/postgresql/14/main/pg_wal
sudo chown -R postgres:postgres /mnt/extra_disk/pg_wal
sudo systemctl start postgresql
F. Dài Hạn: Dọn Dẹp Table Bloat
Dead tuple tích lũy khi autovacuum không theo kịp — tải ghi cao, transaction chạy lâu chặn việc dọn dẹp, hoặc bảng quá lớn vượt ngưỡng autovacuum mặc định. Tìm các bảng tệ nhất trước:
-- Tìm các bảng có nhiều dead tuple nhất
SELECT schemaname, tablename,
pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) AS total_size,
n_dead_tup
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC
LIMIT 10;
-- VACUUM thông thường: đánh dấu không gian để tái sử dụng, không khóa bảng, không trả lại dung lượng cho OS
VACUUM tablename;
-- VACUUM FULL: trả dung lượng về cho OS, nhưng khóa bảng và ghi lại hoàn toàn
-- Dùng cẩn thận trong môi trường production
VACUUM FULL tablename;
Xác Minh Sau Khi Sửa
# Xác nhận dung lượng đã được giải phóng
df -h /var/lib/postgresql
# Kiểm tra PostgreSQL đang hoạt động bình thường
sudo systemctl status postgresql
# Kiểm tra ghi nhanh để xác nhận không có lỗi
psql -U postgres -c "CREATE TEMP TABLE _test (id int); INSERT INTO _test VALUES (1); DROP TABLE _test;"
# Quét log gần đây để tìm lỗi còn sót
sudo tail -50 /var/log/postgresql/postgresql-$(date +%Y-%m-%d)*.log
Ngăn Chặn Tái Diễn
- Cảnh báo khi sử dụng đĩa đạt 80% — đến khi chạm 100%, bạn đã ở trong trạng thái khủng hoảng rồi.
- Giới hạn lưu giữ WAL nếu bạn không dùng replication slot: đặt
wal_keep_size = 1GB(PG13+) hoặcwal_keep_segments = 64(PG12) trongpostgresql.conf. - Bật
log_temp_files = 64MBtrongpostgresql.conf— tùy chọn này ghi log bất kỳ query nào ghi hơn 64 MB dữ liệu tạm, giúp bạn phát hiện kẻ ngốn đĩa trước khi gây ra sự cố. - Kiểm soát
work_mem. Mặc định là 4 MB cho mỗi thao tác sort. Tăng lên 256 MB nghe hấp dẫn, nhưng 20 query song song mỗi cái chạy nhiều sort có thể tiêu tốn hàng gigabyte dung lượng tạm trong vài phút. - Đảm bảo autovacuum đang chạy và không bị chặn — một transaction chạy lâu có thể ngăn nó dọn dẹp bất kỳ bảng nào nó đang tác động.

