Таҳлили амалиётӣ дар меъмории микросервис: кӯмак ва фаврии Postgres FDW

Меъмории Microservice, ба монанди ҳама чиз дар ин ҷаҳон, мусбат ва манфии худро дорад. Баъзе равандҳо бо он осонтар мешаванд, дигарон мушкилтар мешаванд. Ва ба хотири суръати тағирот ва миқёспазирии беҳтар, шумо бояд қурбониҳо кунед. Яке аз онҳо мураккабии таҳлил аст. Агар дар як монолит ҳама таҳлилҳои амалиётиро ба дархостҳои SQL ба репликаи аналитикӣ кам кардан мумкин бошад, пас дар меъмории бисёрхизматрасонӣ ҳар як хидмат пойгоҳи додаи худро дорад ва ба назар чунин мерасад, ки як дархост кофӣ нест (ё шояд ин тавр бошад?). Барои онҳое, ки ба он таваҷҷӯҳ доранд, ки мо мушкилоти таҳлили оперативиро дар ширкати мо чӣ гуна ҳал кардем ва чӣ гуна мо бо ин ҳалли зиндагӣ зиндагӣ карданро ёд гирифтем - хуш омадед.

Таҳлили амалиётӣ дар меъмории микросервис: кӯмак ва фаврии Postgres FDW
Номи ман Павел Сиваш, дар DomClick ман дар гурӯҳе кор мекунам, ки барои нигоҳ доштани анбори додаҳои таҳлилӣ масъул аст. Одатан, фаъолияти моро метавон ба муҳандисии додаҳо рабт дод, аммо дар асл доираи вазифаҳо хеле васеътар аст. Стандартҳои муҳандисии додаҳои ETL / ELT, дастгирӣ ва мутобиқсозии абзорҳои таҳлили додаҳо ва таҳияи асбобҳои худ мавҷуданд. Аз ҷумла, барои ҳисоботи оперативӣ мо тасмим гирифтем, ки "вонамуд" кунем, ки мо монолит дорем ва ба таҳлилгарон як махзани маълумот диҳем, ки тамоми маълумоти заруриро дар бар мегирад.

Умуман, мо вариантҳои гуногунро баррасӣ кардем. Мумкин буд, ки як анбори мукаммал бунёд кунем - мо ҳатто кӯшиш кардем, аммо, ростқавлона, мо натавонистем бо тағироти зуд-зуд дар мантиқ бо раванди хеле сусти сохтани анбор ва ворид кардани тағирот ба он дӯст шавем ( агар касе муваффақ бошад, дар шарҳҳо нависед, ки чӣ тавр). Шумо метавонед ба таҳлилгарон бигӯед: "Бачаҳо, python омӯзед ва ба хатҳои таҳлилӣ биравед", ​​аммо ин як талаботи иловагии ҷалбкунӣ аст ва ба назар чунин менамуд, ки агар имконпазир бошад, аз ин пешгирӣ кардан лозим аст. Мо тасмим гирифтем, ки технологияи FDW (Foreign Data Wrapper) -ро истифода барем: дар асл, ин як dblink стандартӣ аст, ки дар стандарти SQL ҷойгир аст, аммо бо интерфейси хеле қулай. Дар асоси он кароре кабул кардем, ки нихоят реша давонд, дар он карор гирифтем. Тафсилоти он мавзӯи як мақолаи алоҳида ва шояд бештар аз як аст, зеро ман мехоҳам дар бораи бисёр чизҳо гап занам: аз ҳамоҳангсозии схемаи пойгоҳи додаҳо то назорати дастрасӣ ва ғайриперсонализатсияи маълумоти шахсӣ. Инчунин бояд қайд кард, ки ин қарор ҷойгузини пойгоҳи додаҳо ва анбори воқеии таҳлилӣ нест, он танҳо як масъалаи мушаххасро ҳал мекунад.

Дар сатҳи боло чунин ба назар мерасад:

Таҳлили амалиётӣ дар меъмории микросервис: кӯмак ва фаврии Postgres FDW
Як пойгоҳи додаҳои PostgreSQL мавҷуд аст, ки дар он корбарон метавонанд маълумоти кории худро нигоҳ доранд ва муҳимтар аз ҳама, нусхаҳои таҳлилии ҳама хидматҳо ба ин махзани маълумот тавассути FDW пайваст карда мешаванд. Ин имкон медиҳад, ки дархостро ба якчанд пойгоҳи додаҳо нависед ва муҳим нест, ки он чӣ аст: PostgreSQL, MySQL, MongoDB ё чизи дигар (файл, API, агар ногаҳон бастабандии мувофиқ набошад, шумо метавонед худатон нависед). Бале, ҳама чиз хуб ба назар мерасад! Ҷудо шудан?

Агар ҳама чиз ин қадар зуд ва оддӣ ба охир мерасид, пас, эҳтимол, мақола вуҷуд надошт.

Равшан аст, ки чӣ тавр postgres дархостҳоро ба серверҳои дурдаст иҷро мекунад. Ин мантиқӣ ба назар мерасад, аммо аксар вақт одамон ба он аҳамият намедиҳанд: postgres дархостро ба қисмҳое тақсим мекунад, ки дар серверҳои дурдаст мустақилона иҷро мешаванд, ин маълумотро ҷамъ мекунанд ва худи ҳисобҳои ниҳоиро иҷро мекунанд, аз ин рӯ суръати иҷрои дархост аз бисёр ҷиҳат вобаста хоҳад буд. навишта шудааст. Инчунин бояд қайд кард: вақте ки маълумот аз сервери дурдаст меояд, онҳо дигар индекс надоранд, ҳеҷ чиз ба нақшакаш кӯмак намекунад, бинобар ин, танҳо худи мо метавонем кӯмак кунем ва онро пешниҳод кунем. Ва ин аст он чизе ки ман мехоҳам дар бораи муфассалтар сӯҳбат кунам.

Дархости оддӣ ва нақша бо он

Барои нишон додани он ки чӣ тавр Postgres ҷадвали сатри 6 миллионро дар сервери дурдаст дархост мекунад, биёед нақшаи оддиро бубинем.

explain analyze verbose  
SELECT count(1)
FROM fdw_schema.table;

Aggregate  (cost=418383.23..418383.24 rows=1 width=8) (actual time=3857.198..3857.198 rows=1 loops=1)
  Output: count(1)
  ->  Foreign Scan on fdw_schema."table"  (cost=100.00..402376.14 rows=6402838 width=0) (actual time=4.874..3256.511 rows=6406868 loops=1)
        Output: "table".id, "table".is_active, "table".meta, "table".created_dt
        Remote SQL: SELECT NULL FROM fdw_schema.table
Planning time: 0.986 ms
Execution time: 3857.436 ms

Истифодаи изҳороти VERBOSE ба шумо имкон медиҳад, ки дархостеро, ки ба сервери дурдаст фиристода мешавад ва натиҷаҳои онро мо барои коркарди минбаъда қабул мекунем (сатри RemoteSQL) бубинед.

Биёед каме пештар равем ва ба дархости мо якчанд филтрҳо илова кунем: якто булӣ майдон, яке аз рӯи вуруд нусхаи дар як фосила ва як jsonb.

explain analyze verbose
SELECT count(1)
FROM fdw_schema.table 
WHERE is_active is True
AND created_dt BETWEEN CURRENT_DATE - INTERVAL '7 month' 
AND CURRENT_DATE - INTERVAL '6 month'
AND meta->>'source' = 'test';

Aggregate  (cost=577487.69..577487.70 rows=1 width=8) (actual time=27473.818..25473.819 rows=1 loops=1)
  Output: count(1)
  ->  Foreign Scan on fdw_schema."table"  (cost=100.00..577469.21 rows=7390 width=0) (actual time=31.369..25372.466 rows=1360025 loops=1)
        Output: "table".id, "table".is_active, "table".meta, "table".created_dt
        Filter: (("table".is_active IS TRUE) AND (("table".meta ->> 'source'::text) = 'test'::text) AND ("table".created_dt >= (('now'::cstring)::date - '7 mons'::interval)) AND ("table".created_dt <= ((('now'::cstring)::date)::timestamp with time zone - '6 mons'::interval)))
        Rows Removed by Filter: 5046843
        Remote SQL: SELECT created_dt, is_active, meta FROM fdw_schema.table
Planning time: 0.665 ms
Execution time: 27474.118 ms

Ин аст лаҳзае, ки шумо бояд ҳангоми навиштани пурсишҳо ба он диққат диҳед. Филтрҳо ба сервери дурдаст интиқол дода нашуданд, ки ин маънои онро дорад, ки барои иҷро кардани он, postgres ҳама 6 миллион қаторро барои филтр кардани маҳаллӣ (хати Филтр) ва баъдтар ҷамъоварӣ мекунад. Калиди муваффақият ин навиштани дархост аст, то филтрҳо ба мошини дурдаст интиқол дода шаванд ва мо танҳо сатрҳои заруриро қабул ва ҷамъ мекунем.

Ин як чизи мантиқист

Бо майдонҳои булӣ ҳама чиз оддӣ аст. Дар дархости аслӣ, мушкилот бо сабаби оператор буд is. Агар мо онро бо =, пас мо натиҷаи зеринро ба даст меорем:

explain analyze verbose
SELECT count(1)
FROM fdw_schema.table
WHERE is_active = True
AND created_dt BETWEEN CURRENT_DATE - INTERVAL '7 month' 
AND CURRENT_DATE - INTERVAL '6 month'
AND meta->>'source' = 'test';

Aggregate  (cost=508010.14..508010.15 rows=1 width=8) (actual time=19064.314..19064.314 rows=1 loops=1)
  Output: count(1)
  ->  Foreign Scan on fdw_schema."table"  (cost=100.00..507988.44 rows=8679 width=0) (actual time=33.035..18951.278 rows=1360025 loops=1)
        Output: "table".id, "table".is_active, "table".meta, "table".created_dt
        Filter: ((("table".meta ->> 'source'::text) = 'test'::text) AND ("table".created_dt >= (('now'::cstring)::date - '7 mons'::interval)) AND ("table".created_dt <= ((('now'::cstring)::date)::timestamp with time zone - '6 mons'::interval)))
        Rows Removed by Filter: 3567989
        Remote SQL: SELECT created_dt, meta FROM fdw_schema.table WHERE (is_active)
Planning time: 0.834 ms
Execution time: 19064.534 ms

Тавре ки шумо мебинед, филтр ба сервери дурдаст парвоз кард ва вақти иҷроиш аз 27 то 19 сония кам шуд.

Бояд гуфт, ки оператор is аз оператор фарқ мекунад = касе, ки метавонад бо арзиши Null кор кунад. Ин чунин маъно дорад Дуруст нест дар филтр арзишҳои False ва Null -ро тарк мекунанд, дар ҳоле ки != Дуруст танҳо арзишҳои бардурӯғ боқӣ мемонад. Бинобар ин, хангоми иваз кардани оператор нест, шумо бояд ду шартро ба филтр бо оператори OR гузаред, масалан, КУҶО (col != True) Ё (col нөл аст).

Бо булӣ фаҳмидед, идома диҳед. Дар ҳамин ҳол, биёед филтрро аз рӯи арзиши логикӣ ба шакли аввалааш баргардонем, то ки таъсири дигар тағйиротҳоро мустақилона баррасӣ кунем.

вақт тамғаи? hz

Умуман, ба шумо лозим меояд, ки чӣ гуна дуруст навиштани дархосте, ки серверҳои дурдастро дар бар мегирад, озмоиш кунед ва танҳо пас аз он фаҳмонед, ки чаро ин рӯй медиҳад. Дар ин бора дар интернет маълумоти хеле кам пайдо кардан мумкин аст. Ҳамин тавр, дар таҷрибаҳо мо дарёфтем, ки филтри санаи муқарраршуда бо таркиш ба сервери дурдаст парвоз мекунад, аммо вақте ки мо мехоҳем санаро динамикӣ таъин кунем, масалан, now() ё CURRENT_DATE, ин тавр намешавад. Дар мисоли мо, мо филтрро илова кардем, то сутуни created_at маълумотро барои 1 моҳи гузашта дар бар гирад (BETWEEN CURRENT_DATE - INTERVAL '7 моҳ' ВА CURRENT_DATE - INTERVAL '6 моҳ'). Дар ин ҳолат мо чӣ кор кардем?

explain analyze verbose
SELECT count(1)
FROM fdw_schema.table 
WHERE is_active is True
AND created_dt >= (SELECT CURRENT_DATE::timestamptz - INTERVAL '7 month') 
AND created_dt <(SELECT CURRENT_DATE::timestamptz - INTERVAL '6 month')
AND meta->>'source' = 'test';

Aggregate  (cost=306875.17..306875.18 rows=1 width=8) (actual time=4789.114..4789.115 rows=1 loops=1)
  Output: count(1)
  InitPlan 1 (returns $0)
    ->  Result  (cost=0.00..0.02 rows=1 width=8) (actual time=0.007..0.008 rows=1 loops=1)
          Output: ((('now'::cstring)::date)::timestamp with time zone - '7 mons'::interval)
  InitPlan 2 (returns $1)
    ->  Result  (cost=0.00..0.02 rows=1 width=8) (actual time=0.002..0.002 rows=1 loops=1)
          Output: ((('now'::cstring)::date)::timestamp with time zone - '6 mons'::interval)
  ->  Foreign Scan on fdw_schema."table"  (cost=100.02..306874.86 rows=105 width=0) (actual time=23.475..4681.419 rows=1360025 loops=1)
        Output: "table".id, "table".is_active, "table".meta, "table".created_dt
        Filter: (("table".is_active IS TRUE) AND (("table".meta ->> 'source'::text) = 'test'::text))
        Rows Removed by Filter: 76934
        Remote SQL: SELECT is_active, meta FROM fdw_schema.table WHERE ((created_dt >= $1::timestamp with time zone)) AND ((created_dt < $2::timestamp with time zone))
Planning time: 0.703 ms
Execution time: 4789.379 ms

Мо аз банақшагир хоҳиш кардем, ки санаро пешакӣ дар зерпурсиш ҳисоб кунад ва тағирёбандаи аллакай омодашударо ба филтр гузаронад. Ва ин маслиҳат ба мо натиҷаи олӣ дод, дархост қариб 6 маротиба тезтар шуд!

Боз ҳам, дар ин ҷо эҳтиёт шудан муҳим аст: навъи маълумот дар зерпурсиш бояд ҳамон майдоне бошад, ки мо аз рӯи он филтр мекунем, вагарна банақшагир тасмим мегирад, ки азбаски намудҳо гуногунанд ва аввал ҳама маълумотро гирифтан лозим аст. маълумот ва онро ба таври маҳаллӣ филтр кунед.

Биёед филтрро аз рӯи сана ба арзиши аслии худ баргардонем.

Фредди против. jsonb

Умуман, майдонҳо ва санаҳои логикӣ аллакай дархости моро ба қадри кофӣ суръат бахшидаанд, аммо як намуди дигари маълумот мавҷуд буд. Мубориза бо филтр кардани он, росташро гӯям, ҳанӯз ба охир нарасидааст, гарчанде ки дар ин ҷо низ муваффақиятҳо мавҷуданд. Ҳамин тавр, ин аст, ки мо тавонистем аз филтр гузарем jsonb майдон ба сервери дурдаст.

explain analyze verbose
SELECT count(1)
FROM fdw_schema.table 
WHERE is_active is True
AND created_dt BETWEEN CURRENT_DATE - INTERVAL '7 month' 
AND CURRENT_DATE - INTERVAL '6 month'
AND meta @> '{"source":"test"}'::jsonb;

Aggregate  (cost=245463.60..245463.61 rows=1 width=8) (actual time=6727.589..6727.590 rows=1 loops=1)
  Output: count(1)
  ->  Foreign Scan on fdw_schema."table"  (cost=1100.00..245459.90 rows=1478 width=0) (actual time=16.213..6634.794 rows=1360025 loops=1)
        Output: "table".id, "table".is_active, "table".meta, "table".created_dt
        Filter: (("table".is_active IS TRUE) AND ("table".created_dt >= (('now'::cstring)::date - '7 mons'::interval)) AND ("table".created_dt <= ((('now'::cstring)::date)::timestamp with time zone - '6 mons'::interval)))
        Rows Removed by Filter: 619961
        Remote SQL: SELECT created_dt, is_active FROM fdw_schema.table WHERE ((meta @> '{"source": "test"}'::jsonb))
Planning time: 0.747 ms
Execution time: 6727.815 ms

Ба ҷои филтр кардани операторҳо, шумо бояд ҳузури як операторро истифода баред. jsonb дар дигар. Ба ҷои 7 сония 29 сония. То ҳол ин ягона варианти муваффақ барои интиқоли филтрҳо мебошад jsonb ба сервери дурдаст, аммо дар ин ҷо муҳим аст, ки як маҳдудиятро ба назар гирифт: мо версияи 9.6-и базаро истифода мебарем, аммо то охири моҳи апрел мо нақша дорем, ки санҷишҳои охиринро анҷом дода, ба версияи 12 гузарем. Вақте ки мо навсозӣ мекунем, мо менависем, ки он чӣ гуна таъсир кардааст, зеро тағйироти зиёде мавҷуданд, ки ба онҳо умедҳои зиёд вуҷуд доранд: json_path, рафтори нави CTE, зер кунед (аз версияи 10 мавҷуд аст). Ман дар ҳақиқат мехоҳам онро ба зудӣ санҷам.

Ӯро тамом кунед

Мо тафтиш кардем, ки ҳар як тағирот ба суръати дархост ба таври инфиродӣ таъсир мерасонад. Биёед ҳоло бубинем, ки вақте ки ҳар се филтр дуруст навишта шудааст, чӣ мешавад.

explain analyze verbose
SELECT count(1)
FROM fdw_schema.table 
WHERE is_active = True
AND created_dt >= (SELECT CURRENT_DATE::timestamptz - INTERVAL '7 month') 
AND created_dt <(SELECT CURRENT_DATE::timestamptz - INTERVAL '6 month')
AND meta @> '{"source":"test"}'::jsonb;

Aggregate  (cost=322041.51..322041.52 rows=1 width=8) (actual time=2278.867..2278.867 rows=1 loops=1)
  Output: count(1)
  InitPlan 1 (returns $0)
    ->  Result  (cost=0.00..0.02 rows=1 width=8) (actual time=0.010..0.010 rows=1 loops=1)
          Output: ((('now'::cstring)::date)::timestamp with time zone - '7 mons'::interval)
  InitPlan 2 (returns $1)
    ->  Result  (cost=0.00..0.02 rows=1 width=8) (actual time=0.003..0.003 rows=1 loops=1)
          Output: ((('now'::cstring)::date)::timestamp with time zone - '6 mons'::interval)
  ->  Foreign Scan on fdw_schema."table"  (cost=100.02..322041.41 rows=25 width=0) (actual time=8.597..2153.809 rows=1360025 loops=1)
        Output: "table".id, "table".is_active, "table".meta, "table".created_dt
        Remote SQL: SELECT NULL FROM fdw_schema.table WHERE (is_active) AND ((created_dt >= $1::timestamp with time zone)) AND ((created_dt < $2::timestamp with time zone)) AND ((meta @> '{"source": "test"}'::jsonb))
Planning time: 0.820 ms
Execution time: 2279.087 ms

Бале, дархост мураккабтар ба назар мерасад, ин нархи маҷбурӣ аст, аммо суръати иҷроиш 2 сония аст, ки беш аз 10 маротиба тезтар аст! Ва мо дар бораи як дархости оддӣ дар маҷмӯи нисбатан хурди маълумот сухан меронем. Дар асоси дархостҳои воқеӣ, мо то садҳо маротиба афзоиш гирифтем.

Хулоса: агар шумо PostgreSQL-ро бо FDW истифода баред, ҳамеша санҷед, ки оё ҳама филтрҳо ба сервери дурдаст фиристода шудаанд ва шумо хушбахт хоҳед буд... Ҳадди ақал то он даме, ки шумо дар байни ҷадвалҳо аз серверҳои гуногун пайваст шавед. Аммо ин як ҳикоя барои мақолаи дигар аст.

Ба диққататон ташаккур! Ман мехоҳам саволҳо, шарҳҳо ва ҳикояҳоро дар бораи таҷрибаи шумо дар шарҳҳо бишнавам.

Манбаъ: will.com

Илова Эзоҳ