Sau chuỗi bài về lưu trữ và bảo trì, ta chuyển sang cấu hình bộ nhớ — và tham số đầu tiên, quan trọng nhất, là shared_buffers. Đây là bộ nhớ đệm chính của PostgreSQL: nơi nó giữ các trang bảng và index trong RAM để không phải đọc lại từ đĩa. Mặc định 128MB thường quá nhỏ cho production, nhưng cám dỗ "đặt thật lớn" cũng sai — vì một lý do đặc thù của PostgreSQL: double buffering với bộ đệm của hệ điều hành. Bài này đo thật cách shared_buffers hoạt động và cho quy tắc đặt đúng.

shared_buffers là gì

shared_buffers là vùng RAM chung mà PostgreSQL dùng làm bộ đệm cho các trang 8KB của bảng và index. Đọc một trang đã có trong shared_buffers là cache hit (nhanh, từ RAM); không có thì phải đọc từ đĩa (hoặc OS cache).

SHOW shared_buffers;        -- mặc định 128MB (thường quá nhỏ)
SHOW effective_cache_size;  -- 4GB — chỉ là GỢI Ý cho planner, không cấp phát

Với 128MB, shared_buffers chứa được 128MB / 8KB = 16.384 trang. Đo thật container này: chỉ 478 buffer đang dùng, phần lớn là các bảng catalog hệ thống (pg_attribute, pg_statistic...).

Ảnh chụp đoạn mã SQL nền tối minh hoạ shared_buffers bộ nhớ đệm chính đặt bao nhiêu, shared_buffers bằng bộ đệm PostgreSQL giữ trang bảng index trong RAM SHOW shared_buffers mặc định 128MB thường quá nhỏ SHOW effective_cache_size 4GB gợi ý cho planner không cấp phát 128MB chia 8KB bằng 16384 trang đọc trang trong SB bằng cache hit nhanh, cạm bẫy không phải càng lớn càng tốt double buffering dữ liệu PostgreSQL được cache ở hai nơi 1 shared_buffers bộ đệm của PG 2 OS page cache bộ đệm của hệ điều hành đặt SB quá lớn cùng trang cache 2 lần phí RAM RAM đó nên để cho OS cache work_mem và kết nối, quy tắc khoảng 25 phần trăm RAM không hơn RAM 8GB shared_buffers khoảng 2GB RAM 32GB shared_buffers khoảng 8GB effective_cache_size khoảng 50-75 phần trăm RAM SB cộng ước lượng OS cache đây chỉ là gợi ý cho planner chọn index scan vs seq scan, theo dõi cache hit ratio cộng pg_buffercache SELECT sum blks_hit nhân 100 chia sum blks_hit cộng blks_read FROM pg_stat_database OLTP nên đạt lớn hơn 99 phần trăm thấp bằng SB nhỏ so với tập dữ liệu nóng SELECT c.relname count FROM pg_buffercache b JOIN pg_class c ON GROUP BY 1 SB đang chứa gì

Hình 1: shared_buffers là bộ đệm chính giữ trang trong RAM. Cạm bẫy là double buffering với OS cache — đặt quá lớn phí RAM. Quy tắc ~25% RAM; theo dõi bằng cache hit ratio và pg_buffercache.

Đo thật: bảng lớn hơn shared_buffers

Bảng pgbench_accounts là 648 MB — lớn hơn nhiều so với shared_buffers 128MB, nên nó không thể nằm hết trong bộ đệm:

Ảnh chụp bảng kết quả đo thật nền tối shared_buffers pg_buffercache cache hit PostgreSQL 16 shared_buffers 128MB effective_cache_size 4GB pg_stat_database pg_statio pg_buffercache, shared_buffers hiện có gì 128MB bằng 16384 trang tổng buffer 16384 128MB chia 8KB đang dùng 478 chủ yếu catalog pg_attribute pg_statistic, bảng lớn hơn shared_buffers SB hit thấp pgbench_accounts 648 MB lớn hơn SB 128MB cache hit trong SB 37,53 phần trăm bảng không vừa SB nên phần lớn đọc từ OS cache đĩa cache hit toàn cục 92,53 phần trăm OLTP nên lớn hơn 99 phần trăm, nhưng tập nóng ở lại SB đọc lần 2 bằng 0 read quét subset aid nhỏ hơn 100000 lần 1 lạnh shared hit 4687 read 1458 lần 2 nóng shared hit 6145 read 0 subset nhỏ hay dùng nằm trong SB sau lần đọc đầu lần sau 100 phần trăm cache hit, quy tắc đặt shared_buffers RAM máy chủ 8 GB shared_buffers khoảng 25 phần trăm 2 GB effective_cache_size khoảng 50-75 phần trăm 4-6 GB RAM 32 GB shared_buffers 8 GB effective_cache_size 16-24 GB, cốt lõi shared_buffers là bộ đệm chính của PostgreSQL mặc định 128MB thường quá nhỏ quy tắc khoảng 25 phần trăm RAM không hơn vì OS page cache cũng cache dữ liệu PG double buffering SB quá lớn phí RAM effective_cache_size chỉ là gợi ý cho planner khoảng 50-75 phần trăm RAM theo dõi cache hit ratio OLTP nên lớn hơn 99 phần trăm

Hình 2: shared_buffers 128MB chứa 16.384 trang (chỉ 478 đang dùng). pgbench_accounts 648 MB lớn hơn SB nên cache hit trong SB chỉ 37,53%. Nhưng tập nóng ở lại: quét subset lần 1 read=1458, lần 2 read=0.

  • Cache hit trong SB của pgbench_accounts: chỉ 37,53% — vì bảng lớn hơn shared_buffers, phần lớn dữ liệu phải đọc từ OS cache/đĩa. Cache hit toàn cục 92,53% (OLTP tốt nên đạt >99%).
  • Nhưng tập nóng ở lại SB: quét một subset nhỏ (aid<100.000) lần đầu đọc read=1458 từ đĩa; lần thứ hai read=0 — subset đó giờ nằm hoàn toàn trong shared_buffers. Đây là điều quan trọng: shared_buffers không cần chứa cả dữ liệu, chỉ cần chứa tập nóng hay được truy cập.

Cạm bẫy: double buffering — vì sao không đặt quá lớn

Đây là điểm đặc thù của PostgreSQL. Dữ liệu của PostgreSQL được cache ở hai nơi cùng lúc: shared_buffers (bộ đệm của PostgreSQL) và OS page cache (bộ đệm của hệ điều hành). Khi bạn đặt shared_buffers quá lớn:

  • Cùng một trang có thể bị cache hai lần (trong SB và trong OS cache) — lãng phí RAM.
  • RAM lấy cho SB là RAM không còn cho OS cache, work_mem (bộ nhớ sort/hash — bài sau), và các kết nối.

Vì vậy quy tắc phổ biến là ~25% RAM cho shared_buffers, không hơn — để OS cache dùng phần còn lại hiệu quả. Đặt SB 80% RAM thường chậm hơn SB 25% vì mất double buffering và bỏ đói các thành phần khác.

effective_cache_size: chỉ là gợi ý cho planner

Đừng nhầm effective_cache_size (4GB ở đây) với một cấp phát bộ nhớ — nó không cấp phát gì cả. Nó chỉ nói cho planner biết tổng bộ nhớ cache khả dụng (shared_buffers + ước lượng OS cache), để planner quyết định index scan có lợi hơn seq scan không (nếu dữ liệu có thể trong cache thì index scan rẻ hơn). Đặt nó khoảng 50-75% RAM — cao hơn shared_buffers vì nó tính cả OS cache.

Đánh đổi cần cân nhắc

RAM máy chủ shared_buffers (~25%) effective_cache_size (~50-75%)
8 GB ~2 GB 4-6 GB
32 GB ~8 GB 16-24 GB

25% là điểm khởi đầu, không phải luật. Với workload đọc-nặng và tập dữ liệu nóng vừa RAM, tăng SB cao hơn (40%) có thể tốt. Với máy chủ chia sẻ nhiều dịch vụ, hạ thấp hơn. Đo cache hit ratio thật và điều chỉnh — đừng copy một con số mù quáng.

Đổi shared_buffers cần khởi động lại. Khác nhiều tham số reload được, shared_buffers là bộ nhớ chung cấp phát lúc khởi động — đổi nó phải ALTER SYSTEM SET shared_buffers rồi restart PostgreSQL. Lên kế hoạch cho downtime ngắn.

Trên container/cloud, RAM khả dụng có thể khác RAM máy. Trong Docker (như lab này), giới hạn RAM của container mới là con số để tính 25%, không phải RAM máy chủ vật lý. Trên dịch vụ quản lý, thường nhà cung cấp đã đặt sẵn shared_buffers theo instance size — kiểm trước khi chỉnh.

Ba ý mang về

  1. shared_buffers là bộ đệm chính của PostgreSQL, mặc định 128MB thường quá nhỏ: nó chỉ cần chứa tập dữ liệu nóng, không cần cả bảng — đo thật, subset hay truy cập ở lại SB (read 1458 → 0 ở lần đọc thứ hai) dù bảng 648 MB lớn hơn SB.
  2. Quy tắc ~25% RAM, không hơn, vì double buffering: dữ liệu PG được cache ở cả shared_buffers lẫn OS page cache — đặt SB quá lớn phí RAM lẽ ra để cho OS cache, work_mem, và kết nối.
  3. effective_cache_size chỉ là gợi ý cho planner (~50-75% RAM), không cấp phát: theo dõi cache hit ratio (OLTP nên >99%) và pg_buffercache để biết SB đủ chưa; đổi shared_buffers cần restart.

Phần sau ta xét tham số bộ nhớ quan trọng thứ hai, cấp phát theo từng truy vấn: Phần sau mổ xẻ work_mem — RAM cho sort và hash, vì sao đặt sai gây tràn đĩa hoặc hết bộ nhớ, và cách tính an toàn theo số kết nối.