Кечаси ишга туширилган импорт скрипти эрталаб ярим йўлда тўхтаб қолган, логда эса фақат битта сўз ёзилган: Killed. Ёки онлайн дўконнинг катта ҳисобот саҳифаси очилмасдан 500 хато беради. Бу ҳолатларнинг умумий сабаби кўпинча бир хил: жараён ўзига рухсат этилган оператив хотирадан кўпроқ сўраган ва тизим уни мажбуран тўхтатган. Бундай ҳолат инглиз тилида OOM, яъни "out of memory" деб аталади. Ушбу мақолада OOM қандай юзага келишини, уни бошқа хатолардан қандай ажратишни ва кодни қандай қилиб хотирани тежаб ишлайдиган қилишни кўриб чиқамиз.
Икки хил хотира чегараси
Хостингда хотира билан боғлиқ иккита алоҳида чегара бор ва уларни чалкаштириш кўп вақтни йўқотишга олиб келади. Биринчиси - PHP'нинг ўз созламаси бўлган memory_limit. У битта PHP скрипт қанча хотира ишлатиши мумкинлигини белгилайди. Скрипт бу чегарадан ошса, PHP ўзи ишни тўхтатади ва логга аниқ хабар ёзади:
PHP Fatal error: Allowed memory size of 134217728 bytes exhausted
(tried to allocate 20480 bytes) in /var/www/USER/data/www/example.uz/report.php on line 42
Бу хабарда қайси файл ва қайси қаторда хотира тугагани кўрсатилган, шунинг учун сабабни топиш нисбатан осон. 134217728 байт 128 мегабайтга тенг, яъни скрипт шу чегарага етган.
Иккинчи чегара - бутун аккаунтингиз учун ажратилган RAM. У PHP, Python, Node.js ва бошқа барча жараёнларингиз хотирасини биргаликда ҳисоблайди. Агар жами хотира шу чегарадан ошса, операцион тизим жараёнлардан бирини, одатда энг кўп хотира олганини, огоҳлантиришсиз тўхтатади. Бу ҳолда дастур ўзи хато ёзишга улгурмайди: терминалда Killed сўзи чиқади, веб-саҳифа эса 500 ёки 503 хато билан якунланади. Айнан шу ҳолат OOM kill деб аталади.
OOM белгиларини қандай таниш мумкин
Баъзи белгилар OOM'га аниқ ишора қилади. Python ёки Node.js скрипт ишлаётган жойида изсиз тўхтаб қолади ва чиқиш коди 137 бўлади. Бу код жараён ташқаридан SIGKILL сигнали билан ўлдирилганини билдиради. Узоқ ишлайдиган бот ёки илова вақти-вақти билан ўз-ўзидан қайта ишга тушади. Сайт одатда яхши ишлайди, лекин катта экспорт, расм юклаш ёки оғир ҳисобот пайтида хато беради. Хостинг панелидаги ресурс графигида эса хотира чизиғи айнан шу дақиқаларда юқори чегарага тегиб туради.
Қуйидаги буйруқлар қайси жараён қанча хотира олаётганини аниқлашга ёрдам беради:
# Jarayonlaringiz xotira bo'yicha saralangan (RSS, kilobaytda)
ps -u "$USER" -o pid,rss,etime,cmd --sort=-rss | head
# Skript ishlaganda eng ko'p qancha xotira olganini o'lchash
/usr/bin/time -v python3 import_orders.py 2>&1 | grep "Maximum resident"
# Oxirgi buyruq "Killed" bilan tugagan bo'lsa, chiqish kodi 137 bo'ladi
echo $?
ps буйруғи жараёнларингизни хотира ҳажми бўйича саралайди. /usr/bin/time -v эса скрипт иши давомида энг юқори хотира истеъмолини кўрсатади, бу рақамни тарифингиздаги RAM чегараси билан солиштириш мумкин.
Хотира нега тугайди: одатий сабаблар
Бутун маълумотни бирданига юклаш
Энг кўп учрайдиган хато - катта файлни ёки базадаги бутун жадвални бир марта хотирага ўқиш. Юз минг қаторли Excel файл ёки буюртмалар жадвали хотирада асл ҳажмидан бир неча баробар кўп жой эгаллайди, чунки ҳар бир қиймат дастур ичида алоҳида объектга айланади.
Катта расмларни қайта ишлаш
Телефон камерасидан олинган расм дискда бир неча мегабайт бўлса ҳам, ўлчамини ўзгартириш учун очилганда у сиқилмаган ҳолда хотирага ёзилади ва ўнлаб мегабайт жой олади. Бир вақтда бир нечта шундай расм юкланса, хотира тез тугайди.
Жуда кўп ишчи жараёнлар
Python иловаларини Gunicorn ёки шунга ўхшаш серверлар билан ишга туширганда ҳар бир ишчи (worker) илованинг тўлиқ нусхасини хотирада сақлайди. Тўртта ишчи тўрт баробар хотира дегани. Умумий хостинг ёки кичик VPS учун ишчилар сони кўпинча ортиқча белгиланади.
Хотира оқиши
Баъзан дастур ишлатиб бўлган хотирани бўшатмайди: масалан, ҳар бир сўровда глобал рўйхатга янги элемент қўшилади ва у ҳеч қачон тозаланмайди. Бундай илова бир неча соат яхши ишлайди, кейин эса секин-аста хотирани тўлдириб, OOM билан тўхтайди.
Мисол: Бухородаги ресторан боти
Бухородаги ресторан буюртмалар учун Python'да ёзилган Telegram бот ишлатарди. Ҳар куни кечқурун администратор Excel файлдан ойлик ҳисоботни ботга юклар, бот эса уни таҳлил қилиб базага ёзарди. Файл катталашган сари бот ҳар сафар шу пайтда тўхтаб қола бошлади, логда эса фақат Killed қоларди. Текширув шуни кўрсатдики, файл тўлиқ хотирага очилиб, кейин барча қаторлар аввал рўйхатга йиғилиб, сўнгра базага ёзилаётган эди. Код қаторма-қатор ишлайдиган қилиб ўзгартирилди:
from openpyxl import load_workbook
# read_only=True faylni to'liq xotiraga yuklamaydi
wb = load_workbook("buyurtmalar.xlsx", read_only=True)
ws = wb.active
for row in ws.iter_rows(min_row=2, values_only=True):
save_order(row) # har bir qatorni darhol bazaga yozish
wb.close()
Шундан кейин скриптнинг энг юқори хотира истеъмоли кескин камайди ва бот тўхташни бас қилди. Муаммо кучлироқ тариф билан эмас, икки қатор кодни ўзгартириш билан ҳал бўлди.
Хотирани тежаш усуллари
PHP'да катта экспорт ёки ҳисоботларни бўлаклаб бажариш энг самарали йўл. Бутун жадвални бир сўровда олиш ўрнига уни қисмларга бўлиб ишлаш хотира истеъмолини барқарор ушлаб туради:
// Hammasini bir marta olish o'rniga 500 tadan bo'lib ishlash
$offset = 0;
do {
$rows = $db->query("SELECT id, name, total FROM orders ORDER BY id LIMIT 500 OFFSET $offset")->fetchAll();
foreach ($rows as $r) { fputcsv($out, $r); }
$offset += 500;
} while (count($rows) === 500);
Python веб-иловалари учун ишчилар сонини реал эҳтиёжга мосланг ва хотира оқиши бўлса, ишчиларни маълум миқдордаги сўровдан кейин автоматик янгилаб туринг:
gunicorn app:app --workers 2 --max-requests 500 --max-requests-jitter 50
Бу созлама ҳар бир ишчини тахминан 500 сўровдан кейин қайта яратади ва оқиб кетган хотира бўшайди. Бу вақтинчалик ечим эканини унутманг: оқишнинг асл сабабини топиш барибир керак. Расмлар учун эса юклаш пайтида максимал ўлчамни чекланг ва уларни навбат билан, биттадан қайта ишланг.
memory_limit қийматини ошириш ҳам баъзан тўғри қадам, масалан WordPress администратор панелида оғир плагин ишлаётган бўлса. Лекин бу фақат PHP чегарасини кўтаради, аккаунтнинг умумий RAM чегарасини эмас. Агар аккаунт чегараси тугаётган бўлса, memory_limit'ни ошириш OOM kill'ни тезлаштириши ҳам мумкин.
Қачон кўпроқ RAM керак
Агар код оптималлаштирилган, ишчилар сони тўғри ва оқиш йўқ бўлса-ю, илова барибир чегарада ишласа, демак унга ҳақиқатан кўпроқ хотира керак. Доимий ишлайдиган ботлар, Node.js ёки Python иловалар учун кўпинча VPS қулайроқ, чунки у ерда хотира фақат сизники ва жараёнларни ўзингиз бошқарасиз. sayt.uz кабинетидан хостинг тарифини ўзгартириш ёки VPS буюртма қилиш мумкин, ресурс графиклари бўйича саволлар бўлса, sayt.uz техник ёрдамига тикет ёзинг.