"Database as Code" Esperienza

"Database as Code" Esperienza

SQL, chì puderia esse più simplice? Ognunu di noi pò scrive una dumanda simplice - scrivemu selezziunà, elencu e culonne richieste, allora da, nome di a tavula, qualchi cundizioni in induva è questu hè tuttu - dati utili sò in a nostra sacchetta, è (quasi) indipendentemente da quale DBMS hè sottu à u cappucciu à quellu tempu (o forsi micca un DBMS in tuttu). In u risultatu, u travagliu cù quasi ogni fonte di dati (relazionale è micca cusì) pò esse cunsideratu da u puntu di vista di u codice ordinariu (cù tuttu ciò chì implica - cuntrollu di versione, revisione di codice, analisi statica, autotest, è questu hè tuttu). È questu ùn hè micca solu per i dati stessi, schemi è migrazioni, ma in generale per tutta a vita di l'almacenamiento. In questu articulu, parlemu di i travaglii di ogni ghjornu è di i prublemi di travaglià cù diverse basa di dati sottu a lente di "base di dati cum'è codice".

È partemu da ghjustu ORM. I primi battaglie di u tipu "SQL vs ORM" sò stati rimarcati in daretu pre-Petrine Rus'.

Cartografia oggettu-relativa

I sustenituri di l'ORM tradiziunale valore a velocità è a facilità di sviluppu, l'indipendenza da u DBMS è u codice pulito. Per parechji di noi, u codice per travaglià cù a basa di dati (è spessu a basa di dati stessu)

di solitu pare qualcosa cusì ...

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

U mudellu hè appiccicatu cù annotazioni intelligenti, è in qualchì parte daretu à e scene un ORM valente genera è eseguisce tunnellate di qualchì codice SQL. A propositu, i sviluppatori facenu u megliu per isolà da a so basa di dati cù chilometri di astrazioni, chì indicanu qualchi "SQL odiu".

À l'altra parte di e barricate, l'aderenti di SQL puramente "fattu a manu" notanu a capacità di stringhje tuttu u sucu da u so DBMS senza strati supplementari è astrazioni. In u risultatu, i prughjetti "data-centric" appariscenu, induve persone furmati apposta sò implicati in a basa di dati (sò ancu "basicisti", sò ancu "basicisti", sò ancu "basdeners", etc.), è i sviluppatori. basta à "tirà" i viste pronti è e prucedure almacenate, senza entre in i dettagli.

E se avemu avutu u megliu di i dui mondi? Cumu questu hè fattu in un strumentu maravigliu cù un nome chì affirma a vita Yesql. Daraghju un paru di linii da u cuncettu generale in a mo traduzzione libera, è pudete fà cunniscenze cun ellu in più detail. ccà.

Clojure hè una lingua fresca per creà DSL, ma SQL stessu hè un DSL cool, è ùn avemu micca bisognu di un altru. L'espressioni S sò grandi, ma ùn aghjunghjenu nunda di novu quì. In u risultatu, avemu i parentesi per a fine di parentesi. Ùn sò micca d'accordu? Allora aspettate u mumentu quandu l'astrazione nantu à a basa di dati principia à fughje è avete principiatu a lotta cù a funzione (raw-sql)

Allora chì deve fà ? Lasciamu SQL cum'è SQL regulare - un schedariu per dumanda:

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

... è dopu leghje stu schedariu, trasfurmendu in una funzione Clojure regulare:

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

Aderendu à u principiu "SQL da ellu stessu, Clojure da ellu stessu", ottene:

  • Nisuna sorpresa sintattica. A vostra basa di dati (cum'è qualsiasi altra) ùn hè micca 100% cumpletu cù u standard SQL - ma questu ùn importa micca per Yesql. Ùn perderà mai u tempu di caccia per e funzioni cù sintassi equivalente SQL. Ùn averete mai à vultà à una funzione (raw-sql "alcuni('funky'::SYNTAX)")).
  • U megliu supportu di l'editore. U vostru editore hà digià un eccellente supportu SQL. Salvendu SQL cum'è SQL pudete simpricimenti aduprà.
  • Cumpatibilità di a squadra. I vostri DBA ponu leghje è scrive l'SQL chì utilizate in u vostru prughjettu Clojure.
  • Sintonizazione di rendiment più faciule. Avete bisognu di custruisce un pianu per una quistione problematica? Questu ùn hè micca un prublema quandu a vostra dumanda hè SQL regulare.
  • Riutilizazione di e dumande. Trascinate è lasciate quelli stessi schedarii SQL in altri prughjetti perchè hè solu un vechju SQL - basta sparte.

In my opinion, l'idea hè assai cool è à u stessu tempu assai simplice, grazia à quale u prugettu hà guadagnatu assai seguitori in una varietà di lingue. E dopu pruvà à applicà una filusufìa simile di separà u codice SQL da tuttu u restu assai oltre l'ORM.

Managers IDE & DB

Cuminciamu cù un compitu simplice di ogni ghjornu. Spessu avemu da circà qualchi oggetti in a basa di dati, per esempiu, truvà una tavula in u schema è studià a so struttura (chì culonni, chjave, indici, limitazioni, etc. sò usati). È da ogni IDE graficu o un pocu DB-manager, prima di tuttu, aspittemu esattamente queste capacità. Cusì hè veloce è ùn avete micca aspittà una meza ora finu à chì una finestra cù l'infurmazioni necessarii hè disegnata (in particulare cù una cunnessione lenta à una basa di dati remota), è à u stessu tempu, l'infurmazioni ricevuti sò freschi è pertinenti, è micca cache junk. Inoltre, u più cumplessu è più grande a basa di dati è u più grande u numeru di elli, u più difficiule hè di fà questu.

Ma di solitu scaccià u mouse è scrive solu u codice. Dicemu chì avete bisognu di sapè quale tavule (è cù quali proprietà) sò cuntenuti in u schema "HR". In a maiò parte di i DBMS, u risultatu desideratu pò esse ottenutu cù sta dumanda simplice da information_schema:

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

Da una basa di dati à una basa di dati, u cuntenutu di tali tabelle di riferimentu varieghja secondu e capacità di ogni DBMS. È, per esempiu, per MySQL, da u stessu libru di riferimentu pudete uttene paràmetri di tabella specifichi per questu DBMS:

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

Oracle ùn cunnosci micca information_schema, ma hà Metadata Oracle, è ùn ci sò micca grandi prublemi:

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

ClickHouse ùn hè micca eccezzioni:

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

Qualcosa di simile pò esse fattu in Cassandra (chì hà columnfamilies invece di tavule è keyspaces invece di schemi):

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

Per a maiò parte di l'altri basa di dati, pudete ancu fà dumande simili (ancu Mongo hà cullizzioni di sistema speciale, chì cuntene infurmazione nantu à tutte e cullezzione in u sistema).

Di sicuru, in questu modu pudete uttene infurmazione micca solu nantu à e tavule, ma nantu à qualsiasi ughjettu in generale. Da u tempu à u tempu, e persone gentili sparte tali codice per diverse basa di dati, cum'è, per esempiu, in a seria di articuli habra "Funzioni per documentà e basa di dati PostgreSQL" (Ayb, Ben, palestra). Di sicuru, mantene tutta sta muntagna di dumande in a mo testa è scrivite constantemente hè un piacè, cusì in u mo IDE / editore preferitu aghju un set di snippets pre-preparati per e dumande spessu usate, è tuttu ciò chì resta hè di scrive u nomi di l'uggetti in u mudellu.

In u risultatu, stu metudu di navigazione è di ricerca di l'uggetti hè assai più flexible, salva assai tempu, è permette di ottene esattamente l'infurmazioni in a forma in quale hè avà necessariu (cum'è, per esempiu, descrittu in u post). "Esportazione di dati da una basa di dati in ogni formatu: ciò chì l'IDE ponu fà nantu à a piattaforma IntelliJ").

Operazioni cù l'uggetti

Dopu avè truvatu è studiatu l'uggetti necessarii, hè u tempu di fà qualcosa d'utile cun elli. Naturalmente, ancu senza caccià i vostri ditte da u teclatu.

Ùn hè micca un sicretu chì l'eliminazione di una tavula serà u listessu in quasi tutte e basa di dati:

drop table hr.persons

Ma cù a creazione di a tavula diventa più interessante. Quasi ogni DBMS (inclusi assai NoSQL) pò "creà una tavola" in una forma o l'altru, è a parte principale di questu serà ancu un pocu sfarente (nome, lista di culonni, tipi di dati), ma altri dettagli ponu differisce dramaticamente è dipendenu da u dispusitivu internu è capacità di un DBMS specificu. U mo esempiu preferitu hè chì in a documentazione Oracle ci sò solu BNF "nudi" per a sintassi "create table". occupanu 31 pagine. L'altri DBMS anu capacità più modeste, ma ognuna di elle hà ancu parechje caratteristiche interessanti è uniche per creà tavule (postgres, U vostru servore SQL, scarafaghju, cassandra). Hè improbabile chì qualsiasi "mago" gràficu da un altru IDE (in particulare un universale) puderà copre tutte queste capacità, è ancu s'ellu pò, ùn serà micca un spettaculu per i deboli di cori. À u listessu tempu, una dichjarazione scritta curretta è puntuale crià a tavola vi permetterà à aduprà facirmenti tutti li, fà u almacenamentu è accessu à i vostri dati affidabili, ottimali è cum'è cunfortu pussibule.

Inoltre, assai DBMS anu u so propiu tipu d'uggetti specifichi chì ùn sò micca dispunibili in altri DBMS. Inoltre, pudemu eseguisce operazioni micca solu nantu à l'uggetti di basa di dati, ma ancu in u DBMS stessu, per esempiu, "uccide" un prucessu, liberà una zona di memoria, attivà a traccia, cambia à u modu "sola lettura", è assai di più.

Avà disegnu un pocu

Unu di i travaglii più cumuni hè di custruisce un diagramma cù l'uggetti di basa di dati è vede l'uggetti è e cunnessione trà elli in una bella stampa. Quasi ogni IDE graficu, utilità separati di "linea di cummandu", strumenti grafichi specializati è mudelli ponu fà questu. Tiraranu qualcosa per voi "cum'è u megliu pussibule", è pudete influenzà stu prucessu un pocu solu cù l'aiutu di uni pochi di paràmetri in u schedariu di cunfigurazione o checkboxes in l'interfaccia.

Ma stu prublema pò esse risolta assai più simplice, più flexible è eleganti, è sicuru cù l'aiutu di u codice. Per creà diagrammi di qualsiasi cumplessità, avemu parechje lingue di marcatura specializate (DOT, GraphML, etc.), è per elli una sparghjera sana di applicazioni (GraphViz, PlantUML, Mermaid) chì ponu leghje tali struzzioni è visualizà in una varietà di formati. . Ebbè, sapemu digià cumu uttene infurmazioni nantu à l'uggetti è e cunnessione trà elli.

Eccu un picculu esempiu di ciò chì questu puderia vede, usendu PlantUML è basa di dati demo per PostgreSQL (à a manca hè una dumanda SQL chì generà l'istruzzioni necessaria per PlantUML, è à a diritta hè u risultatu):

"Database as Code" Esperienza

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'

E s'è vo pruvate un pocu, allura basatu nantu Template ER per PlantUML pudete ottene qualcosa assai simili à un veru diagramma ER:

A quistione SQL hè un pocu più complicata

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

"Database as Code" Esperienza

Se guardate attentamente, sottu u cappucciu parechji strumenti di visualizazione utilizanu ancu dumande simili. True, sti dumande sò generalmente prufonda "hardwired" in u codice di l'applicazione stessa è sò difficiuli di capiscenu, per ùn parlà di alcuna mudificazione di elli.

Metriche è surviglianza

Passemu à un tema tradiziunale cumplessu - u monitoraghju di u rendiment di a basa di dati. Mi ricordu di una piccula storia vera chì m'hà dettu "unu di i mo amichi". Nant'à un altru prughjettu hà campatu un certu DBA putente, è pocu di i sviluppatori l'anu cunnisciutu personalmente, o l'avianu mai vistu in persona (malgradu u fattu chì, sicondu i rumuri, hà travagliatu in un locu in u prossimu edificio). À l'ora "X", quandu u sistema di pruduzzione di un grande retailer hà cuminciatu à "sentite male" una volta di più, hà mandatu in silenziu screenshots di grafici da Oracle Enterprise Manager, nantu à quale hà evidenziatu currettamente i posti critichi cun un marcatu rossu per "comprensibilità" ( chistu, per dì un pocu, ùn hà micca aiutatu assai). E basatu annantu à sta "carta di foto" aghju avutu trattatu. À u listessu tempu, nimu hà avutu accessu à u preziosu (in i dui sensi di a parolla) Enterprise Manager, perchè u sistema hè cumplessu è caru, di colpu "i sviluppatori s'imbazzanu in qualcosa è rompe tuttu". Per quessa, i sviluppatori "empirically" anu truvatu u locu è a causa di i freni è liberatu un patch. Se a lettera minacciosa da u DBA ùn hè micca ghjunta di novu in un futuru vicinu, allora tutti respiraranu un suspiru di sollievu è tornanu à i so compiti attuali (finu à a nova Lettera).

Ma u prucessu di surviglianza pò vede più divertente è amichevule, è più impurtante, accessibile è trasparente per tutti. Almenu a so parte basica, cum'è un aghjuntu à i sistemi di monitoraghju principali (chì sò certamenti utili è in parechji casi insustituibili). Ogni DBMS hè liberamente è assolutamente gratuitu per sparte infurmazioni nantu à u so statu attuale è u so rendiment. In u stessu Oracle DB "sanguinatu", quasi ogni infurmazione nantu à u rendiment pò esse ottenuta da vista di u sistema, chì varieghja da prucessi è sessioni à u statu di u buffer cache (per esempiu, Scripts DBA, sezione "Monitoring"). Postgresql hà ancu una mansa di vista di u sistema per surviglianza di basa di dati, in particulare quelli chì sò indispensabili in a vita di ogni ghjornu di qualsiasi DBA, cum'è pg_stat_attività, pg_stat_database, pg_stat_bgwriter. MySQL hà ancu un schema separatu per questu. schema_prestazione. A In Mongo integratu prufilatore aggrega dati di rendiment in una cullizzioni di sistema sistemu.profile.

Cusì, armatu cù un tipu di cullettore di metriche (Telegraf, Metricbeat, Collectd) chì ponu realizà e dumande sql persunalizate, un almacenamentu di sti metrichi (InfluxDB, Elasticsearch, Timescaledb) è un visualizatore (Grafana, Kibana), pudete ottene un abbastanza faciule. è un sistema di surviglianza flexible chì serà integratu strettamente cù altre metriche di u sistema (ottenutu, per esempiu, da u servitore di l'applicazione, da u SO, etc.). Cum'è, per esempiu, questu hè fattu in pgwatch2, chì usa a cumminazione InfluxDB + Grafana è un inseme di dumande à vista di u sistema, chì ponu ancu accede. aghjunghje dumande persunalizate.

Tuttu

È questu hè solu una lista apprussimata di ciò chì pò esse fattu cù a nostra basa di dati cù u codice SQL regular. Sò sicuru chì pudete truvà assai più usi, scrivite in i cumenti. E parlemu di cumu (è più impurtante perchè) per automatizà tuttu questu è includenu in u vostru pipeline CI / CD a prossima volta.

Source: www.habr.com

Add a comment