SQL, čo môže byť jednoduchšie? Každý z nás môže napísať jednoduchú požiadavku – napíšeme vybrať, potom uveďte požadované stĺpce z, názov tabuľky, niekoľko podmienok v kde a to je všetko - užitočné údaje v našom vrecku a (takmer) bez ohľadu na to, ktorý DBMS je v tom čase pod kapotou (alebo možno
A začnime rovno s
Objektové relačné mapovanie
Zástancovia ORM tradične oceňujú rýchlosť a jednoduchosť vývoja, nezávislosť od DBMS a čistotu kódu. Pre mnohých z nás je kód pre prácu s databázou (a často aj samotnou databázou)
zvyčajne to vyzerá takto...
@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;
}
...
Model je zabalený do šikovných anotácií a niekde v zákulisí statočný ORM generuje a spúšťa tony nejakého SQL kódu. Mimochodom, vývojári sa zo všetkých síl snažia izolovať od svojej databázy kilometrami abstrakcií, čo naznačuje
Na druhej strane barikády prívrženci čistého „ručného“-SQL poznamenávajú schopnosť vytlačiť všetku šťavu zo svojho DBMS bez ďalších vrstiev a abstrakcií. V dôsledku toho sa objavujú „data-centrické“ projekty, kde sú do databázy zapojení špeciálne vyškolení ľudia (tiež sú „základisti“, sú aj „základisti“, sú tiež „bastardi“ atď.) a vývojári iba musia „vytiahnuť“ hotové pohľady a uložené procedúry bez toho, aby zachádzali do detailov.
A čo keď si vezmete to najlepšie z oboch svetov? Ako sa to robí v nádhernom nástroji s názvom, ktorý potvrdzuje život
Clojure je skvelý jazyk na vytváranie DSL, ale samotný SQL je skvelý DSL a nepotrebujeme ďalší. S-výrazy sú skvelé, ale nepridávajú tu nič nové. V dôsledku toho dostaneme zátvorky kvôli zátvorkám. Nesúhlas? Potom počkajte na moment, kedy unikne abstrakcia nad databázou, a začnete s funkciou bojovať (raw-sql)
A čo robiť? Nechajme SQL ako normálny SQL – jeden súbor na požiadavku:
-- name: users-by-country
select *
from users
where country_code = :country_code
...a potom si prečítajte tento súbor a premeňte ho na bežnú funkciu 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" ...} ...)
Dodržaním zásady „SQL oddelené, Clojure oddelené“ získate:
- Žiadne syntaktické prekvapenia. Vaša databáza (ako každá iná) nie je 100% kompatibilná s SQL – ale Yesql to nezaujíma. Nikdy nebudete strácať čas hľadaním funkcií so syntaxou ekvivalentnou SQL. Už sa nikdy nemusíte vracať k funkcii (raw-sql "some('funky'::SYNTAX)")).
- Lepšia podpora editorov. Váš editor už má vynikajúcu podporu SQL. Uložením SQL ako SQL ho môžete jednoducho použiť.
- Tímová kompatibilita. Vaši DBA môžu čítať a zapisovať SQL, ktorý používate vo svojom projekte Clojure.
- Jednoduchšie ladenie výkonu. Potrebujete zostaviť plán pre problematický dopyt? Toto nie je problém, keď je váš dotaz bežný SQL.
- Opätovné použitie dopytov. Presuňte tie isté súbory SQL do iných projektov, pretože je to len starý dobrý SQL – stačí ho zdieľať.
Nápad je to podľa mňa veľmi cool a zároveň veľmi jednoduchý, vďaka čomu si projekt veľa získal
IDE a DB manažéri
Začnime jednoduchou každodennou úlohou. Často musíme hľadať nejaké objekty v databáze, napríklad nájsť tabuľku v schéme a študovať jej štruktúru (ktoré stĺpce, kľúče, indexy, obmedzenia atď. sú použité). A od akéhokoľvek grafického IDE alebo malého DB-managera v prvom rade čakáme práve na tieto schopnosti. Aby to bolo rýchle a nemuselo sa čakať pol hodiny, kým sa vykreslí okno s potrebnými informáciami (najmä pri pomalom pripojení na vzdialenú databázu), a zároveň, aby prijaté informácie boli čerstvé a relevantné a nevyžiadaná pošta. Navyše, čím je databáza zložitejšia a väčšia a čím väčší je ich počet, tým je to ťažšie.
Ale väčšinou hodím myš ďaleko a len napíšem kód. Povedzme, že chcete vedieť, ktoré tabuľky (a s akými vlastnosťami) sú obsiahnuté v schéme „HR“. Vo väčšine DBMS je možné požadovaný výsledok dosiahnuť pomocou takého jednoduchého dotazu z information_schema:
select table_name
, ...
from information_schema.tables
where schema = 'HR'
Od databázy k databáze sa obsah takýchto vyhľadávacích tabuliek líši v závislosti od možností každého DBMS. A napríklad pre MySQL z rovnakého adresára môžete získať parametre tabuľky špecifické pre tento DBMS:
select table_name
, storage_engine -- Используемый "движок" ("MyISAM", "InnoDB" etc)
, row_format -- Формат строки ("Fixed", "Dynamic" etc)
, ...
from information_schema.tables
where schema = 'HR'
Oracle nepozná information_schema, ale vie
select table_name
, pct_free -- Минимум свободного места в блоке данных (%)
, pct_used -- Минимум используемого места в блоке данных (%)
, last_analyzed -- Дата последнего сбора статистики
, ...
from all_tables
where owner = 'HR'
ClickHouse nie je výnimkou:
select name
, engine -- Используемый "движок" ("MergeTree", "Dictionary" etc)
, ...
from system.tables
where database = 'HR'
Niečo podobné sa dá urobiť v Cassandre (ktorá má namiesto tabuliek a klávesových priestorov namiesto schém columnfamilies):
select columnfamily_name
, compaction_strategy_class -- Стратегия сборки мусора
, gc_grace_seconds -- Время жизни мусора
, ...
from system.schema_columnfamilies
where keyspace_name = 'HR'
Pre väčšinu ostatných databáz môžete tiež prísť s podobnými dotazmi (dokonca aj Mongo
Samozrejme, týmto spôsobom môžete získať informácie nielen o tabuľkách, ale vo všeobecnosti o akomkoľvek objekte. Z času na čas láskaví ľudia zdieľajú takýto kód pre rôzne databázy, ako napríklad v sérii článkov habra „Funkcie pre dokumentovanie PostgreSQL databáz“ (
Vďaka tomu je tento spôsob navigácie a vyhľadávania objektov oveľa flexibilnejší, šetrí veľa času, umožňuje získať presne tie informácie a vo forme, v akej ich práve potrebujete (ako je napríklad popísané v príspevku
Operácie s predmetmi
Potom, čo sme našli a preštudovali potrebné predmety, je čas urobiť s nimi niečo užitočné. Prirodzene aj bez toho, aby ste zložili prsty z klávesnice.
Nie je žiadnym tajomstvom, že jednoduché odstránenie tabuľky bude vyzerať rovnako v takmer všetkých databázach:
drop table hr.persons
Ale s vytvorením tabuľky je to už zaujímavejšie. Takmer každý DBMS (vrátane mnohých NoSQL) dokáže „vytvoriť tabuľku“ v tej či onej forme a jej hlavná časť sa bude dokonca len málo líšiť (názov, zoznam stĺpcov, typy údajov), ale ostatné detaily sa môžu dramaticky líšiť a závisia od interného zariadenia. a schopnosti konkrétneho DBMS. Môj obľúbený príklad je, že v dokumentácii Oracle je len jeden „holý“ BNF pre syntax „vytvoriť tabuľku“
Mnohé DBMS majú tiež svoje vlastné špecifické typy objektov, ktoré nie sú dostupné v iných DBMS. Okrem toho môžeme vykonávať operácie nielen na databázových objektoch, ale aj na samotnom DBMS, napríklad „zabiť“ proces, uvoľniť časť pamäte, povoliť sledovanie, prepnúť do režimu „iba na čítanie“ a oveľa viac.
A teraz poďme trochu kresliť
Jednou z najbežnejších úloh je zostaviť diagram s databázovými objektmi, vidieť objekty a vzťahy medzi nimi v krásnom obrázku. Dokáže to takmer každé grafické IDE, samostatné obslužné programy „príkazového riadku“, špecializované grafické nástroje a modelári. Ktoré vám niečo vykreslia „ako najlepšie vedia“ a tento proces môžete mierne ovplyvniť len pomocou niekoľkých parametrov v konfiguračnom súbore alebo checkboxov v rozhraní.
Ale tento problém sa dá vyriešiť oveľa jednoduchším, flexibilnejším a elegantnejším spôsobom a samozrejme pomocou kódu. Na zostavenie diagramov akejkoľvek zložitosti máme niekoľko špecializovaných značkovacích jazykov naraz (DOT, GraphML atď.) a pre nich - celý rad aplikácií (GraphViz, PlantUML, Mermaid), ktoré dokážu prečítať takéto pokyny a vizualizovať ich v rôznych formátov. No už vieme, ako získať informácie o objektoch a vzťahoch medzi nimi.
Uveďme si malý príklad, ako by to mohlo vyzerať s použitím PlantUML a
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'
A ak sa trochu pokúsite, tak na základe
SQL dotaz je trochu komplikovanejší
-- Шапка
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'
Ak sa pozriete pozorne, pod kapotou mnohé vizualizačné nástroje tiež používajú podobné dopyty. Je pravda, že tieto požiadavky sú zvyčajne hlboké
Metriky a monitorovanie
Prejdime k tradične ťažkej téme – monitorovaniu výkonu databázy. Spomínam si na malý skutočný príbeh, ktorý mi povedal „jeden z mojich priateľov“. Na ďalšom projekte žil istý mocný DBA a máloktorý z vývojárov ho osobne poznal a skutočne mu niekedy videl do očí (napriek tomu, že podľa povestí pracoval niekde v neďalekej budove). V hodine „X“, keď sa produkčný systém veľkého maloobchodníka opäť začal „necítiť“, v tichosti poslal screenshoty grafov z Oracle Enterprise Manager, na ktorých pre „jasnosť“ starostlivo zvýraznil kritické miesta červenou značkou. (toto, mierne povedané, veľmi nepomohlo). A táto „fotokartička“ musela byť ošetrená. Zároveň nikto nemal prístup k vzácnemu (v oboch významoch slova) Enterprise Manager. systém je zložitý a drahý, zrazu "vývojári na niečo narazia a všetko rozbijú." Preto vývojári „empiricky“ našli miesto a príčinu bŕzd a vydali záplatu. Ak ten strašný list od DBA v blízkej dobe opäť neprišiel, tak si všetci vydýchli a vrátili sa k svojim aktuálnym úlohám (až do nového Listu).
Proces monitorovania však môže vyzerať zábavnejšie a priateľskejšie, a čo je najdôležitejšie, prístupný a transparentný pre každého. Aspoň jeho základná časť, ako doplnok k hlavným monitorovacím systémom (ktoré sú určite užitočné a v mnohých prípadoch nenahraditeľné). Akýkoľvek DBMS je voľne a úplne zadarmo pripravený zdieľať informácie o svojom aktuálnom stave a výkone. V tej istej "krvavej" Oracle DB je možné získať takmer akékoľvek informácie o výkone zo systémových zobrazení, od procesov a relácií až po stav vyrovnávacej pamäte (napr.
Takže vyzbrojení nejakým zberačom metrík (Telegraf, Metricbeat, Collectd), ktorý dokáže vykonávať vlastné SQL dotazy, úložiskom týchto metrík (InfluxDB, Elasticsearch, Timescaledb) a vizualizérom (Grafana, Kibana), môžete získať pomerne jednoduchý a flexibilný monitorovací systém, ktorý bude úzko integrovaný s ostatnými celosystémovými metrikami (získanými napr. z aplikačného servera, z OS atď.). Ako napríklad v pgwatch2, ktorý používa balík InfluxDB + Grafana a sadu dotazov na systémové zobrazenia, ktoré môžu byť
Celkom
A to je len približný zoznam toho, čo sa dá s našou databázou robiť pomocou bežného SQL kódu. Som si istý, že nájdete oveľa viac aplikácií, napíšte do komentárov. A o tom, ako (a hlavne prečo) toto všetko zautomatizovať a zaradiť do vášho CI / CD pipeline, si povieme nabudúce.
Zdroj: hab.com