26 β Performance va keshlash¶
β¬ οΈ Oldingi: 25 β Testlash: PHPUnit, wp-env, PHPCS/WPCS Β· π README Β· Keyingi: 27 β WooCommerce kengaytirish (HPOS) β‘οΈ
Bu bobda: plugin nima uchun saytni sekinlashtirishini (ko'p va og'ir so'rovlar, keshlanmagan tashqi chaqiruvlar, shishib ketgan autoload) tushunib,
WP_Queryniposts_per_page,no_found_rows,fields => 'ids'vaupdate_post_*_cachebilan optimallashtirishni;meta_queryo'rniga taxonomiya ishlatishni; object cache (wp_cache_get/wp_cache_set/wp_cache_delete) va uning transient'dan farqini; autoloaded options yukini vaadd_option(..., autoload: false)ni; tsikldagi N+1 so'rovlardan qochishni vapre_get_postsbilan asosiy query'ni o'zgartirishni; hamda Query Monitor vaSAVEQUERIESbilan o'lchashni o'rganamiz.
Muammo: plugin saytni nega sekinlashtiradi?¶
"Kitoblar katalogi" plugin'ingiz mahalliy testda bir zumda ishladi: 10 ta kitob, hammasi tez. Lekin mijoz saytida 8000 ta kitob, 50000 ta ko'rish yozuvi va og'ir trafik bor. Endi katalog sahifasi 4 sekundda ochiladi, admin panel sekin, hosting "siz juda ko'p MySQL so'rov yuborayapsiz" deb ogohlantiradi.
Plugin sekinligi deyarli har doim shu uch sababdan biri:
- Juda ko'p yoki og'ir so'rovlar β
posts_per_page => -1bilan butun jadvalni tortib olish, tsiklda har element uchun yangi so'rov (N+1), indekslanmaganmeta_query. - Keshlanmagan takroriy ish β har sahifa yuklanishida bir xil og'ir hisoblashni yoki bir xil tashqi API chaqiruvini qayta-qayta bajarish.
- Autoload shishishi β katta yoki kam kerak bo'ladigan ma'lumotni
autoload = truebilan saqlash, natijada u har so'rovda xotiraga yuklanadi.
π Oltin qoida: tezlikni "his qilib" emas, o'lchab tuzating. Avval Query Monitor (pastda) bilan qaysi so'rov sekin ekanini toping, keyin aynan shuni optimallashtiring. Ko'r-ko'rona "optimizatsiya" β vaqt isrofi.
βΉοΈ Bu bob 10-bobning ($wpdb, options, transients) va 07-08-boblarning (CPT, taxonomiya) davomi β o'sha mexanizmlarni endi tez ishlatamiz.
WP_Query ni optimallashtirish¶
WP_Query (07-bob) qulay, lekin default sozlamalari ko'p ortiqcha ish bajaradi: har so'rovda topilgan qatorlar umumiy sonini hisoblaydi (paginatsiya uchun), barcha postlarning meta va term keshini oldindan to'ldiradi. Agar sizga bularning hammasi kerak bo'lmasa β o'chiring.
Eng muhim performance argumentlari¶
Quyidagi argumentlarning hammasi WP_Query (va get_posts()) tomonidan rasmiy qo'llab-quvvatlanadi:
| Argument | Default | Nima qiladi | Qachon o'zgartiring |
|---|---|---|---|
posts_per_page |
10 |
Nechta post olinadi | Har doim aniq cheklang; -1 dan saqlaning |
no_found_rows |
false |
true β umumiy qatorlar sonini hisoblamaydi |
Paginatsiya kerak emas bo'lsa true |
fields |
'all' |
'ids' β faqat ID massivi; 'id=>parent' |
Faqat ID kerak bo'lsa 'ids' |
update_post_meta_cache |
true |
Postlar meta keshini oldindan to'ldiradi | Meta o'qimasangiz false |
update_post_term_cache |
true |
Postlar term keshini oldindan to'ldiradi | Term o'qimasangiz false |
ignore_sticky_posts |
false |
true β yopishtirilgan postlarni aralashtirmaydi |
O'z ro'yxatlaringizda true |
cache_results |
true |
Post natijalarini object cache'ga yozadi | Odatda true qoldiring |
β οΈ posts_per_page => -1 xavfli. U "hamma postni ol" degani. 10 ta postda muammo yo'q, lekin 8000 ta kitobda bu butun jadvalni xotiraga tortadi β sayt qotadi yoki PHP xotira limiti tugaydi. Har doim mantiqiy chegara qo'ying (masalan 100); haqiqatan ko'p kerak bo'lsa, paginatsiya yoki partiyalab (batch) o'qing.
Yaxshi vs yomon β bir misol¶
// β YOMON: hamma kitobni, meta va term keshi bilan, qatorlar sonini hisoblab oladi
$query = new \WP_Query( [
'post_type' => 'kitob',
'posts_per_page' => -1, // butun jadval β xavfli
'meta_query' => [ // indekslanmagan postmeta JOIN β sekin
[ 'key' => 'tavsiya', 'value' => '1' ],
],
] );
// β
YAXSHI: faqat ID kerak (havola yasash uchun), paginatsiya kerak emas
$ids = ( new \WP_Query( [
'post_type' => 'kitob',
'post_status' => 'publish',
'posts_per_page' => 12, // aniq cheklov
'no_found_rows' => true, // paginatsiya kerak emas -> sonni hisoblamaymiz
'fields' => 'ids', // faqat ID massivi -> yengil
'update_post_meta_cache' => false, // meta o'qimaymiz
'update_post_term_cache' => false, // term o'qimaymiz
'ignore_sticky_posts' => true, // o'z ro'yxatimiz, sticky aralashmasin
'tax_query' => [ // meta_query o'rniga taxonomiya (indeksli)
[ 'taxonomy' => 'janr', 'field' => 'slug', 'terms' => 'ilmiy' ],
],
] ) )->posts; // 'fields' => 'ids' bilan ->posts endi ID massivi
π‘ no_found_rows => true SQL_CALC_FOUND_ROWS (yoki uning o'rnini bosuvchi qo'shimcha COUNT so'rovini) o'chiradi. Bu β "Sahifa 1 / 47" kabi paginatsiya kerak bo'lmagan har bir ro'yxat (sidebar, widget, "tegishli kitoblar") uchun bepul tezlik.
meta_query nega sekin β taxonomiyani afzal ko'ring¶
wp_postmeta jadvali kalit-qiymat (10-bob): har post uchun ko'p qator, meta_value ustuni indekslanmagan (uzun matn). meta_query bilan filtrlash wp_postmeta ga JOIN qiladi va indekssiz qatorlarni skanlaydi β minglab postda bu juda sekin.
π Agar biror maydon bo'yicha tez-tez filtrlasangiz (masalan "ilmiy" kitoblar), uni meta emas, taxonomiya qiling (08-bob). Taxonomiya term_taxonomy_id orqali indeksli JOIN qiladi β meta_query dan ancha tez. Meta'ni faqat post bilan birga ko'rsatish uchun saqlang, filtrlash/saralash uchun emas.
β οΈ 'orderby' => 'meta_value' (yoki meta_value_num) ayniqsa og'ir β u JOIN ustiga saralash qo'shadi. Mumkin bo'lsa, saralanadigan qiymatni taxonomiya yoki o'z jadval ustuni (10-bob) qiling.
pre_get_posts: asosiy query'ni to'g'ri o'zgartirish¶
Ko'pincha katalog sahifasida post_type=kitob chiqishini yoki "har sahifada 12 ta" bo'lishini xohlaysiz. Yangi WP_Query yaratish β ikkinchi so'rov degani (asosiy so'rov baribir ishlaydi). To'g'ri yo'l β WordPress allaqachon bajaradigan asosiy so'rovni pre_get_posts action'i bilan o'zgartirish.
pre_get_posts β so'rov o'zgaruvchilari yaratilgandan keyin, lekin so'rov bajarilishidan oldin ishlaydigan action. Callback'ga WP_Query obyekti havola orqali (&) keladi β uni o'zgartirsangiz, asosiy so'rov o'zgaradi.
add_action( 'pre_get_posts', function ( \WP_Query $query ): void {
// MUHIM: faqat front-end ASOSIY query'ni o'zgartiramiz
if ( is_admin() || ! $query->is_main_query() ) {
return; // admin so'rovlariga va ikkilamchi WP_Query'larga tegmaymiz
}
// Masalan, janr arxivida har sahifada 12 ta kitob ko'rsatamiz
if ( $query->is_tax( 'janr' ) ) {
$query->set( 'posts_per_page', 12 );
$query->set( 'ignore_sticky_posts', true );
}
} );
β οΈ is_admin() va is_main_query() tekshiruvi SHART. Ularni unutsangiz, admin paneldagi ro'yxatlarni yoki har bir ikkilamchi WP_Query ni ham o'zgartirib, saytni buzasiz. Va global is_main_query() o'rniga passed obyekt metodini ishlating β $query->is_main_query() β chunki global versiya $wp_query ga tekshiradi, qo'lingizdagi obyektga emas.
π‘ pre_get_posts ikki foydadan beradi: (1) ikkinchi so'rovni yo'q qiladi (asosiy so'rovni qayta ishlatasiz), (2) WordPress paginatsiya, is_paged() va shablon mantiqini avtomatik to'g'ri tutadi.
Tsiklda so'rov qilmang: N+1 muammosi¶
Eng yashirin sekinlik manbai β N+1 so'rov: bitta so'rov bilan N ta element olasiz, keyin har bir element uchun yana bir so'rov qilasiz. 100 ta kitob = 1 + 100 = 101 so'rov.
// β YOMON: N+1 β har kitob uchun alohida meta so'rovi
$kitoblar = get_posts( [ 'post_type' => 'kitob', 'posts_per_page' => 100 ] );
foreach ( $kitoblar as $kitob ) {
// Har iteratsiyada DB ga boradi (agar meta keshi to'ldirilmagan bo'lsa)
$isbn = get_post_meta( $kitob->ID, 'isbn', true );
echo esc_html( $isbn );
}
π Yechim β meta'ni oldindan keshlash. WP_Query/get_posts default update_post_meta_cache => true bilan bitta so'rovda barcha postlarning meta'sini oldindan oladi. Keyin get_post_meta() keshdan o'qiydi β qo'shimcha so'rov yo'q. Demak yuqoridagi misolda agar update_post_meta_cache ni false qilib qo'ymagan bo'lsangiz, meta aslida keshdan keladi. Asosiy xato β uni false qilib, keyin tsiklda get_post_meta chaqirish:
// β ENG YOMON: meta keshini o'chirib, keyin tsiklda meta so'rash -> haqiqiy N+1
$kitoblar = get_posts( [
'post_type' => 'kitob',
'posts_per_page' => 100,
'update_post_meta_cache' => false, // keshni o'chirdik...
] );
foreach ( $kitoblar as $kitob ) {
$isbn = get_post_meta( $kitob->ID, 'isbn', true ); // ...endi har biri DB so'rovi!
}
π‘ Qoida sodda: agar tsiklda meta o'qisangiz, meta keshini O'CHIRMANG (update_post_meta_cache ni true qoldiring). Agar meta o'qimasangiz β false qilib tezlashtiring. Ikkalasini bir vaqtda noto'g'ri kombinatsiya qilmang.
β οΈ Xuddi shu mantiq term'lar uchun: tsiklda get_the_terms()/wp_get_post_terms() chaqirsangiz, update_post_term_cache ni true qoldiring; aks holda har post uchun alohida term so'rovi ketadi. Agar bir nechta ID ro'yxati uchun meta kerak bo'lsa, update_meta_cache( 'post', $ids ) bilan ham qo'lda oldindan to'ldira olasiz.
Object cache: wp_cache_*¶
10-bobda transient'ni ko'rdik. Object cache β uning "ukasi": so'rov davomida (yoki persistent backend bo'lsa, so'rovlararo) ma'lumotni xotirada saqlaydi. WordPress yadrosi uni keng ishlatadi (postlar, meta, options shu yerda keshlanadi).
namespace Oqil\KitobKatalog;
class Stats {
const CACHE_GROUP = 'kitoblar_katalogi';
public static function kitob_korishlari( int $kitob_id ): int {
$kalit = 'korishlar_' . $kitob_id;
// 1) Object cache'dan o'qish. $found havola orqali HIT/MISS ni aniq aytadi.
$soni = wp_cache_get( $kalit, self::CACHE_GROUP, false, $found );
if ( $found ) {
return (int) $soni; // KESH HIT β DB ga bormaymiz
}
// 2) MISS -> og'ir so'rov (10-bobdagi o'z jadval)
global $wpdb;
$jadval = $wpdb->prefix . 'kitob_korishlar';
$soni = (int) $wpdb->get_var(
$wpdb->prepare(
"SELECT COUNT(*) FROM {$jadval} WHERE kitob_id = %d",
$kitob_id
)
);
// 3) Keshga yozamiz (so'rov davomida; persistent backend bo'lsa uzoqroq)
wp_cache_set( $kalit, $soni, self::CACHE_GROUP, HOUR_IN_SECONDS );
return $soni;
}
// Yangi ko'rish yozilganda keshni bekor qilamiz
public static function keshni_tozala( int $kitob_id ): void {
wp_cache_delete( 'korishlar_' . $kitob_id, self::CACHE_GROUP );
}
}
π Funksiya imzolari (rasmiy hujjat):
wp_cache_get( int|string $key, string $group = '', bool $force = false, bool|null &$found = null ): mixed|falsewp_cache_set( int|string $key, mixed $data, string $group = '', int $expire = 0 ): boolwp_cache_delete( int|string $key, string $group = '' ): bool
π‘ $found parametrini ishlating. wp_cache_get MISS'da false qaytaradi β lekin keshlangan haqiqiy qiymat ham false, 0 yoki '' bo'lishi mumkin. To'rtinchi $found (havola) MISS'ni "qiymat aslida false" dan ajratadi. Bu β transient'dagi false !== $kesh muammosining object cache versiyasi.
β οΈ $group doim bering (masalan plugin slug'i). Guruh β kesh "papkasi"; u nomlar to'qnashuvining oldini oladi (sizning korishlar_5 boshqa plugin'ning korishlar_5 bilan aralashmaydi).
Object cache va Transient farqi¶
Object cache (wp_cache_*) |
Transient (set_transient) |
|
|---|---|---|
| Doimiylik | Persistent backend (Redis/Memcached) bo'lsagina so'rovlararo qoladi; aks holda faqat shu so'rov | Har doim doimiy (object cache bo'lmasa wp_options'da) |
| Qayerda | Xotirada (yoki backend) | wp_options (yoki object cache bo'lsa o'sha yerda) |
| Muddat | $expire (persistent backend uchun) |
$expiration har doim hurmat qilinadi |
| Qachon | Bitta so'rovda takror hisoblash; yadro/meta keshi | Og'ir hisob/tashqi API natijasini soatlarga saqlash |
βΉοΈ Qoidaning ixchami: natija server qayta ishga tushsa ham qolishi kerak bo'lsa (tashqi API, og'ir hisob) β transient. Natija shu bitta so'rov ichida bir necha marta kerak bo'lsa β wp_cache_*. Redis/Memcached o'rnatilgan saytda ikkalasi ham xotiradan ishlaydi va transient avtomatik tezlashadi β kodingiz o'zgarmaydi.
Tashqi chaqiruvlarni keshlang¶
18-bobda wp_remote_get() bilan tashqi HTTP so'rovni ko'rdik. Har sahifa yuklanishida tashqi API ga chiqish β falokat: tarmoq sekinligi sizning saytingiz sekinligiga aylanadi, API tushsa β sizning sahifangiz ham qotadi.
function kitoblar_katalogi_valyuta_kursi(): ?float {
$kalit = 'kitoblar_katalogi_usd_kurs';
$kesh = get_transient( $kalit );
if ( false !== $kesh ) {
return (float) $kesh; // KESH HIT β tashqi API ga bormaymiz
}
// MISS -> tashqi API (18-bob). Timeout qo'ying, aks holda sahifa kutib qoladi.
$javob = wp_remote_get( 'https://example.com/api/usd', [ 'timeout' => 5 ] );
if ( is_wp_error( $javob ) || 200 !== wp_remote_retrieve_response_code( $javob ) ) {
// Xato bo'lsa qisqa muddatga keshlab, API ni "tinch qo'yamiz" (negative cache)
set_transient( $kalit, '0', 5 * MINUTE_IN_SECONDS );
return null;
}
$kurs = (float) wp_remote_retrieve_body( $javob );
set_transient( $kalit, $kurs, 6 * HOUR_IN_SECONDS ); // 6 soatga keshlaymiz
return $kurs;
}
β οΈ Halol eslatma: wp_remote_get natijasi va kesh xatti-harakati faqat ishlab turgan saytda ko'rinadi. Yuqoridagi kod docs'ga mos va sintaktik to'g'ri; uni o'z saytingizda Query Monitor'ning "HTTP API Calls" panelida kuzating β kesh ishlasa, ikkinchi yuklashda chaqiruv yo'qoladi.
Autoloaded options: yashirin yuk¶
Bu β 10-bobda tegib o'tgan, lekin chuqurroq tushuntirishga arzigulik mavzu. WordPress har sahifa so'rovida wp_load_alloptions() orqali autoload = 'yes' belgilangan barcha options'ni bitta so'rovda o'qib, xotiraga oladi. Bu tez-tez kerak bo'ladigan kichik sozlamalar uchun zo'r β har bittasi uchun alohida so'rov ketmaydi.
β οΈ Muammo: agar siz katta (keshlangan API javobi, import log, uzun ro'yxat) yoki kam kerak bo'ladigan ma'lumotni autoload = true bilan saqlasangiz, u har so'rovda β hatto kerak bo'lmasa ham β xotiraga yuklanadi. alloptions shishadi (megabaytlarga yetishi mumkin), har sahifa sekinlashadi. Bu β keng tarqalgan, ammo ko'rinmas performance muammosi.
// β
Tez-tez kerak, kichik -> autoload: true (default heuristika ham buni tanlaydi)
update_option( 'kitoblar_katalogi_sozlamalar', $kichik_sozlamalar, true );
// β
Katta yoki kam kerak -> autoload: false (faqat get_option chaqirilganda yuklanadi)
add_option( 'kitoblar_katalogi_import_log', $katta_log, '', false );
π add_option imzosi (rasmiy hujjat): add_option( string $option, mixed $value = '', string $deprecated = '', bool|null $autoload = null ). To'rtinchi argument $autoload:
trueβ har so'rovda yukla (kichik, tez-tez kerak uchun).falseβ faqatget_option()chaqirilganda yukla (katta/kam kerak uchun).nullβ WordPress o'zi heuristika bilan qaror qilsin (6.7.0 dan yangi standart).
β οΈ Eski 'yes'/'no' string qiymatlari 6.7.0 dan eskirgan β bool|null ishlating.
π‘ Tekshirish. O'z saytingizda autoload yukini WP-CLI bilan o'lchang:
# Eng katta autoloaded option'larni topish (o'z saytingizda)
wp option list --fields=option_name,size_bytes --autoload=on --orderby=size_bytes --order=desc | head -20
Agar plugin'ingizning katta option'i ro'yxat tepasida bo'lsa β uni autoload = false ga o'tkazing. Mavjud option uchun (WP 6.7+): wp_set_option_autoload( 'nom', false ) yoki o'sha qiymatni add_option bilan qayta yozing.
O'lchash: Query Monitor va SAVEQUERIES¶
"O'lchamasdan optimallashtirma." WordPress'da bunda ikki asosiy asbob bor.
SAVEQUERIES¶
wp-config.php ga (faqat dev muhitda β produksiyada emas, u sekinlashtiradi va xotira yeydi) quyidagini qo'shsangiz, WordPress har so'rovni, uning vaqtini va chaqiruv stekini $wpdb->queries ga yozadi:
// wp-config.php (faqat development) β har SQL so'rovni qayd qiladi
define( 'SAVEQUERIES', true );
// So'rovlarni dasturiy ko'rish (masalan footer'da, faqat dev'da)
add_action( 'wp_footer', function (): void {
if ( ! defined( 'SAVEQUERIES' ) || ! SAVEQUERIES || ! current_user_can( 'manage_options' ) ) {
return;
}
global $wpdb;
error_log( 'Jami so\'rovlar: ' . count( $wpdb->queries ) );
// Har element: [0] => SQL, [1] => vaqt (sekund), [2] => chaqiruv steki
}, 999 );
Query Monitor plugin¶
Amalda eng qulay asbob β Query Monitor (bepul plugin, o'z saytingizga o'rnatasiz). U admin panelda har so'rovni, uning vaqtini, qaysi plugin/funksiya chaqirganini, sekin so'rovlarni (qizil bilan), HTTP API chaqiruvlarini, object cache HIT/MISS statistikasini va PHP xatolarni ko'rsatadi.
π Ish jarayoni:
- Query Monitor'ni o'rnating, katalog sahifasini oching.
- "Queries by Component" da sizning plugin'ingiz nechta so'rov qilganini ko'ring.
- Sekin (qizil) so'rovni toping β odatda
meta_queryyoki keshlanmagan takror. - Yuqoridagi usullar bilan tuzating:
tax_query,no_found_rows,fields => 'ids', transient/object cache. - Sahifani qayta oching β so'rovlar soni va vaqti kamayganini tasdiqlang.
β οΈ Halol eslatma: jonli profiling raqamlari (so'rovlar soni, millisekundlar, kesh HIT nisbati) faqat ishlab turgan saytda ko'rinadi β bu bobdagi raqamlar illyustrativ. Query Monitor'ni o'z saytingizda ishlatib, oldin/keyin ni o'zingiz o'lchang. Maqsad β aniq son emas, tendentsiya: so'rovlar kamaydimi, sekin so'rovlar yo'qoldimi.
π‘ WP_DEBUG, WP_DEBUG_LOG (02-bob) bilan birga Query Monitor β plugin tezligini tushunishning eng tez yo'li. Optimallashtirishni har doim shu o'lchovdan boshlang.
Birga qo'yamiz: tezlik nazorat ro'yxati¶
"Kitoblar katalogi" plugin'i endi har bir performance nuqtasini hisobga oladi:
WP_Queryβposts_per_pagecheklangan, paginatsiya kerakmas joydano_found_rows => true, faqat ID kerak joydafields => 'ids', meta/term o'qilmasa keshlarfalse.- Filtrlash β
meta_queryo'rnigajanrtaxonomiyasi (indeksli). - Asosiy query β yangi
WP_Queryemas,pre_get_posts(is_admin/is_main_querytekshiruvi bilan). - N+1 yo'q β tsiklda so'rov qilinmaydi; meta keshi to'g'ri ishlatiladi.
- Keshlash β og'ir hisob va tashqi API transient bilan, so'rov-ichi takror
wp_cache_*bilan. - Autoload β katta/kam kerak option'lar
autoload: false. - O'lchov β Query Monitor +
SAVEQUERIESbilan oldin/keyin tasdiqlangan.
Keyingi bobda WooCommerce'ni (HPOS β High-Performance Order Storage) kengaytiramiz; u o'z jadval va so'rov optimizatsiyasi g'oyalarini katta miqyosda qo'llaydi.
26-bob mashqlari¶
Oson¶
- (Oson)
WP_Queryargumentlaridan qaysilari performance'ga ta'sir qiladi va nima qiladi:no_found_rows,fields,update_post_meta_cache? Har biriga bir jumla. - (Oson) Nega
'posts_per_page' => -1xavfli? Uning o'rniga nima qilasiz? - (Oson) Paginatsiya kerak bo'lmagan "tegishli kitoblar" ro'yxati uchun qaysi bitta argumentni
trueqilasiz va nega? - (Oson) Faqat kitob ID'lari kerak bo'lsa (havola yasash uchun), qaysi argumentni qanday qiymat bilan berasiz? Natijada
->postsnima qaytaradi? - (Oson) Object cache (
wp_cache_*) va transient (set_transient) orasidagi asosiy farqni (doimiylik bo'yicha) bir jumlada ayting. - (Oson)
define( 'SAVEQUERIES', true )ni qayerga qo'yasiz va nega faqat development muhitida?
O'rta¶
- (O'rta)
meta_querynegatax_querydan sekin? Qachon meta'ni taxonomiyaga aylantirish kerak? -
(O'rta)
pre_get_postscallback'ida negais_admin()va$query->is_main_query()tekshiruvi shart? Birini unutsa nima bo'ladi?Yechim
pre_get_postssaytning har so'rovida (front-end ham, admin ham) va harWP_Querynamunasida ishlaydi. Agaris_admin()ni tekshirmasangiz, admin paneldagi postlar ro'yxati so'rovini ham o'zgartirib qo'yasiz (masalan admin'da kitoblar yo'qolib qoladi). Agar$query->is_main_query()ni tekshirmasangiz, sahifadagi har bir ikkilamchiWP_Query(widget, "tegishli postlar") ham o'zgaradi. Ikkalasi birga faqat front-end asosiy so'rovni ajratadi. Globalis_main_query()emas, passed obyekt metodini ($query->is_main_query()) ishlatish kerak β global versiya$wp_queryga tekshiradi, qo'lingizdagi obyektga emas. -
(O'rta) N+1 so'rov nima? Tsiklda
get_post_metachaqiradigan kod qachon N+1 ga aylanadi va qachon aylanmaydi?Yechim
N+1 β bitta so'rov bilan N ta element olib, keyin har biri uchun yana bir so'rov qilish (jami 1 + N).
WP_Query/get_postsdefaultupdate_post_meta_cache => truebilan barcha postlar meta'sini bitta so'rovda oldindan keshlaydi β shu holatda tsikldagiget_post_meta()keshdan o'qiydi, qo'shimcha so'rov yo'q. Lekin agarupdate_post_meta_cache => falseqilib qo'yib, keyin tsikldaget_post_meta()chaqirsangiz β har iteratsiya alohida DB so'rovi bo'ladi (haqiqiy N+1). Qoida: tsiklda meta o'qisangiz meta keshini o'chirmang; o'qimasangiz βfalseqilib tezlashtiring. -
(O'rta)
wp_cache_getning to'rtinchi$foundparametri nima uchun kerak?if ( $natija )bilan tekshirsangiz qanday qiymatlar muammo keltiradi?Yechim
wp_cache_getMISS'dafalseqaytaradi, lekin keshlangan haqiqiy qiymat hamfalse,0yoki''bo'lishi mumkin.if ( $natija )bularni "yo'q" deb hisoblab, har safar og'ir so'rovni qayta bajaradi (kesh ishlamaydi). To'rtinchi$found(havola orqali) MISS'ni qiymatdan aniq ajratadi:Bu β transient'dagi$soni = wp_cache_get( 'korishlar_5', 'kitoblar_katalogi', false, $found ); if ( $found ) { return (int) $soni; // hatto $soni === 0 bo'lsa ham HIT } // MISS -> hisobla va wp_cache_set bilan keshlafalse !== $keshmantiqining object cache analogi. -
(O'rta) Autoloaded options nima va
alloptionsshishishi nima uchun saytni sekinlashtiradi? Katta option'ni qanday saqlaysiz? - (O'rta) Query Monitor'da plugin'ingiz qaysi so'rovlarni qilganini va sekin so'rovni qanday topasiz? Optimallashtirishdan oldin nega o'lchash kerak?
Qiyin¶
-
(Qiyin) Bosh sahifadagi "Tavsiya etilgan kitoblar" ro'yxatini (faqat sarlavha + havola, paginatsiya kerak emas, "tavsiya" janri) optimal
WP_Querybilan yozing: cheklangan,no_found_rows, meta/term keshsiz, taxonomiya bilan.Yechim
namespace Oqil\KitobKatalog; function tavsiya_kitoblar( int $limit = 6 ): array { $query = new \WP_Query( [ 'post_type' => 'kitob', 'post_status' => 'publish', 'posts_per_page' => $limit, // aniq cheklov, -1 emas 'no_found_rows' => true, // paginatsiya kerak emas 'update_post_meta_cache' => false, // meta o'qimaymiz (faqat sarlavha) 'update_post_term_cache' => false, // term o'qimaymiz 'ignore_sticky_posts' => true, // o'z ro'yxatimiz 'tax_query' => [ // meta_query emas β indeksli taxonomiya [ 'taxonomy' => 'janr', 'field' => 'slug', 'terms' => 'tavsiya' ], ], ] ); $natija = []; foreach ( $query->posts as $post ) { $natija[] = [ 'title' => get_the_title( $post ), 'url' => get_permalink( $post ), ]; } return $natija; }Tushuntirish:
posts_per_page => $limitbutun jadvalni tortmaydi.no_found_rows => trueumumiy son hisobini o'chiradi (paginatsiya yo'q).update_post_meta_cache/update_post_term_cache => falseβ biz faqat sarlavha va havola olamiz, demak meta/term keshini oldindan to'ldirish ortiqcha.tax_queryjanrtaxonomiyasini indeksliJOINbilan ishlatadi βmeta_querydan tez.ignore_sticky_posts => trueo'z ro'yxatimizga yopishtirilgan postlar aralashmasligi uchun. -
(Qiyin) Kitob ko'rishlari sonini qaytaradigan funksiyani object cache (
wp_cache_*) bilan yozing: avval keshdan ($foundbilan), MISS'da$wpdbso'rovi, so'ng keshga yozish; va yangi ko'rish yozilganda keshni tozalovchi funksiya.Yechim
namespace Oqil\KitobKatalog; const CACHE_GROUP = 'kitoblar_katalogi'; function kitob_korishlari( int $kitob_id ): int { $kalit = 'korishlar_' . $kitob_id; $soni = wp_cache_get( $kalit, CACHE_GROUP, false, $found ); if ( $found ) { return (int) $soni; // HIT (0 bo'lsa ham hurmat qilinadi) } global $wpdb; $jadval = $wpdb->prefix . 'kitob_korishlar'; $soni = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$jadval} WHERE kitob_id = %d", $kitob_id ) ); wp_cache_set( $kalit, $soni, CACHE_GROUP, HOUR_IN_SECONDS ); return $soni; } function korish_yozildi( int $kitob_id ): void { // Yangi ko'rish -> kesh eskirdi, bekor qilamiz (keyingi so'rov qayta hisoblaydi) wp_cache_delete( 'korishlar_' . $kitob_id, CACHE_GROUP ); }Tushuntirish:
$found(havola) MISS'ni "qiymat 0" dan ajratadi.$group(kitoblar_katalogi) nomlar to'qnashuvining oldini oladi.wp_cache_setning$expire(HOUR_IN_SECONDS) faqat persistent backend (Redis/Memcached) bo'lsa ahamiyatli β backend bo'lmasa kesh shu so'rov oxirida yo'qoladi (bu ham foydali: bitta so'rovda takror chaqiruvni tejaydi).wp_cache_deleteβ cache invalidation: ma'lumot o'zgarganda eski keshni bekor qiladi. -
(Qiyin)
pre_get_postsbilankitobarxiv sahifasini optimallashtirib o'zgartiring: har sahifada 12 ta, faqatpublish, sticky aralashmasin β lekin admin va ikkilamchi so'rovlarga tegmasin. Nega yangiWP_Queryo'rnigapre_get_postsafzal?Yechim
add_action( 'pre_get_posts', function ( \WP_Query $query ): void { // Faqat front-end asosiy so'rov if ( is_admin() || ! $query->is_main_query() ) { return; } // Faqat kitob CPT arxivi if ( $query->is_post_type_archive( 'kitob' ) ) { $query->set( 'posts_per_page', 12 ); $query->set( 'post_status', 'publish' ); $query->set( 'ignore_sticky_posts', true ); } } );Tushuntirish:
pre_get_postsWordPress allaqachon bajaradigan asosiy so'rovni o'zgartiradi β demak ikkinchi (qo'shimcha)WP_Queryso'rovi yaratilmaydi, bitta so'rov tejaladi. Bundan tashqari paginatsiya,is_paged(), shablon ierarxiyasi avtomatik to'g'ri ishlaydi (yangiWP_Queryda bularning hammasini qo'lda boshqarish kerak bo'lardi).is_admin()admin paneldagi ro'yxatni himoya qiladi;$query->is_main_query()ikkilamchi so'rovlarni chetlab o'tadi;is_post_type_archive( 'kitob' )faqat kerakli sahifani nishonlaydi. -
(Qiyin) Tashqi valyuta API'sidan kursni 6 soatga keshlovchi funksiya yozing: transient bilan,
wp_remote_getdatimeout, xato bo'lsa qisqa "negative cache". Nega xatoni ham keshlaymiz?Yechim
function kitoblar_katalogi_kurs(): ?float { $kalit = 'kitoblar_katalogi_usd_kurs'; $kesh = get_transient( $kalit ); if ( false !== $kesh ) { return ( '0' === $kesh ) ? null : (float) $kesh; // HIT (xato ham keshlangan) } $javob = wp_remote_get( 'https://example.com/api/usd', [ 'timeout' => 5 ] ); if ( is_wp_error( $javob ) || 200 !== wp_remote_retrieve_response_code( $javob ) ) { // Negative cache: xatoni 5 daqiqaga saqlaymiz set_transient( $kalit, '0', 5 * MINUTE_IN_SECONDS ); return null; } $kurs = (float) wp_remote_retrieve_body( $javob ); set_transient( $kalit, $kurs, 6 * HOUR_IN_SECONDS ); return $kurs; }Tushuntirish:
false !== $keshHIT/MISS ni to'g'ri ajratadi (10-bob).timeout => 5β API sekin bo'lsa sahifa cheksiz kutib qolmaydi. Negative cache (xatoni qisqa muddatga keshlash) muhim: agar API tushgan bo'lsa, har sahifa yuklanishida unga qayta-qayta urinish (har biri 5 sekund kutib) saytni butunlay sekinlashtiradi. Xatoni 5 daqiqaga keshlab, API ga bosimni kamaytiramiz va saytni himoyalaymiz. Muvaffaqiyatli javob 6 soatga keshlanadi. -
(Qiyin) Plugin'ingiz katta import logini (
autoload = truebilan) saqlab, har sahifani sekinlashtiryapti. Muammoniadd_option/wp_set_option_autoloadbilan tuzating va WP-CLI'da eng katta autoloaded option'larni qanday topishni ko'rsating.Yechim
// β Muammo: katta log autoload = true bilan -> har so'rovda yuklanadi // update_option( 'kitoblar_katalogi_import_log', $katta_log ); // default autoload // β Tuzatish 1: yangi saqlashda autoload = false add_option( 'kitoblar_katalogi_import_log', $katta_log, '', false ); // β Tuzatish 2: mavjud option'ning autoload'ini o'zgartirish (WP 6.7+) wp_set_option_autoload( 'kitoblar_katalogi_import_log', false );# Eng katta autoloaded option'larni topish (o'z saytingizda, WP-CLI) wp option list --fields=option_name,size_bytes --autoload=on --orderby=size_bytes --order=desc | head -20Tushuntirish:
autoload = trueoption'larwp_load_alloptions()orqali har sahifa so'rovida xotiraga yuklanadi. Katta (yoki kam kerak) ma'lumot uchun bu ortiqcha yuk βalloptionsshishadi.autoload = falseesa option faqatget_option()chaqirilganda yuklanadi. Yangi qiymat uchunadd_option(..., autoload: false), mavjud qiymat uchunwp_set_option_autoload()(6.7+). WP-CLI buyrug'i eng og'ir autoloaded option'larni saralab ko'rsatadi β plugin'ingiznikini topib,falsega o'tkazasiz. (Eski'yes'/'no'string'lar 6.7.0 dan eskirgan.) -
(Qiyin) Bir nechta kitob ID ro'yxati uchun meta kerak. N+1'dan qochish uchun meta keshini bitta so'rovda qanday oldindan to'ldirasiz? Kodni yozing va nega bu N+1'dan tez ekanini tushuntiring.
Yechim
function kitoblar_isbnlari( array $kitob_ids ): array { // Bitta so'rovda barcha ID'lar uchun meta keshini to'ldiramiz update_meta_cache( 'post', $kitob_ids ); $natija = []; foreach ( $kitob_ids as $id ) { // Endi get_post_meta keshdan o'qiydi β qo'shimcha so'rov YO'Q $natija[ $id ] = get_post_meta( $id, 'isbn', true ); } return $natija; }Tushuntirish:
update_meta_cache( 'post', $ids )barcha berilgan post'lar meta'sini bittaWHERE post_id IN (...)so'rovida olib, object cache'ga yozadi. So'ng tsikldagi harget_post_meta()chaqiruvi shu keshdan o'qiydi β DB ga bormaydi. N+1'da 100 ta ID = 100 ta so'rov bo'lardi; bu yerda 1 ta so'rov. (WP_QueryID emas, to'liq post olganda buniupdate_post_meta_cache => trueorqali avtomatik qiladi β bu funksiya esa sizda faqat ID ro'yxati bo'lganda foydali.)
β¬ οΈ Oldingi: 25 β Testlash: PHPUnit, wp-env, PHPCS/WPCS Β· π README Β· Keyingi: 27 β WooCommerce kengaytirish (HPOS) β‘οΈ