Микросервис архитектурасында операциялык аналитика: Postgres FDW жардам жана ыкчам

Микросервис архитектурасы, бул дүйнөдөгү бардык нерселер сыяктуу, анын жакшы жана жаман жактары бар. Аны менен кээ бир процесстер жеңилдейт, башкалары татаалдашат. Жана өзгөртүү ылдамдыгы жана жакшыраак масштабдалуу үчүн, сиз курмандыктарга барышыңыз керек. Алардын бири - аналитиканын татаалданышы. Эгерде монолитте бардык операциялык аналитиканы SQL сурамдарына чейин аналитикалык репликага чейин кыскартууга мүмкүн болсо, анда мультисервистүү архитектурада ар бир кызматтын өзүнүн маалымат базасы бар жана бир суроону аткаруу мүмкүн эместей сезилет (же мүмкүнбү?). Биздин компанияда оперативдүү аналитика маселесин кантип чечкенибизге жана бул чечим менен кантип жашоону үйрөнгөнүбүзгө кызыккандар үчүн - кош келиңиздер.

Микросервис архитектурасында операциялык аналитика: Postgres FDW жардам жана ыкчам
Менин атым Павел Сиваш, DomClickте мен аналитикалык маалымат кампасын жүргүзүү үчүн жооптуу командада иштейм. Шарттуу түрдө биздин иш-аракеттерибизди маалымат инженериясы катары классификациялоого болот, бирок, чындыгында, милдеттердин диапазону алда канча кеңири. Берилиштерди инженериялоо, маалыматтарды талдоо үчүн куралдарды колдоо жана адаптациялоо жана өз куралдарыңызды иштеп чыгуу үчүн ETL/ELT стандарты бар. Тактап айтканда, оперативдүү отчеттуулук үчүн биз монолит бар деп “көрсөтүүнү” чечтик жана аналитиктерге бардык керектүү маалыматтарды камтыган бир маалымат базасын берүүнү чечтик.

Жалпысынан биз ар кандай варианттарды карап чыктык. Толук кандуу репозиторийди курууга мүмкүн болду - биз аракет кылдык, бирок, чынын айтсам, биз логикадагы тез-тез өзгөрүүлөрдү репозиторийди куруу жана ага өзгөртүүлөрдү киргизүү процесси менен айкалыштыра алган жокпуз (эгерде кимдир бирөө ийгиликке жетсе , кантип комментарийге жазыңыз). Талдоочуларга: "Балдар, питонду үйрөнүп, аналитикалык репликага өтүңүз" деп айтууга болот эле, бирок бул жалдоо үчүн кошумча талап жана мүмкүн болсо, мындан качуу керек окшойт. Биз FDW (Foreign Data Wrapper) технологиясын колдонууга аракет кылууну чечтик: негизинен, бул SQL стандартында болгон стандарттуу dblink, бирок өзүнүн бир топ ыңгайлуу интерфейси менен. Анын негизинде биз бир чечимге келдик, акыры ал ишке ашты жана биз аны чечтик. Анын чоо-жайы өзүнчө макаланын темасы, балким, бирден көп, анткени мен көп нерсе жөнүндө айткым келет: маалымат базасынын схемаларын синхрондоштуруудан тартып, жеке маалыматтарды башкарууга жана жеке маалыматтарга ээ болууга чейин. Бул чечим чыныгы аналитикалык маалымат базаларын жана репозиторийлерди алмаштыруу эмес, ал белгилүү бир маселени гана чечет деп эскертип коюу керек;

Жогорку деңгээлде мындай көрүнөт:

Микросервис архитектурасында операциялык аналитика: 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 маанилерин калтырат, ал эми != Туура Жалган баалуулуктарды гана калтырат. Ошондуктан, операторду алмаштырууда эмес, ЖЕ оператору менен эки шарт чыпкага өтүшү керек, мисалы, WHERE (кол != Чын) ЖЕ (кол нөл).

Биз логикалык маанини бөлүп койдук, уланталы. Азырынча башка өзгөрүүлөрдүн таасирин өз алдынча карап чыгуу үчүн буль чыпкасын баштапкы формасына кайтаралы.

timestamptz? hz

Жалпысынан алганда, сиз көп учурда алыскы серверлерди камтыган суроо-талапты кантип туура жазууга эксперимент жасашыңыз керек, андан кийин гана бул эмне үчүн болгонун түшүндүрүүнү издешиңиз керек. Бул тууралуу абдан аз маалыматты Интернеттен табууга болот. Ошентип, эксперименттерде биз белгиленген дата чыпкасы алыскы серверге жарылуу менен учарын таптык, бирок биз датаны динамикалык түрдө орноткубуз келгенде, мисалы, now() же CURRENT_DATE, андай болбойт. Биздин мисалда, created_at тилкесинде так 1 айдын ичинде өткөн (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 эсе тезирээк болду!

Дагы бир жолу, бул жерде этият болуу маанилүү: подсурумдагы маалымат түрү биз чыпкалап жаткан талаага окшош болушу керек, антпесе пландоочу типтер ар башка болгондуктан, алгач баарын алуу керек деп чечет. маалыматтарды жана аны жергиликтүү чыпкалоо.

Келгиле, күн чыпкасын баштапкы маанисине кайтаралы.

Фредди vs. 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 менен колдонсоңуз, бардык чыпкалардын алыскы серверге жөнөтүлгөнүн дайыма текшериңиз, ошондо сиз бактылуу болосуз... Жок дегенде, ар кандай серверлердин таблицаларына кошулганга чейин. Бирок бул башка макала үчүн окуя.

Конул бурганын учун рахмат! Комментарийлерде сиздин тажрыйбаңыз тууралуу суроолорду, комментарийлерди жана окуяларды уккум келет.

Source: www.habr.com

Комментарий кошуу