Lịch sử System Design · Phụ lục · về mục lục

Phụ lục D. Giới hạn mặc định

những con số hay bị chạm trước phần cứng

Bảng tra cứu các giới hạn mặc định của app server, database, connection pool, hệ điều hành, load balancer và Kubernetes. Mỗi hàng ghi giá trị mặc định, nó điều khiển gì, và đổi nó thì tầng nào chịu.

Đối chiếu docs 27/10/2026 Bảng kiểm kèm URL 07-evolution/_build/c0-verify.md
Theo docs giá trị mặc định lấy từ docs chính thức; mở docs đúng phiên bản bạn chạy trước khi dựa vào

D.1 App server và runtime

Theo docs

Mỗi giới hạn dưới đây là một chữ L trong định luật Little (chương 0): số việc được xử lý dở cùng lúc. Biết mặc định thì biết hệ thống sẽ kẹt ở đâu trước.

Tham sốMặc địnhGiá trị hợp lệLàm gìKhi nào đổi · tác động
Tomcat maxThreadsrủi ro200số nguyênSố thread tối đa xử lý request cùng lúc của connector HTTP.Theo Little: 200 thread, mỗi request 100 ms thì trần khoảng 2.000 request/giây; request chậm thành 1 giây thì trần tụt còn 200. Tăng thread tốn RAM cho stack của mỗi thread và không giúp gì nếu nút cổ chai là database.
Tomcat acceptCount100số nguyênSố kết nối được xếp hàng chờ khi mọi thread đều bận.Hàng chờ dài hơn thì ít lỗi kết nối hơn lúc đột biến nhưng độ trễ tăng; quá giới hạn thì kết nối mới bị từ chối.
HikariCP maximumPoolSizerủi ro10số nguyênSố kết nối database tối đa một instance app giữ.Nhân với số instance app phải nhỏ hơn giới hạn kết nối của database. 20 instance × 10 = 200 kết nối, đã vượt mặc định của PostgreSQL. Pool lớn quá làm database chậm vì tranh chấp, không làm nhanh hơn.
HikariCP connectionTimeout30000 mssố nguyên, msThời gian tối đa một request chờ lấy kết nối từ pool.Để dài thì lúc database chậm, thread app bị giữ lâu và chuỗi quá tải lan lên tầng trên. Thường nên ngắn hơn timeout của bên gọi.
nginx worker_connections512số nguyênSố kết nối tối đa mỗi worker process của nginx mở cùng lúc, tính cả kết nối tới upstream.Khi làm reverse proxy, mỗi request dùng hai kết nối (client và upstream), nên số client thật chỉ khoảng một nửa. Tăng thì phải tăng cả giới hạn file mở của hệ điều hành.
Node.js server.keepAliveTimeoutrủi ro5000 mssố nguyên, msThời gian server giữ kết nối keep-alive rảnh trước khi đóng. 5000 ms là giá trị ở các bản đã phát hành (docs Node 22); docs nhánh phát triển đã đổi thành 65000 ms.Nếu ngắn hơn idle timeout của load balancer phía trước, server đóng kết nối trong khi load balancer vẫn gửi request vào đó, gây lỗi 502 lẻ tẻ khó tìm. Nên đặt dài hơn idle timeout của load balancer.
Node.js server.requestTimeout300000 mssố nguyên, ms; 0 là tắtThời gian tối đa để nhận xong toàn bộ request từ client.Giữ để chống client gửi chậm cố ý chiếm kết nối.
Go http.Server ReadTimeout, WriteTimeout, IdleTimeoutrủi ro0time.Duration; 0 là không giới hạnGiới hạn thời gian đọc request, ghi phản hồi và giữ kết nối rảnh. 0 nghĩa là không giới hạn; riêng IdleTimeout bằng 0 thì dùng giá trị của ReadTimeout.Mặc định không giới hạn: một client chậm hay treo giữ goroutine và kết nối mãi. Service Go chạy thật nên đặt cả ba.

D.2 Database và connection pool

Theo docs

Tham sốMặc địnhGiá trị hợp lệLàm gìKhi nào đổi · tác động
PostgreSQL max_connectionsrủi ro100số nguyên; đổi phải restartSố kết nối tối đa vào server. PostgreSQL tạo một process riêng cho mỗi kết nối. Mặc định có thể thấp hơn 100 nếu lúc initdb kernel không cho phép.Mỗi kết nối là một process nên tốn RAM và CPU chuyển ngữ cảnh. Cần nhiều kết nối từ nhiều instance app thì đặt pooler như PgBouncer phía trước, đừng tăng max_connections lên hàng nghìn.
MySQL max_connectionsrủi ro151số nguyênSố client kết nối cùng lúc tối đa.Vượt giới hạn thì client nhận lỗi "Too many connections". MySQL dùng thread cho mỗi kết nối nên rẻ hơn process, nhưng vẫn tốn RAM theo buffer của từng kết nối.
PgBouncer default_pool_size20số nguyênSố kết nối thật tới database cho mỗi cặp user và database.Đây mới là số kết nối database thật sự thấy; nhiều client dùng chung số kết nối này. Đặt theo Little: tốc độ query nhân thời gian query.
PgBouncer max_client_conn100số nguyênSố kết nối client tối đa PgBouncer nhận.Có thể đặt lớn hơn nhiều so với max_connections của database, đó là mục đích của pooler. Cần đủ file descriptor trên máy chạy PgBouncer.
Redis maxclients10000số nguyênSố client kết nối cùng lúc tối đa; thấp hơn 10000 nếu giới hạn file descriptor của process nhỏ hơn.Hay chạm khi mỗi instance app mở pool riêng lớn. Redis xử lý lệnh trên một luồng chính, nên thêm kết nối không làm nó nhanh hơn.
Nhân số instance với kích thước pool trước khi scale ra. Tăng tầng app từ 5 lên 30 pod, mỗi pod giữ 10 kết nối, là đổi từ 50 lên 300 kết nối vào database. PostgreSQL mặc định nhận 100 kết nối sẽ từ chối phần còn lại, dù CPU của nó vẫn rảnh.

D.3 Hệ điều hành, load balancer, Kubernetes

Theo docs

Tham sốMặc địnhGiá trị hợp lệLàm gìKhi nào đổi · tác động
Linux net.core.somaxconn4096số nguyênĐộ dài tối đa hàng chờ kết nối đã bắt tay xong nhưng app chưa nhận (accept). Trước kernel 5.4 mặc định là 128.Trên kernel cũ giá trị nhỏ làm rớt kết nối lúc đột biến. Giá trị thật còn bị giới hạn bởi tham số backlog mà app truyền vào listen().
AWS ALB idle timeoutrủi ro60 giâygiâyThời gian load balancer giữ kết nối không có dữ liệu trước khi đóng.Request chạy lâu hơn mức này mà không gửi byte nào sẽ bị cắt. Keep-alive của app phía sau nên dài hơn giá trị này để tránh lỗi 502.
GCP external Application LB backend service timeoutrủi ro30 giâygiâyThời gian load balancer chờ backend trả lời. Backend serverless (serverless NEG) có giới hạn khác.Request lâu hơn mức này bị trả lỗi dù backend vẫn đang xử lý; backend làm việc vô ích. Việc dài nên chuyển sang xử lý bất đồng bộ (chương 6).
Kubernetes probe timeoutSecondsrủi ro1 giâygiâyThời gian chờ một lần kiểm tra liveness hoặc readiness.Mặc định rất ngắn. Lúc app quá tải, probe trả lời chậm, Kubernetes tưởng app chết và restart, làm phần tải dồn sang các pod còn lại: một vòng sập dây chuyền.
Kubernetes probe periodSeconds, failureThreshold10 giây; 3 lầngiây; số lầnBao lâu kiểm một lần, và thất bại bao nhiêu lần liên tiếp thì coi là hỏng.Thời gian phát hiện hỏng xấp xỉ periodSeconds × failureThreshold. Ngắn thì phát hiện nhanh nhưng dễ restart nhầm lúc tải cao.
Timeout phải xếp bậc từ ngoài vào trong. Timeout của bên gọi nên dài hơn tổng timeout bên trong, và keep-alive của backend nên dài hơn idle timeout của load balancer. Xếp ngược lại thì bên ngoài bỏ cuộc trong khi bên trong vẫn làm, hoặc kết nối bị đóng giữa chừng.