Incidentu reaģēšana

ClickHouse "Too many partitions for single INSERT block": cēloņi un risinājumi

Vēsturisko datu ielāde vai jauns datu plūsmas process beidzas ar kodu 252 un garu lekciju par particionēšanu. Biežāk nekā ne ClickHouse ir taisnība. Lūk, ko limits patiesībā skaita, kad to drīkst palielināt un kā salabot partīciju atslēgu, kad nedrīkst.

· 14 min lasīšanai · AG Data komanda

ClickHouse kļūda "Too many partitions for single INSERT block" nozīmē, ka vienā INSERT blokā bija rindas vairāk nekā 100 dažādām partīcijām, tāpēc ClickHouse atteicās to ierakstīt. Parasti tā parādās divās situācijās: kāds ielādē mēnešu vai gadu vēsturi tabulā, kas particionēta pa dienām, vai tabulas PARTITION BY ir pārāk sīka. Pirmajā gadījumā problēmu dažās minūtēs atrisina ielāde pa daļām. Otrajā kļūda ir brīdinājums par shēmas problēmu, kas jums tik un tā vēlāk kaitēs.

Ziņojums ir neparasti garš, jo ClickHouse to izmanto, lai paskaidrotu particionēšanu:

Code: 252. DB::Exception: Too many partitions for single INSERT block (more than 100).
The limit is controlled by 'max_partitions_per_insert_block' setting. Large number of
partitions is a common misconception. It will lead to severe negative performance impact,
including slow server startup, slow INSERT queries and slow SELECT queries. Recommended
total number of partitions for a table is under 1000..10000. ... (TOO_MANY_PARTS)

Ko limits skaita

Pārbaude atrodas failā MergeTreeDataWriter.cpp, kur ClickHouse pirms ierakstīšanas sadala ienākošo rindu bloku pēc partīcijas atslēgas. Katrai jaunai partīcijas vērtībai, ko tas atrod blokā, tas salīdzina līdzšinējo skaitu ar max_partitions_per_insert_block. Kad ar noklusējuma iestatījumu blokā parādās 101. atšķirīgā partīcija, šī bloka ievietošana neizdodas ar kodu 252 (TOO_MANY_PARTS, tas pats kods, kas kļūdai "Too many parts").

Praksē svarīgas trīs detaļas:

Atrodiet partīcijas atslēgu un partīciju skaitu

Sāciet ar to, pēc kā tabula ir particionēta. Tabulas system.tables kolonna partition_key parāda izteiksmi:

SELECT database, name, partition_key, sorting_key
FROM system.tables
WHERE engine LIKE '%MergeTree%' AND partition_key != ''
ORDER BY database, name;

Tad paskatieties, cik partīciju tabulām jau ir:

SELECT database, table,
       uniqExact(partition) AS partitions,
       count() AS active_parts
FROM system.parts
WHERE active
GROUP BY database, table
ORDER BY partitions DESC
LIMIT 20;

Ja avota tabulai ir materializētie skati, pārbaudiet arī to mērķa tabulas. Skats raksta savus blokus savā tabulā, un tur skaitās tieši tās tabulas partīcijas atslēga.

Pirms partijas ielādes ar vienu vaicājumu var uzzināt, vai tā ietilps limitā:

-- use the exact expression from partition_key
SELECT uniqExact(toYYYYMMDD(event_time)) AS partitions_in_batch
FROM staging.events_import;

1. gadījums: atslēga ir laba, partija ir par platu

Tabula, kas particionēta pa dienām (toDate vai toYYYYMMDD), ir izplatīta žurnāliem un notikumiem. Ikdienas ievietošana skar vienu vai divas partīcijas, un limitu neviens nepamana, līdz kāds vienā reizē ielādē gada vēsturi. Gads ir 365 dienu partīcijas vienā vaicājumā, tāpēc bloki neizdodas.

Tīrs risinājums ir ielādēt daļās, kas paliek zem limita:

-- viens mēnesis katrā INSERT: ne vairāk kā ~31 dienas partīcija blokā
INSERT INTO analytics.events
SELECT * FROM staging.events_import
WHERE toYYYYMM(event_time) = 202401;

-- tad 202402, 202403, ... ar skriptu, pēc katra pārbaudot rindu skaitu

Katrs mēnesis dod ne vairāk kā aptuveni 31 daļu blokā, tāpēc apvienošana (merge) paliek mierīga, un neizdevušos daļu ir viegli atkārtot. Viens brīdinājums: nepieņemiet, ka neizdevies liels INSERT neko neierakstīja. ClickHouse glosārijs saka, ka ievietošana ir atomāra tikai tad, ja tās rindas ierakstītas kā viens bloks, un ievietošana, kas aptver vairākas partīcijas, ir atomāra atsevišķi katrai partīcijai. Liels INSERT, kas neizdevās vēlākā blokā, var jau būt ierakstījis iepriekšējos. Pirms jebko palaižat atkārtoti, salīdziniet rindu skaitu, citādi dabūsiet dublikātus.

Vienreizējai vēsturisko datu ielādei limitu var palielināt arī tikai savai sesijai:

-- clickhouse-client sesija tikai vienreizējai vēsturisko datu ielādei
SET max_partitions_per_insert_block = 1000;
INSERT INTO analytics.events SELECT * FROM staging.events_import;

Tas ir pieņemami, ja tabulas partīciju skaits ir saprātīgs un jūs precīzi zināt, cik partīciju partija aptver. Pēc tam gaidiet mazu daļu vilni un noslogotu apvienošanas rindu. Neielieciet palielinātu vērtību noklusējuma profilā tikai tāpēc, lai pazustu datu plūsmas kļūda.

ClickHouse 23.8 LTS un jaunākās versijās ir arī throw_on_max_partitions_per_insert_block (noklusējums true). Ja to iestata uz false, ievietošana neizdodas vietā servera žurnālā tiek ierakstīts brīdinājums ar lietotāju, vaicājuma ID un partīciju skaitu. Iestatījumu dokumentācija to iesaka, lai pārbaudītu ietekmi uz lietotājiem pirms max_partitions_per_insert_block maiņas. Tas ir mērīšanas rīks, nevis risinājums.

2. gadījums: partīcijas atslēga ir pārāk sīka

Ja limitu sasniedz parasta ikdienas ievietošana, problēma ir shēmā. Tipiski piemēri ir PARTITION BY customer_id, PARTITION BY (tenant, toDate(ts)), particionēšana pa stundām vai pēc kāda ID, kas vienkārši izskatījās pēc dabiska grupējuma.

ClickHouse dokumentācija par to runā tieši. Pielāgotās partīcijas atslēgas lapa saka, ka vairumā gadījumu partīcijas atslēga nav vajadzīga, un lielākajā daļā pārējo nav vajadzīgs nekas sīkāks par mēnesi, bet dienas partīcijas ir izplatītas novērojamības (observability) datiem. Tā arī saka, ka nevajag partīcijas veidot pēc klientu identifikatoriem vai nosaukumiem, bet identifikatoru likt pirmo ORDER BY. Labās prakses ceļvedis particionēšanu sauc galvenokārt par datu pārvaldības paņēmienu, nevis vaicājumu optimizācijas rīku. Pats kļūdas ziņojums saka to pašu: diapazona vaicājumus ātrus padara kārtošanas atslēga, bet partīcijas ir domātas tādām darbībām kā DROP PARTITION un datu glabāšanas termiņiem.

Partīcijas atslēga ir daļa no tabulas definīcijas, un praksē tās maiņa nozīmē jaunas tabulas izveidi un datu pārvietošanu:

CREATE TABLE analytics.events_new
(
    event_time  DateTime,
    customer_id UInt64,
    event_type  LowCardinality(String),
    payload     String
)
ENGINE = MergeTree
PARTITION BY toYYYYMM(event_time)          -- bija: PARTITION BY (customer_id, toDate(event_time))
ORDER BY (customer_id, event_time);

-- kopējiet pa mēnešiem (tā pati ideja kā sadalītajā ielādē augstāk)
INSERT INTO analytics.events_new
SELECT * FROM analytics.events WHERE toYYYYMM(event_time) = 202401;

-- salīdziniet skaitus, tad atomāri samainiet nosaukumus (Atomic datubāzes dzinējs)
EXCHANGE TABLES analytics.events AND analytics.events_new;

Replicētā klasterī tie paši soļi notiek ar ON CLUSTER un replicētiem dzinējiem, un kopēšana prasa reālu laiku un diska vietu. Kamēr pastāv abas kopijas, rēķinieties, ka vajadzēs brīvu vietu tabulas izmēra apmērā. Ja jūs vispār tikai pārceļat lielu mantotu datu kopu uz ClickHouse, partīcijas atslēgu vērts izlemt pirms pirmās ielādes; par to rakstījām mūsu ceļvedī par mantoto datu pārcelšanu uz ClickHouse datu noliktavu.

Neiestatiet limitu uz 0, lai kļūda pazustu. Ievietošana sāks strādāt, bet cena pārvietosies citur: tūkstošiem sīku daļu katrā ievietošanā, apvienošana, kas nekad netiek līdzi, kļūdas "Too many parts", lēnāka palaišana un lēnāki vaicājumi. Limits ir lēta apdrošināšana tieši pret to.

Kā novērst atkārtošanos

SELECT event_time, user, query_id, left(query, 120) AS q
FROM system.query_log
WHERE exception_code = 252
  AND exception LIKE 'Too many partitions%'
  AND event_time BETWEEN now() - INTERVAL 7 DAY AND now()
ORDER BY event_time DESC
LIMIT 50;

Galvenās atziņas

ClickHouse Particionēšana INSERT Problēmu novēršana
Sadarbība ar mums

ClickHouse particionēšana rada problēmas?

Mēs pārskatām ClickHouse shēmas, partīciju atslēgas un datu ielādes procesus un plānojam tabulu pārbūvi bez dīkstāves. Pastāstiet par savām tabulām, un mēs palīdzēsim izvēlēties noturīgu dizainu.

Sazināties Vairāk no emuāra