«Andmed kui kood» kogemus

«Andmed kui kood» kogemus

SQL, mis vĂ”iks olla lihtsam? IgaĂŒhel meist on vĂ”imalik kirjutada lihtne pĂ€ring — kirjutame select, loetleme vajalikud veerud, seejĂ€rel from, tabeli nimi, natuke tingimusi where ja kĂ”ik — kasulikud andmed on meie kĂ€sutuses, (peaaegu) sĂ”ltumatult sellest, millisest andmebaasisĂŒsteemist me rÀÀgime (vĂ”ib-olla see ei ole isegi ande sĂŒsteem)). Tulemuseks on see, et igasuguste andmeallikatega, olgu need siis relatsioonilised vĂ”i mitte, saab töötada nii, nagu see oleks lihtsalt kood (kĂ”ik sellega kaasneva — version control, code review, staatiline analĂŒĂŒs, automaattestimine ja kĂ”ik see). Ja see ei puuduta ainult andmeid, skeeme ja migreerimist, vaid tegelikult kogu andmehoidla elu. Selles artiklis rÀÀgime igapĂ€evastest ĂŒlesannetest ja probleemidest, millega puutuvad kokku erinevad andmebaasid, keskendudes "andmebaas kui kood".

Ja alustame otse ORM. Esimesed lahingud stiilis "SQL vs ORM" mÀrgati juba eelkÀija Venemaal.

Objekt-relatsiooniline kaardistamine

ORM-i toetajad hindavad traditsiooniliselt arendamise kiirust ja lihtsust, sĂ”ltumatust andmebaasisĂŒsteemist ning koodi puhtust. Paljude jaoks nĂ€eb andmebaasitöö kood (ja sageli isegi andmebaas ise)

tavaliselt vÀlja umbes nii


@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 varustatud nutikate annotatsioonidega, samal ajal kui hiilgav ORM genereerib ja tĂ€idab tohutult SQL-koodi. Pealegi pĂŒĂŒavad arendajad igasuguste abstraktsioonidega ennast oma andmebaasist eemal hoida, mis viitab teatud "SQL vihkamine".

Barrikaadide teisel poolel rÔhutavad puhta "handmade"-SQL-i pooldajad, et nad saavad oma andmebaasist maksimumi vÀlja pigistada ilma tÀiendavate kihtideta ja abstraktsioonideta. Selle tulemusena tekivad "data-centric" projektid, kus andmebaasidega tegelevad spetsiaalselt koolitatud inimesed (neid nimetatakse "andmebaasi spetsialistideks" vÔi "andmebaasi halduriteks" jne), ja arendajatele jÀÀb vaid "tÔmmata" valmis vaateid ja salvestatud protseduure, ilma detailidesse laskumata.

Aga mis oleks, kui vĂ”tta parimad omadused kummastki maailmast? Nii on tehtud selles suurepĂ€rases tööriistas, millel on elujĂ”uline nimi Yesql. Toodan paar rida ĂŒldisest kontseptsioonist oma vabalt tĂ”lgituna, aga rohkemate detailidega saab tutvuda siit.

Clojure on Ă€ge keel DSL-ide loomiseks, aga SQL on iseenesest juba Ă€ge DSL ning me ei vaja veel ĂŒht. S-vĂ€ljendid on kaunid, aga siin ei lisa need midagi uut. LĂ”puks saame meid ĂŒmbritsevaid sulge. Ei ole nĂ”us? Siis oodake hetk, mil andmebaasi abstraktsioon hakkab lekima ja te alustate vĂ”itlust funktsiooniga (raw-sql)

Ja mida teha? JĂ€tame SQL-i tavaliseks SQL-iks — ĂŒks fail ĂŒhe pĂ€ringu jaoks:

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


 ning seejÀrel lugege see fail ning muundage see tavaliseks Clojure funktsiooniks:

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

;;; Funktsioon nimega `users-by-country` on loodud.
;;; Kasutame seda:
(users-by-country {:country_code "GB"})
;=> ({:name "Kris" :country_code "GB" ...} ...)

JÀrgides pÔhimÔtet "SQL eraldi, Clojure eraldi", saad:

  • Ei mingeid sĂŒntaktilisi ĂŒllatusi. Teie andmebaas (nagu ĂŒkskĂ”ik milline teine) ei vasta SQL standardile 100% — aga Yesql jaoks pole see oluline. Te kunagi ei raiska aega funktsioonide otsimisele, mille sĂŒntaks on SQL-i omaga vĂ”rreldav. Te ei pea kunagi tagasi minema funktsiooni juurde (raw-sql "some (‘funky’ :: SYNTAX)")).
  • Parim toimetaja tugi. Teie toimetajal on juba suurepĂ€rane SQL-tugi. Hoides SQL-d SQL-ina, saate seda lihtsalt kasutada.
  • Klienditugi. Teie DBA-d saavad lugeda ja kirjutada SQL-i, mida kasutate oma Clojure projektis.
  • Lihtsam jĂ”udluse seadistamine. Kas peate koostama plaani probleemse pĂ€ringu jaoks? See ei ole probleem, kui teie pĂ€ring on tavaline SQL.
  • PĂ€ringute taaskasutamine. Looge need samad SQL-failid teistesse projektidesse, sest see on lihtsalt vana hea SQL — jagage seda lihtsalt.

Minu arvates on see idee vĂ€ga Ă€ge ja samas vĂ€ga lihtne, mille tĂ”ttu projekt on saanud palju jĂ€rgijad erinevates keeltes. Proovime edasi rakendada sarnast filosoofiat, et eraldada SQL-kood kĂ”igest muust kaugelt ĂŒle ORM-i piiri.

IDE & DB-haldurid

Alustame lihtsast igapĂ€evasest ĂŒlesandest. Tihti peame otsima andmebaasist teatud objekte, nĂ€iteks leidma skeemist tabeli ja uurima selle struktuuri (milliseid veerge, vĂ”ti, indekseid, piiranguid jne kasutatakse). Iga graafilise IDE vĂ”i vĂ€hemalt mingisuguse andmebaasi haldurilt ootame eelkĂ”ige just neid oskusi. Tahame, et see toimiks kiiresti ja et ei peaks ootama pool tundi, kuni avaneb aken vajaliku teabega (eriti aeglase kaugandmebaasi ĂŒhenduse korral), ja et saadud teave oleks vĂ€rske ja ajakohane, mitte vananenud vahemĂ€lu. Mida keerulisem ja suurem on andmebaas ning mida rohkem neid on, seda keerulisem on seda saavutada.

Kuid tavaliselt viskan hiire kusagile Ă€ra ja lihtsalt kirjutan koodi. Oletame, et soovime teada, millised tabelid (ja milliste omadustega) sisaldub skeemis "HR". Enamiku andmebaasi juhtimissĂŒsteemide puhul saame soovitud tulemuse kergesti selle lihtsa pĂ€ringuga information_schema's:

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

Tabelite sisu varieerub kindlasti pĂ”hjaliku andmebaasi ja iga SÜBDB vĂ”imete jĂ€rgi. NĂ€iteks MySQL-i puhul on vĂ”imalik sama juhendi abil saada selle SÜBDB spetsiifilisi tabeli parameetreid:

select table_name
     , storage_engine -- Kasutatav "mootor" ("MyISAM", "InnoDB" jne)
     , row_format     -- Rea formaat ("Fixed", "Dynamic" jne)
     , ...
  from information_schema.tables
 where schema = 'HR'

Oracle ei kasuta information_schema, kuid tal on olemas Oracle metadata, mis ei tekita suuri probleeme:

select table_name
     , pct_free       -- minimaalne vabade kohtade prosent andmeplokis (%)
     , pct_used       -- minimaalne kasutatud kohtade prosent andmeplokis (%)
     , last_analyzed  -- Viimase statistika kogumise kuupÀev
     , ...
  from all_tables
 where owner = 'HR'

Sama kehtib ka ClickHouse'i kohta:

select name
     , engine -- Kasutatav "mootor" ("MergeTree", "Dictionary" jne)
     , ...
  from system.tables
 where database = 'HR'

Midagi sarnast saab teha ka Cassandra-s (kus on columnfamilies tabelite asemel ja keyspace’id skeemide asemel):

select columnfamily_name
     , compaction_strategy_class  -- Prahdihaldustrateegia
     , gc_grace_seconds           -- Prahti eluaeg
     , ...
  from system.schema_columnfamilies
 where keyspace_name = 'HR'

Enamik teisi andmebaase vĂ”imaldab samuti sarnaste pĂ€ringute koostamist (isegi Mongo-s on olemas spetsiaalne sĂŒsteemi kogu, mis sisaldab teavet kĂ”ikide sĂŒsteemi kogumite kohta).

Loomulikult on sellisel viisil vĂ”imalik saada teavet mitte ainult tabelite, vaid ka iga objekti kohta. Aeg-ajalt jagavad head inimesed sellist koodi erinevatele andmebaasidele, nĂ€iteks habra-artiklite seerias "PostgreSQL andmebaaside dokumenteerimise funktsioonid" (aib, ben, gim). Loomulikult on kogu selle hulk pĂ€ringute meeles hoidmine ja pidev nende sisestamine - see on "nii-nii" nauding, seetĂ”ttu on mu lemmik IDE/sĂŒntesaatoris mul ettevalmistatud komplekt snippet'e sageli kasutatavate pĂ€ringute jaoks ning jÀÀb vaid sisestada objektide nimed mallidesse.

SeetÔttu on selline navigeerimise ja objektide otsimise meetod palju paindlikum, sÀÀstab aega ja vÔimaldab saada just seda teavet ja sellisel viisil, nagu praegu vajalik (nagu on kirjeldatud postituses "Andmete eksportimine andmebaasist mis tahes vormingus: mida oskavad IntelliJ platvormi IDE-d").

Objektide toimingud

PÀrast seda, kui oleme leidnud ja uurinud vajalikud objektid, on aeg nendega midagi kasulikku teha. Loomulikult, hoides sÔrmi klaviatuurilt lahus.

Pole saladus, et lihtne tabeli kustutamine nÀeb vÀlja peaaegu kÔigis andmebaasides sama:

drop table hr.persons

Kuid tabeli loomine on juba huvitavam. Praktiselt iga andmebaasisĂŒsteem (sealhulgas paljud NoSQL lahendused) suudab mingil kujul "create table" ja selle pĂ”hiosa ei erine eriti palju (nimi, veergude loetelu, andmetĂŒĂŒbid), kuid ĂŒlejÀÀnud detailid vĂ”ivad olla drastiliselt erinevad ning sĂ”ltuvad konkreetse andmebaasisĂŒsteemi sisemisest ĂŒlesehitusest ja vĂ”imalustest. Minu lemmiknĂ€ide on, et Oracle'i dokumentatsioonis on ainult "paljad" BNF-id "create table" sĂŒntaksi jaoks nakatab 31 lehekĂŒlge. Teised andmebaasisĂŒsteemid omavad tagasihoidlikumaid vĂ”imalusi, kuid igaĂŒhel neist on samuti palju huvitavaid ja unikaalseid omadusi tabelite loomisel (postgres, mysql, cockroach, cassandra). Harva suudab mĂ”ni graafiline "wizard" uuest IDE-st (eriti universaalne) neid kĂ”iki oskusi tĂ€ielikult katta, ja kui see ka peaks toimuma, siis on see vaatepilt mitte nĂ”rgema nĂ€rvikava jaoks. Samas, Ă”igesti ja Ă”igeaegselt kirjutatud kĂ€sk create table vĂ”imaldab hĂ”lpsasti kasutada kĂ”iki neid, tagades teie andmete talletamise ja juurdepÀÀsu usaldusvÀÀrse, optimaalse ja maksimaalselt mugavana.

Paljudes andmebaasides on ka spetsiifilised objekti tĂŒĂŒbid, mida teistes andmebaasides ei esine. Me saame teha operatsioone mitte ainult andmebaasi objektide, vaid ka andmebaasi enda ĂŒle, nĂ€iteks "tapma" protsessi, vabastama mĂ€luruumi, kĂ€ivitama jĂ€lgimise, minema "ainult lugemis" reĆŸiimi ja palju muud.

Ja nĂŒĂŒd natuke joonistame

Üks levinumaid ĂŒlesandeid on ehitada diagramm andmebaasi objektidega, et ilusalt visualiseerida objekte ja nende vahelisi seoseid. Seda oskab praktiliselt iga graafiline IDE, eraldi kĂ€surea tööriistad, spetsialiseeritud graafilised tööriistad ja mudelid. Need joonistavad midagi, "kuidas oskavad", ja veidi saab sellele protsessile mĂ”ju avaldada ainult konfiguratsioonifaili paariparametri vĂ”i liidese valikute kaudu.

Kuid selle probleemi saab lahendada palju lihtsamalt, paindlikumalt ja elegantsemalt, loomulikult koodi abil. Iga keerukuse diagrammide loomiseks on meil mitmeid spetsialiseeritud markeerimiskeeli (DOT, GraphML jne), ja nende jaoks terve hulk rakendusi (GraphViz, PlantUML, Mermaid), mis oskavad selliseid juhiseid lugeda ja visualiseerida erinevates formaatides. Teavet objektide ja nende vaheliste seoste kohta teame me juba kuidas hankida.

Toome vÀikese nÀite sellest, kuidas see vÔiks vÀlja nÀha, kasutades PlantUML ja demonstreerimist PostgreSQL andmebaasist (vasakul SQL-pÀring, mis genereerib PlantUML jaoks vajaliku juhise, paremal on tulemus):

«Andmed 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 vaeva nÀha, siis saab ER-mallil PlantUML jaoks luua midagi, mis sarnaneb tÔelise ER-diagrammiga: SQL-pÀring on natuke keerulisem

SQL-pÀring on veidi keerulisem

-- Pealkiri
select '@startuml
        !define Table(name,desc) class name as "desc" << (T,#FFAAAA) >&gt;
        !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
-- Tabelid
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
-- Tabelite vahelised seosed
select distinct ccu.table_name || ' "1" --&gt; "0..N" ' || tc.table_name || format(' : "A %s may have 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
-- Jalus
select '@enduml'

«Andmed kui kood» kogemus

Kui hoolikalt vaadata, kasutavad paljud visualiseerimistööriistad tĂ”epoolest sarnaseid pĂ€ringuid. TĂ”si, need pĂ€ringud on tavaliselt sĂŒgavalt "koodis" ise ja on keerulised mĂ”ista, rÀÀkimata nende muutmisest.

MÔÔdikud ja jÀlgimine

Liigume ĂŒle traditsiooniliselt keerulisele teemale – andmebaaside jĂ”udluse jĂ€lgimine. Meenutan ĂŒhte tĂ”elist lugu, mille rÀÀkis "ĂŒks mu sĂ”ber". Ühel projektil elas ja töötas ĂŒks vĂ”imas DBA, keda harva keegi arendajatest isiklikult tundis, ja kellest keegi polnud kunagi isegi nĂ€gemiseks Ă”nne saanud (kuigi kuulduste jĂ€rgi töötas ta kuskil naabruses). Kella ajal "X", kui suure jaehulgiga ettevĂ”tte tootmisseade taas "halvasti tundma hakkas", saatis ta vaikselt ekraanipilte Oracle Enterprise Managerist, kus ta kriitilised kohad hoolikalt punase markeriga esile tĂ”i, et neid "paremini mĂ”ista" (see aitas, Ă”rnalt öeldes, vĂ€he). Nii tuli nende "fotode" alusel probleeme lahendada. Samas ei olnud kellelgi juurdepÀÀsu sellele kallile (mĂ”lemal tĂ€hendusel) Enterprise Managerile, kuna sĂŒsteem on keeruline ja kallis, Ă€kki "arendajad leiavad millegi ja rikuvad kĂ”ik Ă€ra". SeetĂ”ttu leidsid arendajad "empriirilise" meetodi abil aeglustuse pĂ”hjuse ning laskusid patĆĄi vĂ€lja andma. Kui hirmutav kiri DBA-lt ei tulnud lĂ€hiajal uuesti, hingas kĂ”ik kergendatult vĂ€lja ja naasis oma praeguste ĂŒlesannete juurde (kuni uue kirja saabumiseni).

Kuid jĂ€lgimisprotsess vĂ”ib olla ka lĂ”busam ja sĂ”bralikum, ning mis peamine — juurdepÀÀsetavam ja lĂ€bipaistvam kĂ”igile. VĂ€hemalt baastasandi osas, mis tĂ€iendaks pĂ”himonitooringusĂŒsteeme (mis on kindlasti kasulikud ja paljudes olukordades asendamatud). Iga andmebaas (DB) on valmis jagama teavet oma praeguse oleku ja jĂ”udluse kohta tĂ€iesti tasuta. Sama "verine" Oracle DB puhul on praktiliselt kĂ”ike teavet jĂ”udluse kohta vĂ”imalik saada sĂŒsteemivaadetest, alates protsessidest ja seanssidest kuni vahemĂ€lu olekuni. DBA skriptid, sektsioon "JĂ€lgimine". Postgresql-is on samuti terve hulk sĂŒsteemivaateid, mis on mĂ”eldud andmebaasi jĂ€lgimiseks, sealhulgas sellised hindamatud, nagu pg_stat_activity, pg_stat_database, pg_stat_bgwriter. MySQL-is on selleks isegi eraldi skeem performance_schema. Ja Mongodb-s on sisseehitatud profiler andmete kogumiseks jĂ”udluse kohta sĂŒsteemi kogumisse system.profile.

Seega, kasutades mĂ”nda mÔÔdiku kogumist (Telegraf, Metricbeat, Collectd), mis suudab sooritada kohandatud SQL-pĂ€ringuid, mÔÔtmete salvestamise sĂŒsteemi (InfluxDB, Elasticsearch, Timescaledb) ja visuaalset tööriista (Grafana, Kibana), on vĂ”imalik luua piisavalt kerge ja paindlik jĂ€lgimissĂŒsteem, mis on tihedalt seotud teiste sĂŒsteemi mÔÔtmistega (mille saadakse nĂ€iteks rakenduste serverilt, opsĂŒsteemilt jne). NĂ€iteks on see tehtud pgwatch2-s, kus kasutatakse kombinatsiooni InfluxDB + Grafana ja komplekti pĂ€ringutest sĂŒsteemi vaadetele, millele saab samuti lisada kohandatud pĂ€ringuid.

Kokku

Ja see on ainult ligikaudne loetelu, mida saab meie andmebaasiga tavalise SQL-koodi abil teha. Olen kindel, et on vÔimalik leida veel palju rakendusi, kirjutage kommentaaridesse. Ja sellest, kuidas (ja mis kÔige tÀhtsam, milleks) seda kÔik automatiseerida ja oma CI/CD torustikku kaasata, rÀÀgime jÀrgmine kord.

Allikas: habr.com

Osta usaldusvÀÀrne veebihosting DDoS kaitsega, VPS VDS serverid đŸ”„ Osta usaldusvÀÀrne veebihosting DDoS kaitsega, VPS VDS serverid | ProHoster