SQL, ce poate fi mai simplu? Fiecare dintre noi poate scrie o cerere simplă - tastăm selecta, enumerați apoi coloanele necesare din, numele tabelului, unele condiții în Unde și asta-i tot - datele utile sunt în buzunarul nostru și (aproape) indiferent de ce DBMS se află sub capotă în acel moment (sau poate
Și să începem chiar de la
Maparea obiect-relațională
Susținătorii ORM apreciază în mod tradițional viteza și ușurința de dezvoltare, independența față de DBMS și codul curat. Pentru mulți dintre noi, codul pentru lucrul cu baza de date (și adesea baza de date în sine)
de obicei arata cam asa...
@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;
}
...
Modelul este agățat cu adnotări inteligente și undeva în spatele scenei un ORM curajos generează și execută tone de cod SQL. Apropo, dezvoltatorii fac tot posibilul să se izoleze de baza lor de date cu kilometri de abstracții, ceea ce indică unele
De cealaltă parte a baricadelor, adepții SQL pur „făcut manual” remarcă capacitatea de a stoarce tot sucul din DBMS-ul lor fără straturi și abstracții suplimentare. Ca urmare, apar proiecte „data-centric”, în care în baza de date sunt implicați oameni special pregătiți (sunt și „baziciști”, sunt și „baziciști”, sunt și „basdeneri”, etc.), iar dezvoltatorii trebuie doar să „trageți” vizualizările gata făcute și procedurile stocate, fără a intra în detalii.
Și dacă am avea ce este mai bun din ambele lumi? Cum se face acest lucru într-un instrument minunat cu un nume care afirmă viața
Clojure este un limbaj cool pentru a crea DSL-uri, dar SQL-ul în sine este un DSL cool și nu avem nevoie de altul. Expresiile S sunt grozave, dar nu adaugă nimic nou aici. Ca rezultat, primim paranteze de dragul parantezelor. Nu sunt de acord? Apoi așteptați momentul în care abstractizarea din baza de date începe să curgă și începeți să vă certați cu funcția (raw-sql)
Si ce ar trebui sa fac? Să lăsăm SQL ca SQL obișnuit - un fișier per cerere:
-- name: users-by-country
select *
from users
where country_code = :country_code
... și apoi citiți acest fișier, transformându-l într-o funcție obișnuită Clojure:
(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" ...} ...)
Prin aderarea la principiul „SQL de la sine, Clojure de la sine”, obțineți:
- Fără surprize sintactice. Baza de date (ca oricare alta) nu este 100% conformă cu standardul SQL - dar acest lucru nu contează pentru Yesql. Nu veți pierde niciodată timpul căutând funcții cu sintaxă echivalentă SQL. Nu va trebui niciodată să vă întoarceți la o funcție (raw-sql "unele('funky'::SYNTAX)")).
- Cel mai bun suport pentru editor. Editorul dvs. are deja suport excelent pentru SQL. Salvând SQL ca SQL, îl puteți utiliza pur și simplu.
- Compatibilitate cu echipa. DBA-urile dvs. pot citi și scrie SQL-ul pe care îl utilizați în proiectul dvs. Clojure.
- Reglare mai ușoară a performanței. Trebuie să construiți un plan pentru o interogare problematică? Aceasta nu este o problemă atunci când interogarea dvs. este SQL obișnuită.
- Reutilizarea interogărilor. Trageți și plasați aceleași fișiere SQL în alte proiecte, deoarece este pur și simplu vechi SQL - doar partajați-l.
În opinia mea, ideea este foarte cool și în același timp foarte simplă, datorită căreia proiectul a câștigat multe
Manageri IDE și DB
Să începem cu o sarcină simplă de zi cu zi. De multe ori trebuie să căutăm unele obiecte în baza de date, de exemplu, să găsim un tabel în schemă și să studiem structura acestuia (ce coloane, chei, indici, constrângeri etc. sunt folosite). Și de la orice IDE grafic sau un mic DB-manager, în primul rând, ne așteptăm exact la aceste abilități. Ca să fie rapid și să nu fii nevoit să aștepți o jumătate de oră până când se desenează o fereastră cu informațiile necesare (mai ales cu o conexiune lentă la o bază de date la distanță) și, în același timp, informațiile primite sunt proaspete și relevante, și nu junk stocate în cache. Mai mult, cu cât baza de date este mai complexă și mai mare și cu cât numărul acestora este mai mare, cu atât este mai dificil să faci acest lucru.
Dar de obicei arunc mouse-ul și doar scriu cod. Să presupunem că trebuie să aflați ce tabele (și cu ce proprietăți) sunt conținute în schema „HR”. În majoritatea SGBD-urilor, rezultatul dorit poate fi atins cu această interogare simplă din information_schema:
select table_name
, ...
from information_schema.tables
where schema = 'HR'
De la bază de date la bază de date, conținutul unor astfel de tabele de referință variază în funcție de capacitățile fiecărui SGBD. Și, de exemplu, pentru MySQL, din aceeași carte de referință puteți obține parametrii de tabel specifici acestui DBMS:
select table_name
, storage_engine -- Используемый "движок" ("MyISAM", "InnoDB" etc)
, row_format -- Формат строки ("Fixed", "Dynamic" etc)
, ...
from information_schema.tables
where schema = 'HR'
Oracle nu știe information_schema, dar are
select table_name
, pct_free -- Минимум свободного места в блоке данных (%)
, pct_used -- Минимум используемого места в блоке данных (%)
, last_analyzed -- Дата последнего сбора статистики
, ...
from all_tables
where owner = 'HR'
ClickHouse nu face excepție:
select name
, engine -- Используемый "движок" ("MergeTree", "Dictionary" etc)
, ...
from system.tables
where database = 'HR'
Ceva similar se poate face în Cassandra (care are familii de coloane în loc de tabele și spații de chei în loc de scheme):
select columnfamily_name
, compaction_strategy_class -- Стратегия сборки мусора
, gc_grace_seconds -- Время жизни мусора
, ...
from system.schema_columnfamilies
where keyspace_name = 'HR'
Pentru majoritatea celorlalte baze de date, puteți veni și cu interogări similare (chiar și Mongo are
Desigur, în acest fel puteți obține informații nu numai despre tabele, ci și despre orice obiect în general. Din când în când, oameni amabili împărtășesc un astfel de cod pentru diferite baze de date, ca, de exemplu, în seria de articole habra „Funcții pentru documentarea bazelor de date PostgreSQL” (
Drept urmare, această metodă de navigare și căutare a obiectelor este mult mai flexibilă, economisește mult timp și vă permite să obțineți exact informațiile în forma în care este acum necesară (cum, de exemplu, este descrisă în postare).
Operații cu obiecte
După ce am găsit și am studiat obiectele necesare, este timpul să facem ceva util cu ele. Desigur, și fără a-ți lua degetele de pe tastatură.
Nu este un secret că simpla ștergere a unui tabel va arăta la fel în aproape toate bazele de date:
drop table hr.persons
Dar odată cu crearea mesei devine mai interesant. Aproape orice DBMS (inclusiv multe NoSQL) poate „crea tabel” într-o formă sau alta, iar partea principală a acestuia va diferi chiar ușor (nume, listă de coloane, tipuri de date), dar alte detalii pot diferi dramatic și pot depinde de dispozitivul intern și capacitățile unui anumit SGBD. Exemplul meu preferat este că în documentația Oracle există doar BNF-uri „despuși” pentru sintaxa „create table”
De asemenea, multe SGBD-uri au propriile lor tipuri specifice de obiecte care nu sunt disponibile în alte SGBD-uri. Mai mult, putem efectua operațiuni nu numai asupra obiectelor bazei de date, ci și asupra DBMS-ului însuși, de exemplu, „omorâm” un proces, eliberăm o zonă de memorie, activam urmărirea, trecem la modul „numai citire” și multe altele.
Acum hai să desenăm puțin
Una dintre cele mai comune sarcini este de a construi o diagramă cu obiecte de bază de date și de a vedea obiectele și conexiunile dintre ele într-o imagine frumoasă. Aproape orice IDE grafic, utilitare separate „linie de comandă”, instrumente grafice specializate și modelatori pot face acest lucru. Ei vor desena ceva pentru tine „cât de bine pot” și poți influența puțin acest proces doar cu ajutorul câtorva parametri din fișierul de configurare sau a casetelor de selectare din interfață.
Dar această problemă poate fi rezolvată mult mai simplu, mai flexibil și elegant, și bineînțeles cu ajutorul codului. Pentru a crea diagrame de orice complexitate, avem mai multe limbaje de marcare specializate (DOT, GraphML etc), și pentru ele o întreagă împrăștiere de aplicații (GraphViz, PlantUML, Mermaid) care pot citi astfel de instrucțiuni și le pot vizualiza într-o varietate de formate. . Ei bine, știm deja cum să obținem informații despre obiecte și conexiunile dintre ele.
Iată un mic exemplu despre cum ar putea arăta, folosind PlantUML și
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'
Și dacă încerci puțin, atunci bazează-te pe
Interogarea SQL este puțin mai complicată
-- Шапка
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'
Dacă te uiți cu atenție, sub capotă multe instrumente de vizualizare folosesc, de asemenea, interogări similare. Adevărat, aceste cereri sunt de obicei profunde
Metrici și monitorizare
Să trecem la un subiect tradițional complex - monitorizarea performanței bazei de date. Îmi amintesc o mică poveste adevărată spusă mie de „unul dintre prietenii mei”. Într-un alt proiect a trăit un anumit DBA puternic și puțini dintre dezvoltatori l-au cunoscut personal sau l-au văzut vreodată în persoană (în ciuda faptului că, conform zvonurilor, a lucrat undeva în clădirea alăturată) . La ora „X”, când sistemul de producție al unui mare retailer a început să „se simtă rău” din nou, el a trimis în tăcere capturi de ecran cu grafice de la Oracle Enterprise Manager, pe care a evidențiat cu atenție locurile critice cu un marcator roșu pentru „înțeles” ( asta, ca să spunem ușor, nu a ajutat prea mult). Și pe baza acestei „carte foto” a trebuit să tratez. În același timp, nimeni nu a avut acces la prețiosul (în ambele sensuri ale cuvântului) Enterprise Manager, deoarece sistemul este complex și costisitor, dintr-o dată „dezvoltatorii dau peste ceva și sparg totul”. Prin urmare, dezvoltatorii au găsit „empiric” locația și cauza frânelor și au lansat un patch. Dacă scrisoarea amenințătoare de la DBA nu ar sosi din nou în viitorul apropiat, atunci toată lumea ar putea răsufla ușurată și ar reveni la sarcinile lor curente (până la noua Scrisoare).
Dar procesul de monitorizare poate părea mai distractiv și prietenos și, cel mai important, accesibil și transparent pentru toată lumea. Cel puțin partea sa de bază, ca o completare la principalele sisteme de monitorizare (care sunt cu siguranță utile și în multe cazuri de neînlocuit). Orice SGBD este liber și absolut gratuit pentru a partaja informații despre starea și performanța sa actuală. În același „sângeros” Oracle DB, aproape orice informație despre performanță poate fi obținută din vizualizările sistemului, de la procese și sesiuni până la starea memoriei cache-ului tampon (de exemplu,
Astfel, înarmat cu un fel de colector de metrici (Telegraf, Metricbeat, Collectd) care poate efectua interogări SQL personalizate, o stocare a acestor metrici (InfluxDB, Elasticsearch, Timescaledb) și un vizualizator (Grafana, Kibana), puteți obține un instrument destul de ușor. și un sistem de monitorizare flexibil care va fi strâns integrat cu alte metrici la nivel de sistem (obținute, de exemplu, de la serverul de aplicații, de la sistemul de operare etc.). Ca, de exemplu, acest lucru se face în pgwatch2, care folosește combinația InfluxDB + Grafana și un set de interogări către vizualizările de sistem, care pot fi, de asemenea, accesate
În total
Și aceasta este doar o listă aproximativă a ceea ce se poate face cu baza noastră de date folosind cod SQL obișnuit. Sunt sigur că găsești multe alte utilizări, scrie în comentarii. Și vom vorbi despre cum (și cel mai important de ce) să automatizezi toate acestea și să le includem în conducta CI/CD data viitoare.
Sursa: www.habr.com