Prod serverimdə (15 GB-lıq Virtualmin host: nginx, PHP-FPM 8.3, MariaDB 10.11) tək bir Redis demonu işləyir və o, eyni anda yarım onlarla WordPress saytının obyekt keşinə xidmət edir. Redis 2026-05-19-da performans tənzimləməsi zamanı quruldu — eyni vaxtda PHP-FPM-i dynamic-dən ondemand-a keçirdim, InnoDB buffer pool-u 3 GB-a qaldırdım, OPcache-i memory_consumption=256-ya artırdım. Bütün bu işin dürüst nəticəsi belə oldu: əsl ağrı verən yeganə sorğu üçün Redis heç nə etmədi. Aşağıda düşdüyüm tələlər var — hamısı real.
Bir Redis, çoxlu icarədar: prefiks yox, məntiqi DB-lər
Redis 16 məntiqi verilənlər bazası ilə gəlir (0–15), hər biri CLI-də SELECT n və ya -n n ilə ünvanlanır. Bütün saytları prefikslərlə tək keyspace-ə doldurmaq əvəzinə, hər WordPress quruluşuna Till Krüss-ün Redis Object Cache plaginini və wp-config.php-də bir sətir vasitəsilə öz məntiqi DB-sini verirəm:
define( 'WP_REDIS_DATABASE', 2 ); // site-c prod; staging 4-dür və s.
Bu serverdəki xəritə — vacib olduğu üçün yazıb saxlayıram:
| Sayt | WP_REDIS_DATABASE |
|---|---|
| site-a | 0 |
| site-b | 1 |
| site-c (prod) | 2 |
| site-d | 3 |
| staging | 4 |
Demonun özü kiçik və ehtiyatlıdır: maxmemory 512mb, çıxarma siyasəti allkeys-lru. Prefiks yerinə məntiqi DB-lərin səbəbi bir sözdür: FLUSHDB. Ayrı DB-lərlə bir saytın keşini təmizləmək redis-cli -n 4 FLUSHDB-dir və o, fiziki olaraq başqa icarədara toxuna bilmir. Paylaşılan keyspace üstəgəl səhv yazılmış bir FLUSHALL hamını silir. Bu xəritə öz ayağından vurma alətini cərrah bıçağına çevirir.
301 dövrəsi: düzgün verilənlər bazası düzgün sayt demək deyil
Mənə bir axşamımı qurban verən hadisə budur. Produ staging.example.com-a klonladım — eyni server, ayrı Virtualmin domeni, Redis DB 4. Prosedur açıq-aşkar idi:
rsync -a /home/example/public_html/ /home/staging/public_html/
mysqldump --no-tablespaces example_wordpress | mysql staging
wp config set DB_NAME staging
wp config set WP_REDIS_DATABASE 4 --raw
wp search-replace https://example.com https://staging.example.com --skip-columns=guid
# -> 127 əvəzləmə
Sonra https://staging.example.com-u açdım və dərhal 301 ilə proda atıldım. Hər bir sorğuda. Halbuki verilənlər bazası qüsursuz idi — siteurl və home sətirləri https://staging.example.com oxuyurdu, əllə yoxladım.
Problem budur: wp search-replace verilənlər bazasını yeniləyir, amma Redis-ə heç vaxt toxunmur. WordPress bütöv alloptions paketini obyekt keşində saxlayır, ona görə də PHP-də home_url() və site_url() hələ də keşlənmiş https://example.com-u qaytarırdı. Sonra redirect_canonical() sorğu hostunu (staging) keşlənmiş kanonik home ilə (prod) müqayisə etdi, uyğun gəlmədiklərini qərara aldı və hər müraciətdə proda 301 atdı. Tamamilə köhnəlmiş obyekt keşinin törətdiyi yönləndirmə dövrəsi — özü də artıq 100% düzgün olan bir verilənlər bazasının üstündə. Klassik “DB-də düzəltdim, niyə hələ də səhvdir” tələsi.
Həlli bir sətirdir:
redis-cli -n 4 FLUSHDB
Sonradan eynilə yazdığım qayda: search-replace-dən sonra klonlanmış saytın Redis DB-sini həmişə FLUSHDB et.
Kanal kənarı yazılar invalidasiyanı atlayır — hər dəfə
Daha dərin dərs klonlamadan çox kənara çıxır. WordPress obyekt keşini yalnız yazı öz yolundan keçəndə — update_option(), wp_cache_set() və onların işə saldığı hook-lar — etibarsız edir. Verilənlər bazasını kanal kənarı dəyişən hər şey Redis-i heç bir xəbərdarlıq olmadan köhnə data ilə qoyur:
wp search-replace- xam
mysql/mysqldumpidxalları - birbaşa
UPDATE wp_options ... - DB tap-əvəz alətləri (Interconnect/it, WP Migrate DB)
Bunların heç biri keş hook-larını işə salmır, çünki birbaşa SQL danışırlar. Müalicə həmişə eynidir: əməliyyatdan dərhal sonra həmin saytın DB-sinə əhatəli (scoped) flush. Məhz buna görə də FLUSHALL paylaşılan serverdə normal lüğətinizdə heç vaxt olmamalıdır — redis-cli FLUSHALL bütün 16 məntiqi DB-ni silir, yəni site-a (0), site-b (1), prod (2), site-d (3) və staging (4) eyni anda məhv olur. Tək icarədarlı serverdə adamlar refleks kimi FLUSHALL yazır; bu serverdə isə o, beş sayt üçün xidmət kəsilməsidir. Həmişə -n N FLUSHDB.
rsync wp-config-i əzir — keşiniz səssizcə yenidən istiqamətlənir
Staging-i proddan təzələmək /home/example/public_html/-dən faylları rsync etmək deməkdir və həmin rsync wp-config.php-ni produn nüsxəsi ilə üzərinə yazır. Produn nüsxəsində WP_REDIS_DATABASE = 2 var. Beləliklə, rsync bitən anda staging produn Redis DB 2-sinə (və produn DB məlumatlarına) yönəlir — mən bunu düzəldənə qədər. Unutsam, staging produn keyspace-inə oxuyur və yazır — incə və həqiqətən pis tapılan icarədararası keş çirklənməsi, çünki heç nə xəta vermir; sadəcə səhv saytın datasını verir.
Tam təzələmə yoxlama siyahısı məhz ona görə mövcuddur ki, hər mühitə xas dəyər mənbə tərəfindən əzilir — fayllar rsync ilə, DB seçimləri dump ilə:
rsync -a /home/example/public_html/ /home/staging/public_html/
mysqldump example_wordpress | mysql staging
wp config set DB_NAME staging
wp config set DB_PASSWORD '...'
wp config set WP_REDIS_DATABASE 4 --raw # <-- rsync əzir
wp search-replace https://example.com https://staging.example.com --skip-columns=guid
redis-cli -n 4 FLUSHDB # <-- ƏSAS flush
wp option update blog_public 0 # <-- DB idxalı əzir
İki fərqli mexanizm iki fərqli şeyi əzir: rsync WP_REDIS_DATABASE-i yeyir (fayl sabiti), mysqldump | mysql idxalı isə blog_public-i yeyir (DB seçimi). Hər ikisini yenidən tətbiq etməlisən.
Və unutma ki, iki keş var, bir yox. Hər kod deploy-unda Redis-i (data) və OPcache-i (kompilyasiya olunmuş bytecode) flush edirəm — onlar müstəqil təbəqələrdir və hər ikisi sancır. 2026-05-22-də deploy etdiyim bir funksiya üçün jurnalım hərfən belə yazır: “Redis DB 2 + opcache flushed, front/admin 200.” Redis seçimləri və sorğulanmış obyektləri keşləyir; OPcache (/etc/php/8.3/fpm/conf.d/99-opcache-tuning.ini) isə FPM işçilərinin artıq kompilyasiya etdiyi .php bytecode-unu keşləyir. Yalnız birini flush etsən, “düzəltdiyin” faylın hələ də köhnə davrandığını araşdırmalı olacaqsan, ya da əksinə. FPM altında OPcache flush adətən redis-cli əmri yox, systemctl reload php8.3-fpm-dir — fərqli təbəqə, fərqli mexanizm.
Redis heç nə etməyəndə: keşi göz ardı edən sorğu
Məni təvazökar edən hadisə budur. Prodda Redis qurmuşdum və saytlarımdan birində kataloq tipli axtarış səhifəsi ləng gələndə, 2026-05-27-də onu profil çıxartdım — obyekt keşinin məni xilas edəcəyinə tam əmin idim. O, sıfır fayda verdi.
Səbəbi mənimsənilməyə dəyər və bu, bütöv bir plagin sinfinə aiddir, hər hansı birinə yox. Redis yalnız WordPress-in obyekt-keş API-sinə yazılmış kodu sürətləndirir — WP_Query, get_option, get_post, transient-lər. Amma bron / CRM / e-ticarət sahəsindəki bir çox plagin oxumalarını xam $wpdb->get_results() / $wpdb->query() ilə birbaşa MySQL-ə işlədir. Həmin sorğular heç vaxt wp_cache_get() çağırmır, ona görə də obyekt keşi onları görmür və Redis o yollar üçün dəqiqliklə heç nə etmir. “Redis obyekt keşi qur” universal sürətləndirmə deyil; o, yalnız həqiqətən keşdən soruşan kod yollarına kömək edir. Əgər isti yolun əllə yazılmış SQL-dirsə, keş onun üçün görünməzdir və sorğunun özünü düzəltməlisən.
Profil çıxarış bu səhifədə məhz bunu göstərdi. Səhifə öz siyahısını nəticə sətrinə bir neçə əlavə sorğu işlədən dövrədə yığırdı və tam xətti şəkildə artırdı: 11 nəticə üçün 77 sorğu, 37 üçün 235 — dərslik N+1-i, və (yuxarıdakı səbəbə görə) keşin toxuna biləcəyi heç nə deyil. Birləşmələrdən ikisi indekslənməmiş cədvələ dəyirdi, ona görə də MySQL tam skana və Block Nested Loop-a düşürdü, üstəlik GROUP BY müvəqqəti cədvəl plus filesort-a məcbur edirdi.
Mənim aşağı riskli həllim indekslər idi — üçü, online DDL, bu qədər kiçik cədvəllərdə ani — birləşmələrin filtrlədiyi xarici açar sütunlarını və siyahının əhatə olunduğu (status, tenant_id) cütünü örtən. Ən pis birləşmə yüzlərlə sətir üzərində Block Nested Loop ilə type = ALL-dan bir neçə sətir üzərində təmiz type = ref indeks axtarışına keçdi. Müvəqqəti cədvəl və filesort: yox oldu. İcra vaxtı ~62 ms → ~35 ms, təxminən 44% daha sürətli — üç birsətirli ALTER-dən. Qələbə yavaş oxumaları düzəltməli olan keşdən yox, verilənlər bazasından gəldi.
Redis sonda burada da kömək edir — amma yalnız mənim idarə etdiyim təbəqədə. Təxirə salınmış planım yığılmış nəticə dəstini öz kodumda açıq şəkildə keşləyir: açar = normallaşdırılmış sorğu parametrlərinin md5-i, TTL 300 s, və ən vacibi tarix filtri təyin edildikdə keşləmə, çünki tarix/əlçatımlılıq nəticələri çox dəyişkəndir və öz daha dar keşinə ehtiyacı var. WordPress transient-ləri obyekt keşi mövcud olanda Redis-ə yönəlir, ona görə də ordakı set_transient() Redis-ə düşür — altdakı təbəqə keşdən özü soruşmayanda ondan istifadənin qəsdli yolu. Amma əvvəlcə N+1-i IN(...) dəstləri ilə paketləyib 235 sorğunu ~12–15-ə endirirsən. Sahib olduğun sərhəddə keşlə — özü də aşkar israfı öldürdükdən sonra — və heç vaxt ondan kömək istəməyəcək bir sorğu təbəqəsini örtmək üçün Redis-ə əl atma.