ClickHouse "Memory limit exceeded" ir kļūdas kods 241, MEMORY_LIMIT_EXCEEDED. Tā izskatās pēc vienas kļūdas, taču rodas no vairākiem dažādiem limitiem: viena vaicājuma, viena lietotāja vai visa servera. Smags GROUP BY, kam jāmaina iestatījums, un serveris, kuram patiešām beigusies atmiņa, rada gandrīz vienādu ziņojumu, un, jaucot vienu ar otru, tiek iztērētas stundas. Šis ceļvedis parāda, kā izlasīt kļūdu, atrast, kas patērē atmiņu, un labot pareizo lietu. Tālāk minētie noklusējumi pārbaudīti pret ClickHouse 26.8 LTS pirmkodu un aktuālo dokumentāciju; pārbaudiet system.settings savā versijā, jo vairāki no tiem pēdējās versijās mainījušies.
Izlasiet kļūdas pirmos vārdus
Teksts nāk no ClickHouse atmiņas uzskaitītāja (memory tracker), un tā pirmie vārdi nosauc sasniegto limitu:
Query memory limit exceeded: would use ... (attempt to allocate chunk of ...), maximum: ...nozīmē, ka viens vaicājums sasniedzis savu limitumax_memory_usage. Problēma ir vaicājumā, vai arī limits tam ir par zemu.User memory limit exceedednozīmē, ka visi viena lietotāja vaicājumi kopā sasniegušimax_memory_usage_for_user.(total) memory limit exceeded: would use ..., current RSS: ..., maximum: ...nozīmē, ka viss serveris sasniedzis savu limitu. Vaicājums, kas saņēma kļūdu, ne obligāti ir tas, kas izmanto visvairāk atmiņas; tas varbūt vienkārši bija tas, kas pieprasīja nākamo gabalu.
Vērtība maximum pasaka, uz kuru skaitli skatīties. Ja tā sakrīt ar vaicājumiem iestatītu vērtību, tas ir vaicājuma limits; ja tā ir tuvu lielākajai daļai servera RAM, tas ir visa servera limits.
Limiti un to noklusējumi
max_memory_usage: maksimālā RAM vienam vaicājumam vienā serverī. Iestatījumu pirmkodā noklusējums ir0, kas nozīmē neierobežotu (ClickHouse Cloud tas atkarīgs no replikas RAM). Dokumentācijā norādīts, ka tas neņem vērā, cik atmiņas mašīnai patiesībā ir.max_memory_usage_for_user: tas pats visiem viena lietotāja vaicājumiem kopā. Noklusējums0, neierobežots.max_server_memory_usageunmax_server_memory_usage_to_ram_ratio: visa servera limits. Pirmā noklusējums ir0(nav absolūta ierobežojuma), otrā –0.9, tātad serveris drīkst izmantot 90% pieejamās atmiņas. Jaunākajās versijās limits darbības laikā tiek arī pārrēķināts no rezidentās un pieejamās atmiņas, un cgroup ar atmiņas limitu tiek izmantota cgroup pieejamā atmiņa, nevis resursdatora.merges_mutations_memory_usage_to_ram_ratio: noklusējums0.5. Fona sapludināšanai (merge) un mutācijām tiek dots mīkstais limits – puse atmiņas; kad tas sasniegts, ClickHouse vairs neplāno jaunas sapludināšanas, bet pabeidz jau sāktās.
Tātad pašu uzturētā serverī ar noklusējuma iestatījumiem vienam vaicājumam parasti nav sava limita, un tas darbojas, līdz to aptur visa servera limits. Tāpēc (total) variants ir tik izplatīts serveros, kurus neviens nav noregulējis.
Atrodiet, kas izmantoja atmiņu
Sāciet ar neizdevušamies vaicājumiem. system.query_log pieraksta izņēmumus ar to kodu un atmiņu, ko vaicājums bija izmantojis:
SELECT event_time, user,
formatReadableSize(memory_usage) AS mem,
left(exception, 120) AS error,
left(query, 100) AS q
FROM system.query_log
WHERE event_date >= today() - 1
AND type = 'ExceptionWhileProcessing'
AND exception_code = 241
ORDER BY event_time DESC
LIMIT 20;
(total) gadījumā neizdevies vaicājums ir tikai daļa no ainas. Skatieties, kas darbojās vienlaikus ar to un kuru formu vaicājumi, veiksmīgi izpildoties, izmanto visvairāk atmiņas. normalized_query_hash sagrupē vaicājumus, kas atšķiras tikai ar literāļu vērtībām:
SELECT normalized_query_hash,
count() AS runs,
formatReadableSize(max(memory_usage)) AS peak_mem,
any(left(query, 100)) AS sample
FROM system.query_log
WHERE event_date >= today() - 1
AND type = 'QueryFinish'
GROUP BY normalized_query_hash
ORDER BY max(memory_usage) DESC
LIMIT 10;
Ja problēma notiek tieši tagad, system.processes rāda pašreizējo un maksimālo atmiņu katram strādājošam vaicājumam:
SELECT query_id, user, elapsed,
formatReadableSize(memory_usage) AS mem,
formatReadableSize(peak_memory_usage) AS peak,
left(query, 80) AS q
FROM system.processes
ORDER BY memory_usage DESC;
Dokumentācijā norādīts, ka memory_usage tur var neietvert dažus speciālas atmiņas veidus, tāpēc uztveriet to kā apakšējo robežu. Ja viens vaicājums acīmredzami dominē un nav būtisks, KILL QUERY WHERE query_id = '...' atbrīvo serveri visiem pārējiem. Jaunākie ieraksti system.query_log nonāk pēc izrakstīšanas intervāla; izpildiet SYSTEM FLUSH LOGS, ja tie vajadzīgi nekavējoties.
Biežākie cēloņi
- Augstas kardinalitātes
GROUP BY. Grupēšana pēc lietotāja ID, sesijas ID vai URL vairāku mēnešu datos veido hash tabulu ar ierakstu katrai atšķirīgai atslēgai. Tas ir klasisks veids, kā ClickHouse pietrūkst atmiņas. - Join ar lielu labās puses tabulu. Uz hash balstītie join algoritmi labo pusi veido atmiņā. Ar lielo tabulu
JOINlabajā pusē pietiek, lai izsmeltu RAM. - Lielas kārtošanas ar
ORDER BYlieliem rezultātiem bezLIMIT. - Vienlaicīgums. Desmit vaicājumi, kuriem katram vajag 10% atmiņas, atsevišķi ir kārtībā, bet kopā – liktenīgi. No šejienes rodas
(total)kļūda, lai gan neviens atsevišķs vaicājums neizskatās ekstrēms. - Fona darbs. Sapludināšana un mutācijas konkurē par to pašu atmiņu. Daļu uzkrājums, kā aprakstīts mūsu "Too many parts" ceļvedī, rada serverim papildu slodzi.
Vispirms labojiet vaicājumu
Iestatījumi maina, kur nonāk atmiņa; vaicājuma izmaiņas samazina, cik tās vajag. Pirms palielināt kādu limitu, pārbaudiet, vai vaicājums var darīt mazāk darba:
- Filtrējiet agrāk, pēc primārās un partīcijas atslēgas, lai līdz agregācijai nonāktu mazāk rindu.
- Agregējiet pēc mazāk vai mazākām atslēgām. Aptuvenās funkcijas, piemēram,
uniquniqExactvietā, prasa mazāk atmiņas, ja precīzs skaits nav vajadzīgs; dokumentācijā norādīts, kauniqExactstāvoklis neierobežoti aug, pieaugot atšķirīgo vērtību skaitam. - Mazāko tabulu lieciet
JOINlabajā pusē vai iepriekš agregējiet to apakšvaicājumā. - Atlasiet tikai vajadzīgās kolonnas. ClickHouse ir kolonnu datubāze, un katra lieka plata kolonna maksā atmiņu kārtošanā un join.
Ļaujiet smagām operācijām izmantot disku
ClickHouse var daļu agregācijas, kārtošanas vai join pārvietot uz disku, nevis beigties ar kļūdu. Saskaņā ar iestatījumu izmaiņu vēsturi izlikšana uz disku GROUP BY un ORDER BY pēc noklusējuma ieslēgta kopš 25.1: max_bytes_ratio_before_external_group_by un max_bytes_ratio_before_external_sort ir 0.5, tātad operācija sāk izmantot disku, kad sasniegusi pusi pieejamās atmiņas. Versija 26.5 pievienoja tādu pašu attiecību hash join – max_bytes_ratio_before_external_join, arī 0.5. Absolūtie sliekšņi max_bytes_before_external_group_by un max_bytes_before_external_sort pēc noklusējuma ir 0.
Ja iestatāt absolūto slieksni, GROUP BY dokumentācija iesaka max_memory_usage iestatīt aptuveni divreiz lielāku, jo agregācija notiek divos posmos un uz disku var izlikt tikai pirmajā. Tās piemērs: max_bytes_before_external_group_by 10000000000 ar max_memory_usage 20000000000. Izkliedētiem vaicājumiem tā iesaka distributed_aggregation_memory_efficient = 1, lai serverī, kas apvieno rezultātus, atmiņas patēriņš paliktu zems. Un tā godīgi norāda cenu: kad dati nonāk uz diska, vaicājums ilgst vairākas reizes ilgāk, aptuveni trīs reizes.
Join gadījumā algoritms grace_hash sadala labo tabulu daļās (buckets), vienu patur atmiņā un pārējās ieraksta diskā, pievienojot daļas, ja hash tabula pārsniedz limitu. To var izvēlēties konkrētam vaicājumam ar SETTINGS join_algorithm = 'grace_hash'.
Iestatiet limitus, kas aizsargā serveri
Neierobežota atmiņa vaicājumam nozīmē, ka viens slikts vaicājums var nogāzt visu serveri. Iestatījumu profils ad-hoc un atskaišu lietotājiem notur to vaicājumus budžeta robežās:
CREATE SETTINGS PROFILE reports_limits
SETTINGS max_memory_usage = 20000000000,
max_bytes_before_external_group_by = 10000000000,
max_bytes_before_external_sort = 10000000000
TO report_user;
Ar vaicājuma limitu nekontrolēts vaicājums beidzas ar kļūdas Query variantu, nevis iegrūž serveri (total) limitā, paņemot līdzi citus vaicājumus.
ClickHouse ir arī atmiņas overcommit, ko dokumentācija apraksta kā eksperimentālu funkciju. Kad sasniegts visa servera limits, vaicājums īsu brīdi gaida, kamēr atmiņa tiks atbrīvota (memory_usage_overcommit_max_wait_microseconds, pēc noklusējuma 5 sekundes), un, ja nekas netiek atbrīvots, uzskaitītājs aptur vaicājumu ar lielāko piešķirtās atmiņas attiecību pret memory_overcommit_ratio_denominator (noklusējums 1 GiB). Praksē tas nozīmē, ka lielākais patērētājs tiek apturēts ar lielāku varbūtību nekā vaicājums, kas nejauši pēdējais pieprasīja atmiņu.
Nepalieliniet vienkārši limitu. Ja kļūdā redzams (total), max_server_memory_usage_to_ram_ratio palielināšana virs noklusējuma aizstāj tīru vaicājuma kļūdu ar risku, ka operētājsistēma nogalinās visu ClickHouse procesu. Vispirms samaziniet vai ierobežojiet slodzi; atmiņu pievienojiet tikai tad, ja darba datu apjomam tās patiešām vajag.
Ko uzraudzīt
- Vaicājumu skaitu, kas
system.query_logbeidzas arexception_code = 241, sadalot pa lietotājiem. - Maksimālo atmiņu katram normalizētajam vaicājumam laika gaitā, lai redzētu vaicājuma augšanu, pirms tas sāk beigties ar kļūdu.
- Servera atmiņu attiecībā pret tā limitu, kopā ar vienlaicīgo vaicājumu un aktīvo sapludināšanu skaitu. Mūsu rakstā par metrikām, kas paredz datubāzu incidentus aplūkots, kā brīdināt par tendencēm, nevis sliekšņiem.
Galvenās atziņas
- Code 241 ir trīs galvenie varianti:
Query,Userun(total). Ziņojuma pirmie vārdi pasaka, kurš limits sasniegts. - Pēc noklusējuma vaicājumam nav sava atmiņas limita; serveris sev atļauj 90% pieejamās atmiņas.
- Vaininieku meklējiet
system.query_logunsystem.processes;(total)gadījumā skatieties uz visu, kas darbojās vienlaikus. - Vispirms labojiet vaicājumu, tad paļaujieties uz izlikšanu uz disku
GROUP BY,ORDER BYun join, rēķinoties, ka šādi vaicājumi ir lēnāki. - Dodiet ad-hoc un atskaišu lietotājiem
max_memory_usagebudžetu, lai viens vaicājums nevarētu nogāzt visu serveri.