„Duomenų bazės kaip kodas“ patirtis

„Duomenų bazės kaip kodas“ patirtis

SQL, kas gali būti paprasčiau? Kiekvienas iš mūsų gali parašyti paprastą užklausą – spausdiname pasirinkti, išvardykite reikiamus stulpelius, tada nuo, lentelės pavadinimas, kai kurios sąlygos kur ir viskas – naudingi duomenys yra mūsų kišenėje ir (beveik) nepriklausomai nuo to, kuri DBVS tuo metu yra po gaubtu (o gal visai ne DBVS). Dėl to darbas su beveik bet kokiu duomenų šaltiniu (santykiniu ir ne tokiu) gali būti vertinamas įprasto kodo požiūriu (su viskuo, ką jis reiškia - versijų valdymą, kodo peržiūrą, statinę analizę, automatinius testus ir viskas). Ir tai taikoma ne tik patiems duomenims, schemoms ir perkėlimams, bet ir apskritai visam saugyklos gyvavimo laikui. Šiame straipsnyje mes kalbėsime apie kasdienes užduotis ir problemas dirbant su įvairiomis duomenų bazėmis pagal „duomenų bazės kaip kodo“ objektyvą.

Ir pradėkime nuo pat ORM. Pirmieji „SQL vs ORM“ tipo mūšiai buvo pastebėti dar kartą prieš Petrinę Rusiją.

Objektų ir santykių kartografavimas

ORM šalininkai tradiciškai vertina greitį ir kūrimo lengvumą, nepriklausomybę nuo DBVS ir švarų kodą. Daugeliui iš mūsų darbo su duomenų baze (o dažnai ir pačia duomenų baze) kodas

dažniausiai atrodo maždaug taip...

@Entity
@Table(name = "stock", catalog = "maindb", uniqueConstraints = {
        @UniqueConstraint(columnNames = "STOCK_NAME"),
        @UniqueConstraint(columnNames = "STOCK_CODE") })
public class Stock implements java.io.Serializable {

    @Id
    @GeneratedValue(strategy = IDENTITY)
    @Column(name = "STOCK_ID", unique = true, nullable = false)
    public Integer getStockId() {
        return this.stockId;
    }
  ...

Modelis pakabintas su protingomis anotacijomis, o kažkur užkulisiuose narsus ORM generuoja ir vykdo daugybę SQL kodų. Beje, kūrėjai visomis išgalėmis stengiasi atsiriboti nuo savo duomenų bazės su kilometrais abstrakcijų, o tai rodo, „SQL neapykanta“.

Kitoje barikadų pusėje gryno „rankų darbo“ SQL šalininkai pastebi galimybę išspausti visas sultis iš savo DBVS be papildomų sluoksnių ir abstrakcijų. Dėl to atsiranda „į duomenis orientuoti“ projektai, kur į duomenų bazę įtraukiami specialiai apmokyti žmonės (jie irgi „bazistai“, taip pat „bazistai“, taip pat „basdeneriai“ ir t. t.), kūrėjai. belieka „ištraukti“ jau paruoštus vaizdus ir saugomas procedūras, nesigilinant į smulkmenas.

O kas, jei turėtume geriausią iš abiejų pasaulių? Kaip tai daroma nuostabiame įrankyje gyvybę patvirtinančiu pavadinimu Yesql. Aš pateiksiu keletą eilučių iš bendros koncepcijos nemokamo vertimo, ir jūs galite su ja susipažinti išsamiau čia.

„Clojure“ yra puiki DSL kūrimo kalba, tačiau pats SQL yra puikus DSL ir mums nereikia kitos. S-išraiškos puikios, bet čia nieko naujo neprideda. Dėl to skliausteliuose gauname skliaustus. Nesutikti? Tada palaukite momento, kai pradės nutekėti duomenų bazės abstrakcija ir pradėsite kovoti su funkcija (raw-sql)

Taigi ką turėčiau daryti? Palikime SQL kaip įprastą SQL – po vieną failą užklausoje:

-- name: users-by-country
select *
  from users
 where country_code = :country_code

... ir tada perskaitykite šį failą, paversdami jį įprasta Clojure funkcija:

(defqueries "some/where/users_by_country.sql"
   {:connection db-spec})

;;; A function with the name `users-by-country` has been created.
;;; Let's use it:
(users-by-country {:country_code "GB"})
;=> ({:name "Kris" :country_code "GB" ...} ...)

Laikydamiesi principo „SQL pats, Clojure pats“ gaunate:

  • Jokių sintaksinių netikėtumų. Jūsų duomenų bazė (kaip ir bet kuri kita) nėra 100% suderinama su SQL standartu, tačiau tai neturi reikšmės Yesql. Niekada negaišite laiko ieškodami funkcijų su SQL lygiaverte sintaksė. Jums niekada nereikės grįžti prie funkcijos (raw-sql "some('funky'::SYNTAX)")).
  • Geriausias redaktoriaus palaikymas. Jūsų redaktorius jau turi puikų SQL palaikymą. Išsaugoję SQL kaip SQL, galite tiesiog jį naudoti.
  • Komandos suderinamumas. Jūsų DBA gali skaityti ir rašyti SQL, kurį naudojate savo Clojure projekte.
  • Lengvesnis našumo derinimas. Reikia sudaryti probleminės užklausos planą? Tai nėra problema, kai jūsų užklausa yra įprasta SQL.
  • Pakartotinis užklausų naudojimas. Nuvilkite tuos pačius SQL failus į kitus projektus, nes tai tiesiog senas SQL – tiesiog pasidalykite juo.

Mano nuomone, idėja yra labai šauni ir tuo pačiu labai paprasta, kurios dėka projektas įgijo daug sekėjų įvairiomis kalbomis. Toliau bandysime pritaikyti panašią SQL kodo atskyrimo nuo viso kito, toli už ORM ribų, filosofiją.

IDE ir DB vadovai

Pradėkime nuo paprastos kasdienės užduoties. Dažnai duomenų bazėje tenka ieškoti kai kurių objektų, pavyzdžiui, schemoje rasti lentelę ir ištirti jos struktūrą (kokie stulpeliai, raktai, indeksai, apribojimai ir pan. naudojami). Ir iš bet kurios grafinės IDE ar mažos DB tvarkyklės pirmiausia tikimės būtent tokių gebėjimų. Kad būtų greita ir nereikėtų laukti pusvalandžio, kol bus nubraižytas langas su reikiama informacija (ypač lėtai prisijungus prie nuotolinės duomenų bazės), o tuo pačiu gaunama informacija būtų šviežia ir aktuali, o ne talpykloje saugomo šlamšto. Be to, kuo sudėtingesnė ir didesnė duomenų bazė ir kuo didesnis jų skaičius, tuo sunkiau tai padaryti.

Bet dažniausiai išmetu pelę ir tiesiog rašau kodą. Tarkime, kad reikia išsiaiškinti, kurios lentelės (ir su kokiomis savybėmis) yra „HR“ schemoje. Daugumoje DBVS norimą rezultatą galima pasiekti naudojant šią paprastą užklausą iš information_schema:

select table_name
     , ...
  from information_schema.tables
 where schema = 'HR'

Nuo duomenų bazės iki duomenų bazės tokių nuorodų lentelių turinys skiriasi priklausomai nuo kiekvienos DBVS galimybių. Ir, pavyzdžiui, MySQL, iš tos pačios žinyno galite gauti šiai DBVS būdingus lentelės parametrus:

select table_name
     , storage_engine -- Используемый "движок" ("MyISAM", "InnoDB" etc)
     , row_format     -- Формат строки ("Fixed", "Dynamic" etc)
     , ...
  from information_schema.tables
 where schema = 'HR'

Oracle nežino information_schema, bet žino Oracle metaduomenys, ir didelių problemų nekyla:

select table_name
     , pct_free       -- Минимум свободного места в блоке данных (%)
     , pct_used       -- Минимум используемого места в блоке данных (%)
     , last_analyzed  -- Дата последнего сбора статистики
     , ...
  from all_tables
 where owner = 'HR'

ClickHouse nėra išimtis:

select name
     , engine -- Используемый "движок" ("MergeTree", "Dictionary" etc)
     , ...
  from system.tables
 where database = 'HR'

Kažką panašaus galima padaryti naudojant „Cassandra“ (kurioje vietoj lentelių yra stulpelių šeimos, o vietoj schemų – klavišų tarpai):

select columnfamily_name
     , compaction_strategy_class  -- Стратегия сборки мусора
     , gc_grace_seconds           -- Время жизни мусора
     , ...
  from system.schema_columnfamilies
 where keyspace_name = 'HR'

Daugumoje kitų duomenų bazių taip pat galite pateikti panašias užklausas (net Mongo turi specialios sistemos kolekcija, kuriame yra informacija apie visas sistemos kolekcijas).

Žinoma, tokiu būdu galite gauti informacijos ne tik apie lenteles, bet apskritai apie bet kokį objektą. Kartkartėmis malonūs žmonės dalijasi tokiu skirtingų duomenų bazių kodu, kaip, pavyzdžiui, habra straipsnių serijoje „PostgreSQL duomenų bazių dokumentavimo funkcijos“ (Ayb, Benas, sporto salė). Žinoma, labai malonu laikyti visą šį kalną užklausų savo galvoje ir nuolat jas rašyti, todėl savo mėgstamoje IDE/redagavimo priemonėje turiu iš anksto paruoštą fragmentų rinkinį dažnai naudojamoms užklausoms, ir belieka įvesti objektų pavadinimus į šabloną.

Dėl to šis naršymo ir objektų paieškos būdas yra daug lankstesnis, sutaupo daug laiko ir leidžia gauti būtent tokią informaciją, kokia ji dabar reikalinga (kaip, pavyzdžiui, aprašyta įraše "Duomenų eksportavimas iš duomenų bazės bet kokiu formatu: ką IDE gali padaryti IntelliJ platformoje").

Operacijos su objektais

Radę ir ištyrę reikalingus objektus, laikas su jais nuveikti ką nors naudingo. Natūralu, taip pat neatitraukiant pirštų nuo klaviatūros.

Ne paslaptis, kad tiesiog ištrynus lentelę beveik visose duomenų bazėse atrodys vienodai:

drop table hr.persons

Tačiau sukūrus lentelę tampa įdomiau. Beveik bet kuri DBVS (įskaitant daugelį NoSQL) gali „sukurti lentelę“ viena ar kita forma, o pagrindinė jos dalis net šiek tiek skirsis (pavadinimas, stulpelių sąrašas, duomenų tipai), tačiau kitos detalės gali labai skirtis ir priklausyti nuo vidinis įrenginys ir konkrečios DBVS galimybės. Mano mėgstamiausias pavyzdys yra tas, kad „Oracle“ dokumentacijoje yra tik „nuogi“ BNF „sukurti lentelę“ sintaksei. užima 31 puslapį. Kitos DBVS turi kuklesnes galimybes, tačiau kiekviena iš jų taip pat turi daug įdomių ir unikalių lentelių kūrimo funkcijų (postgres, mySQL, tarakonas, Cassandra). Vargu ar koks nors grafinis „vedlys“ iš kitos IDE (ypač universalios) sugebės iki galo aprėpti visus šiuos gebėjimus, o jei ir sugebės, tai tikrai nebus reginys silpnaširdžiams. Kartu teisingai ir laiku surašytas pareiškimas sukurti lentelę leis lengvai jomis naudotis, saugoti ir pasiekti savo duomenis patikimus, optimalius ir kuo patogesnius.

Be to, daugelis DBVS turi savo specifinius objektų tipus, kurių nėra kitose DBVS. Be to, galime atlikti operacijas ne tik su duomenų bazės objektais, bet ir su pačia DBVS, pavyzdžiui, „nužudyti“ procesą, atlaisvinti dalį atminties, įjungti sekimą, perjungti į „tik skaitymo“ režimą ir dar daugiau.

Dabar šiek tiek pieškime

Viena iš dažniausiai atliekamų užduočių yra sukurti diagramą su duomenų bazės objektais ir pamatyti objektus bei jų ryšius gražiame paveikslėlyje. Tai gali padaryti beveik bet kuri grafinė IDE, atskiros „komandinės eilutės“ paslaugos, specializuoti grafiniai įrankiai ir modeliuotojai. Jie nupieš ką nors už jus „kiek gali“, o jūs galite šiek tiek paveikti šį procesą tik naudodami kelis parametrus konfigūracijos faile arba sąsajos žymimuosius laukelius.

Tačiau šią problemą galima išspręsti daug paprasčiau, lanksčiau ir elegantiškiau, ir, žinoma, naudojant kodą. Norėdami sukurti bet kokio sudėtingumo diagramas, turime keletą specializuotų žymėjimo kalbų (DOT, GraphML ir kt.) ir joms daugybę programų (GraphViz, PlantUML, Mermaid), kurios gali skaityti tokias instrukcijas ir vizualizuoti jas įvairiais formatais. . Na, mes jau žinome, kaip gauti informacijos apie objektus ir ryšius tarp jų.

Štai mažas pavyzdys, kaip tai galėtų atrodyti naudojant PlantUML ir demo duomenų bazė, skirta PostgreSQL (kairėje yra SQL užklausa, kuri sugeneruos reikiamą „PlantUML“ instrukciją, o dešinėje – rezultatas):

„Duomenų bazės kaip kodas“ patirtis

select '@startuml'||chr(10)||'hide methods'||chr(10)||'hide stereotypes' union all
select distinct ccu.table_name || ' --|> ' ||
       tc.table_name as val
  from table_constraints as tc
  join key_column_usage as kcu
    on tc.constraint_name = kcu.constraint_name
  join constraint_column_usage as ccu
    on ccu.constraint_name = tc.constraint_name
 where tc.constraint_type = 'FOREIGN KEY'
   and tc.table_name ~ '.*' union all
select '@enduml'

Ir jei šiek tiek pabandysi, tada remiantis ER šablonas, skirtas PlantUML galite gauti kažką labai panašaus į tikrą ER diagramą:

SQL užklausa yra šiek tiek sudėtingesnė

-- Шапка
select '@startuml
        !define Table(name,desc) class name as "desc" << (T,#FFAAAA) >>
        !define primary_key(x) <b>x</b>
        !define unique(x) <color:green>x</color>
        !define not_null(x) <u>x</u>
        hide methods
        hide stereotypes'
 union all
-- Таблицы
select format('Table(%s, "%s n information about %s") {'||chr(10), table_name, table_name, table_name) ||
       (select string_agg(column_name || ' ' || upper(udt_name), chr(10))
          from information_schema.columns
         where table_schema = 'public'
           and table_name = t.table_name) || chr(10) || '}'
  from information_schema.tables t
 where table_schema = 'public'
 union all
-- Связи между таблицами
select distinct ccu.table_name || ' "1" --> "0..N" ' || tc.table_name || format(' : "A %s may haven many %s"', ccu.table_name, tc.table_name)
  from information_schema.table_constraints as tc
  join information_schema.key_column_usage as kcu on tc.constraint_name = kcu.constraint_name
  join information_schema.constraint_column_usage as ccu on ccu.constraint_name = tc.constraint_name
 where tc.constraint_type = 'FOREIGN KEY'
   and ccu.constraint_schema = 'public'
   and tc.table_name ~ '.*'
 union all
-- Подвал
select '@enduml'

„Duomenų bazės kaip kodas“ patirtis

Jei atidžiai pažvelgsite, daugelis vizualizacijos įrankių po gaubtu taip pat naudoja panašias užklausas. Tiesa, šie prašymai dažniausiai būna gilūs „įjungti“ į pačios programos kodą ir yra sunkiai suprantami, jau nekalbant apie jokias jų modifikacijas.

Metrika ir stebėjimas

Pereikime prie tradiciškai sudėtingos temos – duomenų bazės veikimo stebėjimo. Prisimenu nedidelę tikrą istoriją, kurią man papasakojo „vienas iš mano draugų“. Kitame projekte gyveno tam tikras galingas DBA, ir tik nedaugelis kūrėjų jį pažinojo asmeniškai arba kada nors buvo jį matę (nepaisant to, kad, remiantis gandais, jis dirbo kažkur kitame pastate). „X“ valandą, kai didelio mažmenininko podukcijos sistema vėl pradėjo „blogai jaustis“, jis tyliai išsiuntė „Oracle Enterprise Manager“ grafikų ekrano kopijas, kuriose raudonu „suprantamumo“ žymekliu kruopščiai paryškino svarbiausias vietas ( tai, švelniai tariant, nelabai padėjo). Ir pagal šią „nuotraukų kortelę“ turėjau gydytis. Tuo pačiu metu niekas neturėjo prieigos prie brangaus (abiem šio žodžio prasmėmis) įmonės vadovo, nes sistema yra sudėtinga ir brangi, staiga „kūrėjai ant kažko suklumpa ir viską sulaužo“. Todėl kūrėjai „empiriškai“ surado stabdžių vietą ir priežastį bei išleido pleistrą. Jei grėsmingas laiškas iš DBA artimiausiu metu vėl nepasiektų, tada visi lengviau atsikvėptų ir grįžtų prie savo dabartinių užduočių (iki naujo Laiško).

Tačiau stebėjimo procesas gali atrodyti linksmesnis ir draugiškesnis, o svarbiausia – visiems prieinamas ir skaidrus. Bent jau pagrindinė jo dalis, kaip priedas prie pagrindinių stebėjimo sistemų (kurios tikrai naudingos ir daugeliu atvejų nepakeičiamos). Bet kuri DBVS gali laisvai ir visiškai nemokamai dalytis informacija apie dabartinę jos būseną ir veikimą. Toje pačioje „kruvinoje“ Oracle DB iš sistemos rodinių galima gauti beveik bet kokią informaciją apie našumą, pradedant nuo procesų ir seansų iki buferio talpyklos būsenos (pvz., DBA scenarijaiskiltyje „Stebėjimas“). Postgresql taip pat turi daugybę sistemos vaizdų duomenų bazės stebėjimas, ypač tie, kurie yra būtini bet kurios DBA kasdieniame gyvenime, pvz pg_stat_activity, pg_stat_database, pg_stat_bgwriter. „MySQL“ tam netgi turi atskirą schemą. našumo_schema. A In Mongo įmontuotas profiliuotojas sujungia našumo duomenis į sistemos rinkinį sistema.profilis.

Taigi, apsiginklavę tam tikru metrikų rinktuvu (Telegraf, Metricbeat, Collectd), galinčiu atlikti pasirinktines sql užklausas, šios metrikos saugyklą (InfluxDB, Elasticsearch, Timescaledb) ir vizualizatorių (Grafana, Kibana), galite gauti gana nesunkiai. ir lanksti stebėjimo sistema, kuri bus glaudžiai integruota su kitomis visos sistemos metrikomis (gaunama, pavyzdžiui, iš programų serverio, iš OS ir pan.). Kaip, pavyzdžiui, tai daroma pgwatch2, kuris naudoja InfluxDB + Grafana derinį ir užklausų rinkinį sistemos rodiniams, kuriuos taip pat galima pasiekti pridėti pasirinktinių užklausų.

Iš viso

Ir tai tik apytikslis sąrašas, ką galima padaryti su mūsų duomenų baze naudojant įprastą SQL kodą. Esu tikras, kad rasite daug daugiau naudojimo būdų, parašykite komentaruose. Ir mes kalbėsime apie tai, kaip (ir, svarbiausia, kodėl) visa tai automatizuoti ir įtraukti į savo CI/CD konvejerį kitą kartą.

Šaltinis: www.habr.com

Добавить комментарий