nguyên nhân gốc rễ của các vấn đề về hiệu suất là do code kém, code không hiệu quả.
Do đó, bài viết này chủ yếu nhằm mục đích cung cấp cho các dev và công ty một số nguyên tắc có thể giúp họ giải quyết các nguyên nhân cơ bản của nhiều vấn đề về hiệu suất của WordPress hoặc dành cho những website có lượng data rất lớn.
Đây là một bài viết chia sẻ nâng cao yêu cầu bạn đọc phải có kiến thức nền về lập trình WordPress.
Mục lục
Truy vấn bài viết
WordPress cung cấp khả năng tìm nạp bất kỳ loại bài viết nào từ database cơ sở dữ liệu. Có ba cách cơ bản thường dùng làm việc này là:
- Sử dụng hàm
query_posts()
: Đây là một cách tiếp cận rất trực tiếp, nhưng vấn đề là nó ghi đè lên truy vấn chính , điều này có thể dẫn đến sự bất tiện. Ví dụ: đây có thể là một vấn đề nếu chúng tôi muốn xác định, tại một thời điểm nào đó sau khi tìm nạp các bài đăng (chẳng hạn như bên trong footer.php), chúng tôi đang xử lý loại trang nào. Trên thực tế, tài liệu chính thức có ghi chú khuyến nghị không nên sử dụng chức năng này vì bạn sẽ cần gọi một chức năng bổ sung để khôi phục truy vấn ban đầu. Hơn nữa, việc thay thế truy vấn chính sẽ tác động tiêu cực đến tốc độ thời gian tải trang. - Sử dụng hàm
get_posts()
: Điều này hoạt động gần giống như query_posts(), nhưng nó không sửa đổi truy vấn chính. Mặt khác, get_posts() theo mặc định, thực hiện truy vấn với suppress_filters tham số được đặt thành true. Điều này có thể dẫn đến sự không nhất quán, đặc biệt nếu chúng tôi sử dụng các bộ lọc liên quan đến truy vấn trong code của mình, vì các bài đăng mà bạn không mong đợi trong một trang có thể được hàm này trả về. - Sử dụng class
WP_Query
: Theo mình, đây là cách tốt nhất để lấy truy vấn các bài viết từ database cơ sở dữ liệu. Nó không làm thay đổi truy vấn chính và nó được thực thi theo cách tiêu chuẩn.
Nhưng cho dù chúng ta sử dụng phương pháp nào để tương tác làm việc với cơ sở dữ liệu, vẫn có những phương pháp tối ưu mà chúng ta cần xem xét.
Giới hạn truy vấn
Chúng ta phải luôn chỉ định số lượng bài đăng mà truy vấn của chúng ta phải tìm nạp.
Để thực hiện điều đó, chúng tôi sử dụng đối số posts_per_page.
WordPress cho phép chúng tôi chỉ ra -1 là giá trị có thể có cho đối số đó, trong trường hợp đó, hệ thống sẽ cố gắng tìm nạp tất cả các bài đăng đáp ứng các điều kiện đã xác định.
Đây không phải là một phương pháp hay, ngay cả khi chúng tôi chắc chắn rằng chúng tôi sẽ chỉ cần một vài kết quả trong truy vấn đó.
Thứ nhất, chúng ta hiếm khi có thể chắc chắn về việc chỉ nhận lại một vài kết quả. Và ngay cả khi chúng ta có thể, việc đặt không giới hạn sẽ yêu cầu database cơ sở dữ liệu quét toàn bộ cơ sở dữ liệu để tìm kiếm kết quả phù hợp.
$query = new WP_Query( array( 'posts_per_page' => -1 ) );
Ngược lại, việc giới hạn kết quả thường cho phép công cụ cơ sở dữ liệu chỉ quét một phần dữ liệu, điều này dẫn đến thời gian xử lý ít hơn và tốc độ nhanh hơn nhiều.
$query = new WP_Query( array( 'posts_per_page' => 10 ) );
Một điều quan trọng khác mà WordPress thực hiện theo mặc định, có thể ảnh hưởng xấu đến hiệu suất, đó là nó cố gắng đưa các bài viết cố định và tính toán có bao nhiêu hàng được tìm thấy trên truy vấn.
Tuy nhiên, thông thường chúng ta không thực sự cần những thông tin đó. Việc thêm hai đối số này sẽ vô hiệu hóa các tính năng đó và tăng tốc truy vấn của bạn:
$query = new WP_Query( array(
'ignore_sticky_posts' => true,
'no_found_rows' => true
)
);
Loại trừ bài đăng khỏi truy vấn
Đôi khi chúng tôi muốn loại trừ một số bài đăng khỏi truy vấn. WordPress cung cấp một cách khá trực tiếp để đạt được nó: sử dụng đối số post__not_in
. Ví dụ:
$posts_to_exclude = array( 1, 2, 3 );
$posts_per_page = 10;
$query = new WP_Query( array(
'posts_per_page' => $posts_per_page,
'post__not_in' => $posts_to_exclude
)
);
for ( $i = 0; $i < count( $query->posts ); $i++ ) {
//do stuff with $query->posts[ $i ]
}
Nhưng mặc dù điều này khá đơn giản, nhưng nó không tối ưu vì bên trong nó tạo ra một truy vấn phụ. Đặc biệt rất vấn đề với các database lớn, điều này có thể dẫn đến tốc độ chậm. Sẽ nhanh hơn nếu để quá trình xử lý đó được thực hiện bởi ngôn ngữ lập trình PHP với một số sửa đổi đơn giản:
$posts_to_exclude = array( 1, 2, 3 );
$posts_per_page = 10;
$query = new WP_Query( array(
'posts_per_page' => $posts_per_page + count( $posts_to_exclude )
)
);
for ( $i = 0; $i < count( $query->posts ) && $i < $posts_per_page; $i++ ) {
if ( ! in_array( $query->posts[ $i ]->ID, $posts_to_exclude ) ) {
//do stuff with $query->posts[ $i ]
}
}
mình đã làm gì ở trên?
Về cơ bản, mình đã loại bỏ một số công việc từ database cơ sở dữ liệu và giao việc cho PHP, cách này thực hiện cùng một nội dung nhưng nhanh hơn nhiều.
Làm sao?
Trước tiên, tôi đã xóa đối số post__not_in
khỏi.
Vì truy vấn có thể mang lại cho chúng tôi một số bài đăng mà chúng tôi không muốn, nên tôi đã tăng đối số posts_per_page
. Bằng cách đó, tôi đảm bảo rằng, ngay cả khi tôi có một số bài đăng không mong muốn trong phản hồi của mình, thì ít nhất tôi cũng sẽ có $posts_per_page
những bài đăng mong muốn ở đó.
Sau đó, khi tôi lặp lại các bài đăng, tôi chỉ xử lý những bài không nằm trong array $posts_to_exclude
.
Tránh đối số hóa phức tạp
class WP_Query này cung cấp nhiều khả năng tìm nạp bài viết: theo danh mục, theo tag hoặc giá trị meta, theo ngày, theo tác giả, sản phẩm, đầu tư, bài viết tùy chỉnh v.v.
Và mặc dù tính linh hoạt đó là một tính năng mạnh mẽ, nhưng nó nên được sử dụng một cách thận trọng vì việc đối số hóa đó có thể chuyển thành các phép nối bảng phức tạp và các thao tác cơ sở dữ liệu rất tốn tài nguyên máy chủ.
Trong phần tiếp theo, chúng tôi sẽ phác thảo một cách tinh tế để vẫn đạt được chức năng tương tự mà không ảnh hưởng đến hiệu suất.
Tận dụng tối đa API WordPress
API WordPress cung cấp một loạt công cụ để dễ dàng truy vấn hoặc lưu dữ liệu, cập nhật. Nó hữu ích để xử lý các mẩu thông tin nhỏ mà các cơ chế khác mà WordPress cung cấp (như bài đăng hoặc phân loại) quá phức tạp.
api thêm và xóa thiết lập:
- add_option()
- delete_option()
- add_site_option()
- delete_site_option()
api truy vấn và cập nhật thiết lập:
- get_option()
- update_option()
- get_site_option()
- update_site_option()
Ví dụ: nếu chúng tôi muốn lưu trữ tên website là gì.
WordPress không chỉ cung cấp cho chúng tôi các chức năng để xử lý chúng mà còn cho phép chúng tôi thực hiện điều đó một cách hiệu quả nhất.
Một số phương pháp thậm chí còn được tải trực tiếp khi hệ thống khởi động , do đó giúp chúng tôi truy cập nhanh hơn (khi tạo phương pháp mới, chúng tôi cần cân nhắc xem có muốn autoload tùy chọn đó hay không).
Ví dụ: hãy xem xét một website mà trên đó chúng tôi có một băng chuyền hiển thị các tin mới được chỉ định. code mẫu đầu tiên của chúng mình là sử dụng khóa meta như sau:
// functions.php
add_action( 'save_post', function ( $post_id ) {
// For simplicity, we do not include all the required validation before saving
// the meta key: checking nonces, checking post type and status, checking
// it is not a revision or an autosaving, etc.
update_post_meta( $post_id, 'is_breaking_news', ! empty ( $_POST['is_breaking_news'] ) );
} );
// front-page.php
$query = new WP_Query( array(
'posts_per_page' => 1,
'meta_key' => 'is_breaking_news'
)
);
$breaking_news = $query->posts[0] ?: NULL;
Như bạn có thể thấy, cách tiếp cận này rất đơn giản, nhưng nó chưa tối ưu cho hiệu suất. Nó sẽ thực hiện truy vấn cơ sở dữ liệu để cố gắng tìm một bài đăng có khóa meta cụ thể. Chúng ta có thể sử dụng một phương pháp để đạt được kết quả tương tự nhưng hiệu suất nhanh hơn nhiều:
// functions.php
add_action( 'save_post', function ( $post_id ) {
// Same comment for post validation
if ( ! empty ( $_POST['is_breaking_news'] ) )
update_option( 'breaking_news_id', $post_id );
} );
// front-page.php
if ( $breaking_news_id = get_option( 'breaking_news_id' ) ){
$breaking_news = get_post( $breaking_news_id );
}else{
$breaking_news = NULL;
}
Chức năng hơi thay đổi từ ví dụ này sang ví dụ khác.
Trong cách viết đầu tiên, chúng tôi sẽ luôn nhận được những bài viết mới nhất, về ngày xuất bản của bài đăng.
Trong cách viết thứ hai, mỗi khi một bài đăng mới được đặt là tin mới, nó sẽ ghi đè lên tin mới trước đó.
Nhưng bởi vì chúng tôi có thể muốn một bài đăng tin tức nóng hổi tại một thời điểm, nên đó không phải là vấn đề.
Và cuối cùng, chúng tôi đã thay đổi một truy vấn cơ sở dữ liệu nặng nề (sử dụng class WP_Query
với các khóa meta) thành một truy vấn trực tiếp và đơn giản (hàm get_post()
), đây là cách tiếp cận tốt hơn và cho tốc độ hiệu quả hơn.
Chúng tôi cũng có thể thực hiện một thay đổi nhỏ và sử dụng transient.
Hoạt động tương tự nhưng cho phép chúng tôi chỉ định thời gian hết hạn.
Ví dụ: đối với bài viết mới nhất, nó vừa vặn như một chiếc găng tay vì chúng tôi không muốn một bài đăng cũ là tin tức nóng hổi và nếu chúng tôi giao nhiệm vụ thay đổi hoặc loại bỏ tin tức nóng hổi đó cho quản trị viên, [các] anh ấy có thể quên làm Nó. Vì vậy, với hai thay đổi đơn giản, chúng tôi đã thêm ngày hết hạn:
// functions.php
add_action( 'save_post', function ( $post_id ) {
// Same comment for post validation
// Let's say we want that breaking news for one hour
// (3600 = # of seconds in an hour).
if ( ! empty ( $_POST['is_breaking_news'] ) )
set_transient( 'breaking_news_id', $post_id, 3600 );
} );
// front-page.php
if ( $breaking_news_id = get_transient( 'breaking_news_id' ) )
$breaking_news = get_post( $breaking_news_id );
else
$breaking_news = NULL;
Sử dụng object cache
Object Caching liên quan đến việc lưu trữ các kết quả truy vấn cơ sở dữ liệu (sql) để lần sau khi cần kết quả, nó có thể được phục vụ từ bộ nhớ đệm mà không cần phải truy vấn tìm kiếm lại cơ sở dữ liệu.
vì sao object cache ngon thế mà WordPress không kích hoạt mặc định?
bản thân WordPress cũng có cơ chế object cache gọi là WP Object Cache hàng giả lập nhược điểm của nó rất lớn là tái sử dụng kém, vì vậy cần phải hệ thống object cache bên thứ 3 là redis hoặc memcached. Việc triển khai redis hoặc memcached thì phải yêu cầu từ cấp webserver, redis và memcached không sử dụng ngôn ngữ lập trình PHP mà được viết bằng C, vì vậy phải thiết lập từ cấp webserver rồi kết nối với WordPress thì mới sử dụng được.
Triển khai redis hoặc memcached yêu cầu lượng ram phải dư giả thì mới có thể triển khai hiệu quả được.
bạn có thể tìm hiểu sâu hơn về object cache thì hãy tham khảo ở bài viết này:
Object Caching là gì, hoạt động như thế nào với WordPress
CPU đơn nhân mạnh
Việc truy vấn database muốn nhanh thì rất cần có đơn nhân của một con CPU đời mới xung nhịp cao thì tốc độ truy vấn sẽ nhanh hơn.
Tối ưu hóa database
hãy dọn dẹp tối ưu database dọn rác những dữ liệu dư thừa không sử dụng và đánh index sẽ giúp cải thiện rất nhiều trong việc truy vấn database của bạn.
Dọn dẹp tối ưu cơ sở dữ liệu WordPress
Phá bỏ quan niệm rằng WordPress phải chậm
Đây chỉ là một vài lời khuyên mà các dev nên cân nhắc khi viết code cho WordPress .
Từ những điều trên, rõ ràng nguyên nhân gốc rễ của hiệu suất kém trong WordPress là code không được hiệu quả. Tuy nhiên, WordPress cung cấp tất cả các chức năng cần thiết thông qua các API khác nhau có thể giúp chúng tôi xây dựng các plugin và themes hiệu quả hơn nhiều mà không ảnh hưởng đến tốc độ của toàn bộ nền tảng.
Một số tip chia sẻ này để tối ưu hóa truy vấn database, hy vọng những tip này sẽ giúp website của bạn cải thiện tốc độ tốt hơn đặc biệt hữu ích với những website có database lớn.
Để lại một bình luận