„Adatbázis, mint kód” tapasztalat

„Adatbázis, mint kód” tapasztalat

SQL, mi lehetne egyszerűbb? Mindannyian írhatunk egy egyszerű kérést – gépelünk válasszuk, sorolja fel a szükséges oszlopokat, majd ból ből, tábla neve, néhány feltétel ahol és ez minden – a hasznos adatok a zsebünkben vannak, és (majdnem) függetlenül attól, hogy melyik DBMS van akkoriban (vagy esetleg egyáltalán nem DBMS). Ennek eredményeként a szinte bármilyen (relációs és nem) adatforrással való munkavégzés a közönséges kód szemszögéből is megfontolható (mindennel együtt, amit ez magában foglal - verziókezelés, kódellenőrzés, statikus elemzés, automatikus tesztek, és ez minden). És ez nem csak magára az adatokra, sémákra és migrációkra vonatkozik, hanem általában a tároló teljes élettartamára. Ebben a cikkben a mindennapi feladatokról és a különféle adatbázisokkal való munkavégzés problémáiról fogunk beszélni az „adatbázis mint kód” lencséje alatt.

És kezdjük rögtön onnan ORM. Az első "SQL vs ORM" típusú csatákat még a múltban észlelték pre-Petrine Rus.

Objektum-relációs leképezés

Az ORM támogatói hagyományosan nagyra értékelik a gyorsaságot és a könnyű fejlesztést, a DBMS-től való függetlenséget és a tiszta kódot. Sokunk számára az adatbázissal (és gyakran magával az adatbázissal) való munka kódja

általában valahogy így néz ki...

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

A modell okos megjegyzésekkel van kirakva, és valahol a színfalak mögött egy vitéz ORM generál és hajt végre néhány SQL kódot. A fejlesztők egyébként igyekeznek minden tőlük telhetőt elszigetelni magukat adatbázisuktól kilométeres absztrakciókkal, ami azt jelzi, "SQL gyűlölet".

A barikádok másik oldalán a tisztán „kézzel készített” SQL hívei megjegyzik, hogy további rétegek és absztrakciók nélkül képesek minden levet kipréselni a DBMS-ből. Ennek eredményeként megjelennek az „adatközpontú” projektek, ahol speciálisan képzett embereket vonnak be az adatbázisba (ők is „alapisták”, ők is „alapisták”, ők is „basdenerek” stb.), illetve a fejlesztők. csak a kész nézeteket és tárolt eljárásokat kell „lehúzni”, anélkül, hogy belemennénk a részletekbe.

Mi lenne, ha mindkét világból a legjobbat kapnánk? Hogyan történik ez egy csodálatos eszközzel, életigenlő névvel Yesql. Az általános koncepcióból adok pár sort ingyenes fordításomban, és részletesebben is megismerkedhetsz vele itt.

A Clojure egy klassz nyelv a DSL-ek létrehozásához, de az SQL maga egy klassz DSL, és nincs szükségünk másikra. Az S-kifejezések nagyszerűek, de nem adnak hozzá semmi újat. Ennek eredményeként zárójeleket kapunk a zárójelek kedvéért. Nem ért egyet? Ezután várja meg a pillanatot, amikor az adatbázis feletti absztrakció elkezd kiszivárogni, és harcolni kezd a funkcióval (nyers-sql)

Szóval mit tegyek? Hagyjuk az SQL-t normál SQL-ként – kérésenként egy fájl:

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

... majd olvassa el ezt a fájlt, és alakítsa át normál Clojure-függvénnyel:

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

Az "SQL önmagában, Clojure önmagában" elv betartásával a következőket kapja:

  • Nincs szintaktikai meglepetés. Az Ön adatbázisa (mint bármely más) nem 100%-ban kompatibilis az SQL-szabvánnyal – de ez a Yesql esetében nem számít. Soha nem vesztegeti az idejét az SQL-vel egyenértékű szintaxisú függvények keresésével. Soha nem kell visszatérnie egy funkcióhoz (raw-sql "some('funky'::SYNTAX)")).
  • A legjobb szerkesztő támogatás. Az Ön szerkesztője már kiváló SQL-támogatással rendelkezik. Ha az SQL-t SQL-ként menti, egyszerűen használhatja.
  • Csapatkompatibilitás. A DBA-k képesek olvasni és írni a Clojure projektben használt SQL-t.
  • Könnyebb teljesítményhangolás. Tervet kell készítenie egy problémás lekérdezéshez? Ez nem jelent problémát, ha a lekérdezés normál SQL.
  • Lekérdezések újrafelhasználása. Húzza át ugyanazokat az SQL-fájlokat más projektekbe, mert ez csak egy régi SQL – csak ossza meg.

Véleményem szerint az ötlet nagyon klassz és egyben nagyon egyszerű, aminek köszönhetően a projekt rengeteget nyert követői különféle nyelveken. Ezután megpróbáljuk alkalmazni egy hasonló filozófiát, amely szerint az SQL-kódot elválasztjuk minden mástól, amely messze túlmutat az ORM-en.

IDE és DB menedzserek

Kezdjük egy egyszerű mindennapi feladattal. Gyakran meg kell keresnünk néhány objektumot az adatbázisban, például meg kell találnunk egy táblát a sémában, és tanulmányoznunk kell a szerkezetét (milyen oszlopokat, kulcsokat, indexeket, megszorításokat stb. használunk). És minden grafikus IDE-től vagy egy kis DB-menedzsertől mindenekelőtt pontosan ezeket a képességeket várjuk el. Hogy gyors legyen, és ne kelljen fél órát várni, amíg kirajzolódik egy ablak a szükséges információkkal (főleg távoli adatbázishoz való lassú kapcsolat esetén), ugyanakkor a kapott információ friss és releváns, és nem gyorsítótárazott szemét. Sőt, minél összetettebb és nagyobb az adatbázis, és minél több van belőlük, annál nehezebb ezt megtenni.

De általában eldobom az egeret és csak kódot írok. Tegyük fel, hogy meg kell találnia, hogy a "HR" séma mely táblákat (és milyen tulajdonságokkal) tartalmazza. A legtöbb DBMS-ben a kívánt eredményt az information_schema egyszerű lekérdezésével érhetjük el:

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

Az ilyen referenciatáblázatok tartalma adatbázisonként az egyes DBMS-ek képességeitől függően változik. És például a MySQL-hez ugyanabból a referenciakönyvből szerezheti be a DBMS-re jellemző táblázatparamétereket:

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

Az Oracle nem ismeri az information_schema-t, de igen Oracle metaadatok, és nem merülnek fel nagy problémák:

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

A ClickHouse sem kivétel:

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

Valami hasonlót megtehet a Cassandra (amelyben táblák helyett oszlopcsaládok, sémák helyett kulcsterek vannak):

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

A legtöbb más adatbázis esetében is előállhat hasonló lekérdezések (még a Mongo is speciális rendszergyűjtemény, amely a rendszer összes gyűjteményéről tartalmaz információkat).

Természetesen így nem csak a táblákról, hanem általában bármilyen objektumról szerezhet információkat. A kedves emberek időről időre megosztanak ilyen kódokat különböző adatbázisokhoz, mint például a „PostgreSQL adatbázisok dokumentálásának függvényei” című habra cikksorozatban (Ayb, Ben, tornaterem). Természetesen nagy élvezet a fejemben tartani ezt az egész lekérdezéshegyet és folyamatosan gépelni őket, így kedvenc IDE-mben/szerkesztőmben van egy előre elkészített töredékkészlet a gyakran használt lekérdezésekhez, és már csak be kell gépelni a objektumneveket a sablonba.

Ennek eredményeként a navigáció és az objektumok keresésének ez a módszere sokkal rugalmasabb, sok időt takarít meg, és lehetővé teszi, hogy pontosan az információkat abban a formában kapja meg, amilyenben most szükséges (ahogy például a bejegyzésben le van írva "Adatok exportálása adatbázisból bármilyen formátumban: mire képesek az IDE-k az IntelliJ platformon").

Műveletek tárgyakkal

Miután megtaláltuk és áttanulmányoztuk a szükséges tárgyakat, ideje valami hasznosat tenni velük. Természetesen anélkül, hogy levenné az ujjait a billentyűzetről.

Nem titok, hogy egy táblázat egyszerű törlése szinte minden adatbázisban ugyanúgy fog kinézni:

drop table hr.persons

De a táblázat elkészítésével érdekesebbé válik. Szinte minden DBMS (beleértve a sok NoSQL-t is) képes ilyen vagy olyan formában "táblázatot létrehozni", és ennek nagy része kissé eltér (név, oszloplista, adattípusok), de más részletek drámaian eltérhetnek, és függhetnek a egy adott DBMS belső eszköze és képességei. A kedvenc példám az, hogy az Oracle dokumentációjában csak „csupasz” BNF-ek vannak a „tábla létrehozása” szintaxishoz. 31 oldalt foglal el. Más DBMS-ek szerényebb képességekkel rendelkeznek, de mindegyiknek számos érdekes és egyedi funkciója van a táblák létrehozásához (postgres, mysql, csótány, Cassandra). Nem valószínű, hogy egy másik IDE (főleg egy univerzális) grafikus „varázslója” képes lesz teljes mértékben lefedni ezeket a képességeket, és még ha képes is, akkor sem lesz látványosság a gyengébbeknek. Ugyanakkor helyesen és időben megírt nyilatkozat táblázat létrehozása lehetővé teszi az összes egyszerű használatát, megbízhatóvá, optimálissá és a lehető legkényelmesebbé teszi a tárolást és az adatokhoz való hozzáférést.

Ezenkívül sok DBMS-nek van saját objektumtípusa, amelyek más DBMS-ekben nem állnak rendelkezésre. Sőt, nem csak az adatbázis-objektumokon, hanem magán a DBMS-en is végezhetünk műveleteket, például „megölhetünk” egy folyamatot, felszabadíthatunk némi memóriaterületet, engedélyezhetjük a nyomkövetést, átválthatunk „csak olvasható” módba, és még sok minden mást.

Most rajzoljunk egy kicsit

Az egyik leggyakoribb feladat az adatbázis-objektumokkal diagram összeállítása, és az objektumok és a köztük lévő kapcsolatok gyönyörű képben való megjelenítése. Szinte bármilyen grafikus IDE, különálló „parancssori” segédprogramok, speciális grafikus eszközök és modellezők képesek erre. Lerajzolnak neked valamit, „amennyire csak tudnak”, és ezt a folyamatot csak a konfigurációs fájl néhány paraméterével vagy a felületen található jelölőnégyzetekkel tudod kicsit befolyásolni.

De ez a probléma sokkal egyszerűbben, rugalmasabban és elegánsabban is megoldható, és persze kód segítségével. Bármilyen bonyolultságú diagramok készítéséhez számos speciális jelölőnyelvünk (DOT, GraphML stb.) áll rendelkezésünkre, és ezekhez az alkalmazások egész sora (GraphViz, PlantUML, Mermaid), amelyek képesek elolvasni az ilyen utasításokat és különféle formátumokban megjeleníteni azokat. . Nos, már tudjuk, hogyan szerezzünk információkat az objektumokról és a köztük lévő kapcsolatokról.

Íme egy kis példa arra, hogyan nézhet ki ez a PlantUML és a demo adatbázis a PostgreSQL-hez (bal oldalon egy SQL lekérdezés található, amely a PlantUML-hez szükséges utasítást generálja, a jobb oldalon pedig az eredmény):

„Adatbázis, mint kód” tapasztalat

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'

És ha egy kicsit próbálkozol, akkor az alapján ER-sablon a PlantUML-hez kaphat valami nagyon hasonlót egy valódi ER diagramhoz:

Az SQL lekérdezés egy kicsit bonyolultabb

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

„Adatbázis, mint kód” tapasztalat

Ha alaposan megnézi, a motorháztető alatt sok vizualizációs eszköz is használ hasonló lekérdezéseket. Igaz, ezek a kérések általában mélyek „bekötve” magának az alkalmazásnak a kódjába, és nehezen érthetőek, nem is beszélve azok módosításáról.

Mérések és monitorozás

Térjünk át egy hagyományosan összetett témára - az adatbázis-teljesítmény figyelésére. Emlékszem egy kis igaz történetre, amelyet „egyik barátom” mesélt nekem. Egy másik projektben élt egy bizonyos erős DBA, és a fejlesztők közül kevesen ismerték őt személyesen, vagy valaha is találkoztak vele (annak ellenére, hogy a pletykák szerint valahol a szomszédos épületben dolgozott). Az „X” órában, amikor egy nagy kereskedő podukciós rendszere ismét „rosszul” kezdett érezni magát, csendben elküldte az Oracle Enterprise Manager grafikonjainak képernyőképeit, amelyeken az „érthetőség” érdekében piros jelzővel gondosan kiemelte a kritikus helyeket ( ez enyhén szólva nem sokat segített). És e „fotókártya” alapján kellett kezelnem. Ugyanakkor senki sem férhetett hozzá a becses (a szó mindkét értelmében) Enterprise Managerhez, mert a rendszer bonyolult és drága, hirtelen „a fejlesztők megbotlanak valamiben, és mindent összetörnek”. Ezért a fejlesztők „empirikusan” megtalálták a fékek helyét és okát, és kiadtak egy foltot. Ha a közeljövőben nem érkezne meg újra a fenyegető levél a DBA-tól, akkor mindenki fellélegezne, és visszatérne jelenlegi feladataihoz (az új Levélig).

De a megfigyelési folyamat szórakoztatóbbnak és barátságosabbnak tűnhet, és ami a legfontosabb, mindenki számára elérhető és átlátható. Legalábbis az alapvető része, kiegészítve a főbb felügyeleti rendszerekkel (amelyek minden bizonnyal hasznosak és sok esetben pótolhatatlanok). Bármely DBMS szabadon és teljesen ingyenesen oszthat meg információkat aktuális állapotáról és teljesítményéről. Ugyanabban a „véres” Oracle DB-ben szinte minden teljesítményre vonatkozó információ beszerezhető a rendszernézetekből, a folyamatoktól és a munkamenetektől a puffer-gyorsítótár állapotáig (például DBA szkriptek, "Monitoring" szakasz). A Postgresql-nek egy csomó rendszernézete is van adatbázis felügyelet, különösen azokat, amelyek nélkülözhetetlenek bármely DBA mindennapi életében, mint pl pg_stat_activity, pg_stat_database, pg_stat_bgwriter. A MySQL-nek még külön sémája is van erre. teljesítmény_séma. A In Mongo beépített profilozó rendszergyűjteménybe összesíti a teljesítményadatokat rendszer.profil.

Így felszerelkezve valamilyen metrikagyűjtővel (Telegraf, Metricbeat, Collectd), amely képes egyedi sql-lekérdezéseket végrehajtani, ezen mérőszámok tárolójával (InfluxDB, Elasticsearch, Timescaledb) és egy vizualizálóval (Grafana, Kibana) meglehetősen egyszerűen kaphat valamint egy rugalmas megfigyelőrendszer, amely szorosan integrálva lesz más rendszerszintű mérőszámokkal (például az alkalmazáskiszolgálóról, az operációs rendszerről stb.). Mint például ez megtörténik a pgwatch2-ben, amely az InfluxDB + Grafana kombinációt és a rendszernézetekhez tartozó lekérdezéseket használja, amelyek szintén elérhetők. egyéni lekérdezések hozzáadása.

Összességében

És ez csak egy hozzávetőleges lista arról, hogy mit lehet tenni adatbázisunkkal normál SQL kód használatával. Biztos vagyok benne, hogy még sok felhasználási lehetőséget találsz, írd meg a megjegyzésekben. És arról fogunk beszélni, hogyan (és legfőképpen miért) automatizálhatjuk mindezt, és legközelebb beépíthetjük a CI/CD-folyamatba.

Forrás: will.com

Hozzászólás