
Sot dëshiroj të prezantoj me lexuesit e Habrës një mjet të shkruar në Python, për të punuar me varësitë e tabelave në DBMS PostgreSQL.
API i mjetit është i thjeshtë dhe përbëhet nga tre metoda:
- archive_table â arkivimi/reduktimi recursiv i rreshtave me çelĂ«sa kryesorĂ« tĂ« caktuar
- get_table_references â kĂ«rkimi i varĂ«sive pĂ«r tabelĂ«n (do tĂ« tregojĂ« tabelat qĂ« referohen nga e cila dhe ato qĂ« referohen te ajo)
- get_rows_references â kĂ«rkimi i rreshtave nĂ« tabela tĂ« tjera, qĂ« referohen te rreshtat e caktuar nĂ« tabelĂ«n pĂ«rkatĂ«se
Pas historia
Më quajnë Oleg Borzov, jam zhvillues në ekipin CRM për menaxherët e kreditimit hipotetik në Domklik.
Baza e të dhënave kryesore të sistemit tonë CRM është një nga më të mëdhatë në kompani. Ajo është gjithashtu një nga më të vjetrit: u krijua në fillim të projektit, kur pemët ishin të mëdha, Domklik ishte një start-up, dhe në vend të mikroservisave në një framework të njohur asinkron të Python-it, kishte një monolit të madh në PHP.
Kalimi nga PHP në Python ishte shumë i gjatë dhe kërkonte mbështetje të njëkohshme për të dy sistemet, çka ndikonte në projektezimin e bazës së të dhënave.
Si rezultat, ne kemi një bazë me shumë tabela të lidhura fuqishëm dhe shumë të mëdha me shumë indekse për tipe të ndryshme kërkesash. E gjithë kjo ka një ndikim negativ në performancën e DB: për shkak të tabelave të mëdha dhe shumë lidhjeve midis tyre, kompleksiteti i kërkesave vazhdon të rritet, gjë që është veçanërisht kritike për tabelat më të ngarkuara.
Për të reduktuar ngarkesën në DB, vendosëm të shkruajmë një skenar që çdo ditë, nga krone, do të transferonte regjistrimet e vjetra nga tabelat më voluminoze dhe më të ngarkuara në archiva (p.sh., nga task në task_archive).
Kjo detyrë komplikohet nga numri i madh i lidhjeve midis tabelave: thjesht transmetimi i rreshtave nga task në task_archive nuk është e mjaftueshme, para kësaj duhet të bëjmë të njëjtën gjë në mënyrë rekurzive me të gjitha tabelat që referohen në task tabelat.
Do ta demonstroj me shembuj :

Supozoni, na duhet të fshijmë regjistrimet nga tabela Flights. Thjesht kështu nuk na lejon Postgres: paraprakisht duhet të fshijmë regjistrimet nga të gjitha tabelat që referohen, dhe kështu në mënyrë rekurzive deri te tabelat në të cilat nuk referohet askush.
NĂ« shembullin tonĂ« nĂ« Flights citohet Ticket_flights, dhe ajo â Boarding_passes.
Prandaj duhet të fshihet në këtë rend.
- Marrim vlerat e çelësave primarë (Primary Keys, PK) të rreshtave në
Ticket_flights, të cilat referojnë në rreshta që po fshihen nëFlights. - Marrim PK të rreshtave
Boarding_passes, të cilat referojnë nëTicket_flights. - Fshijmë rreshtat sipas PK nga p.2 në tabelë
Boarding_passes. - Fshijmë rreshtat sipas PK nga p.1 në
Ticket_flights. - Fshijmë rreshtat nga
Flights.
Si rezultat kemi krijuar një utilitet të quajtur PgGraph, të cilin vendosëm ta bëjmë open source.
Si të përdorni
Utiliteti mbështet dy mënyra përdorimi:
- Thirrje nga linja e komandës (
pggraph âŠ). - PĂ«rdorimi nĂ« kodin Python (klasa
PgGraphApi).
Instalimi dhe konfigurimi
Së pari, duhet të instaloni utilitetin nga depo Pypi:
pip3 install pggraphMë pas krijoni një skedar config.ini në makinë lokale me konfigurimin e DB dhe skriptin e arkivimit:
[db]
host = localhost
port = 5432
user = postgres
password = postgres
dbname = postgres
schema = public ; Parametër i opsional, caktuar vlera standarde
[archive] ; Ky seksion është opsional, më poshtë janë caktuar vlerat standarde
is_debug = false
chunk_size = 1000
max_depth = 20
to_archive = true
archive_suffix = 'archive'Ekzekutimi nga console
Parametrat
$ pggraph -h
përdorimi: pggraph veprime [-h] --table TABELA [--ids IDS] [--config_path RRUGA_E_KONFIGURIMI]
argumentet pozicionale:
veprime veprim i kërkuar: arkivo_tabelën, merr_referencat_tabelës, merr_referencat_e_rreshtave
argumentet opsionale:
-h, --help shfaq këtë mesazh ndihme dhe dalë
--table TABELA emri i tabelës
--ids IDS identifikuesit kryesor, të ndarë me presje, p.sh. 1,2,3
--config_path RRUGA_E_KONFIGURIMI rruga për config.ini
--log_path RRUGA_E_LOGUT rruga për dosjen e logut
--log_level NIVELI_I_LOGUT niveli i logut (debug, info, error)Argumentet pozicionale:
veprimâ veprim i kĂ«rkuar:archive_table,get_table_referencesoseget_rows_references.
Argumentet e emrituara:
--config_pathâ rruga pĂ«r skedarin e konfigurimit;--tableâ tabela pĂ«r tĂ« cilĂ«n duhet kryer veprimi;--idsâ lista e id-ve tĂ« ndara me presje, pĂ«r shembull,1,2,3(argument opsional);--log_pathâ rruga pĂ«r dosjen e logut (argument opsional, nĂ« mĂ«nyrĂ« default â dosja e shtĂ«pisĂ«);--log_levelâ niveli i regjistrimit (argument opsional, nĂ« mĂ«nyrĂ« default â INFO).
Shembuj komandash
Arkivimi i tabelës
Funksioni kryesor i utilitarit â arkivimi i tĂ« dhĂ«nave, dmth, transferimi i rreshtave nga tabela kryesore nĂ« tabelĂ«n arkiv (p.sh. nga tabela books nĂ« books_archive).
Po ashtu mbështetet fshirja pa arkivim: për këtë duhet të vendosni parametrin në config.ini to_archive = false).
Parametrat e domosdoshĂ«m â config_path, table dhe ids.
Pas nisjes do të fshihen rekurzivisht shënimet ids në tabelë tavolina dhe në të gjitha tabelat që i referohen asaj.
$ pggraph archive_table --config_path config.hw.local.ini --table flights --ids 1,2,3
2020-06-20 19:27:44 INFO: fluturimet - FILLIM
2020-06-20 19:27:44 INFO: fluturimet - fillimi archive_recursive 3 rreshta (thellësia=0)
2020-06-20 19:27:44 INFO: FILLIM ARCHIVE TABELAT E REFERIMET
2020-06-20 19:27:44 INFO: bileta_fluturimeve - fillimi archive_recursive 3 rreshta (thellësia=1)
2020-06-20 19:27:44 INFO: FILLIM ARCHIVE TABELAT E REFERIMET
2020-06-20 19:27:44 INFO: karta_boarding - fillimi archive_recursive 3 rreshta (thellësia=2)
2020-06-20 19:27:44 INFO: FILLIM ARCHIVE TABELAT E REFERIMET
2020-06-20 19:27:44 INFO: MBARIM ARCHIVE TABELAT E REFERIMET
2020-06-20 19:27:44 INFO: karta_boarding - archive_by_ids 3 rreshta sipas ticket_no, flight_id
2020-06-20 19:27:44 INFO: karta_boarding - fillimi archive_recursive 3 rreshta (thellësia=2)
2020-06-20 19:27:44 INFO: FILLIM ARCHIVE TABELAT E REFERIMET
2020-06-20 19:27:44 INFO: MBARIM ARCHIVE TABELAT E REFERIMET
2020-06-20 19:27:44 INFO: karta_boarding - archive_by_ids 3 rreshta sipas ticket_no, flight_id
2020-06-20 19:27:44 INFO: karta_boarding - fillimi archive_recursive 3 rreshta (thellësia=2)
2020-06-20 19:27:44 INFO: FILLIM ARCHIVE TABELAT E REFERIMET
2020-06-20 19:27:44 INFO: MBARIM ARCHIVE TABELAT E REFERIMET
2020-06-20 19:27:44 INFO: karta_boarding - archive_by_ids 3 rreshta sipas ticket_no, flight_id
2020-06-20 19:27:44 INFO: karta_boarding - fillimi archive_recursive 3 rreshta (thellësia=2)
2020-06-20 19:27:44 INFO: FILLIM ARCHIVE TABELAT E REFERIMET
2020-06-20 19:27:44 INFO: MBARIM ARCHIVE TABELAT E REFERIMET
2020-06-20 19:27:44 INFO: karta_boarding - archive_by_ids 3 rreshta sipas ticket_no, flight_id
2020-06-20 19:27:44 INFO: MBARIM ARCHIVE TABELAT E REFERIMET
2020-06-20 19:27:44 INFO: bileta_fluturimeve - archive_by_ids 3 rreshta sipas ticket_no, flight_id
2020-06-20 19:27:44 INFO: MBARIM ARCHIVE TABELAT E REFERIMET
2020-06-20 19:27:44 INFO: fluturimet - archive_by_ids 3 rreshta sipas id
2020-06-20 19:27:44 INFO: fluturimet - MBARIMKërkimi i varësive për tabelën e specifikuar
Funksioni pĂ«r tĂ« kĂ«rkuar varĂ«sitĂ« e tabelĂ«s sĂ« specifikuar tavolina. Parametrat e detyrueshĂ«m janĂ« â config_path dhe tavolina.
Pasi të filloni, në ekran do të shfaqet një fjalor, ku:
in_refsâ fjalori i tabelave qĂ« i referohen kĂ«saj, ku çelĂ«si Ă«shtĂ« emri i tabelĂ«s, vlera Ă«shtĂ« lista e objekteve Foreign Key (pk_mainâ çelĂ«si primar nĂ« tabelĂ«n kryesore,pk_refâ çelĂ«si primar nĂ« tabelĂ«n e referuar,fk_refâ emri i kolonĂ«s qĂ« Ă«shtĂ« foreign key pĂ«r tabelĂ«n origjinale);out_refsâ fjalori i tabelave, nĂ« tĂ« cilat referohet kjo.
$ pggraph get_table_references --config_path config.hw.local.ini --table flights
{'in_refs': {'ticket_flights': [ForeignKey(pk_main='flight_id', pk_ref='ticket_no, flight_id', fk_ref='flight_id')]},
'out_refs': {'aircrafts': [ForeignKey(pk_main='aircraft_code', pk_ref='flight_id', fk_ref='aircraft_code')],
'airports': [ForeignKey(pk_main='airport_code', pk_ref='flight_id', fk_ref='arrival_airport'),
ForeignKey(pk_main='airport_code', pk_ref='flight_id', fk_ref='departure_airport')]}}Kërkimi i referencave për rreshtat me çelësat Primar të specifikuar
Funksioni pĂ«r tĂ« kĂ«rkuar rreshtat nĂ« tabela tĂ« tjera, tĂ« cilat i referohen pĂ«rmes Foreign Key rreshtave ids tabelĂ«s tavolina. Parametrat e detyrueshĂ«m janĂ« â config_path, tavolina dhe ids.
Pasi të filloni, në ekran do të shfaqet një fjalor me strukturën e mëposhtme:
{
pk_id_1: {
reffering_table_name_1: {
foreign_key_1: [
{row_pk_1: vlera, row_pk_2: vlera},
...
],
...
},
...
},
pk_id_2: {...},
...
}Shembuj thirrjeje:
$ pggraph get_rows_references --config_path config.hw.local.ini --table flights --ids 1,2,3
{1: {'ticket_flights': {'flight_id': [{'flight_id': 1,
'ticket_no': '0005432816945'},
{'flight_id': 1,
'ticket_no': '0005432816941'}]}},
2: {'ticket_flights': {'flight_id': [{'flight_id': 2,
'ticket_no': '0005433101832'},
{'flight_id': 2,
'ticket_no': '0005433101864'},
{'flight_id': 2,
'ticket_no': '0005432919715'}]}},
3: {'ticket_flights': {'flight_id': [{'flight_id': 3,
'ticket_no': '0005432817560'},
{'flight_id': 3,
'ticket_no': '0005432817568'},
{'flight_id': 3,
'ticket_no': '0005432817559'}]}}}Përdorimi në kod
Përveç ekzekutimit në konsolë, biblioteka mund të përdoret në kodin Python. Më poshtë jepen shembuj thirrjeje në mjedisin interaktiv iPython.
Arkivimi i tabelës
>>> from pg_graph.main import setup_logging
>>> setup_logging(log_level='DEBUG')
>>> from pg_graph.api import PgGraphApi
>>> api = PgGraphApi('config.hw.local.ini')
>>> api.archive_table('flights', [4,5])
2020-06-20 23:12:08 INFO: flights - FILLIM
2020-06-20 23:12:08 INFO: flights - fillimi archive_recursive 2 rreshta (depth=0)
2020-06-20 23:12:08 INFO: FILLIM ARKIVIM TABELAVE REFERUAR
2020-06-20 23:12:08 DEBUG: ticket_flights - ForeignKey(pk_main='flight_id', pk_ref='flight_id, ticket_no', fk_ref='flight_id')
2020-06-20 23:12:08 DEBUG: SQL('SELECT flight_id, ticket_no FROM bookings.ticket_flights WHERE (flight_id) IN (%s, %s)')
2020-06-20 23:12:08 INFO: ticket_flights - fillimi archive_recursive 30 rreshta (depth=1)
2020-06-20 23:12:08 INFO: FILLIM ARKIVIM TABELAVE REFERUAR
2020-06-20 23:12:08 DEBUG: boarding_passes - ForeignKey(pk_main='flight_id, ticket_no', pk_ref='flight_id, ticket_no', fk_ref='flight_id, ticket_no')
2020-06-20 23:12:08 INFO: boarding_passes - arkivo_me_fk 30 rreshta nga ForeignKey(pk_main='flight_id, ticket_no', pk_ref='flight_id, ticket_no', fk_ref='flight_id, ticket_no')
2020-06-20 23:12:08 DEBUG: SQL('CREATE TABLE IF NOT EXISTS bookings.boarding_passes_archive (LIKE bookings.boarding_passes)')
2020-06-20 23:12:08 DEBUG: FSHI NGA boarding_passes sipas FK flight_id, ticket_no - 30 rreshta
2020-06-20 23:12:08 INFO: FUND ARKIVIM TABELAVE REFERUAR
2020-06-20 23:12:08 INFO: ticket_flights - arkivo_me_ids 30 rreshta nga flight_id, ticket_no
2020-06-20 23:12:08 DEBUG: SQL('CREATE TABLE IF NOT EXISTS bookings.ticket_flights_archive (LIKE bookings.ticket_flights)')
2020-06-20 23:12:08 DEBUG: FSHI NGA ticket_flights sipas flight_id, ticket_no - 30 rreshta
2020-06-20 23:12:08 DEBUG: FUT NĂ ticket_flights_archive - 30 rreshta
2020-06-20 23:12:08 INFO: ticket_flights - fillimi archive_recursive 30 rreshta (depth=1)
2020-06-20 23:12:08 INFO: FILLIM ARKIVIM TABELAVE REFERUAR
2020-06-20 23:12:08 DEBUG: boarding_passes - ForeignKey(pk_main='flight_id, ticket_no', pk_ref='flight_id, ticket_no', fk_ref='flight_id, ticket_no')
2020-06-20 23:12:08 INFO: boarding_passes - arkivo_me_fk 30 rreshta nga ForeignKey(pk_main='flight_id, ticket_no', pk_ref='flight_id, ticket_no', fk_ref='flight_id, ticket_no')
2020-06-20 23:12:08 DEBUG: SQL('CREATE TABLE IF NOT EXISTS bookings.boarding_passes_archive (LIKE bookings.boarding_passes)')
2020-06-20 23:12:08 DEBUG: FSHI NGA boarding_passes sipas FK flight_id, ticket_no - 30 rreshta
2020-06-20 23:12:08 INFO: FUND ARKIVIM TABELAVE REFERUAR
2020-06-20 23:12:08 INFO: ticket_flights - arkivo_me_ids 30 rreshta nga flight_id, ticket_no
2020-06-20 23:12:08 DEBUG: SQL('CREATE TABLE IF NOT EXISTS bookings.ticket_flights_archive (LIKE bookings.ticket_flights)')
2020-06-20 23:12:08 DEBUG: FSHI NGA ticket_flights sipas flight_id, ticket_no - 30 rreshta
2020-06-20 23:12:08 DEBUG: FUT NĂ ticket_flights_archive - 30 rreshta
2020-06-20 23:12:08 INFO: ticket_flights - fillimi archive_recursive 30 rreshta (depth=1)
2020-06-20 23:12:08 INFO: FILLIM ARKIVIM TABELAVE REFERUAR
2020-06-20 23:12:08 DEBUG: boarding_passes - ForeignKey(pk_main='flight_id, ticket_no', pk_ref='flight_id, ticket_no', fk_ref='flight_id, ticket_no')
2020-06-20 23:12:08 INFO: boarding_passes - arkivo_me_fk 30 rreshta nga ForeignKey(pk_main='flight_id, ticket_no', pk_ref='flight_id, ticket_no', fk_ref='flight_id, ticket_no')
2020-06-20 23:12:08 DEBUG: SQL('CREATE TABLE IF NOT EXISTS bookings.boarding_passes_archive (LIKE bookings.boarding_passes)')
2020-06-20 23:12:08 DEBUG: FSHI NGA boarding_passes sipas FK flight_id, ticket_no - 30 rreshta
2020-06-20 23:12:08 INFO: FUND ARKIVIM TABELAVE REFERUAR
2020-06-20 23:12:08 INFO: ticket_flights - arkivo_me_ids 30 rreshta nga flight_id, ticket_no
2020-06-20 23:12:08 DEBUG: SQL('CREATE TABLE IF NOT EXISTS bookings.ticket_flights_archive (LIKE bookings.ticket_flights)')
2020-06-20 23:12:08 DEBUG: FSHI NGA ticket_flights sipas flight_id, ticket_no - 30 rreshta
2020-06-20 23:12:08 DEBUG: FUT NĂ ticket_flights_archive - 30 rreshta
2020-06-20 23:12:08 INFO: ticket_flights - fillimi archive_recursive 3 rreshta (depth=1)
2020-06-20 23:12:08 INFO: FILLIM ARKIVIM TABELAVE REFERUAR
2020-06-20 23:12:08 DEBUG: boarding_passes - ForeignKey(pk_main='flight_id, ticket_no', pk_ref='flight_id, ticket_no', fk_ref='flight_id, ticket_no')
2020-06-20 23:12:08 INFO: boarding_passes - arkivo_me_fk 3 rreshta nga ForeignKey(pk_main='flight_id, ticket_no', pk_ref='flight_id, ticket_no', fk_ref='flight_id, ticket_no')
2020-06-20 23:12:08 DEBUG: SQL('CREATE TABLE IF NOT EXISTS bookings.boarding_passes_archive (LIKE bookings.boarding_passes)')
2020-06-20 23:12:08 DEBUG: FSHI NGA boarding_passes sipas FK flight_id, ticket_no - 3 rreshta
2020-06-20 23:12:08 INFO: FUND ARKIVIM TABELAVE REFERUAR
2020-06-20 23:12:08 INFO: ticket_flights - arkivo_me_ids 3 rreshta nga flight_id, ticket_no
2020-06-20 23:12:08 DEBUG: SQL('CREATE TABLE IF NOT EXISTS bookings.ticket_flights_archive (LIKE bookings.ticket_flights)')
2020-06-20 23:12:08 DEBUG: FSHI NGA ticket_flights sipas flight_id, ticket_no - 3 rreshta
2020-06-20 23:12:08 DEBUG: FUT NĂ ticket_flights_archive - 3 rreshta
2020-06-20 23:12:08 INFO: FUND ARKIVIM TABELAVE REFERUAR
2020-06-20 23:12:08 INFO: flights - arkivo_me_ids 2 rreshta nga flight_id
2020-06-20 23:12:09 DEBUG: SQL('CREATE TABLE IF NOT EXISTS bookings.flights_archive (LIKE bookings.flights)')
2020-06-20 23:12:09 DEBUG: FSHI NGA flights sipas flight_id - 2 rreshta
2020-06-20 23:12:09 DEBUG: FUT NĂ flights_archive - 2 rreshta
2020-06-20 23:12:09 INFO: flights - FUNDKërkimi i varësive për tabelën e specifikuar
>>> from pg_graph.api import PgGraphApi
>>> from pprint import pprint
>>> api = PgGraphApi('config.hw.local.ini')
>>> res = api.get_table_references('flights')
>>> pprint(res)
{'in_refs': {'ticket_flights': [ForeignKey(pk_main='flight_id', pk_ref='flight_id, ticket_no', fk_ref='flight_id')]},
'out_refs': {'aircrafts': [ForeignKey(pk_main='aircraft_code', pk_ref='flight_id', fk_ref='aircraft_code')],
'airports': [ForeignKey(pk_main='airport_code', pk_ref='flight_id', fk_ref='arrival_airport'),
ForeignKey(pk_main='airport_code', pk_ref='flight_id', fk_ref='departure_airport')]}}Kërkimi i referencave për rreshtat me çelësat Primar të specifikuar
>>> from pg_graph.api import PgGraphApi
>>> from pprint import pprint
>>> api = PgGraphApi('config.hw.local.ini')
>>> rows = api.get_rows_references('flights', [1,2,3])
>>> pprint(rows)
{1: {'ticket_flights': {'flight_id': [{'flight_id': 1,
'ticket_no': '0005432816945'},
{'flight_id': 1,
'ticket_no': '0005432816941'}]}},
2: {'ticket_flights': {'flight_id': [{'flight_id': 2,
'ticket_no': '0005433101832'},
{'flight_id': 2,
'ticket_no': '0005433101864'},
{'flight_id': 2,
'ticket_no': '0005432919715'}]}},
3: {'ticket_flights': {'flight_id': [{'flight_id': 3,
'ticket_no': '0005432817560'},
{'flight_id': 3,
'ticket_no': '0005432817568'},
{'flight_id': 3,
'ticket_no': '0005432817559'}]}}}Kodi burimor i bibliotekës është i disponueshëm në në përputhje me licencën MIT, si dhe në depo .
Do të isha i lumtur për komentet, angazhimet dhe propozimet.
Dhe për pyetjet do të përpiqem të përgjigjem sa më shumë që mundem këtu dhe në depo.
Burimi: habr.com
