Portfolio
My Blog
Scroll down to discover
Search
Categories

CASE STUDY: QUÁ TRÌNH PHÂN TÍCH VÀ TỐI ƯU WEBSITE WORDPRESS BỊ CHẬM

July 1, 2025Category : Bug & Security
🎯 I. Kiểm tra sơ bộ
Trước tiên, cần đo lường thời gian xử lý trang index.php trước đã. Lưu lại để làm xong còn đối chiếu xem mình tối ưu wordpress có hiệu quả hay không.
Áp dụng phương pháp tương tự đã trình bày trong bài: “Tôi đã tối ưu WordPress nhanh hơn 18 lần như thế nào”, mình check xử lý PHP trực tiếp không đi qua Web Server bằng lệnh:
—–

time php index.php

—–
Thời gian xử lý dao động vào khoảng 30s-40s. Quá chậm, như này mà không xử lý được thì toang thật.
🎬 Kiểm tra tài nguyên server:
  • RAM: còn dư nhiều.
  • Load Average: Load cao hơn số core của VPS hiện có => đang bị quá tải (thiếu CPU)
  • IO ổ cứng thì ổn, đạt chuẩn tốc độ SSD.
  • VPS chạy mô hình Reverse Proxy (nginx + apache), trong đó sử dụng Apache chạy mod_php để xử lý PHP. Lệnh “top” cho thấy process httpd thường xuyên chiếm 100% CPU.
  • Kết quả thống kê CPU của lệnh top cho thấy CPU được phân bổ vào %us (user) và %sy (system) => Có thể loại bỏ nguyên nhân chậm do IO (vì %iowait không cao) và các vấn đề khác liên quan interrupt (do %si thấp).
🎬 Kết luận tổng quan:
Sau 30s làm quen, nắm tình hình tổng quan hệ thống cần tối ưu wordpress của khách hàng như sau:
  • Hệ thống đang bị qúa tải CPU, chủ yếu là ở phần xử lý ở user space và các system call của hệ thống. Nhiệm vụ ta cần phải phân tích và tìm cách tối ưu chỗ này.
  • Hệ thống sử dụng mod_php (Apache2Handler), mỗi request đến file PHP sẽ được handle bởi 1 process httpd. Trường hợp nếu chạy chiến dịch marketing, traffic đổ về nhiều và CÙNG LÚC thì sẽ có nhiều process httpd được sinh ra => dẫn đến tình trạng hết RAM => server xử lý chậm => task dồn vào queue nhiều => load cao, delay cao.
  • Process httpd khi handle request của người dùng cắn 100% CPU và thời gian xử lý khá lâu. Đây có lẽ là nút thắt quan trọng nhất cần phải gỡ. Vì khi httpd handle request người dùng nhanh sẽ giúp hạn chế số lượng process httpd được sinh ra. Điều này giúp giảm lượng RAM, CPU sử dụng và giảm delay của người dùng.
🎬 Như đã phân tích ở trên, nhiệm vụ của httpd hiện tại là load mod_php lên và xử lý các file PHP. Suy cho cùng cái chúng ta cần tập trung ở đây là xem xét cách server xử lý các file PHP như thế nào.
🎯 II. Phân tích cách server xử lý PHP
Sau khi khoanh vùng, mình tiến hành xem chi tiết cách hệ thống đang xử lý 1 file PHP. Kiểm tra mặt system call trước, chạy strace để thống kê sơ bộ khi xử lý 1 file PHP thì các system call được gọi như thế nào:
—-

strace -c php index.php

—-
🎬 Kết quả khá rõ ràng, system call “gettimeofday” được gọi đến 1,3 triệu lần và chiếm 96,96%. Đây rõ ràng là điểm quá bất thường. Vậy “gettimeofday” là gì, vì sao lại được gọi nhiều đến thế?
Theo man page của Linux thì “gettimeofday” sẽ trả về: “number of seconds and microseconds since the Epoch”
🎬 Vậy system call này liên quan đến việc lấy thời gian hiện tại, số lần system call này được gọi rất lớn, đến 1,3tr lần … lẽ nào là do thằng “W3 Total cache” không nhỉ?
Có lẽ nào “W3 Total Cache” gọi hàm này để check xem các file cache có hết hạn hay chưa để update file mới, site to thế này nên việc có nhiều file cache sẽ khiến system call này được gọi nhiều lần, hợp lý quá mà.
Nhảy ngay vào source code grep “gettimeofday” xác nhận lại xem sao. Kết qủa trả về chỉ có plugins “w3total_cache” sử dụng hàm này. (Hình 2)
🎬 Hàm “gettimeofday()” được gọi trong hàm “getmicrotime()”. Và hàm “getmicrotime()” được gọi trong hàm “debug()” (Hình 3)
🎬 Nếu debug Level > 0 thì hàm “getmicrotime()” sẽ được gọi, và qua đó “gettimeofday()” sẽ được gọi.
Quá rõ ràng, game hôm nay dễ thế, end game thôi! Vào wp-admin tắt debug của w3total cache là xong!
🎯 III. Kết quả bất ngờ
🎬 Những tưởng quá trình tối ưu WordPress hôm nay đến đây là kết thúc. Tuy nhiên khi báo khách hàng vào tắt debug của “W3 Total Cache”, mình thử lại và kết quả vẫn như cũ.
Tại sao lại như vậy nhỉ?
Nghi ngờ khách hàng tắt chưa đúng, mình xin account admin vào để tự tay mình tắt cho chắc chắn rồi thử lại. Kết quả vẫn như cũ: system call “gettimeofday” vẫn được gọi 1,3tr lần khi xử lý PHP, performance không được cải thiện và delay vẫn như cũ.
Điên máu, mìn move luôn thư mục w3total cache ra khỏi plugins để tạm thời disable nó. Kết quả vẫn không thay đổi! Tại sao?
🎬 Mình chợt nhận ra, có vẻ mình đã bị lừa, hàm “gettimeofday()” trong plugin W3 Total Cache của PHP chưa chắc đã gọi system call “gettimeofday” của kernel.
Mình đã quá vội vàng và xử lý theo suy nghĩ cảm tính trong khi chưa xem xét kỹ các bằng chứng kỹ thuật khác.
Quay lại strace process PHP và xem xét kỹ kết quả, mình chợt nhận ra:
—–

strace -o output.txt php index.php

—–
🎬 “gettimeofday” được gọi mỗi khi chuẩn bị mở file mới hoặc cấp phát một vùng nhớ mới cho process (system call open và brk).
Đặc điểm này giống hệt với cách ghi debug log, kiên nhẫn xem kỹ hơn tôi thấy sự xuất hiện của một nhân tố mới: “NewRelic”.
🎯 IV. NewRelic
🎬 NewRelic là một APM (Application Performance Monitoring), trong thường hợp hiện tại, NewRelic sẽ thọc sâu vào xử lý của PHP và đưa ra các chỉ số đo lường để chúng ta đánh giá, theo dõi ứng dụng PHP như: sự liên kết giữa các file, thời gian xử lý từng file … vậy nên việc gọi “gettimeofday” sẽ đóng vai trò quan trọng trong việc NewRelic gửi metrics về monitor server, vì các giải pháp monitoring sẽ dụng Timeseries Database với metric có dạng: “(time, key => value)”, trong đó time thường là dạng seconds since epoch, rất liên quan tới “gettimeofday”. Đây cũng là 1 điểm đáng phải cân nhắc!
🎬 Lần theo dấu vết của NewRelic, tôi phát hiện thêm điểm nghi vấn khác: “Xdebug”.
Điểm này rất đáng để xem xét, vì theo những thông tin thu thập được, tôi khá chắc chắn rằng “gettimeofday” có liên quan mật thiết đến tính năng debug nào đó.
🎯 V. XDEBUG
Tìm thêm thông tin về Xdebug 
🎬 Xdebug sẽ “record” tất cả function call và variable assignment. Nghe có vẻ đúng như thứ ta đang tìm kiếm rồi. Tiếp tục tìm thêm thông tin (Hình 6)
🎬 Khá nhiều report liên quan đến việc Xdebug làm giảm performance cũng như liên quan đến việc “gettimeofday” được gọi quá nhiều lần.
Mình đã nhìn thấy hình ảnh hệ thống hiện tại trong đấy rồi :)). Tắt extension này và thử lại (Hình 7)
🎬 Bingo! “gettimeofday” hoàn toàn biến mất khỏi top 1. CPU usage khi xử lý PHP cũng giảm xuống đáng kể .
Thời gian xử lý đã rút ngắn xuống chỉ còn vài giây.
🎯 IV. Chưa được phép dừng lại
🎬 Nút thắt quan trọng nhất trong nhiệm vụ tối ưu wordpress đã được gỡ. Mình thở phào nhẹ nhõm và chuẩn bị tinh thần để thông báo tin vui đến khách.
Tuy nhiên, kết quả trên vẫn chưa cho phép mình dừng lại khi thấy strace thống kê “lstat”, “open” và “fstat” là những system call top đầu được gọi.
Vì điều này cho thấy việc truy cập file của hệ thống chưa được tối ưu. Tình huống lúc này tương tự như bài “Tôi đã tối ưu WordPress nhanh hơn 18 lần như thế nào”
🎬 Kiểm tra nhanh thì thấy hệ thống đang sử dụng PHP 5.6 và chưa được triển khai Opcache.
Mình tiến hành nâng cấp lên PHP 7.2 kèm theo Opcache cho họ. Kết quả (Hình 😎
🎬 Đến đây. thời gian xử lý site chỉ còn lại vỏn vẹn 0.5s. Mình nghĩ mình có thể dừng lại tại đây được rồi!
🎬 Sau 1 hồi hì hục thì kết quả đạt được:
  • Giảm thời gian xử lý từ 40s xuống 0.5s
  • Giảm được “1,351,675” lệnh thừa khi xử lý PHP (Hình 9)
  • Hệ thống đã ổn định và tin cậy, tự tin phục vụ cho các chiến dịch marketing.

Case của: Nguyễn Hưng – Bo Vietnix

01.
© Oliver / All rights reserved.
To top