TÔI ĐÃ TỐI ƯU WORDPRESS NHANH HƠN 18 LẦN NHƯ THẾ NÀO?

10/03/2020

Gần đây có nhiều bạn gặp tình trạng website load chậm và PM lên group chat hỏi cách xử lý, sẵn đang chuẩn bị publish cái blog nên lấy nó ra làm case study, sau này training mấy bạn nhân viên mới thì có cái cho đọc, đỡ mắc công train :D. Tuy nhiên, mỗi website sẽ có 1 đặc điểm riêng, không phải site nào tối ưu cũng giống site nào, quan trọng là để các bạn mới nắm được hướng tiếp cận và xác định được các điểm cần phải tập trung cải thiện. Sau một hồi hì hục phân tích và gõ lệnh, kết quả đạt được cũng gọi là chấp nhận được

TTFB: Thời gian chờ nhận byte dữ liệu phản hồi đầu tiên từ server, server xử lý càng nhanh thì thời gian chờ càng ít, chứng tỏ càng hiệu quả:

  • Trước khi tối ưu: 648.92ms
  • Sau khi tối ưu: 36.57ms

Tính ra, website đã load nhanh hơn 18 lần so với ban đầu. OK, giờ ghi chú lại quá trình làm của mình thôi nào!

Trước tiên, muốn giảm độ trễ cần xác định request của mình được xử lý và đi qua những “hop” như thế nào.

  • Khi người dùng truy cập vào trang chủ (GET / ), request này sẽ được gửi lên server qua Internet. Tùy thuộc vào tốc độ kết nối mạng giữa client và server mà độ trễ cao hay thấp. Đây là điểm delay thứ 1.
  • Khi request đến được server sẽ trải qua nhiều bước để lên đến được tầng application và gặp Web Server. Web server sẽ tiếp nhận và xử lý request, đây là điểm delay thứ 2.
  • Web server nhận thấy request cần được PHP xử lý nên đẩy vào trong cho PHP + MySQL, đây là điểm delay thứ 3.
  • Sau khi PHP xử lý xong sẽ ra kết quả là 1 cục HTML, ném cục này lại cho Web Server và Web server sẽ ném cục này lại cho Browser. 
  • Browser nhận được cục HTML sẽ hiển thị lên cho người dùng và đồng thời load tiếp các resource liên quan khác (js, css, image v.v).

Túm lại, sơ bộ đã xác định được 3 điểm cần check, giờ tiến hành khoanh vùng và check từng điểm.

1. Check điểm delay 1: Tốc độ kết nối giữa mình và máy chủ

Đơn giản và hiệu quả chính là câu lệnh ping thần thánh:

Okie, ping đẹp, delay chỉ từ 4ms-5ms. Chỗ này ổn, loại ra khỏi danh sách, tập trung vào các khu vực khác.

2. Check điểm delay 2: Web Server

Ngó sơ tình hình trước cái đã, mở Developer Tools của Browser lên (F12), chọn phần Network và truy cập vào website:

Hmm, thời gian xử lý lâu nhất nguyên cây xanh lè đập vào mắt là chỗ xử lý code của trang chủ. Ngó kỹ vào nó chút:

Phần lớn thời gian là chờ byte dữ liệu phản hồi đầu tiên (TTFB): 649ms, thời gian download cục HTML về chỉ tốn 2.07ms. Vậy việc cần làm là triệt tiêu bớt thời gian chờ, giúp nhận được cục HTML sớm hơn. Như theo mô hình đã khoanh vùng và loại trừ đoạn delay số 1, lúc này cần tập trung vào đoạn delay số 2 và số 3. Theo kinh nghiệm thì chắc chắn đoạn cần tối ưu là đoạn 3 (PHP + MySQL) , tuy nhiên cứ phải check từng bước đã, đừng để kinh nghiệm nó lừa mình.

Để kết quả test không bị ảnh hưởng bởi đoạn delay số 1 (network) thì SSH thẳng lên server check. Lúc này cần đo lường xem request đi qua Web Server tốn bao nhiêu thời gian, request xử lý trực tiếp (không đi qua Web Server) thì tốn bao nhiêu:

  • Check request đi qua web server:
  • Tốn hết 0.663s
  • Check request xử lý trực tiếp không thông qua web server
  • Tốn hết 0.602s

Kết luận: đi qua web server cũng không làm tăng độ trễ nhiều, vấn đề chính là ở cục PHP + MySQL thôi, tới đây loại trừ thằng Web server ra được rồi.

3. Check điểm delay 3: PHP + MySQL

Không ngoài dự đoán, 0.602s là thời gian xử lý của code, tính ra là gần bằng thời gian TTFB, giờ strace thử xem những system call nào đang tốn nhiều thời gian nhất:

Hmm, lstat + fstat + stat tính ra chiếm đến hơn 50% thời gian … những system call này liên quan đến việc lấy thông tin file như permission, file size .. cơ mà, sao lại chiếm nhiều thời gian thế nhỉ? Phải chăng là do số lượng file nhiều? Hay do I/O kém?

Để hiểu rõ hơn buộc phải dump kết quả strace chi tiết ra file để nhìn rồi. 

Lướt từ trên xuống thì thấy cái theme này nhiều file thiệt, phần lớn các system call trên liên quan đến file trong thư mục theme. Vậy giờ phải làm sao để hạn chế việc check file trên ổ cứng? Muốn bypass ổ cứng thì chỉ có phương án là đưa lên RAM … mà lại là PHP … ờ thì opcache chứ còn gì nữa. Triển luôn thôi

Opcache đã có, giờ test lại xem nào:

Sặc, chuyện gì vậy .. 0.832s ? Tại sao lại tốn nhiều thời gian hơn khi không dùng opcache (chỉ 0.602s)? Opcache cli đã chắc chắn được enable rồi, tại sao lại tốn nhiều thời gian hơn? Nếu chỉ là request đầu tiên (cache miss) thì còn chấp nhận được, tại sao các request tiếp theo lại vẫn chậm như vậy?

Google một hồi thì mới hiểu là ở version PHP < 7.0 thì opcache chỉ có tác dụng khi process đang chạy, do đó khi mình chạy bằng command line, lúc process kết thúc cũng là lúc vùng memory lưu opcache của process đó bị hủy, giữa các lần chạy là các process khác nhau và không liên quan gì nhau nữa. Tuy nhiên, nếu vậy thì check qua web server có thể kết quả sẽ tốt hơn, vì mỗi process httpd sẽ handle được nhiều request trước khi exit.

Có thể chứ, đúng như dự đoán, thời gian xử lý thông qua web server được giảm xuống đáng kể, chỉ còn ~0.270s. So với ban đầu (0.663s) thì cũng nhanh hơn được 3 lần, Opcache cũng được phết. Đây là còn chưa bật query cache hay memcache các kiểu lên nữa, mà để tính sau, site bé tí chưa cần thiết.

Hiện tại đang xài PHP 5.6 (do con server này build lâu rồi), sẵn tiện nghe thiên hạ đồn PHP 7 performance tốt hơn, đo lường thử phát. Hì hục nâng cấp PHP lên version 7, và kết quả:

Ồ, tốt hơn thật anh em ạ, so với case này thì PHP 7 nhanh gấp đôi PHP 5. Chưa kể, PHP 7 giải quyết được vụ dùng opcache ở cli nữa. strace lại phát

Tỉ lệ thời gian dành cho các system call fstat, lstat và stat giảm xuống đáng kể, chỉ còn 21%, tuy nhiên là 21% của total 0.07s (khoảng 0,015s), so với ban đầu là 55% của 0.17s (khoảng 0.093s). Phải vậy chứ!

Tới đây thì cũng tạm hài lòng rồi, mà thôi sẵn làm luôn cho tới, đo lường cho vui. Quất thêm lớp cache nữa để bỏ qua luôn phần xử lý của cục PHP + MySQL. Đập thằng W3total Cache lên

Lúc này check trực tiếp trên chính server, thời gian xử lý chỉ còn 0.019s. Tuy nhiên, w3total cache hoạt động dựa trên .htaccess, nghĩa là nginx vẫn còn bước proxy_pass về httpd. Tối ưu thêm chút, quất luôn nginx cache để bỏ qua đoạn proxy_pass luôn xem sao:

Rút ngắn chút đỉnh, còn 0.013s. Test thử từ browser xem kết quả cuối cùng so với ban đầu thế nào

TTFB lúc này chỉ còn 36.57ms, so với ban đầu 648.92s, đã nhanh hơn 18 lần. 

Tuy nhiên, việc sử dụng nginx cache sẽ dính tới việc setup các module xóa cache và tích hợp vào wordpress, giờ lười quá nên thôi bỏ ra, nào rảnh làm tiếp!