"Datumbazo kiel Kodo" Sperto

"Datumbazo kiel Kodo" Sperto

SQL, kio povus esti pli simpla? Ĉiu el ni povas skribi simplan peton - ni tajpas elektu, listigu la postulatajn kolumnojn, do el, tabelnomo, kelkaj kondiĉoj en kie kaj jen ĉio - utilaj datumoj estas en nia poŝo, kaj (preskaŭ) sendepende de kiu DBMS estas sub la kapuĉo tiutempe (aŭ eble tute ne estas DBMS). Kiel rezulto, labori kun preskaŭ ajna datumfonto (rilata kaj ne tiel) povas esti konsiderata el la vidpunkto de ordinara kodo (kun ĉio, kion ĝi implicas - versio-kontrolo, koda revizio, statika analizo, aŭtotestoj, kaj jen ĉio). Kaj ĉi tio validas ne nur por la datumoj mem, skemoj kaj migradoj, sed ĝenerale por la tuta vivo de la stokado. En ĉi tiu artikolo ni parolos pri ĉiutagaj taskoj kaj problemoj pri laboro kun diversaj datumbazoj sub la lenso de "datumbazo kiel kodo".

Kaj ni komencu ĝuste de ORM. La unuaj bataloj de la tipo "SQL vs ORM" estis rimarkitaj reen antaŭ-Petrino Rus'.

Objekt-rilata mapado

Subtenantoj de ORM tradicie taksas rapidecon kaj facilecon de evoluo, sendependecon de la DBMS kaj puran kodon. Por multaj el ni, la kodo por labori kun la datumbazo (kaj ofte la datumbazo mem)

ĝi kutime aspektas kiel ĉi tio...

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

La modelo estas pendigita kun saĝaj komentarioj, kaj ie malantaŭ la scenoj kuraĝa ORM generas kaj efektivigas tunojn da iu SQL-kodo. Cetere, programistoj klopodas por izoli sin de sia datumbazo per kilometroj da abstraktaĵoj, kio indikas iujn "SQL-malamo".

Aliflanke de la barikadoj, adeptoj de pura "manfarita" SQL rimarkas la kapablon elpremi la tutan sukon el siaj DBMS sen pliaj tavoloj kaj abstraktaĵoj. Rezulte aperas projektoj "datumcentraj", kie en la datumbazo partoprenas speciale trejnitaj homoj (ili estas ankaŭ "bazikistoj", ili estas ankaŭ "bazistoj", ili ankaŭ estas "basdenistoj", ktp.), kaj la programistoj. nur devas "tiri" la pretajn vidojn kaj konservitajn procedurojn, sen eniri detalojn.

Kio se ni havus la plej bonan el ambaŭ mondoj? Kiel ĉi tio estas farita en mirinda ilo kun viv-aserta nomo Jesql. Mi donos kelkajn liniojn el la ĝenerala koncepto en mia libera traduko, kaj vi povas konatiĝi kun ĝi pli detale. tie.

Clojure estas bonega lingvo por krei DSL-ojn, sed SQL mem estas bonega DSL, kaj ni ne bezonas alian. S-esprimoj estas bonegaj, sed ili aldonas nenion novan ĉi tie. Kiel rezulto, ni ricevas krampojn pro krampoj. Ĉu ne konsentas? Tiam atendu la momenton, kiam la abstraktado super la datumbazo komencas liki kaj vi komencas batali kun la funkcio (kruda-sql)

Kion do mi faru? Ni lasu SQL kiel ordinara SQL - unu dosieron per peto:

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

... kaj poste legu ĉi tiun dosieron, igante ĝin regula Clojure-funkcio:

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

Aliĝante al la principo "SQL per si mem, Clojure per si mem", vi ricevas:

  • Neniuj sintaksaj surprizoj. Via datumbazo (kiel ĉiu alia) ne 100% konformas al la SQL-normo - sed tio ne gravas por Yesql. Vi neniam malŝparos tempon por ĉasi funkciojn kun SQL-ekvivalenta sintakso. Vi neniam devos reveni al funkcio (raw-sql "kelkaj('funky'::SINTAKSO)")).
  • Plej bona redaktoro subteno. Via redaktilo jam havas bonegan SQL-subtenon. Konservante SQL kiel SQL vi povas simple uzi ĝin.
  • Teama kongruo. Viaj DBA-oj povas legi kaj skribi la SQL kiun vi uzas en via Clojure-projekto.
  • Pli facila agordado de rendimento. Ĉu vi bezonas konstrui planon por problema demando? Ĉi tio ne estas problemo kiam via demando estas regula SQL.
  • Reuzado de demandoj. Trenu kaj faligi tiujn samajn SQL-dosierojn en aliajn projektojn ĉar ĝi estas simple malnova SQL - simple dividu ĝin.

Laŭ mi, la ideo estas tre mojosa kaj samtempe tre simpla, danke al kiu la projekto gajnis multajn sekvantoj en diversaj lingvoj. Kaj ni poste provos apliki similan filozofion apartigi SQL-kodon de ĉio alia multe preter la ORM.

Administrantoj de IDE & DB

Ni komencu per simpla ĉiutaga tasko. Ofte ni devas serĉi kelkajn objektojn en la datumbazo, ekzemple, trovi tabelon en la skemo kaj studi ĝian strukturon (kiaj kolumnoj, ŝlosiloj, indeksoj, limoj ktp. estas uzataj). Kaj de iu ajn grafika IDE aŭ iom DB-administranto, antaŭ ĉio, ni atendas ĝuste ĉi tiujn kapablojn. Por ke ĝi estu rapida kaj vi ne devas atendi duonhoron ĝis desegnita fenestro kun la necesaj informoj (precipe kun malrapida konekto al fora datumbazo), kaj samtempe, la ricevita informo estas freŝa kaj trafa, kaj ne kaŝmemoritaj rubaĵoj. Krome, ju pli kompleksa kaj pli granda la datumbazo kaj ju pli granda estas la nombro da ili, des pli malfacilas fari ĉi tion.

Sed kutime mi forĵetas la muson kaj nur skribas kodon. Ni diru, ke vi devas ekscii, kiuj tabeloj (kaj kun kiuj propraĵoj) estas enhavitaj en la skemo "HR". En la plej multaj DBMSoj, la dezirata rezulto povas esti atingita per ĉi tiu simpla demando de information_schema:

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

De datumbazo al datumbazo, la enhavo de tiaj referencaj tabeloj varias depende de la kapabloj de ĉiu DBMS. Kaj, ekzemple, por MySQL, el la sama konsultlibro vi povas akiri tabelajn parametrojn specifajn por ĉi tiu DBMS:

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

Oracle ne konas informa_skemon, sed ĝi havas Oraklaj metadatenoj, kaj ne aperas grandaj problemoj:

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

ClickHouse ne estas escepto:

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

Io simila povas esti farita en Kasandra (kiu havas kolonfamiliojn anstataŭ tabelojn kaj ŝlosilspacojn anstataŭ skemojn):

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

Por plej multaj aliaj datumbazoj, vi ankaŭ povas elpensi similajn demandojn (eĉ Mongo havas speciala sistema kolekto, kiu enhavas informojn pri ĉiuj kolektoj en la sistemo).

Kompreneble, tiamaniere oni povas ricevi informojn ne nur pri tabeloj, sed pri iu ajn objekto ĝenerale. De tempo al tempo, afablaj homoj dividas tian kodon por malsamaj datumbazoj, kiel, ekzemple, en la serio de habra artikoloj "Funkcioj por dokumentado de PostgreSQL-datumbazoj" (Ayb, Ben, gimnazio). Kompreneble, konservi ĉi tiun tutan monton da demandoj en mia kapo kaj konstante tajpi ilin estas tia plezuro, do en mia plej ŝatata IDE/redaktisto mi havas antaŭpreparitan aron da fragmentoj por ofte uzataj demandoj, kaj restas nur tajpi la objektonomojn en la ŝablonon.

Kiel rezulto, ĉi tiu metodo por navigi kaj serĉi objektojn estas multe pli fleksebla, ŝparas multan tempon kaj ebligas al vi akiri precize la informojn en la formo en kiu ĝi nun estas necesa (kiel, ekzemple, priskribite en la afiŝo). "Eksporti datumojn de datumbazo en iu ajn formato: kion IDEoj povas fari sur la platformo IntelliJ").

Operacioj kun objektoj

Post kiam ni trovis kaj studis la necesajn objektojn, estas tempo fari ion utilan kun ili. Kompreneble, ankaŭ sen depreni viajn fingrojn de la klavaro.

Ne estas sekreto, ke simple forigo de tabelo aspektos same en preskaŭ ĉiuj datumbazoj:

drop table hr.persons

Sed kun la kreado de la tablo ĝi fariĝas pli interesa. Preskaŭ ajna DBMS (inkluzive de multaj NoSQL) povas "krei tabelon" en unu formo aŭ alia, kaj la ĉefa parto de ĝi eĉ iomete malsamas (nomo, listo de kolumnoj, datumtipoj), sed aliaj detaloj povas diferenci draste kaj dependi de la interna aparato kaj kapabloj de specifa DBMS. Mia plej ŝatata ekzemplo estas, ke en la Oracle-dokumentado estas nur "nudaj" BNF-oj por la sintakso "krei tabelon". okupas 31 paĝojn. Aliaj DBMS-oj havas pli modestajn kapablojn, sed ĉiu el ili ankaŭ havas multajn interesajn kaj unikajn funkciojn por krei tabelojn (postgresoj, mysql, blato, Kassandro). Estas neverŝajne, ke iu grafika "sorĉisto" de alia IDE (precipe universala) povos plene kovri ĉiujn ĉi tiujn kapablojn, kaj eĉ se ĝi povas, ĝi ne estos spektaklo por malfortuloj. Samtempe, ĝuste kaj ĝustatempe skribita deklaro krei tabelon permesos vin facile uzi ĉiujn, fari stokadon kaj aliron al viaj datumoj fidindaj, optimumaj kaj kiel eble plej komfortaj.

Ankaŭ, multaj DBMSoj havas siajn proprajn specifajn specojn de objektoj kiuj ne estas haveblaj en aliaj DBMSoj. Plie, ni povas fari operaciojn ne nur sur datumbazaj objektoj, sed ankaŭ sur la DBMS mem, ekzemple, "mortigi" procezon, liberigi iun memorareon, ebligi spuradon, ŝanĝi al "nurlega" reĝimo, kaj multe pli.

Nun ni desegnu iomete

Unu el la plej oftaj taskoj estas konstrui diagramon kun datumbazaj objektoj kaj vidi la objektojn kaj ligojn inter ili en bela bildo. Preskaŭ ajna grafika IDE, apartaj "komandliniaj" utilecoj, specialigitaj grafikaj iloj kaj modelistoj povas fari tion. Ili desegnos ion por vi "kiel eble plej bone", kaj vi povas iomete influi ĉi tiun procezon nur helpe de kelkaj parametroj en la agorda dosiero aŭ markobutonoj en la interfaco.

Sed ĉi tiu problemo povas esti solvita multe pli simple, pli fleksebla kaj eleganta, kaj kompreneble helpe de kodo. Por krei diagramojn de ajna komplekseco, ni havas plurajn specialajn marklingvojn (DOT, GraphML ktp), kaj por ili tutan disvastigon da aplikaĵoj (GraphViz, PlantUML, Mermaid), kiuj povas legi tiajn instrukciojn kaj bildigi ilin en diversaj formatoj. . Nu, ni jam scias kiel akiri informojn pri objektoj kaj ligoj inter ili.

Jen malgranda ekzemplo de kiel tio povus aspekti, uzante PlantUML kaj demo-datumbazo por PostgreSQL (maldekstre estas SQL-demando kiu generos la postulatan instrukcion por PlantUML, kaj dekstre estas la rezulto):

"Datumbazo kiel Kodo" Sperto

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'

Kaj se vi provas iomete, tiam bazita sur ER-ŝablono por PlantUML vi povas akiri ion tre similan al vera ER-diagramo:

La SQL-demando estas iom pli komplika

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

"Datumbazo kiel Kodo" Sperto

Se vi rigardas atente, sub la kapuĉo multaj bildigaj iloj ankaŭ uzas similajn demandojn. Vere, ĉi tiuj petoj estas kutime profunde "kabligitaj" en la kodon de la aplikaĵo mem kaj estas malfacile kompreneblaj, sen mencii ajnan modifon de ili.

Metriko kaj monitorado

Ni transiru al tradicie kompleksa temo - datumbaza agado-monitorado. Mi memoras malgrandan veran historion rakontitan al mi de "unu el miaj amikoj". En alia projekto vivis certa potenca DBA, kaj malmultaj el la programistoj konis lin persone, aŭ iam vidis lin persone (malgraŭ ke, laŭ onidiroj, li laboris ie en la sekva konstruaĵo). Je la horo "X", kiam la sistemo de produktado de granda komercisto komencis "senti malbone" denove, li silente sendis ekrankopiojn de grafikaĵoj de Oracle Enterprise Manager, sur kiuj li zorge reliefigis kritikajn lokojn per ruĝa signo por "komprenebleco" ( ĉi tio, por paroli milde, ne multe helpis). Kaj surbaze de ĉi tiu "fotokarto" mi devis trakti. Samtempe neniu havis aliron al la altvalora (en ambaŭ sencoj de la vorto) Enterprise Manager, ĉar la sistemo estas kompleksa kaj multekosta, subite "la programistoj stumblas pri io kaj rompas ĉion." Tial, la programistoj "empirie" trovis la lokon kaj kaŭzon de la bremsoj kaj liberigis diakilon. Se la minaca letero de la DBA ne alvenus denove en proksima estonteco, tiam ĉiuj spirus trankvile kaj revenus al siaj nunaj taskoj (ĝis la nova Letero).

Sed la kontrola procezo povas aspekti pli amuza kaj amika, kaj plej grave, alirebla kaj travidebla por ĉiuj. Almenaŭ ĝia baza parto, kiel aldono al la ĉefaj monitoraj sistemoj (kiuj certe estas utilaj kaj en multaj kazoj neanstataŭeblaj). Ajna DBMS estas libere kaj absolute senpaga por kunhavi informojn pri sia nuna stato kaj agado. En la sama "sanga" Oracle DB, preskaŭ ajna informo pri rendimento povas esti akirita de sistemaj vidoj, intervalante de procezoj kaj sesioj ĝis la stato de la bufrokaŝmemoro (ekzemple, DBA-Skriptoj, sekcio "Monitorado"). Postgresql ankaŭ havas multajn sistemajn vidojn por datumbaza monitorado, precipe tiuj, kiuj estas nemalhaveblaj en la ĉiutaga vivo de iu ajn DBA, kiel ekzemple pg_stat_aktiveco, pg_stat_database, pg_stat_bgwriter. MySQL eĉ havas apartan skemon por tio. rendimento_skemo. A En Mongo enkonstruita profilisto agregas rendimentajn datumojn en sisteman kolekton sistemo.profilo.

Tiel, armita per ia mezurikolektilo (Telegraf, Metricbeat, Collectd), kiu povas plenumi kutimajn sql-demandojn, stokadon de ĉi tiuj metrikoj (InfluxDB, Elasticsearch, Timescaledb) kaj vidigilon (Grafana, Kibana), vi povas akiri sufiĉe facilan. kaj fleksebla monitoradsistemo kiu estos proksime integrita kun aliaj tutsistemaj metrikoj (akirita, ekzemple, de la aplikaĵoservilo, de la OS, ktp.). Kiel, ekzemple, tio estas farita en pgwatch2, kiu uzas la kombinaĵon InfluxDB + Grafana kaj aron da demandoj al sistemaj vidoj, kiuj ankaŭ alireblas. aldoni kutimajn demandojn.

Tuta

Kaj ĉi tio estas nur proksimuma listo de tio, kion oni povas fari per nia datumbazo per regula SQL-kodo. Mi certas, ke vi povas trovi multajn pliajn uzojn, skribu en la komentoj. Kaj ni parolos pri kiel (kaj plej grave kial) aŭtomatigi ĉion ĉi kaj inkluzivi ĝin en vian CI/KD-dukton venontfoje.

fonto: www.habr.com

Aldoni komenton