"Tietokanta koodina" -kokemus

"Tietokanta koodina" -kokemus

SQL, mikä voisi olla yksinkertaisempaa? Jokainen meistä voi kirjoittaa yksinkertaisen pyynnön - me kirjoitamme valita, luettele sitten tarvittavat sarakkeet alkaen, taulukon nimi, joitain ehtoja jossa ja siinä kaikki - hyödyllistä dataa on taskussamme ja (melkein) riippumatta siitä mikä DBMS on konepellin alla tuolloin (tai ehkä ei ollenkaan DBMS). Seurauksena on, että työskentelyä melkein minkä tahansa tietolähteen kanssa (relaatio- ja ei-toisin sanoen) voidaan harkita tavallisen koodin näkökulmasta (kaikella, mitä se tarkoittaa - versionhallinta, koodin tarkistus, staattinen analyysi, automaattiset testit ja siinä kaikki). Ja tämä ei koske vain itse dataa, skeemoja ja siirtoja, vaan yleensä koko tallennustilan käyttöikää. Tässä artikkelissa puhumme jokapäiväisistä tehtävistä ja eri tietokantojen kanssa työskentelyn ongelmista "tietokanta koodina" linssin alla.

Ja aloitetaan heti käärme. Ensimmäiset "SQL vs ORM" -tyyppiset taistelut havaittiin takaisin pre-Petrinen Venäjä.

Objekti-relaatiokartoitus

ORM:n kannattajat arvostavat perinteisesti nopeutta ja helppoutta kehitystä, riippumattomuutta DBMS:stä ja puhdasta koodia. Monille meistä tietokannan (ja usein itse tietokannan) kanssa työskentelyn koodi

se näyttää yleensä tältä...

@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;
    }
  ...

Malli on ripustettu älykkäillä huomautuksilla, ja jossain kulissien takana urhoollinen ORM luo ja suorittaa tonnia SQL-koodia. Muuten, kehittäjät yrittävät parhaansa mukaan eristää itsensä tietokannastaan ​​kilometrien abstraktioilla, mikä viittaa "SQL-viha".

Barrikadien toisella puolella puhtaan "käsintehdyn" SQL:n kannattajat huomaavat kykynsä puristaa kaikki mehu pois DBMS-järjestelmästään ilman ylimääräisiä kerroksia ja abstraktioita. Tuloksena syntyy "tietokeskeisiä" projekteja, joissa tietokantaan ovat mukana erikoiskoulutetut ihmiset (he ovat myös "perusmiehiä", he ovat myös "perusaattoreita", he ovat myös "basdenereita" jne.) ja kehittäjät. tarvitsee vain "vetää" valmiit näkemykset ja tallennetut menettelyt yksityiskohtiin menemättä.

Mitä jos meillä olisi molempien maailmojen parhaat puolet? Kuinka tämä tehdään upealla työkalulla, jolla on elämää vahvistava nimi Yesql. Annan pari riviä yleisestä konseptista vapaassa käännöksessäni, ja voit tutustua siihen tarkemmin täällä.

Clojure on hieno kieli DSL:ien luomiseen, mutta SQL itsessään on hieno DSL, emmekä tarvitse toista. S-lausekkeet ovat hienoja, mutta ne eivät lisää tähän mitään uutta. Seurauksena on, että saamme kiinnikkeet kiinnikkeiden vuoksi. Ei ole samaa mieltä? Odota sitten hetkeä, jolloin tietokannan ylittävä abstraktio alkaa vuotaa ja alat taistella funktion kanssa (raw-sql)

Eli mitä minun pitäisi tehdä? Jätetään SQL tavalliseksi SQL:ksi - yksi tiedosto per pyyntö:

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

... ja lue sitten tämä tiedosto muuttamalla se tavalliseksi Clojure-funktioksi:

(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" ...} ...)

Noudattamalla "SQL itsessään, Clojure itsessään" -periaatetta saat:

  • Ei syntaktisia yllätyksiä. Tietokantasi (kuten mikään muu) ei ole 100% SQL-standardin mukainen - mutta tällä ei ole merkitystä Yesql:lle. Et koskaan tuhlaa aikaa SQL-vastaavan syntaksin funktioiden metsästämiseen. Sinun ei koskaan tarvitse palata toimintoon (raw-sql "some('funky'::SYNTAX)")).
  • Paras editorin tuki. Editorillasi on jo erinomainen SQL-tuki. Tallentamalla SQL:n SQL:nä voit yksinkertaisesti käyttää sitä.
  • Joukkueen yhteensopivuus. DBA:si voivat lukea ja kirjoittaa SQL:ää, jota käytät Clojure-projektissasi.
  • Helpompi suorituskyvyn viritys. Tarvitsetko suunnitelman ongelmallista kyselyä varten? Tämä ei ole ongelma, kun kyselysi on tavallinen SQL.
  • Kyselyjen uudelleenkäyttö. Vedä ja pudota samat SQL-tiedostot muihin projekteihin, koska se on pelkkä vanha SQL - jaa se.

Minusta idea on erittäin siisti ja samalla hyvin yksinkertainen, minkä ansiosta projekti on saanut paljon seuraajia useilla eri kielillä. Ja seuraavaksi yritämme soveltaa samanlaista filosofiaa erottaa SQL-koodi kaikesta muusta paljon ORM:n ulkopuolella.

IDE- ja DB-päälliköt

Aloitetaan yksinkertaisesta jokapäiväisestä tehtävästä. Usein joudumme etsimään tietokannasta joitain objekteja, esimerkiksi etsimään kaaviosta taulukon ja tutkimaan sen rakennetta (mitä sarakkeita, avaimia, indeksejä, rajoitteita jne. käytetään). Ja miltä tahansa graafiselta IDE:ltä tai pieneltä DB-managerilta odotamme ensinnäkin juuri näitä kykyjä. Jotta se on nopeaa ja sinun ei tarvitse odottaa puolta tuntia, kunnes tarvittavat tiedot on piirretty (etenkin jos yhteys on hidas etätietokantaan), ja samalla vastaanotettu tieto on tuoretta ja relevanttia, eikä välimuistissa olevaa roskaa. Lisäksi mitä monimutkaisempi ja suurempi tietokanta ja mitä enemmän niitä on, sitä vaikeampaa se on tehdä.

Mutta yleensä heitän hiiren pois ja kirjoitan vain koodin. Oletetaan, että sinun on selvitettävä, mitkä taulukot (ja millä ominaisuuksilla) sisältyvät "HR"-skeemaan. Useimmissa DBMS-järjestelmissä haluttu tulos voidaan saavuttaa tällä yksinkertaisella kyselyllä information_schema:

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

Tietokannasta tietokantaan tällaisten viitetaulukoiden sisältö vaihtelee kunkin DBMS:n ominaisuuksien mukaan. Ja esimerkiksi MySQL:lle, samasta hakuteoksesta saat tälle tietokantajärjestelmälle ominaiset taulukkoparametrit:

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

Oracle ei tunne information_schemaa, mutta se tietää Oraclen metatiedotja suuria ongelmia ei synny:

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

ClickHouse ei ole poikkeus:

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

Jotain vastaavaa voidaan tehdä Cassandrassa (jossa on sarakeperheet taulukoiden sijaan ja avainvälit skeemojen sijaan):

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

Useimmille muille tietokannoille voit myös keksiä samanlaisia ​​kyselyitä (jopa Mongolla on erityinen järjestelmäkokoelma, joka sisältää tiedot kaikista järjestelmän kokoelmista).

Tietenkin tällä tavalla voit saada tietoa ei vain taulukoista, vaan mistä tahansa objektista yleensä. Ajoittain ystävälliset ihmiset jakavat tällaista koodia eri tietokantoihin, kuten esimerkiksi habra-artikkelisarjassa "PostgreSQL-tietokantojen dokumentointitoiminnot" (Ayb, ben, kuntosali). Tietysti on ilo pitää mielessäni tämä koko kyselyvuoristo ja kirjoittaa niitä jatkuvasti, joten suosikki-IDE-editorissani on valmiiksi valmisteltu joukko katkelmia usein käytettyjä kyselyitä varten, ja sinun tarvitsee vain kirjoittaa objektien nimet malliin.

Tämän seurauksena tämä tapa navigoida ja etsiä kohteita on paljon joustavampi, säästää paljon aikaa ja antaa sinun saada tarkalleen tiedot siinä muodossa, jossa ne nyt ovat tarpeen (kuten esimerkiksi viestissä on kuvattu "Tietojen vienti tietokannasta missä tahansa muodossa: mitä IDE:t voivat tehdä IntelliJ-alustalla").

Toiminnot esineiden kanssa

Kun olemme löytäneet ja tutkineet tarvittavat esineet, on aika tehdä niille jotain hyödyllistä. Luonnollisesti myös ilman sormia näppäimistöltä.

Ei ole mikään salaisuus, että pelkkä taulukon poistaminen näyttää samalta lähes kaikissa tietokannoista:

drop table hr.persons

Mutta taulukon luomisen myötä siitä tulee mielenkiintoisempaa. Melkein mikä tahansa DBMS (mukaan lukien monet NoSQL:t) voi "luoda taulukon" muodossa tai toisessa, ja suurin osa siitä eroaa jopa hieman (nimi, sarakeluettelo, tietotyypit), mutta muut yksityiskohdat voivat vaihdella dramaattisesti ja riippua sisäinen laite ja tietyn DBMS:n ominaisuudet. Suosikkiesimerkkini on, että Oraclen dokumentaatiossa on vain "alasti" BNF:t "luo taulukko" -syntaksille. vievät 31 sivua. Muilla DBMS-järjestelmillä on vaatimattomammat ominaisuudet, mutta jokaisessa niistä on myös monia mielenkiintoisia ja ainutlaatuisia ominaisuuksia taulukoiden luomiseen (postgres, mysql, torakka, Cassandra). On epätodennäköistä, että mikään toisen IDE:n graafinen "velho" (etenkin yleismaailmallisesta) pystyisi täysin peittämään kaikki nämä kyvyt, ja vaikka pystyisikin, se ei ole spektaakkeli heikkohermoisille. Samalla oikein ja oikea-aikaisesti kirjoitettu lausunto luo taulukko avulla voit helposti käyttää niitä kaikkia, tehdä tallennustilasta ja pääsystä tietoihisi luotettavaa, optimaalista ja mahdollisimman mukavaa.

Lisäksi monilla DBMS-järjestelmillä on omat tietyntyyppiset objektinsa, joita ei ole saatavilla muissa DBMS-järjestelmissä. Lisäksi voimme suorittaa toimintoja tietokantaobjektien lisäksi myös itse DBMS:lle, esimerkiksi "tappaa" prosessin, vapauttaa jonkin verran muistialuetta, ottaa käyttöön jäljityksen, vaihtaa "vain luku"-tilaan ja paljon muuta.

Piirretään nyt vähän

Yksi yleisimmistä tehtävistä on rakentaa kaavio tietokantaobjekteista ja nähdä objektit ja niiden väliset yhteydet kauniissa kuvassa. Lähes mikä tahansa graafinen IDE, erilliset "komentorivi"-apuohjelmat, erikoistuneet graafiset työkalut ja mallintajat voivat tehdä tämän. He piirtävät sinulle jotain "niin kuin pystyvät", ja voit vaikuttaa tähän prosessiin hieman vain muutaman parametrin avulla konfiguraatiotiedostossa tai käyttöliittymän valintaruuduilla.

Mutta tämä ongelma voidaan ratkaista paljon yksinkertaisemmin, joustavammin ja tyylikkäämmin, ja tietysti koodin avulla. Kaiken monimutkaisten kaavioiden luomiseksi meillä on useita erikoistuneita merkintäkieliä (DOT, GraphML jne.) ja niille koko joukko sovelluksia (GraphViz, PlantUML, Mermaid), jotka voivat lukea tällaisia ​​​​ohjeita ja visualisoida ne useissa eri muodoissa. . Tiedämme jo, kuinka saada tietoa esineistä ja niiden välisistä yhteyksistä.

Tässä on pieni esimerkki siitä, miltä tämä voisi näyttää käyttämällä PlantUML- ja demotietokanta PostgreSQL:lle (vasemmalla on SQL-kysely, joka luo vaaditun käskyn PlantUML:lle, ja oikealla on tulos):

"Tietokanta koodina" -kokemus

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'

Ja jos yrität vähän, niin sen perusteella ER-malli PlantUML:lle voit saada jotain hyvin samanlaista kuin todellinen ER-kaavio:

SQL-kysely on hieman monimutkaisempi

-- Шапка
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'

"Tietokanta koodina" -kokemus

Jos katsot tarkasti, monet visualisointityökalut käyttävät myös samanlaisia ​​kyselyitä. Totta, nämä pyynnöt ovat yleensä syvällisiä "kiinnitetty" itse sovelluksen koodiin ja niitä on vaikea ymmärtää, puhumattakaan niiden muuttamisesta.

Mittarit ja seuranta

Siirrytään perinteisesti monimutkaiseen aiheeseen - tietokannan suorituskyvyn seurantaan. Muistan pienen tositarinan, jonka minulle kertoi "yksi ystäväni". Toisessa projektissa asui tietty voimakas DBA, ja harvat kehittäjät tunsivat hänet henkilökohtaisesti tai olivat koskaan nähneet häntä henkilökohtaisesti (huolimatta siitä, että hän työskenteli huhujen mukaan jossain viereisessä rakennuksessa). Tunnilla “X”, kun suuren jälleenmyyjän poduction-järjestelmä alkoi taas “tuntua pahalta”, hän lähetti hiljaa kuvakaappauksia kaavioista Oracle Enterprise Managerista, joissa hän korosti huolellisesti kriittiset paikat punaisella ”ymmärrettävyyden” merkillä ( tämä, lievästi sanottuna, ei auttanut paljon). Ja tämän "valokuvakortin" perusteella minun piti hoitaa. Samaan aikaan kenelläkään ei ollut pääsyä arvokkaaseen (sanan molemmissa merkityksissä) Enterprise Manageriin, koska järjestelmä on monimutkainen ja kallis, yhtäkkiä "kehittäjät kompastuvat johonkin ja rikkovat kaiken". Siksi kehittäjät löysivät "empiirisesti" jarrujen sijainnin ja syyn ja julkaisivat korjaustiedoston. Jos DBA:n uhkaava kirje ei saapuisi uudelleen lähitulevaisuudessa, niin kaikki henkäisivät helpotuksesta ja palaisivat nykyisiin tehtäviinsä (uuteen kirjeeseen asti).

Mutta seurantaprosessi voi näyttää hauskempaa ja ystävällisempää, ja mikä tärkeintä, kaikkien saatavilla ja läpinäkyvämpää. Ainakin sen perusosa, lisäyksenä tärkeimpiin valvontajärjestelmiin (jotka ovat varmasti hyödyllisiä ja monissa tapauksissa korvaamattomia). Mikä tahansa DBMS on vapaasti ja täysin maksuton jakaa tietoa sen nykyisestä tilasta ja suorituskyvystä. Samassa "verisessä" Oracle DB:ssä lähes kaikki tiedot suorituskyvystä voidaan saada järjestelmänäkymistä, prosesseista ja istunnoista puskurin välimuistin tilaan (esim. DBA-skriptitosio "Seuranta"). Postgresqlissa on myös koko joukko järjestelmänäkymiä varten tietokannan seuranta, erityisesti ne, jotka ovat välttämättömiä minkä tahansa DBA:n jokapäiväisessä elämässä, kuten pg_stat_activity, pg_stat_tietokanta, pg_stat_bgwriter. MySQL:llä on jopa erillinen skeema tätä varten. suorituskykykaavio. A In Mongo sisäänrakennettu profiloija kokoaa suorituskykytiedot järjestelmäkokoelmaksi system.profile.

Siten aseistettuna jonkinlaisella metriikkakerääjällä (Telegraf, Metricbeat, Collectd), joka pystyy suorittamaan mukautettuja sql-kyselyitä, näiden mittareiden tallennustilalla (InfluxDB, Elasticsearch, Timescaledb) ja visualisoijalla (Grafana, Kibana), voit saada melko helpon ja joustava seurantajärjestelmä, joka integroidaan tiiviisti muihin järjestelmän laajuisiin mittareihin (saatu esimerkiksi sovelluspalvelimelta, käyttöjärjestelmästä jne.). Kuten esimerkiksi, tämä tehdään pgwatch2:ssa, joka käyttää InfluxDB + Grafana -yhdistelmää ja kyselyjoukkoa järjestelmänäkymiin, joihin pääsee myös lisää mukautettuja kyselyitä.

Yhteensä

Ja tämä on vain likimääräinen luettelo siitä, mitä voidaan tehdä tietokannassamme käyttämällä tavallista SQL-koodia. Olen varma, että löydät monia muita käyttötapoja, kirjoita kommentteihin. Ja puhumme siitä, kuinka (ja mikä tärkeintä, miksi) automatisoida tämä kaikki ja sisällyttää se CI/CD-putkistoon seuraavan kerran.

Lähde: will.com

Lisää kommentti