"Datu bāze kā kods" pieredze

"Datu bāze kā kods" pieredze

SQL, kas var bÅ«t vienkārŔāks? Katrs no mums var uzrakstÄ«t vienkārÅ”u pieprasÄ«jumu ā€“ mēs ierakstām atlasÄ«t, uzskaitiet vajadzÄ«gās kolonnas un pēc tam no, tabulas nosaukums, daži nosacÄ«jumi kur un tas arÄ« viss ā€” noderÄ«gi dati ir mÅ«su kabatā un (gandrÄ«z) neatkarÄ«gi no tā, kura DBVS tajā laikā atrodas zem pārsega (vai varbÅ«t nemaz nav DBVS). Rezultātā darbu ar gandrÄ«z jebkuru datu avotu (relāciju un ne tik) var uzskatÄ«t no parastā koda viedokļa (ar visu, ko tas nozÄ«mē - versiju kontroli, koda pārskatÄ«Å”anu, statisko analÄ«zi, automātiskos testus un tas arÄ« viss). Un tas attiecas ne tikai uz paÅ”iem datiem, shēmām un migrācijām, bet kopumā uz visu krātuves kalpoÅ”anas laiku. Å ajā rakstā mēs runāsim par ikdienas uzdevumiem un problēmām darbā ar dažādām datu bāzēm zem ā€œdatubāzes kā kodaā€ objektÄ«va.

Un sāksim tieÅ”i no ORM. Pirmās "SQL vs ORM" tipa cīņas tika pamanÄ«tas atpakaļ pirms PetrÄ«nas Krievijas.

Objektu relāciju kartÄ“Å”ana

ORM atbalstÄ«tāji tradicionāli novērtē ātrumu un attÄ«stÄ«bas vieglumu, neatkarÄ«bu no DBVS un tÄ«ru kodu. Daudziem no mums kods darbam ar datu bāzi (un bieži arÄ« paÅ”u datu bāzi)

tas parasti izskatās apmēram Ŕādi...

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

Modelis ir piekārts ar gudrām anotācijām, un kaut kur aizkulisēs drosmÄ«gs ORM Ä£enerē un izpilda daudz SQL koda. Starp citu, izstrādātāji cenÅ”as visu iespējamo, lai izolētu sevi no savas datu bāzes ar abstrakciju kilometriem, kas norāda uz dažiem "SQL naids".

Otrā barikāžu pusē tÄ«ra ā€œroku darbsā€ SQL piekritēji atzÄ«mē spēju izspiest visu sulu no DBVS bez papildu slāņiem un abstrakcijām. Rezultātā parādās ā€œdatu centriskiā€ projekti, kur datu bāzē tiek iesaistÄ«ti speciāli apmācÄ«ti cilvēki (viņi arÄ« ir ā€œbasicistsā€, viņi arÄ« ir ā€œbasicistsā€, viņi arÄ« ir ā€œbasdenersā€ utt.), un izstrādātāji. atliek tikai ā€œizvilktā€ jau gatavus skatus un saglabātās procedÅ«ras, neiedziļinoties.

Kā bÅ«tu, ja mums bÅ«tu labākais no abām pasaulēm? Kā tas tiek darÄ«ts brÄ«niŔķīgā rÄ«kā ar dzÄ«vi apliecinoÅ”u nosaukumu Yesql. Es doÅ”u pāris rindiņas no vispārējās koncepcijas manā bezmaksas tulkojumā, un jÅ«s varat iepazÄ«ties ar to sÄ«kāk Å”eit.

Clojure ir forÅ”a valoda DSL izveidei, bet pati SQL ir forÅ”a DSL, un mums nav vajadzÄ«ga cita. S-izteiksmes ir lieliskas, taču tās Å”eit neko jaunu nepievieno. Rezultātā mēs iegÅ«stam kronÅ”teinus kronÅ”teinu labad. Vai nepiekrÄ«tu? Tad pagaidiet brÄ«di, kad sāks noplÅ«st abstrakcija pār datubāzi, un jÅ«s sākat cÄ«nÄ«ties ar funkciju (raw-sql)

Tātad, kas man jādara? Atstāsim SQL kā parasto SQL ā€” viens fails katram pieprasÄ«jumam:

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

... un pēc tam izlasiet Å”o failu, pārvērÅ”ot to par parastu Clojure funkciju:

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

Ievērojot principu "SQL pats par sevi, Clojure pats par sevi", jūs iegūstat:

  • Nav sintaktisku pārsteigumu. JÅ«su datu bāze (tāpat kā jebkura cita) nav 100% saderÄ«ga ar SQL standartu, taču tas nav svarÄ«gi attiecÄ«bā uz Yesql. JÅ«s nekad netērēsit laiku, meklējot funkcijas ar SQL ekvivalentu sintaksi. Jums nekad nebÅ«s jāatgriežas pie funkcijas (raw-sql "some('funky'::SYNTAX)")).
  • Labākais redaktora atbalsts. JÅ«su redaktoram jau ir lielisks SQL atbalsts. Saglabājot SQL kā SQL, varat to vienkārÅ”i izmantot.
  • Komandu saderÄ«ba. JÅ«su DBA var lasÄ«t un rakstÄ«t SQL, ko izmantojat savā Clojure projektā.
  • VienkārŔāka veiktspējas regulÄ“Å”ana. NepiecieÅ”ams izveidot plānu problemātiskam vaicājumam? Tā nav problēma, ja jÅ«su vaicājums ir parasts SQL.
  • Vaicājumu atkārtota izmantoÅ”ana. Velciet un nometiet tos paÅ”us SQL failus citos projektos, jo tas ir vienkārÅ”i vecs SQL ā€” vienkārÅ”i kopÄ«gojiet to.

Manuprāt, ideja ir ļoti forÅ”a un tajā paŔā laikā ļoti vienkārÅ”a, pateicoties kam projekts ir ieguvis daudzus sekotāji dažādās valodās. Tālāk mēs mēģināsim pielietot lÄ«dzÄ«gu filozofiju par SQL koda atdalÄ«Å”anu no visa pārējā, kas ir tālu ārpus ORM.

IDE un DB vadītāji

Sāksim ar vienkārÅ”u ikdienas uzdevumu. Bieži datu bāzē ir jāmeklē kādi objekti, piemēram, shēmā jāatrod tabula un jāizpēta tās struktÅ«ra (kādas kolonnas, atslēgas, indeksi, ierobežojumi utt. tiek lietoti). Un no jebkura grafiskā IDE vai neliela DB pārvaldnieka, pirmkārt, mēs sagaidām tieÅ”i Ŕīs spējas. Lai tas ir ātri un nav jāgaida pusstunda, lÄ«dz tiek uzzÄ«mēts logs ar nepiecieÅ”amo informāciju (Ä«paÅ”i ar lēnu savienojumu ar attālo datu bāzi), un tajā paŔā laikā saņemtā informācija ir svaiga un aktuāla, un nav keÅ”atmiņas junk. Turklāt, jo sarežģītāka un lielāka datu bāze un jo lielāks to skaits, jo grÅ«tāk to izdarÄ«t.

Bet parasti es izmetu peli un vienkārÅ”i uzrakstu kodu. Pieņemsim, ka jums ir jānoskaidro, kuras tabulas (un ar kurām Ä«paŔībām) ir ietvertas "HR" shēmā. Lielākajā daļā DBVS vēlamo rezultātu var sasniegt ar Å”o vienkārÅ”o vaicājumu no information_schema:

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

Dažādās datubāzēs Ŕādu atsauces tabulu saturs atŔķiras atkarÄ«bā no katras DBVS iespējām. Un, piemēram, MySQL no tās paÅ”as atsauces grāmatas varat iegÅ«t tabulas parametrus, kas raksturÄ«gi Å”ai DBVS:

select table_name
     , storage_engine -- Š˜ŃŠæŠ¾Š»ŃŒŠ·ŃƒŠµŠ¼Ń‹Š¹ "Š“Š²ŠøŠ¶Š¾Šŗ" ("MyISAM", "InnoDB" etc)
     , row_format     -- Š¤Š¾Ń€Š¼Š°Ń‚ стрŠ¾ŠŗŠø ("Fixed", "Dynamic" etc)
     , ...
  from information_schema.tables
 where schema = 'HR'

Oracle nezina information_schema, bet tā ir Oracle metadati, un lielas problēmas nerodas:

select table_name
     , pct_free       -- ŠœŠøŠ½ŠøŠ¼ŃƒŠ¼ сŠ²Š¾Š±Š¾Š“Š½Š¾Š³Š¾ Š¼ŠµŃŃ‚Š° Š² Š±Š»Š¾ŠŗŠµ Š“Š°Š½Š½Ń‹Ń… (%)
     , pct_used       -- ŠœŠøŠ½ŠøŠ¼ŃƒŠ¼ ŠøсŠæŠ¾Š»ŃŒŠ·ŃƒŠµŠ¼Š¾Š³Š¾ Š¼ŠµŃŃ‚Š° Š² Š±Š»Š¾ŠŗŠµ Š“Š°Š½Š½Ń‹Ń… (%)
     , last_analyzed  -- Š”Š°Ń‚Š° ŠæŠ¾ŃŠ»ŠµŠ“Š½ŠµŠ³Š¾ сŠ±Š¾Ń€Š° стŠ°Ń‚ŠøстŠøŠŗŠø
     , ...
  from all_tables
 where owner = 'HR'

ClickHouse nav izņēmums:

select name
     , engine -- Š˜ŃŠæŠ¾Š»ŃŒŠ·ŃƒŠµŠ¼Ń‹Š¹ "Š“Š²ŠøŠ¶Š¾Šŗ" ("MergeTree", "Dictionary" etc)
     , ...
  from system.tables
 where database = 'HR'

Kaut ko līdzīgu var izdarīt programmā Cassandra (kurā tabulu vietā ir kolonnu saimes, bet shēmu vietā ir atslēgas vietas):

select columnfamily_name
     , compaction_strategy_class  -- Š”трŠ°Ń‚ŠµŠ³Šøя сŠ±Š¾Ń€ŠŗŠø Š¼ŃƒŃŠ¾Ń€Š°
     , gc_grace_seconds           -- Š’Ń€ŠµŠ¼Ń Š¶ŠøŠ·Š½Šø Š¼ŃƒŃŠ¾Ń€Š°
     , ...
  from system.schema_columnfamilies
 where keyspace_name = 'HR'

Lielākajai daļai citu datu bāzu varat arÄ« piedāvāt lÄ«dzÄ«gus vaicājumus (pat Mongo ir Ä«paÅ”as sistēmas kolekcija, kurā ir informācija par visām kolekcijām sistēmā).

Protams, tādā veidā var iegÅ«t informāciju ne tikai par tabulām, bet par jebkuru objektu kopumā. Laiku pa laikam laipni cilvēki dalās ar tādu kodu dažādām datu bāzēm, kā, piemēram, habra rakstu sērijā ā€œPostgreSQL datu bāzu dokumentÄ“Å”anas funkcijasā€ (Ayb, Bens, sporta zāle). Protams, glabāt galvā visu Å”o vaicājumu kalnu un nemitÄ«gi tos rakstÄ«t ir ļoti patÄ«kami, tāpēc manā iecienÄ«tākajā IDE/redaktorā man ir iepriekÅ” sagatavota fragmentu kopa bieži lietotiem vaicājumiem, un atliek tikai ierakstÄ«t objektu nosaukumus veidnē.

LÄ«dz ar to Ŕī navigācijas un objektu meklÄ“Å”anas metode ir daudz elastÄ«gāka, ietaupa daudz laika un ļauj iegÅ«t tieÅ”i informāciju tādā formā, kādā tā Å”obrÄ«d ir nepiecieÅ”ama (kā, piemēram, aprakstÄ«ts ierakstā "Datu eksportÄ“Å”ana no datu bāzes jebkurā formātā: ko IDE var darÄ«t IntelliJ platformā").

Darbības ar objektiem

Kad esam atraduÅ”i un izpētÄ«juÅ”i nepiecieÅ”amos objektus, ir pienācis laiks ar tiem darÄ«t kaut ko noderÄ«gu. Protams, arÄ« neatraujot pirkstus no tastatÅ«ras.

Nav noslēpums, ka, vienkārÅ”i dzÄ“Å”ot tabulu, gandrÄ«z visās datu bāzēs izskatÄ«sies vienādi:

drop table hr.persons

Bet ar tabulas izveidi kļūst interesantāk. GandrÄ«z jebkura DBVS (tostarp daudzas NoSQL) var ā€œizveidot tabuluā€ vienā vai otrā veidā, un tās galvenā daļa pat nedaudz atŔķirsies (nosaukums, kolonnu saraksts, datu tipi), bet citas detaļas var krasi atŔķirties un ir atkarÄ«gas no iekŔējā ierÄ«ce un konkrētas DBVS iespējas. Mans iecienÄ«tākais piemērs ir tāds, ka Oracle dokumentācijā sintaksei ā€œizveidot tabuluā€ ir tikai ā€œkailiā€ BNF. aizņem 31 lappusi. Citām DBVS ir pieticÄ«gākas iespējas, taču katrai no tām ir arÄ« daudzas interesantas un unikālas iespējas tabulu izveidoÅ”anai (postgres, mysql, tarakāns, Cassandra). Maz ticams, ka kāds grafiskais ā€œvednisā€ no citas IDE (Ä«paÅ”i universālā) spēs pilnÄ«bā aptvert visas Ŕīs spējas, un pat ja var, tas nebÅ«s skats vājprātÄ«gajiem. Tajā paŔā laikā pareizi un savlaicÄ«gi rakstisks paziņojums izveidot tabulu ļaus ērti izmantot tos visus, padarot datu uzglabāŔanu un piekļuvi tiem uzticamu, optimālu un pēc iespējas ērtāku.

Turklāt daudzām DBVS ir savi specifiski objektu veidi, kas nav pieejami citās DBVS. Turklāt mēs varam veikt darbÄ«bas ne tikai ar datu bāzes objektiem, bet arÄ« ar paÅ”u DBVS, piemēram, ā€œnogalinātā€ procesu, atbrÄ«vot daļu atmiņas, iespējot izsekoÅ”anu, pārslēgties uz ā€œtikai lasÄ«Å”anasā€ režīmu un daudz ko citu.

Tagad nedaudz zīmēsim

Viens no biežākajiem uzdevumiem ir izveidot diagrammu ar datu bāzes objektiem un redzēt objektus un savienojumus starp tiem skaistā attēlā. To var izdarÄ«t gandrÄ«z jebkura grafiskā IDE, atseviŔķas "komandrindas" utilÄ«tas, specializēti grafiskie rÄ«ki un modelētāji. Viņi jums kaut ko uzzÄ«mēs, "cik labi vien varēs", un jÅ«s varat nedaudz ietekmēt Å”o procesu, tikai izmantojot dažus parametrus konfigurācijas failā vai interfeisa izvēles rÅ«tiņas.

Bet Å”o problēmu var atrisināt daudz vienkārŔāk, elastÄ«gāk un elegantāk, un, protams, ar koda palÄ«dzÄ«bu. Lai izveidotu jebkuras sarežģītÄ«bas diagrammas, mums ir vairākas specializētas iezÄ«mÄ“Å”anas valodas (DOT, GraphML uc), un tām ir vesela lietojumprogrammu izkliede (GraphViz, PlantUML, Mermaid), kas var lasÄ«t Ŕādas instrukcijas un vizualizēt tās dažādos formātos. . Nu, mēs jau zinām, kā iegÅ«t informāciju par objektiem un savienojumiem starp tiem.

Å eit ir neliels piemērs tam, kā tas varētu izskatÄ«ties, izmantojot PlantUML un demo datu bāze PostgreSQL (kreisajā pusē ir SQL vaicājums, kas Ä£enerēs nepiecieÅ”amo instrukciju PlantUML, un labajā pusē ir rezultāts):

"Datu bāze kā kods" pieredze

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'

Un, ja jūs mēģināt nedaudz, tad pamatojoties uz ER veidne PlantUML jūs varat iegūt kaut ko ļoti līdzīgu īstai ER diagrammai:

SQL vaicājums ir nedaudz sarežģītāks

-- ŠØŠ°ŠæŠŗŠ°
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'

"Datu bāze kā kods" pieredze

Ja paskatās cieÅ”i, zem pārsega daudzi vizualizācijas rÄ«ki izmanto arÄ« lÄ«dzÄ«gus vaicājumus. Tiesa, Å”ie lÅ«gumi parasti ir dziļi ā€œieslēgtiā€ paÅ”as lietojumprogrammas kodā un ir grÅ«ti saprotami, nemaz nerunājot par jebkādām to modifikācijām.

Metrika un uzraudzība

Pārejam pie tradicionāli sarežģītas tēmas ā€“ datu bāzes veiktspējas monitoringa. Es atceros mazu patiesu stāstu, ko man stāstÄ«ja "viens no maniem draugiem". Citā projektā dzÄ«voja zināms spēcÄ«gs DBA, un daži no izstrādātājiem viņu pazina personÄ«gi vai kādreiz bija redzējuÅ”i viņu klātienē (neskatoties uz to, ka saskaņā ar baumām viņŔ strādāja kaut kur blakus ēkā). ā€œXā€ stundā, kad liela mazumtirgotāja poduction sistēma atkal sāka ā€œsliktiā€, viņŔ klusÄ«bā nosÅ«tÄ«ja Oracle Enterprise Manager grafiku ekrānuzņēmumus, kuros viņŔ rÅ«pÄ«gi iezÄ«mēja kritiskās vietas ar sarkanu marÄ·ieri ā€œsaprotamÄ«baiā€ ( tas, maigi izsakoties, neko daudz nepalÄ«dzēja). Un, pamatojoties uz Å”o "fotokarti", man bija jāārstē. Tajā paŔā laikā nevienam nebija pieejams dārgais (Ŕī vārda abās nozÄ«mēs) Uzņēmuma vadÄ«tājs, jo sistēma ir sarežģīta un dārga, pēkŔņi "izstrādātāji uz kaut ko paklupa un visu sabojā." Tāpēc izstrādātāji ā€œempÄ«riskiā€ atrada bremžu atraÅ”anās vietu un cēloni un izlaida ielāpu. Ja draudÄ«gā vēstule no DBA tuvākajā laikā vairs nepienāktu, tad visi atviegloti uzelpotu un atgrieztos pie saviem paÅ”reizējiem uzdevumiem (lÄ«dz jaunajai Vēstulei).

Taču uzraudzÄ«bas process var izskatÄ«ties jautrāks un draudzÄ«gāks, un pats galvenais ā€“ pieejams un pārskatāms ikvienam. Vismaz tā pamata daļa, kā papildinājums galvenajām uzraudzÄ«bas sistēmām (kuras noteikti ir noderÄ«gas un daudzos gadÄ«jumos neaizvietojamas). Jebkura DBVS var brÄ«vi un pilnÄ«gi bez maksas dalÄ«ties ar informāciju par tās paÅ”reizējo stāvokli un veiktspēju. Tajā paŔā "asiņainajā" Oracle DB gandrÄ«z jebkuru informāciju par veiktspēju var iegÅ«t no sistēmas skatiem, sākot no procesiem un sesijām lÄ«dz bufera keÅ”atmiņas stāvoklim (piemēram, DBA skripti, sadaļā "UzraudzÄ«ba"). Postgresql ir arÄ« vesela virkne sistēmas skatu datu bāzes uzraudzÄ«ba, jo Ä«paÅ”i tie, kas ir neaizstājami jebkura DBA ikdienas dzÄ«vē, piemēram, pg_stat_activity, pg_stat_database, pg_stat_bgwriter. MySQL Å”im nolÅ«kam pat ir atseviŔķa shēma. veiktspējas_shēma. A In Mongo iebÅ«vēts profilētājs apkopo veiktspējas datus sistēmas kolekcijā sistēma.profils.

Tādējādi, bruņojoties ar sava veida metrikas savācēju (Telegraf, Metricbeat, Collectd), kas var veikt pielāgotus sql vaicājumus, Å”o rādÄ«tāju krātuvi (InfluxDB, Elasticsearch, Timescaledb) un vizualizētāju (Grafana, Kibana), jÅ«s varat iegÅ«t diezgan vienkārÅ”u un elastÄ«ga uzraudzÄ«bas sistēma, kas tiks cieÅ”i integrēta ar citiem sistēmas mēroga rādÄ«tājiem (iegÅ«ta, piemēram, no lietojumprogrammu servera, no OS utt.). Kā, piemēram, tas tiek darÄ«ts pgwatch2, kas izmanto InfluxDB + Grafana kombināciju un vaicājumu kopu sistēmas skatiem, kuriem var arÄ« piekļūt pievienot pielāgotus vaicājumus.

Kopā

Un tas ir tikai aptuvens saraksts ar to, ko var paveikt ar mūsu datubāzi, izmantojot parasto SQL kodu. Esmu pārliecināts, ka jūs varat atrast daudz vairāk lietojumu, rakstiet komentāros. Un mēs runāsim par to, kā (un vissvarīgāk, kāpēc) to visu automatizēt un iekļaut savā CI/CD konveijerā nākamreiz.

Avots: www.habr.com

Pievieno komentāru