DevOps

Tối ưu hiệu năng VPS Ubuntu 24.04: CPU, RAM, disk, Nginx, PHP, DB

tối ưu hiệu năng VPS Ubuntu

Sau khi đã có monitoringlogging, đây mới là thời điểm hợp lý để tối ưu hiệu năng VPS. Trước đó, mọi tối ưu chỉ là đoán. Bạn có thể tăng worker Nginx, tăng PHP-FPM children, tăng buffer pool MariaDB, bật cache hoặc chỉnh sysctl, nhưng nếu không biết bottleneck thật nằm ở đâu, rất dễ làm hệ thống chậm hơn hoặc mất ổn định hơn.

Bài Tối ưu hiệu năng VPS Ubuntu 24.04: CPU, RAM, disk, Nginx, PHP, DB đi theo hướng thực chiến cho VPS Ubuntu 24.04 LTS chạy stack LEMP. Trọng tâm không phải là copy một file tuning thật dài, mà là học cách đọc số liệu, xác định nghẽn ở CPU, RAM, disk, Nginx, PHP-FPM hay MariaDB, sau đó chỉnh từng lớp nhỏ và kiểm tra lại.

Mục tiêu sau bài này là bạn có một workflow rõ ràng: đo baseline, đọc CPU/RAM/disk, kiểm tra log, tune Nginx ở mức an toàn, tính pm.max_children cho PHP-FPM theo RAM thật, bật OPcache hợp lý, kiểm tra slow query MariaDB, và biết khi nào nên nâng cấp VPS thay vì tiếp tục vặn config.

1. Hiểu đúng về tối ưu hiệu năng VPS Ubuntu

1.1. Tối ưu hiệu năng không phải là tăng mọi giới hạn

Một lỗi rất phổ biến khi tối ưu VPS là thấy website chậm thì tăng mọi thứ: tăng worker_connections, tăng pm.max_children, tăng max_connections, tăng buffer, tăng timeout, tăng cache. Nghe có vẻ hợp lý, nhưng thực tế có thể gây tác dụng ngược.

Nếu VPS chỉ có 1-2GB RAM mà bạn tăng PHP-FPM children quá cao, mỗi request PHP có thể ngốn thêm RAM cho đến khi server swap hoặc bị OOM. Nếu tăng MariaDB buffer pool quá lớn trên cùng máy chạy Nginx/PHP-FPM, database có thể khỏe hơn một chút nhưng PHP lại thiếu RAM. Nếu tăng timeout để che request chậm, người dùng vẫn chờ lâu và worker vẫn bị giữ.

Tối ưu đúng không phải là “tăng”. Tối ưu đúng là đặt giới hạn phù hợp với tài nguyên thật và workload thật.

1.2. Phải đo trước khi chỉnh

Trước mỗi thay đổi, hãy biết trạng thái hiện tại:

  • CPU đang bận do PHP, MariaDB, bot traffic hay backup?
  • RAM còn MemAvailable không hay đang swap?
  • Disk có đầy không?
  • Disk I/O có cao không?
  • Nginx đang trả nhiều 502/504 không?
  • PHP-FPM có chạm max_children không?
  • MariaDB có slow query không?
  • Website chậm ở TTFB hay tải asset?

Nếu không đo, bạn không biết chỉnh gì. Nếu không đo lại sau khi chỉnh, bạn không biết thay đổi đó có giúp gì hay không.

1.3. Thứ tự tối ưu nên đi từ dữ liệu đến cấu hình

Thứ tự hợp lý cho VPS LEMP:

  • Đo baseline bằng curl, monitoring và log.
  • Kiểm tra CPU, RAM, disk, network.
  • Xử lý lỗi rõ ràng trước: disk đầy, OOM, 502, slow query.
  • Tối ưu Nginx ở mức an toàn.
  • Tune PHP-FPM theo RAM thật.
  • Bật và kiểm tra OPcache.
  • Tune MariaDB theo workload và RAM còn lại.
  • Chỉ benchmark nhẹ sau khi đã có backup và monitoring.

Không nên nhảy ngay vào “Nginx config tối ưu 100k request/s” khi website thật đang chậm vì database query không có index.

2. Chuẩn bị công cụ đo hiệu năng

2.1. Cài bộ công cụ cơ bản

Cài các công cụ thường dùng:

sudo apt update
sudo apt install -y htop sysstat iotop iftop ncdu curl time lsof

Ý nghĩa nhanh:

  • htop: xem process, CPU, RAM trực quan.
  • sysstat: có iostat, mpstat, pidstat, sar.
  • iotop: xem process nào đang đọc/ghi disk nhiều.
  • iftop: xem network realtime.
  • ncdu: tìm thư mục chiếm disk.
  • curl: đo HTTP timing.
  • lsof: kiểm tra file/socket/process.

2.2. Bật sysstat để có dữ liệu lịch sử

Mở file:

sudo nano /etc/default/sysstat

Đảm bảo có:

ENABLED="true"

Bật service/timer:

sudo systemctl enable --now sysstat
sudo systemctl status sysstat --no-pager || true
systemctl list-timers | grep sysstat || true

Không phải mọi hệ thống hiển thị timer giống nhau, nhưng mục tiêu là sysstat có thể thu thập số liệu định kỳ để bạn dùng sar khi cần xem lại quá khứ.

2.3. Đo HTTP baseline bằng curl

Đo trang chủ:

curl -o /dev/null -s -w "dns=%{time_namelookup} connect=%{time_connect} tls=%{time_appconnect} ttfb=%{time_starttransfer} total=%{time_total}\n" https://example.com/

Đo health-check nếu có:

curl -o /dev/null -s -w "ttfb=%{time_starttransfer} total=%{time_total}\n" https://example.com/healthz

Ghi lại kết quả trước khi chỉnh. Sau mỗi thay đổi lớn, đo lại cùng URL, cùng thời điểm tương đối, cùng điều kiện mạng nếu có thể.

2.4. Kiểm tra log trước khi tối ưu

sudo tail -n 100 /var/log/nginx/example.com.access.log
sudo tail -n 100 /var/log/nginx/example.com.error.log
sudo journalctl -u php8.3-fpm --since "1 hour ago" --no-pager
sudo journalctl -u mariadb --since "1 hour ago" --no-pager

Nếu log đang đầy lỗi 502, permission denied, database connection failed hoặc upstream timeout, hãy xử lý lỗi trước khi tối ưu hiệu năng.

3. Đọc CPU đúng cách

3.1. Load average không phải lúc nào cũng là CPU cao

Lệnh:

uptime
nproc

Ví dụ VPS có 2 vCPU. Nếu load average 1 phút thường xuyên quanh 2, server có thể đang dùng gần hết năng lực xử lý. Nếu load quanh 6-8 trong thời gian dài, server đang quá tải hoặc có nhiều process chờ CPU/I/O.

Nhưng load average không chỉ đến từ CPU. Process chờ disk I/O cũng có thể làm load cao. Vì vậy cần xem thêm mpstat, iostat và process list.

3.2. Xem CPU theo core bằng mpstat

mpstat -P ALL 1 5

Cách đọc nhanh:

  • %usr cao: ứng dụng/user process dùng CPU nhiều, thường là PHP, database, worker.
  • %sys cao: kernel/system call nhiều, có thể do network, disk, process quá nhiều.
  • %iowait cao: CPU đang chờ disk I/O.
  • %steal cao: trên VPS, hypervisor đang lấy CPU time; có thể do node provider quá tải.
  • %idle thấp trong thời gian dài: CPU đang bận thật.

Nếu %steal cao thường xuyên, tối ưu Nginx/PHP có thể không giải quyết triệt để. Bạn có thể cần đổi gói VPS hoặc provider.

3.3. Tìm process ăn CPU

htop

Hoặc dùng lệnh:

ps -eo pid,ppid,user,cmd,%cpu,%mem --sort=-%cpu | head -n 20

Xem theo tiến trình trong vài giây:

pidstat 1 5

Nếu process CPU cao là PHP-FPM, hãy kiểm tra URL nào đang chậm trong Nginx log. Nếu là MariaDB, kiểm tra slow query. Nếu là backup/compression, cân nhắc đổi lịch backup sang giờ thấp điểm hoặc giảm mức nén.

3.4. Khi nào CPU cao là bình thường?

CPU tăng ngắn hạn khi deploy, backup, nén log, chạy cron, import database hoặc có spike traffic là bình thường. Vấn đề là CPU cao kéo dài và ảnh hưởng TTFB hoặc service.

Đừng tối ưu vì một spike 10 giây. Hãy tối ưu khi số liệu cho thấy xu hướng lặp lại: CPU cao nhiều phút, load cao, request time tăng, PHP-FPM chạm giới hạn hoặc database query chậm.

4. Đọc RAM và swap đúng cách

4.1. Đừng chỉ nhìn “used RAM”

Linux dùng RAM cho cache, vì vậy nhìn “used” cao không đồng nghĩa server thiếu RAM. Hãy xem MemAvailable:

free -h
grep -E 'MemTotal|MemAvailable|SwapTotal|SwapFree' /proc/meminfo

Nếu MemAvailable còn thấp trong thời gian dài và swap tăng, VPS có thể thiếu RAM hoặc cấu hình service đang dùng quá nhiều RAM.

4.2. Tìm process ăn RAM

ps -eo pid,ppid,user,cmd,%mem,rss --sort=-rss | head -n 20

RSS hiển thị bộ nhớ resident theo KB. Với PHP-FPM, bạn sẽ thấy nhiều process php-fpm. Cần tính trung bình mỗi child dùng bao nhiêu RAM để đặt pm.max_children hợp lý.

4.3. Kiểm tra OOM

journalctl -k --since "24 hours ago" --no-pager | grep -Ei 'oom|out of memory|killed process' || true

Nếu có OOM, server đã từng thiếu RAM nghiêm trọng đến mức kernel phải kill process. Khi đó, tăng thêm PHP-FPM children hoặc MariaDB buffer là hướng sai. Cần giảm RAM dùng, thêm swap hợp lý hoặc nâng cấp VPS.

4.4. Có nên dùng swap?

Swap không làm server nhanh hơn, nhưng giúp giảm nguy cơ OOM khi có spike ngắn. Với VPS nhỏ 1-2GB RAM, có một lượng swap vừa phải thường hữu ích. Tuy nhiên, nếu server dùng swap liên tục, nghĩa là RAM thật đang thiếu hoặc cấu hình đang sai.

Kiểm tra swap:

swapon --show
free -h

Nếu chưa có swap và VPS rất ít RAM, có thể tạo swapfile. Nhưng trước khi làm, hãy chắc chắn disk còn đủ và hiểu rằng swap trên disk chậm hơn RAM rất nhiều.

5. Đọc disk và I/O đúng cách

5.1. Disk đầy là lỗi hiệu năng và lỗi vận hành

Kiểm tra disk:

df -h
sudo du -xh / | sort -h | tail -n 30

Dùng ncdu để xem dễ hơn:

sudo ncdu /

Disk đầy có thể làm MariaDB lỗi, log không ghi được, backup fail, Certbot renew fail và app không upload/cache được.

5.2. Kiểm tra I/O bằng iostat

iostat -xz 1 5

Cách đọc nhanh:

  • %util cao liên tục: disk đang bận.
  • await cao: request I/O phải chờ lâu.
  • r/s, w/s: số lần đọc/ghi mỗi giây.
  • rkB/s, wkB/s: throughput đọc/ghi.

Nếu disk I/O cao trong lúc website chậm, bottleneck có thể không nằm ở CPU. Hãy kiểm tra log, database, backup job, rotate log hoặc process ghi file nhiều.

5.3. Tìm process đang ghi disk

sudo iotop -oPa

Nếu chưa muốn dùng interactive:

pidstat -d 1 5

Các process thường gây I/O cao:

  • MariaDB khi query nặng hoặc flush nhiều.
  • Backup nén dữ liệu.
  • Log tăng quá nhanh.
  • App ghi cache/session/file upload nhiều.
  • Bot traffic tạo nhiều request động.

5.4. Dọn log và cache đúng cách

Kiểm tra log:

sudo du -sh /var/log
journalctl --disk-usage
sudo find /var/log -type f -printf '%s %p\n' | sort -nr | head -n 20 | awk '{printf "%.2f MB %s\n", $1/1024/1024, $2}'

Dọn journal theo chính sách:

sudo journalctl --vacuum-size=500M

Không xóa log đang được service giữ mà không reload/restart service. Nếu xóa file nhưng disk không giảm, kiểm tra:

sudo lsof | grep deleted | head

5.5. Kiểm tra TRIM nếu dùng SSD

Trên nhiều VPS SSD/NVMe, TRIM có thể hữu ích nếu storage hỗ trợ. Kiểm tra timer:

systemctl status fstrim.timer --no-pager || true
systemctl list-timers | grep fstrim || true

Bật nếu chưa có:

sudo systemctl enable --now fstrim.timer

Không phải mọi VPS/provider đều thể hiện TRIM giống nhau, nhưng timer này thường an toàn trên Ubuntu hiện đại.

6. Tối ưu Nginx ở mức an toàn

6.1. Kiểm tra cấu hình hiện tại

nginx -v
sudo nginx -T | grep -nE 'worker_processes|worker_connections|sendfile|tcp_nopush|tcp_nodelay|keepalive_timeout|gzip|access_log|error_log'

Trước khi chỉnh:

sudo cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak.$(date +%F-%H%M%S)
sudo nginx -t

6.2. worker_processes và worker_connections

Với đa số VPS, nên để:

worker_processes auto;

Trong block events, một cấu hình vừa phải:

events {
    worker_connections 1024;
    multi_accept off;
}

Không cần tăng worker_connections lên quá cao nếu file descriptor limit, RAM, upstream PHP-FPM và database không đủ. Website PHP động thường nghẽn ở PHP/database trước khi nghẽn ở Nginx connection handling.

6.3. sendfile, tcp_nopush, tcp_nodelay

Trong block http:

sendfile on;
tcp_nopush on;
tcp_nodelay on;

Đây là các thiết lập phổ biến cho Nginx phục vụ static file và HTTP response hiệu quả hơn. Tuy nhiên, đừng kỳ vọng chúng sửa được backend chậm. Nếu PHP-FPM hoặc database đang chậm, Nginx tuning chỉ giúp phần ngoài cùng.

6.4. keepalive_timeout vừa phải

Ví dụ:

keepalive_timeout 30;
keepalive_requests 1000;

keepalive_timeout quá cao có thể giữ connection lâu hơn cần thiết trên VPS nhỏ. Quá thấp có thể làm client phải mở connection mới nhiều hơn. Mức 15-30 giây thường là điểm bắt đầu hợp lý cho website nhỏ.

6.5. Không tắt access log bừa bãi

Access log giúp debug hiệu năng và traffic. Có thể tắt log cho static asset nếu quá nhiều noise, nhưng không nên tắt toàn bộ access log của site.

Nếu đã làm bài B7/C5, nên dùng log format có timing:

access_log /var/log/nginx/example.com.access.log timing;
error_log  /var/log/nginx/example.com.error.log warn;

6.6. Kiểm tra sau khi chỉnh Nginx

sudo nginx -t
sudo systemctl reload nginx
sudo systemctl status nginx --no-pager
curl -I https://example.com
sudo tail -n 50 /var/log/nginx/example.com.error.log

Không restart Nginx nếu chỉ cần reload config hợp lệ. Reload giúp áp dụng cấu hình nhẹ nhàng hơn.

7. Tối ưu PHP-FPM theo RAM thật

7.1. PHP-FPM thường là điểm nghẽn chính của app PHP

Với website PHP, Nginx thường xử lý rất nhẹ. Request động sẽ được chuyển sang PHP-FPM. Nếu PHP-FPM thiếu worker, request phải chờ. Nếu PHP-FPM quá nhiều worker, RAM cạn và server swap/OOM.

Vì vậy, pm.max_children là một trong những thông số quan trọng nhất khi tune PHP-FPM.

7.2. Đo RAM trung bình của PHP-FPM child

Xem process PHP-FPM:

ps -ylC php-fpm8.3 --sort:rss 2>/dev/null || ps -ylC php-fpm --sort:rss

Tính RSS trung bình gần đúng:

ps -o rss= -C php-fpm8.3 2>/dev/null | awk '{sum+=$1; n++} END {if (n>0) printf "avg_rss=%.1f MB\n", sum/n/1024; else print "no php-fpm process found"}'

Nếu process name khác, kiểm tra:

ps aux | grep '[p]hp-fpm'

7.3. Công thức tính pm.max_children

Công thức thực tế:

pm.max_children = RAM dành cho PHP-FPM / RAM trung bình mỗi PHP child

Ví dụ VPS có 2GB RAM. Bạn muốn để lại:

  • 400MB cho hệ thống, Nginx, cache.
  • 600MB cho MariaDB.
  • Phần còn lại khoảng 1GB cho PHP-FPM.

Nếu mỗi PHP child dùng trung bình 60MB:

1024 / 60 = 17

Khi đó có thể đặt pm.max_children quanh 12-16 để an toàn hơn. Đừng lấy con số tính được rồi đặt sát trần ngay.

7.4. Chọn pm dynamic hay ondemand

Mở pool config:

sudo nano /etc/php/8.3/fpm/pool.d/www.conf

Với VPS nhỏ, ít traffic, ondemand tiết kiệm RAM hơn:

pm = ondemand
pm.max_children = 12
pm.process_idle_timeout = 10s
pm.max_requests = 500

Với website có traffic ổn định, dynamic phản hồi đều hơn vì giữ sẵn một số process:

pm = dynamic
pm.max_children = 16
pm.start_servers = 3
pm.min_spare_servers = 2
pm.max_spare_servers = 6
pm.max_requests = 500

Không có cấu hình đúng cho mọi website. ondemand hợp VPS ít RAM và traffic thấp. dynamic hợp site có traffic đều hơn. static chỉ nên dùng khi bạn hiểu workload và RAM rất rõ.

7.5. Bật PHP-FPM status path local

Trong pool config:

pm.status_path = /fpm-status
ping.path = /fpm-ping
ping.response = pong

Thêm vào Nginx server block, chỉ cho localhost:

location ~ ^/(fpm-status|fpm-ping)$ {
    allow 127.0.0.1;
    deny all;

    include snippets/fastcgi-php.conf;
    fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}

Kiểm tra:

sudo nginx -t
sudo systemctl reload nginx
sudo systemctl restart php8.3-fpm

curl http://127.0.0.1/fpm-ping -H "Host: example.com"
curl http://127.0.0.1/fpm-status -H "Host: example.com"

Không public status page ra internet.

7.6. Kiểm tra PHP-FPM có chạm max_children không

sudo journalctl -u php8.3-fpm --since "24 hours ago" --no-pager | grep -Ei 'max_children|server reached|pool' || true

Nếu thấy cảnh báo chạm pm.max_children, có hai khả năng:

  • Traffic thật tăng và cần thêm PHP worker.
  • Một số request PHP quá chậm làm worker bị giữ lâu.

Đừng tăng max_children ngay. Hãy xem request time trong Nginx log, PHP slowlog và database slow query.

8. Bật và kiểm tra OPcache

8.1. OPcache giúp gì?

PHP là ngôn ngữ động. Nếu không có OPcache, PHP phải đọc và biên dịch script nhiều lần. OPcache lưu bytecode đã biên dịch trong shared memory, giúp giảm overhead cho request PHP.

Với production PHP, OPcache gần như nên được bật trừ khi bạn có lý do đặc biệt.

8.2. Kiểm tra OPcache

php -m | grep -i opcache
php -i | grep -i "opcache.enable"

Kiểm tra file cấu hình:

php --ini
ls -la /etc/php/8.3/fpm/conf.d/ | grep opcache || true
ls -la /etc/php/8.3/mods-available/ | grep opcache || true

8.3. Cấu hình OPcache gợi ý cho VPS nhỏ

Tạo file override:

sudo nano /etc/php/8.3/fpm/conf.d/99-devnook-opcache.ini

Nội dung:

opcache.enable=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=10000
opcache.validate_timestamps=1
opcache.revalidate_freq=60
opcache.save_comments=1

Với deploy production có quy trình reload PHP-FPM sau mỗi release, bạn có thể cân nhắc:

opcache.validate_timestamps=0

Nhưng chỉ dùng khi bạn chắc chắn deploy luôn reload/restart PHP-FPM hoặc reset OPcache. Nếu không, website có thể tiếp tục chạy code cũ sau deploy.

8.4. Restart PHP-FPM và kiểm tra

sudo systemctl restart php8.3-fpm
sudo systemctl status php8.3-fpm --no-pager
php -i | grep -E 'opcache.enable|opcache.memory_consumption|opcache.max_accelerated_files|opcache.validate_timestamps|opcache.revalidate_freq'

Kiểm tra lỗi:

sudo journalctl -u php8.3-fpm --since "10 minutes ago" --no-pager

9. Bật PHP-FPM slowlog để tìm request chậm

9.1. Vì sao cần slowlog?

Khi website chậm, bạn cần biết PHP đang chậm ở đoạn nào. PHP-FPM slowlog có thể ghi lại stack trace của request chạy quá lâu.

Không nên bật slowlog quá chi tiết mãi mãi trên production nếu log sinh nhiều hoặc chứa thông tin nhạy cảm. Nhưng khi cần điều tra, slowlog rất hữu ích.

9.2. Cấu hình slowlog

Mở pool config:

sudo nano /etc/php/8.3/fpm/pool.d/www.conf

Thêm hoặc chỉnh:

request_slowlog_timeout = 5s
slowlog = /var/log/php8.3-fpm-slow.log

Kiểm tra:

sudo php-fpm8.3 -t
sudo systemctl restart php8.3-fpm

Xem log:

sudo tail -f /var/log/php8.3-fpm-slow.log

9.3. Đọc slowlog thế nào?

Nếu slowlog chỉ ra một plugin, một query, một external API call hoặc một file PHP cụ thể, hãy xử lý nguyên nhân đó. Đừng chỉ tăng request_terminate_timeout hoặc pm.max_children để che request chậm.

Request chậm giữ PHP worker lâu hơn. Khi worker bị giữ nhiều, site dễ chạm pm.max_children và sinh 502/504.

10. Tối ưu MariaDB theo dữ liệu thật

10.1. MariaDB thường chậm vì query, không phải vì thiếu một dòng config bí mật

Nhiều người tìm “my.cnf tối ưu” rồi copy một file cấu hình dài. Cách này rất rủi ro. MariaDB performance thường cần nhìn ba thứ trước:

  • Database có slow query không?
  • Có query thiếu index không?
  • RAM có đủ cho working set không?

Chỉ sau khi biết workload, bạn mới nên chỉnh buffer/cache.

10.2. Kiểm tra MariaDB hiện tại

sudo systemctl status mariadb --no-pager
sudo journalctl -u mariadb --since "1 hour ago" --no-pager
sudo mariadb -e "SHOW GLOBAL STATUS LIKE 'Threads_connected';"
sudo mariadb -e "SHOW GLOBAL STATUS LIKE 'Max_used_connections';"
sudo mariadb -e "SHOW VARIABLES LIKE 'max_connections';"
sudo mariadb -e "SHOW VARIABLES LIKE 'innodb_buffer_pool_size';"

10.3. Bật slow query log tạm thời

Tạo file cấu hình:

sudo nano /etc/mysql/mariadb.conf.d/60-devnook-performance.cnf

Nội dung:

[mysqld]
slow_query_log = 1
slow_query_log_file = /var/log/mysql/mariadb-slow.log
long_query_time = 1
log_queries_not_using_indexes = 0

Tạo thư mục log nếu cần:

sudo mkdir -p /var/log/mysql
sudo chown mysql:adm /var/log/mysql
sudo chmod 750 /var/log/mysql

Restart MariaDB:

sudo mariadb -e "SELECT 1;"
sudo systemctl restart mariadb
sudo systemctl status mariadb --no-pager

Kiểm tra biến:

sudo mariadb -e "SHOW VARIABLES LIKE 'slow_query_log';"
sudo mariadb -e "SHOW VARIABLES LIKE 'long_query_time';"

Xem slow log:

sudo tail -f /var/log/mysql/mariadb-slow.log

10.4. Đừng bật log_queries_not_using_indexes quá sớm

log_queries_not_using_indexes có thể tạo rất nhiều log, đặc biệt với WordPress hoặc app có nhiều query nhỏ. Hãy bắt đầu với long_query_time = 1 hoặc 2, xem slow query thật trước. Khi cần điều tra sâu hơn mới bật thêm.

10.5. Tune innodb_buffer_pool_size cẩn thận

Kiểm tra RAM:

free -h

Với server chỉ chạy database riêng, buffer pool có thể chiếm tỷ lệ lớn RAM. Nhưng với VPS LEMP cùng chạy Nginx, PHP-FPM, MariaDB, monitoring, backup, bạn không nên cấp quá nhiều RAM cho MariaDB.

Ví dụ VPS 2GB RAM chạy LEMP nhỏ, có thể bắt đầu với:

[mysqld]
innodb_buffer_pool_size = 512M

VPS 4GB RAM có thể thử:

[mysqld]
innodb_buffer_pool_size = 1G

Đây chỉ là điểm bắt đầu, không phải công thức tuyệt đối. Sau khi chỉnh, theo dõi MemAvailable, swap, MariaDB log và hiệu năng query.

10.6. max_connections không nên đặt quá cao

Kiểm tra:

sudo mariadb -e "SHOW VARIABLES LIKE 'max_connections';"
sudo mariadb -e "SHOW GLOBAL STATUS LIKE 'Max_used_connections';"

Nếu Max_used_connections thấp, không cần tăng max_connections. Mỗi connection có thể dùng thêm memory. Tăng quá cao không làm app nhanh hơn, mà có thể làm server chết chậm hơn khi traffic xấu hoặc app lỗi connection pooling.

Với VPS nhỏ, có thể giữ mức vừa phải:

[mysqld]
max_connections = 80

Nhưng hãy chỉnh theo app thật. Nếu app cần nhiều connection hợp lệ, phải tính lại RAM tổng thể.

10.7. Dùng EXPLAIN cho query chậm

Khi slow log chỉ ra query cụ thể, copy query đó vào database test hoặc staging và chạy:

EXPLAIN SELECT ...;

Cần chú ý:

  • Query có dùng index không?
  • Có full table scan không?
  • Có filesort hoặc temporary table không?
  • WHERE/JOIN có cột phù hợp để index không?

Rất nhiều vấn đề database được giải quyết bằng index/schema/query tốt hơn, không phải bằng tăng buffer.

11. Tối ưu WordPress hoặc PHP app ở lớp ứng dụng

11.1. Server tuning không cứu được app quá nặng

Nếu app load quá nhiều plugin, query không có index, gọi API ngoài mỗi request, tạo ảnh quá lớn, không cache view hoặc có bot spam login, server tuning chỉ giúp một phần.

Với WordPress, các điểm thường ảnh hưởng lớn:

  • Plugin quá nhiều hoặc plugin kém chất lượng.
  • Theme nặng.
  • Query admin-ajax quá nhiều.
  • Không cache page cho khách chưa đăng nhập.
  • Ảnh quá lớn, chưa dùng WebP/AVIF nếu phù hợp.
  • Không có object cache khi site có nhiều query.
  • Bot đánh wp-login.php hoặc xmlrpc.php.

11.2. Cache page và cache static asset

Bài B7 đã xử lý gzip, cache static asset, headers và HTTP/2. Với website động, bạn có thể cân nhắc page cache ở app/plugin hoặc Nginx FastCGI cache.

Tuy nhiên, FastCGI cache cần làm cẩn thận vì dễ cache nhầm nội dung theo user. Nếu website có login, giỏ hàng, dashboard, comment form hoặc nội dung cá nhân hóa, hãy test kỹ trước khi bật cache động ở Nginx.

11.3. Tối ưu ảnh trước khi đổ lỗi cho server

Nếu trang chủ tải 10MB ảnh, tối ưu Nginx/PHP-FPM sẽ không giúp nhiều. Hãy kiểm tra:

  • Ảnh có đúng kích thước hiển thị không?
  • Có lazy-load không?
  • Có nén ảnh không?
  • Có dùng định dạng hiện đại khi phù hợp không?
  • Static asset có cache header không?

11.4. Bot traffic cũng là vấn đề hiệu năng

Bot scan có thể tạo nhiều request động, làm PHP-FPM và database bận. Kiểm tra Nginx access log:

sudo awk '{print $1}' /var/log/nginx/example.com.access.log | sort | uniq -c | sort -nr | head -n 30
sudo awk '$9 ~ /^[45]/ {print $7}' /var/log/nginx/example.com.access.log | sort | uniq -c | sort -nr | head -n 30

Nếu thấy bot đánh endpoint nhạy cảm liên tục, hãy xử lý bằng Fail2ban, Cloudflare rule, rate limit hoặc hardening app.

12. Benchmark an toàn

12.1. Không load test mạnh trên production

Các công cụ như ab, wrk, hey có thể tạo tải thật. Nếu chạy quá tay trên VPS nhỏ, bạn có thể tự làm website chậm hoặc sập.

Production chỉ nên test nhẹ, có monitoring mở sẵn và có kế hoạch dừng test.

12.2. Test nhẹ bằng curl nhiều lần

for i in {1..10}; do
  curl -o /dev/null -s -w "ttfb=%{time_starttransfer} total=%{time_total}\n" https://example.com/
done

Ghi lại kết quả trước và sau khi chỉnh.

12.3. Test bằng ab ở mức rất nhẹ

Cài ApacheBench nếu cần:

sudo apt install -y apache2-utils

Test nhẹ:

ab -n 100 -c 5 https://example.com/

Không dùng concurrency cao nếu bạn chưa biết giới hạn server. Với app PHP/database, test -c 50 hoặc -c 100 trên VPS nhỏ có thể làm server nghẽn thật.

12.4. Luôn xem log và metrics trong lúc test

Mở song song:

htop
iostat -xz 1
sudo tail -f /var/log/nginx/example.com.error.log
sudo journalctl -u php8.3-fpm -f

Nếu CPU, RAM, disk hoặc error log tăng bất thường, dừng test.

13. Khi nào nên nâng cấp VPS thay vì tiếp tục tune?

13.1. Dấu hiệu cần thêm RAM

  • MemAvailable thấp kéo dài.
  • Swap dùng liên tục.
  • Có OOM kill.
  • Không thể tăng PHP-FPM children dù traffic thật cần.
  • MariaDB buffer pool quá nhỏ so với working set nhưng server không còn RAM.

13.2. Dấu hiệu cần thêm CPU

  • CPU idle rất thấp trong thời gian dài.
  • Load cao hơn số vCPU liên tục.
  • PHP/database đã tối ưu cơ bản nhưng vẫn nghẽn CPU.
  • Traffic thật tăng và cache không thể giảm thêm nhiều.

13.3. Dấu hiệu cần disk tốt hơn

  • iowait cao thường xuyên.
  • iostat cho thấy latency disk cao.
  • Database chậm do I/O.
  • Backup/log/DB cùng tranh disk.
  • Provider có storage quá chậm hoặc noisy neighbor.

13.4. Tối ưu không thay thế kiến trúc

Khi website lớn hơn, có thể cần tách lớp:

  • CDN cho static asset.
  • Database riêng.
  • Object storage cho upload.
  • Redis/object cache.
  • Queue worker riêng.
  • Monitoring/backup tách khỏi VPS app.

Một VPS đơn có thể đi khá xa nếu tối ưu đúng, nhưng không nên ép nó làm mọi thứ mãi mãi.

14. Quy trình tối ưu mẫu cho VPS LEMP

14.1. Bước 1: Đo baseline

curl -o /dev/null -s -w "ttfb=%{time_starttransfer} total=%{time_total}\n" https://example.com/
uptime
free -h
df -h
iostat -xz 1 3
sudo tail -n 50 /var/log/nginx/example.com.error.log

14.2. Bước 2: Xác định bottleneck

  • CPU cao: xem process và request chậm.
  • RAM thấp/swap: tính lại PHP-FPM/MariaDB.
  • Disk đầy/I/O cao: xem log, DB, backup, cache.
  • 502/504: xem Nginx error log và PHP-FPM.
  • TTFB cao: xem PHP/database, không chỉ Nginx.

14.3. Bước 3: Chỉnh một thay đổi nhỏ

Ví dụ:

  • Giảm pm.max_children nếu OOM.
  • Tăng nhẹ pm.max_children nếu còn RAM và đang chạm giới hạn.
  • Bật OPcache nếu chưa bật.
  • Tăng innodb_buffer_pool_size nếu còn RAM và DB đọc nhiều.
  • Thêm index cho query chậm.
  • Giảm retention log nếu disk gần đầy.

14.4. Bước 4: Kiểm tra lại

sudo nginx -t
sudo systemctl status nginx php8.3-fpm mariadb --no-pager
curl -o /dev/null -s -w "ttfb=%{time_starttransfer} total=%{time_total}\n" https://example.com/
sudo journalctl -p warning --since "30 minutes ago" --no-pager

14.5. Bước 5: Ghi chú thay đổi

Mỗi lần tune, hãy ghi lại:

  • Ngày giờ chỉnh.
  • File đã sửa.
  • Giá trị trước và sau.
  • Lý do chỉnh.
  • Kết quả đo trước/sau.
  • Cách rollback.

Không ghi chú, vài tuần sau bạn sẽ không nhớ vì sao pm.max_children là 17 hay buffer pool là 768MB.

15. Lỗi thường gặp khi tối ưu hiệu năng

15.1. Tăng PHP-FPM children quá cao

Đây là lỗi phổ biến nhất. Tăng children có thể giảm hàng đợi request, nhưng mỗi child dùng RAM. Nếu RAM không đủ, server swap/OOM và chậm hơn trước.

15.2. Tăng MariaDB max_connections quá cao

max_connections cao không làm query nhanh hơn. Nó chỉ cho phép nhiều connection hơn. Nếu app tạo quá nhiều connection, hãy xử lý app/pooling trước khi tăng giới hạn.

15.3. Tăng timeout để che request chậm

Tăng timeout có thể giảm lỗi 504, nhưng request vẫn chậm và worker vẫn bị giữ. Hãy tìm nguyên nhân: query, API ngoài, plugin, lock, disk I/O.

15.4. Không bật OPcache trên production PHP

Nếu OPcache chưa bật, PHP phải biên dịch lại script nhiều hơn cần thiết. Đây là tối ưu cơ bản nhưng rất hay bị bỏ sót.

15.5. Không xem slow query log

Nếu database chậm mà bạn chỉ tăng buffer, có thể bỏ lỡ vấn đề thật: thiếu index hoặc query không tối ưu. Slow query log là bước bắt buộc khi nghi DB là bottleneck.

15.6. Benchmark production quá mạnh

Load test không kiểm soát có thể tự tạo downtime. Hãy test nhẹ hoặc dùng staging.

15.7. Copy cấu hình tuning từ server khác

Server khác có RAM khác, traffic khác, app khác, database khác, disk khác. Copy tuning mà không hiểu có thể làm hỏng server của bạn.

16. Checklist tối ưu hiệu năng VPS Ubuntu

16.1. Checklist đo đạc

  • Đã đo TTFB/total time bằng curl.
  • Đã xem CPU bằng mpstat, htop.
  • Đã xem RAM bằng free -hMemAvailable.
  • Đã kiểm tra OOM trong journal.
  • Đã kiểm tra disk bằng df -h, du, iostat.
  • Đã xem Nginx error/access log.
  • Đã xem PHP-FPM và MariaDB journal.

16.2. Checklist Nginx

  • nginx -t thành công trước khi reload.
  • worker_processes auto.
  • worker_connections vừa phải, không tăng mù.
  • sendfile, tcp_nopush, tcp_nodelay cấu hình hợp lý.
  • keepalive_timeout không quá cao.
  • Access log có timing để debug.

16.3. Checklist PHP-FPM

  • Đã tính pm.max_children theo RAM thật.
  • Chọn ondemand hoặc dynamic theo traffic.
  • pm.max_requests để giảm rủi ro leak dài hạn.
  • OPcache đã bật.
  • PHP-FPM status/ping chỉ local.
  • Slowlog được bật khi cần điều tra request chậm.

16.4. Checklist MariaDB

  • Đã bật slow query log khi cần debug.
  • Đã kiểm tra innodb_buffer_pool_size.
  • Không tăng max_connections mù.
  • Đã xem Max_used_connections.
  • Query chậm được kiểm tra bằng EXPLAIN.
  • Không copy file tuning lạ từ internet.

16.5. Checklist sau khi chỉnh

  • Service vẫn active.
  • Không có warning/error mới trong journal.
  • TTFB không tăng bất thường.
  • RAM không cạn.
  • Swap không tăng liên tục.
  • Disk không tăng nhanh vì log/cache.
  • Có ghi chú thay đổi và cách rollback.

17. FAQ

17.1. VPS 1GB RAM có chạy LEMP được không?

Có thể chạy được website nhỏ, nhưng cần rất cẩn thận với PHP-FPM, MariaDB và monitoring. Nên dùng pm = ondemand, buffer pool MariaDB vừa phải, OPcache bật, log retention gọn và tránh chạy quá nhiều service trên cùng VPS.

17.2. Nên tăng Nginx worker_connections lên bao nhiêu?

Không có con số đúng cho mọi VPS. Với website PHP nhỏ, bottleneck thường nằm ở PHP-FPM hoặc database trước Nginx. Bắt đầu với 1024, theo dõi log và metrics. Chỉ tăng khi có bằng chứng Nginx connection là giới hạn.

17.3. pm.max_children nên đặt bao nhiêu?

Hãy đo RSS trung bình của PHP-FPM child, sau đó chia RAM dành cho PHP-FPM cho con số đó. Đừng copy số của người khác. Nếu đặt quá thấp, request phải chờ; nếu đặt quá cao, server hết RAM.

17.4. OPcache có cần cho WordPress không?

Có. WordPress là app PHP, OPcache giúp giảm overhead biên dịch PHP script. Đây là tối ưu cơ bản nên có trên production.

17.5. MariaDB nên dùng bao nhiêu RAM cho buffer pool?

Nếu server chỉ chạy database, buffer pool có thể dùng tỷ lệ lớn RAM. Nhưng nếu cùng VPS còn chạy Nginx, PHP-FPM, backup và monitoring, hãy cấp thận trọng hơn. Với VPS nhỏ, bắt đầu thấp, theo dõi RAM/swap/slow query rồi tăng dần.

17.6. Có nên dùng MySQLTuner không?

MySQLTuner có thể hữu ích để tham khảo, nhưng không nên áp dụng mọi khuyến nghị máy móc. Hãy hiểu workload, RAM tổng thể và slow query trước. Tool chỉ gợi ý, người vận hành vẫn phải quyết định.

17.7. Tối ưu xong có cần restart toàn bộ server không?

Không. Hầu hết thay đổi chỉ cần reload/restart service liên quan: Nginx, PHP-FPM hoặc MariaDB. Reboot chỉ nên dùng khi cập nhật kernel, xử lý lỗi hệ thống hoặc có lý do rõ ràng.

17.8. Sau bài này nên học gì tiếp?

Sau khi biết tối ưu dựa trên số liệu, bước tiếp theo là troubleshooting playbook. Khi gặp 502, SSL fail, permission lỗi, disk đầy, load cao hoặc database chậm, bạn cần một quy trình xử lý sự cố theo từng bước.

18. Kết luận

Tối ưu hiệu năng VPS Ubuntu không phải là copy một bộ cấu hình “thần thánh”. Tối ưu đúng là đo trước, xác định bottleneck, chỉnh một thay đổi nhỏ, kiểm tra lại, ghi chú và có đường rollback.

Trong bài này, bạn đã đi qua toàn bộ workflow cho VPS Ubuntu 24.04 LTS chạy LEMP: đọc CPU bằng mpstat, đọc RAM bằng MemAvailable, kiểm tra OOM, đọc disk/I/O bằng df, iostat, iotop, tune Nginx ở mức an toàn, tính PHP-FPM pm.max_children theo RAM thật, bật OPcache, dùng PHP-FPM slowlog, bật MariaDB slow query log và chỉnh buffer pool một cách thận trọng.

Khi đã có monitoring, logging và quy trình tối ưu rõ ràng, bạn sẽ không còn vận hành VPS bằng cảm giác. Bạn sẽ biết khi nào cần chỉnh config, khi nào cần sửa query, khi nào cần giảm plugin, khi nào cần chặn bot, và khi nào nên nâng cấp VPS. Đây là nền tảng quan trọng cuối cùng trước khi bước sang bài troubleshooting playbook cho các sự cố production thường gặp.

Đừng bỏ lỡ

Theo dõi bản tin mới nhất tại đây

Chào bạn 👋
Rất vui được làm quen.

Đăng ký để nhận nội dung hấp dẫn mỗi tháng qua email.

Bằng việc đăng ký, bạn đồng ý với Chính sách bảo mật của DevNook.

Tác giả

Tuấn Lê

Tại DevNook, mình chia sẻ kiến thức thực chiến, kinh nghiệm làm sản phẩm và những công cụ hữu ích giúp bạn làm việc hiệu quả hơn mỗi ngày.