ClickHouse + Graphite: kung paano makabuluhang bawasan ang pagkonsumo ng espasyo sa disk

ClickHouse + Graphite: kung paano makabuluhang bawasan ang pagkonsumo ng espasyo sa disk

Pagbati, habr.

Kung may nagsasamantala sa sistema graphite-web at nakatagpo ng isyu sa pagganap ng storage bumulong (IO, naubos ang puwang sa disk), kung gayon ang pagkakataon na ang ClickHouse ay na-cast bilang kapalit ay dapat na may posibilidad na isa. Ang pahayag na ito ay nagpapahiwatig na ang isang third-party na pagpapatupad ay ginagamit na bilang isang daemon na tumatanggap ng mga sukatan, halimbawa carbonwriter o go-carbon.

Niresolba ng ClickHouse ang mga inilarawang problema nang maayos. Halimbawa, pagkatapos maglipat ng 2TiB ng data mula sa whisper, magkasya ang mga ito sa 300GiB. Hindi ako magtatagal sa paghahambing nang detalyado; maraming mga artikulo sa paksang ito. Bilang karagdagan, hanggang kamakailan, hindi lahat ay perpekto sa aming imbakan ng ClickHouse.

Mga problema sa naubos na espasyo

Sa unang sulyap, dapat gumana nang maayos ang lahat. Sumusunod dokumentasyon, gumawa ng config para sa metrics storage scheme (karagdagang retention), pagkatapos ay lumikha ng isang talahanayan ayon sa rekomendasyon ng napiling backend para sa graphite-web: carbon-clickhouse+graphite-clickhouse o graphhouse, depende sa kung aling stack ang ginagamit. At... sumabog ang time bomb.

Upang maunawaan kung alin, kailangan mong malaman kung paano gumagana ang mga pagsingit at ang karagdagang landas ng buhay ng data sa mga talahanayan ng mga makina ng * pamilyaMergeTree ClickHouse (mga chart na kinuha mula sa mga pagtatanghal Alexey Zatelepin):

  • Ipinasok Π±Π»ΠΎΠΊ datos. Sa aming kaso, ito ay ang mga sukatan na dumating.
    ClickHouse + Graphite: kung paano makabuluhang bawasan ang pagkonsumo ng espasyo sa disk
  • Ang bawat bloke ay pinagsunod-sunod ayon sa susi bago isulat sa disk. ORDER BYtinukoy kapag lumilikha ng talahanayan.
  • Pagkatapos ayusin, кусок (part) ang data ay isinulat sa disk.
    ClickHouse + Graphite: kung paano makabuluhang bawasan ang pagkonsumo ng espasyo sa disk
  • Sinusubaybayan ng server sa background upang walang ganoong mga piraso, at naglulunsad ng background слияния (merge, pagkatapos ay pagsamahin).
    ClickHouse + Graphite: kung paano makabuluhang bawasan ang pagkonsumo ng espasyo sa disk
    ClickHouse + Graphite: kung paano makabuluhang bawasan ang pagkonsumo ng espasyo sa disk
  • Ang server ay huminto sa pagtakbo ay nagsasama sa sarili nitong sa sandaling ang data ay huminto sa aktibong pagdaloy sa ΠΏΠ°Ρ€Ρ‚ΠΈΡ†ΠΈΡŽ (partition), ngunit maaari mong simulan ang proseso nang manu-mano gamit ang utos OPTIMIZE.
  • Kung may isang piraso na lang na natitira sa partition, hindi mo magagawang patakbuhin ang merge gamit ang karaniwang command; dapat mong gamitin OPTIMIZE ... FINAL

Kaya, dumating ang mga unang sukatan. At kumukuha sila ng ilang espasyo. Ang mga kasunod na kaganapan ay maaaring medyo mag-iba depende sa maraming mga kadahilanan:

  • Ang partitioning key ay maaaring maging napakaliit (isang araw) o napakalaki (ilang buwan).
  • Maaaring magkasya ang config ng pagpapanatili sa ilang makabuluhang threshold ng pagsasama-sama ng data sa loob ng aktibong partition (kung saan naitala ang mga sukatan), o maaaring hindi.
  • Kung mayroong maraming data, kung gayon ang pinakamaagang mga chunks, na dahil sa pagsasama ng background ay maaaring malaki na (kung pipiliin mo ang isang hindi pinakamainam na partitioning key), ay hindi magsasama sa kanilang mga sarili sa mga sariwang maliliit na chunks.

At palagi itong nagtatapos. Ang puwang na inookupahan ng mga sukatan sa ClickHouse ay tataas lamang kung:

  • huwag mag-apply OPTIMIZE ... FINAL mano-mano o
  • huwag magpasok ng data sa lahat ng mga partisyon sa patuloy na batayan, nang sa gayon ay magsisimula ang isang pagsasanib sa background

Ang pangalawang paraan ay tila ang pinakamadaling ipatupad at, samakatuwid, ito ay hindi tama at sinubukan muna.
Sumulat ako ng medyo simpleng script ng python na nagpadala ng mga dummy na sukatan para sa bawat araw sa nakalipas na 4 na taon at nagpapatakbo ng cron bawat oras.
Dahil ang buong operasyon ng ClickHouse DBMS ay batay sa katotohanan na ang sistemang ito ay maaga o huli ay gagawin ang lahat ng gawain sa background, ngunit hindi alam kung kailan, hindi ako makapaghintay para sa sandali kung kailan ang mga lumang malalaking piraso ay naghahangad na magsimulang pagsamahin sa mga bagong maliliit. Naging malinaw na kailangan naming maghanap ng paraan para i-automate ang sapilitang pag-optimize.

ClickHouse + Graphite: kung paano makabuluhang bawasan ang pagkonsumo ng espasyo sa disk

Impormasyon sa mga talahanayan ng sistema ng ClickHouse

Tingnan natin ang istraktura ng talahanayan sistema.mga bahagi. Ito ay komprehensibong impormasyon tungkol sa bawat piraso ng lahat ng mga talahanayan sa server ng ClickHouse. Naglalaman, bukod sa iba pang mga bagay, ang mga sumusunod na column:

  • db pangalan (database);
  • pangalan ng talahanayan (table);
  • pangalan ng partisyon at ID (partition & partition_id);
  • kapag ginawa ang piraso (modification_time);
  • minimum at maximum na petsa sa isang piraso (ginagawa ang partitioning ayon sa araw) (min_date & max_date);

May table din system.graphite_retentions, kasama ang mga sumusunod na kawili-wiling field:

  • db pangalan (Tables.database);
  • pangalan ng talahanayan (Tables.table);
  • panukat na edad kung kailan dapat ilapat ang susunod na pagsasama-sama (age);

Kaya:

  1. Mayroon kaming isang talahanayan ng mga chunks at isang talahanayan ng mga panuntunan sa pagsasama-sama.
  2. Pinagsasama namin ang kanilang intersection at makuha ang lahat ng mga talahanayan *GraphiteMergeTree.
  3. Hinahanap namin ang lahat ng mga partisyon kung saan:
    • higit sa isang piraso
    • o dumating na ang oras upang ilapat ang susunod na panuntunan sa pagsasama-sama, at modification_time mas matanda kaysa sa sandaling ito.

Pagpapatupad

Ang hiling na ito

SELECT
    concat(p.database, '.', p.table) AS table,
    p.partition_id AS partition_id,
    p.partition AS partition,
    -- Π‘Π°ΠΌΠΎΠ΅ "староС" ΠΏΡ€Π°Π²ΠΈΠ»ΠΎ, ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ ΠΏΡ€ΠΈΠΌΠ΅Π½Π΅Π½ΠΎ для
    -- ΠΏΠ°Ρ€Ρ‚ΠΈΡ†ΠΈΠΈ, Π½ΠΎ Π½Π΅ Π² Π±ΡƒΠ΄ΡƒΡ‰Π΅ΠΌ, см (*)
    max(g.age) AS age,
    -- ΠšΠΎΠ»ΠΈΡ‡Π΅ΡΡ‚Π²ΠΎ кусков Π² ΠΏΠ°Ρ€Ρ‚ΠΈΡ†ΠΈΠΈ
    countDistinct(p.name) AS parts,
    -- Π—Π° ΡΠ°ΠΌΡƒΡŽ ΡΡ‚Π°Ρ€ΡˆΡƒΡŽ ΠΌΠ΅Ρ‚Ρ€ΠΈΠΊΡƒ Π² ΠΏΠ°Ρ€Ρ‚ΠΈΡ†ΠΈΠΈ принимаСтся 00:00:00 ΡΠ»Π΅Π΄ΡƒΡŽΡ‰Π΅Π³ΠΎ дня
    toDateTime(max(p.max_date + 1)) AS max_time,
    -- Когда партиция Π΄ΠΎΠ»ΠΆΠ½Π° Π±Ρ‹Ρ‚ΡŒ ΠΎΠΏΡ‚ΠΈΠΌΠΈΠ·ΠΈΡ€ΠΎΠ²Π°Π½Π½Π°
    max_time + age AS rollup_time,
    -- Когда самый старый кусок Π² ΠΏΠ°Ρ€Ρ‚ΠΈΡ†ΠΈΠΈ Π±Ρ‹Π» ΠΎΠ±Π½ΠΎΠ²Π»Ρ‘Π½
    min(p.modification_time) AS modified_at
FROM system.parts AS p
INNER JOIN
(
    -- ВсС ΠΏΡ€Π°Π²ΠΈΠ»Π° для всСх Ρ‚Π°Π±Π»ΠΈΡ† *GraphiteMergeTree
    SELECT
        Tables.database AS database,
        Tables.table AS table,
        age
    FROM system.graphite_retentions
    ARRAY JOIN Tables
    GROUP BY
        database,
        table,
        age
) AS g ON
    (p.table = g.table)
    AND (p.database = g.database)
WHERE
    -- Волько Π°ΠΊΡ‚ΠΈΠ²Π½Ρ‹Π΅ куски
    p.active
    -- (*) И Ρ‚ΠΎΠ»ΡŒΠΊΠΎ строки, Π³Π΄Π΅ ΠΏΡ€Π°Π²ΠΈΠ»Π° Π°Π³Π³Ρ€Π΅Π³Π°Ρ†ΠΈΠΈ ΡƒΠΆΠ΅ Π΄ΠΎΠ»ΠΆΠ½Ρ‹ Π±Ρ‹Ρ‚ΡŒ ΠΏΡ€ΠΈΠΌΠ΅Π½Π΅Π½Ρ‹
    AND ((toDateTime(p.max_date + 1) + g.age) < now())
GROUP BY
    table,
    partition
HAVING
    -- Волько ΠΏΠ°Ρ€Ρ‚ΠΈΡ†ΠΈΠΈ, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ младшС ΠΌΠΎΠΌΠ΅Π½Ρ‚Π° ΠΎΠΏΡ‚ΠΈΠΌΠΈΠ·Π°Ρ†ΠΈΠΈ
    (modified_at < rollup_time)
    -- Или с нСсколькими кусками
    OR (parts > 1)
ORDER BY
    table ASC,
    partition ASC,
    age ASC

ibinabalik ang bawat isa sa *GraphiteMergeTree table partition na ang pagsasama ay dapat magbakante ng espasyo sa disk. Ang tanging bagay na natitira upang gawin ay upang pumunta sa pamamagitan ng lahat ng ito na may isang kahilingan OPTIMIZE ... FINAL. Ang pangwakas na pagpapatupad ay isinasaalang-alang din ang katotohanan na hindi na kailangang hawakan ang mga partisyon na may aktibong pag-record.

Ito mismo ang ginagawa ng proyekto graphite-ch-optimizer. Sinubukan ito ng mga dating kasamahan mula sa Yandex.Market sa produksyon, ang resulta ng trabaho ay makikita sa ibaba.

ClickHouse + Graphite: kung paano makabuluhang bawasan ang pagkonsumo ng espasyo sa disk

Kung patakbuhin mo ang program sa isang server na may ClickHouse, magsisimula lang itong magtrabaho sa daemon mode. Isang beses sa isang oras, isasagawa ang isang kahilingan, tinitingnan kung may mga bagong partition na mas matanda sa tatlong araw na lumitaw na maaaring i-optimize.

Ang aming mga agarang plano ay magbigay ng hindi bababa sa mga pakete ng deb, at kung maaari ring rpm.

Sa halip ng isang konklusyon

Sa nakalipas na 9+ na buwan ay nasa loob ako ng aking kumpanya innogames gumugol ng maraming oras sa pag-iisip sa intersection ng ClickHouse at graphite-web. Ito ay isang magandang karanasan, na nagresulta sa isang mabilis na paglipat mula sa bulong patungo sa ClickHouse bilang isang imbakan ng sukatan. Umaasa ako na ang artikulong ito ay isang bagay sa simula ng isang serye tungkol sa kung anong mga pagpapabuti ang ginawa namin sa iba't ibang bahagi ng stack na ito, at kung ano ang gagawin sa hinaharap.

Ilang litro ng beer at araw ng admin ang ginugol sa pagbuo ng kahilingan, kasama ng v0devil, kung saan nais kong ipahayag ang aking pasasalamat sa kanya. At para din sa pagsusuri sa artikulong ito.

Pahina ng proyekto sa github

Pinagmulan: www.habr.com

Magdagdag ng komento