Tối ưu WordPress bị chậm

Kiểm tra và tối ưu System cho blog WordPress của doanh nghiệp trong lĩnh vực Internet ở Việt Nam với lượng truy cập tương đối lớn. Website gặp tình trạng thường bị load chậm hoặc quá tải khi chạy các chiến dịch marketing, dù cho khách hàng có đội dev riêng rất giỏi và đã tối ưu rất tốt từ mặt coding đến database, caching (sử dụng W3 Total Cache).

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 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”, tôi 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 như 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, 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).

Túm lại, sau 30s làm quen, nắm tình hình tổng quan hệ thống của khách hàng như sau:

Hệ thống đang bị quá 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, giảm lượng RAM sử dụng, giảm CPU usage 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.

Phân tích cách server xử lý PHP

Sau khi khoanh vùng, tôi 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àm gettimeofday() được gọi trong hàm getmicrotime(). Và Hàm getmicrotime() được gọi trong hàm debug().

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!

Kết quả khi kiểm tra

Sau khi tắt debug của W3 Total Cache, tôi thử lại, tuy nhiên 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, tôi 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, tôi 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?

Tôi 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. Tôi đã quá vội vàng và xử lý theo suy nghĩ cảm tính của mì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ả, tôi chợt nhận ra:

$ strace -o output.txt php index.php

𝗴𝗲𝘁𝘁𝗶𝗺𝗲𝗼𝗳𝗱𝗮𝘆 đượ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.

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 đó. 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. Tiếp tục tìm thêm thông tin

Yeah! 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 đang kể và thời gian xử lý đã được rút ngắn chỉ còn vài giây.

Nút thắt quan trọng nhất đã được gỡ, tôi 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 tôi 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 trở về tương tự như bài “Tôi đã tối ưu WordPress nhanh hơn 18 lần như thế nào”

======== Đến đây. thời gian xử lý site chỉ còn lại vỏn vẹn 0.5s, tôi 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.3tr câu lệnh dư thừa khi xử lý PHP

P/S: NHỮNG CÂU HỎI THƯƠNG GẶP

  • 1/ system call gettimeofday là gì? -> system call: từ dùng để gọi chung cho các hàm của kernel Linux system call gettimeofday ->nghĩa là mình hiểu đây là hàm gettimeofday của kernel
  • 2/ hàm munmap đc gọi từ đâu vậy? – > munmap là system call liên quan đến việc ánh xạ virtual address của process lên RAM,

Bình luận

avatar
  Subscribe  
Notify of