"Andmebaas kui kood" kogemus

"Andmebaas kui kood" kogemus

SQL, mis võiks olla lihtsam? Igaüks meist saab kirjutada lihtsa päringu – me kirjutame valima, loetlege vajalikud veerud ja seejärel Alates, tabeli nimi, mõned tingimused kus ja see on kõik – kasulikud andmed on meie taskus ja (peaaegu) olenemata sellest, milline DBMS on sel ajal kapoti all (või võib-olla pole üldse DBMS). Selle tulemusena võib peaaegu iga andmeallikaga (relatsioonilise ja mitte nii) töötamist vaadelda tavalise koodi seisukohast (koos kõige sellega, mida see tähendab - versioonikontroll, koodi ülevaatus, staatiline analüüs, automaattestid ja see on kõik). Ja see ei kehti ainult andmete endi, skeemide ja migratsioonide kohta, vaid üldiselt kogu salvestuse eluea kohta. Selles artiklis räägime igapäevastest ülesannetest ja erinevate andmebaasidega töötamise probleemidest "andmebaas kui kood" objektiivi all.

Ja alustame kohe ORM. Esimesi "SQL vs ORM" tüüpi lahinguid märgati tagasi eel-Petriini Venemaa.

Objektide suhteline kaardistamine

ORM-i toetajad hindavad traditsiooniliselt kiirust ja arenduslihtsust, sõltumatust DBMS-ist ja puhast koodi. Paljude meist on andmebaasiga (ja sageli ka andmebaasiga) töötamise kood

tavaliselt näeb see välja umbes selline...

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

Mudel on üles riputatud nutikate annotatsioonidega ja kuskil kulisside taga genereerib ja käivitab vapper ORM tonni SQL-koodi. Muide, arendajad püüavad anda endast parima, et isoleerida end oma andmebaasist kilomeetrite pikkuste abstraktsioonidega, mis viitab "SQL vihkamine".

Teisel pool barrikaade märgivad puhta "käsitsi valmistatud" SQL-i järgijad võimet oma DBMS-ist kogu mahl ilma täiendavate kihtide ja abstraktsioonideta välja pigistada. Selle tulemusena tekivad “andmekesksed” projektid, kus andmebaasi on kaasatud spetsiaalselt koolitatud inimesed (nad on ka “basicists”, nemad on ka “basicists”, nad on ka “basdenerid” jne), ja arendajad. tuleb vaid valmis vaated ja salvestatud protseduurid “tõmmata”, detailidesse laskumata.

Mis siis, kui meil oleks mõlemast maailmast parim? Kuidas seda tehakse imelises elujaatava nimega tööriistas Yesql. Toon oma vabas tõlkes paar rida üldkontseptsioonist ja saate sellega lähemalt tutvuda siin.

Clojure on lahe keel DSL-ide loomiseks, kuid SQL ise on lahe DSL ja me ei vaja teist. S-avaldised on toredad, aga midagi uut nad siia juurde ei anna. Selle tulemusena saame sulgude huvides sulgud. Ei nõustu? Seejärel oota hetke, mil andmebaasi üle olev abstraktsioon hakkab lekkima ja hakkad funktsiooniga võitlema (raw-sql)

Mida ma siis tegema peaksin? Jätame SQL-i tavaliseks SQL-iks – üks fail päringu kohta:

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

... ja seejärel lugege seda faili, muutes selle tavaliseks Clojure'i funktsiooniks:

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

Järgides põhimõtet "SQL ise, Clojure ise" saate:

  • Ei mingeid süntaktilisi üllatusi. Teie andmebaas (nagu mis tahes muu) ei ühildu 100% SQL-i standardiga, kuid see ei ole Yesqli jaoks oluline. Te ei raiska kunagi aega SQL-i samaväärse süntaksiga funktsioonide otsimisele. Te ei pea kunagi funktsiooni juurde tagasi pöörduma (raw-sql "some('funky'::SYNTAX)")).
  • Parim toimetaja tugi. Teie redaktoril on juba suurepärane SQL-i tugi. Salvestades SQL-i SQL-ina, saate seda lihtsalt kasutada.
  • Meeskonna ühilduvus. Teie DBA-d saavad lugeda ja kirjutada SQL-i, mida kasutate oma Clojure'i projektis.
  • Lihtsam jõudluse häälestamine. Kas vajate probleemse päringu jaoks plaani koostamist? See ei ole probleem, kui teie päring on tavaline SQL.
  • Päringute taaskasutamine. Pukseerige need samad SQL-failid teistesse projektidesse, sest see on lihtsalt vana SQL – lihtsalt jagage seda.

Idee on minu meelest väga lahe ja samas väga lihtne, tänu millele on projekt saanud palju juurde järgijaid erinevates keeltes. Järgmisena proovime rakendada sarnast filosoofiat, mille kohaselt eraldame SQL-koodi kõigest muust, mis jääb ORM-ist kaugemale.

IDE ja DB haldurid

Alustame lihtsa igapäevase ülesandega. Tihtipeale peame andmebaasist mingeid objekte otsima, näiteks leidma skeemis tabeli ja uurima selle struktuuri (millisi veerge, võtmeid, indekseid, piiranguid jne kasutatakse). Ja mis tahes graafiliselt IDE-lt või väikeselt DB-haldurilt ootame kõigepealt just neid võimeid. Et oleks kiire ja ei peaks pool tundi ootama, kuni joonistub vajaliku infoga aken (eriti aeglase ühenduse puhul kaugandmebaasiga) ning samas oleks saadud info värske ja asjakohane, ja mitte vahemällu salvestatud rämpsu. Veelgi enam, mida keerulisem ja suurem on andmebaas ja mida suurem on nende arv, seda keerulisem on seda teha.

Aga tavaliselt viskan hiire ära ja kirjutan lihtsalt koodi. Oletame, et peate välja selgitama, millised tabelid (ja milliste omadustega) sisalduvad skeemis "HR". Enamikus DBMS-ides saab soovitud tulemuse saavutada selle lihtsa päringuga teabe_skeemist:

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

Andmebaasiti varieerub selliste viitetabelite sisu sõltuvalt iga DBMS-i võimalustest. Ja näiteks MySQL-i jaoks saate samast teatmeraamatust hankida selle DBMS-i spetsiifilised tabeliparameetrid:

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

Oracle ei tea information_schema, kuid teab Oracle'i metaandmedja suuri probleeme ei teki:

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

ClickHouse pole erand:

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

Midagi sarnast saab teha Cassandras (millel on tabelite asemel veerupered ja skeemide asemel võtmeruumid):

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

Enamiku teiste andmebaaside puhul saate esitada ka sarnaseid päringuid (isegi Mongol on spetsiaalne süsteemikogu, mis sisaldab teavet kõigi süsteemi kogude kohta).

Loomulikult saate sel viisil teavet mitte ainult tabelite, vaid üldiselt iga objekti kohta. Aeg-ajalt jagavad lahked inimesed erinevate andmebaaside jaoks sellist koodi, nagu näiteks habra artiklite sarjas “PostgreSQL-i andmebaaside dokumenteerimise funktsioonid” (Ayb, Ben, Jõusaal). Muidugi on kogu selle päringute mäe peas hoidmine ja nende pidev tippimine nii meeldiv, nii et mu lemmik-IDE/redaktoris on sageli kasutatavate päringute jaoks eelnevalt ettevalmistatud juppide komplekt ja jääb üle vaid sisestada objektide nimed malli.

Tänu sellele on see objektide navigeerimise ja otsimise meetod palju paindlikum, säästab palju aega ja võimaldab teil saada täpselt teavet sellisel kujul, nagu see on nüüd vajalik (nagu näiteks postituses kirjeldatud "Andmete eksportimine andmebaasist mis tahes vormingus: mida IDE-d saavad IntelliJ platvormil teha").

Operatsioonid objektidega

Kui oleme vajalikud esemed leidnud ja uurinud, on aeg nendega midagi kasulikku ette võtta. Loomulikult ka sõrmi klaviatuurilt ära võtmata.

Pole saladus, et tabeli lihtsalt kustutamine näeb peaaegu kõigis andmebaasides välja sama:

drop table hr.persons

Kuid tabeli loomisega muutub see huvitavamaks. Peaaegu kõik DBMS-id (sh paljud NoSQL-id) võivad ühel või teisel kujul "tabelit luua" ja selle põhiosa võib isegi veidi erineda (nimi, veergude loend, andmetüübid), kuid muud üksikasjad võivad oluliselt erineda ja sõltuda konkreetse DBMS-i siseseade ja võimalused. Minu lemmiknäide on see, et Oracle'i dokumentatsioonis on tabeli loomise süntaksi jaoks ainult "paljad" BNF-id võtab enda alla 31 lehekülge. Teistel DBMS-idel on tagasihoidlikumad võimalused, kuid igal neist on ka palju huvitavaid ja ainulaadseid funktsioone tabelite loomiseks (postgres, MySQL, prussakas, Cassandra). On ebatõenäoline, et mõni muu IDE (eriti universaalne) graafiline “viisard” suudab kõiki neid võimeid täielikult katta ja isegi kui suudab, ei saa see nõrganärvilistele vaatemänguks. Samas korrektselt ja õigeaegselt kirjutatud avaldus loo tabel võimaldab teil neid kõiki hõlpsasti kasutada, muuta andmete salvestamise ja juurdepääsu usaldusväärseks, optimaalseks ja võimalikult mugavaks.

Samuti on paljudel DBMS-idel oma kindlat tüüpi objektid, mis pole teistes DBMS-ides saadaval. Lisaks saame teha toiminguid mitte ainult andmebaasiobjektidega, vaid ka DBMS-i endaga, näiteks "tappa" protsessi, vabastada mäluruumi, võimaldada jälgimist, lülituda "kirjutuskaitstud" režiimi ja palju muud.

Nüüd joonistame natuke

Üks levinumaid ülesandeid on koostada andmebaasiobjektidega diagramm ning näha objekte ja nendevahelisi seoseid ilusal pildil. Peaaegu iga graafiline IDE, eraldi käsurea utiliidid, spetsiaalsed graafilised tööriistad ja modelleerijad saavad seda teha. Nad joonistavad teie jaoks midagi "nii hästi kui suudavad" ja saate seda protsessi veidi mõjutada ainult mõne konfiguratsioonifaili parameetri või liidese märkeruutude abil.

Kuid seda probleemi saab lahendada palju lihtsamalt, paindlikumalt ja elegantsemalt ning loomulikult koodi abil. Mis tahes keerukusega diagrammide loomiseks on meil mitu spetsiaalset märgistuskeelt (DOT, GraphML jne) ja nende jaoks terve hulk rakendusi (GraphViz, PlantUML, Mermaid), mis suudavad selliseid juhiseid lugeda ja neid erinevates vormingutes visualiseerida. . Noh, me juba teame, kuidas saada teavet objektide ja nendevaheliste seoste kohta.

Siin on väike näide sellest, kuidas see välja näeb, kasutades PlantUML ja demo andmebaas PostgreSQL jaoks (vasakul on SQL-päring, mis genereerib PlantUML-i jaoks vajaliku juhise, ja paremal on tulemus):

"Andmebaas kui kood" kogemus

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 kui natuke proovida, siis selle põhjal PlantUML-i ER-mall võite saada midagi väga sarnast tõelise ER diagrammiga:

SQL-päring on veidi keerulisem

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

"Andmebaas kui kood" kogemus

Kui vaatate tähelepanelikult, kasutavad sarnaseid päringuid ka paljud visualiseerimistööriistad. Tõsi, need taotlused on tavaliselt sügavad "juhtmega" ühendatud rakenduse enda koodiga ja neid on raske mõista, rääkimata nende muutmisest.

Mõõdikud ja seire

Liigume edasi traditsiooniliselt keerulise teema juurde – andmebaaside jõudluse jälgimine. Mäletan väikest tõestisündinud lugu, mille rääkis mulle "üks mu sõber". Teises projektis elas teatud võimas DBA ja vähesed arendajad tundsid teda isiklikult või olid teda kunagi isiklikult näinud (hoolimata sellest, et kuulujuttude kohaselt töötas ta kuskil kõrvalhoones). Kell “X”, kui suure jaemüüja poduktsioonisüsteem hakkas taas “halvasti tundma”, saatis ta vaikselt Oracle Enterprise Managerilt graafikute ekraanipilte, millel ta “arusaadavuse” huvides punase markeriga kriitilised kohad hoolikalt esile tõstis ( see pehmelt öeldes ei aidanud palju). Ja selle “fotokaardi” põhjal pidin ravima. Samas polnud kellelgi ligipääsu hinnalisele (selle sõna mõlemas tähenduses) ettevõttejuhile, sest süsteem on keeruline ja kallis, äkki "arendajad komistavad millegi otsa ja lõhuvad kõik." Seetõttu leidsid arendajad “empiiriliselt” pidurite asukoha ja põhjuse ning vabastasid plaastri. Kui DBA-st ähvardav kiri lähiajal uuesti ei saabuks, siis hingaksid kõik kergendatult ja naaseksid oma seniste ülesannete juurde (kuni uue kirjani).

Kuid jälgimisprotsess võib tunduda lõbusam ja sõbralikum ning mis kõige tähtsam, kõigile kättesaadav ja läbipaistev. Vähemalt selle põhiosa, lisana peamistele seiresüsteemidele (mis on kindlasti kasulikud ja paljudel juhtudel asendamatud). Iga DBMS on vabalt ja täiesti tasuta, et jagada teavet oma hetkeseisu ja toimivuse kohta. Samas "verises" Oracle DB-s saab süsteemivaadetest saada peaaegu igasugust teavet jõudluse kohta, alates protsessidest ja seanssidest kuni puhvervahemälu olekuni (näiteks DBA skriptid, jaotis "Jälgimine"). Postgresqlil on ka terve hulk süsteemivaateid andmebaasi jälgimine, eriti need, mis on mis tahes DBA igapäevaelus asendamatud, nt pg_stat_activity, pg_stat_database, pg_stat_bgwriter. MySQL-il on selle jaoks isegi eraldi skeem. jõudlusskeem. A In Mongo sisseehitatud profileerija koondab jõudlusandmed süsteemikogusse süsteem.profiil.

Seega, olles relvastatud mingisuguse mõõdikukogujaga (Telegraf, Metricbeat, Collectd), mis suudab sooritada kohandatud SQL-päringuid, nende mõõdikute salvestusruumi (InfluxDB, Elasticsearch, Timescaledb) ja visualiseerijaga (Grafana, Kibana), saate üsna lihtsa ja paindlik seiresüsteem, mis integreeritakse tihedalt teiste kogu süsteemi hõlmavate mõõdikutega (saadakse näiteks rakendusserverist, OS-ist jne). Nagu näiteks, seda tehakse pgwatch2-s, mis kasutab kombinatsiooni InfluxDB + Grafana ja päringute komplekti süsteemivaadetele, millele pääseb juurde ka lisada kohandatud päringuid.

Kogusummas

Ja see on vaid ligikaudne loetelu sellest, mida saab meie andmebaasiga tavalise SQL-koodi abil teha. Olen kindel, et leiate palju rohkem kasutusviise, kirjutage kommentaaridesse. Ja me räägime sellest, kuidas (ja mis kõige tähtsam, miks) seda kõike automatiseerida ja järgmisel korral oma CI/CD torusse lisada.

Allikas: www.habr.com

Lisa kommentaar