"Ma'lumotlar bazasi kod sifatida" tajribasi

"Ma'lumotlar bazasi kod sifatida" tajribasi

SQL, nima oddiyroq bo'lishi mumkin? Har birimiz oddiy so'rov yozishimiz mumkin - biz yozamiz tanlash, kerakli ustunlarni ro'yxatlang, keyin dan, jadval nomi, ba'zi shartlar qayerda va bu hammasi - foydali ma'lumotlar bizning cho'ntagimizda va (deyarli) o'sha paytda qaysi DBMS kaput ostida bo'lishidan qat'i nazar (yoki ehtimol umuman DBMS emas). Natijada, deyarli har qanday ma'lumot manbasi bilan ishlash (aloqaviy va unchalik emas) oddiy kod nuqtai nazaridan ko'rib chiqilishi mumkin (u nazarda tutadigan barcha narsalar bilan - versiyani boshqarish, kodni ko'rib chiqish, statik tahlil, avtotestlar va hammasi). Va bu nafaqat ma'lumotlarning o'zi, sxemalari va migratsiyalariga, balki umuman saqlashning butun muddatiga ham tegishli. Ushbu maqolada biz kundalik vazifalar va turli ma'lumotlar bazalari bilan ishlash muammolari haqida "kod sifatida ma'lumotlar bazasi" ob'ektivida gaplashamiz.

Va to'g'ridan-to'g'ri boshlaylik ORM. "SQL vs ORM" turidagi birinchi janglar yana kuzatilgan Petrindan oldingi rus.

Ob'ektga aloqador xaritalash

ORM tarafdorlari an'anaviy ravishda tezlik va rivojlanish qulayligini, DBMSdan mustaqillikni va toza kodni qadrlashadi. Ko'pchiligimiz uchun ma'lumotlar bazasi bilan ishlash uchun kod (va ko'pincha ma'lumotlar bazasining o'zi)

odatda shunday ko'rinadi ...

@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 aqlli izohlar bilan osilgan va sahna ortida bir joyda jasur ORM tonnalab SQL kodlarini ishlab chiqaradi va bajaradi. Aytgancha, ishlab chiquvchilar o'zlarining ma'lumotlar bazasidan kilometrlarcha abstraktsiyalar bilan ajralib turishga harakat qilmoqdalar, bu ba'zi narsalarni ko'rsatadi. "SQLdan nafratlanish".

Barrikadalarning boshqa tomonida sof "qo'lda ishlangan" SQL tarafdorlari qo'shimcha qatlamlar va abstraktsiyalarsiz o'zlarining DBMSlaridan barcha sharbatni siqib chiqarish qobiliyatini ta'kidlashadi. Natijada "ma'lumotlarga asoslangan" loyihalar paydo bo'ladi, bu erda ma'lumotlar bazasiga maxsus o'qitilgan odamlar jalb qilinadi (ular ham "bazachilar", ular ham "bazachilar", ular ham "basdenatorlar" va boshqalar) va ishlab chiquvchilar. Tafsilotlarga kirmasdan, faqat tayyor ko'rinishlarni va saqlangan protseduralarni "tortib olish" kerak.

Agar biz ikkala dunyoning eng yaxshisiga ega bo'lsak-chi? Bu hayotni tasdiqlovchi ism bilan ajoyib vositada qanday amalga oshiriladi Yesql. Men bepul tarjimamda umumiy tushunchadan bir-ikki satr beraman va u bilan batafsilroq tanishishingiz mumkin. shu yerda.

Clojure - bu DSL yaratish uchun ajoyib til, lekin SQLning o'zi ajoyib DSL va bizga boshqa til kerak emas. S-ifodalari ajoyib, lekin ular bu erda yangi hech narsa qo'shmaydi. Natijada, biz qavslar uchun qavslarni olamiz. Rozi emasmisiz? Keyin ma'lumotlar bazasi ustidan abstraksiya oqishi va siz funktsiya bilan kurashishni boshlagan paytni kuting (raw-sql)

Xo'sh, nima qilishim kerak? Keling, SQLni oddiy SQL sifatida qoldiraylik - har bir so'rov uchun bitta fayl:

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

... va keyin ushbu faylni oddiy Clojure funktsiyasiga aylantirib o'qing:

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

"SQL o'z-o'zidan, Clojure o'z-o'zidan" tamoyiliga rioya qilib, siz quyidagilarni olasiz:

  • Sintaktik kutilmagan hodisalar yo'q. Sizning ma'lumotlar bazasi (boshqalar kabi) SQL standartiga 100% mos kelmaydi - lekin bu Yesql uchun muhim emas. SQL ekvivalenti sintaksisi bilan funksiyalarni qidirishga hech qachon vaqt sarflamaysiz. Siz hech qachon funktsiyaga qaytishingiz shart emas (raw-sql "ba'zi('funky'::SYNTAX)")).
  • Eng yaxshi muharrirni qo'llab-quvvatlash. Muharriringiz allaqachon mukammal SQL yordamiga ega. SQLni SQL sifatida saqlash orqali siz undan oddiygina foydalanishingiz mumkin.
  • Jamoa muvofiqligi. Sizning DBA'laringiz Clojure loyihangizda foydalanadigan SQL-ni o'qishi va yozishi mumkin.
  • Ishlashni oson sozlash. Muammoli so'rov uchun reja tuzish kerakmi? So'rovingiz oddiy SQL bo'lsa, bu muammo emas.
  • So'rovlarni qayta ishlatish. Xuddi shu SQL fayllarini boshqa loyihalarga sudrab olib tashlang, chunki bu oddiy eski SQL - shunchaki baham ko'ring.

Menimcha, g'oya juda zo'r va ayni paytda juda oddiy, buning natijasida loyiha ko'pchilikni qo'lga kiritdi izdoshlar turli tillarda. Va keyin biz SQL kodini ORM dan tashqaridagi hamma narsadan ajratishning o'xshash falsafasini qo'llashga harakat qilamiz.

IDE va ​​DB menejerlari

Keling, oddiy kundalik vazifadan boshlaylik. Ko'pincha ma'lumotlar bazasida ba'zi ob'ektlarni qidirishga to'g'ri keladi, masalan, sxemada jadvalni topib, uning tuzilishini o'rganishimiz kerak (qanday ustunlar, kalitlar, indekslar, cheklovlar va boshqalar ishlatiladi). Va har qanday grafik IDE yoki kichik DB-menejerdan, birinchi navbatda, biz aynan shu qobiliyatlarni kutamiz. Bu tez bo'lishi va kerakli ma'lumotlarga ega oyna paydo bo'lguncha (ayniqsa masofaviy ma'lumotlar bazasiga sekin ulanishda) yarim soat kutishingizga to'g'ri kelmasligi va shu bilan birga olingan ma'lumotlar yangi va dolzarb bo'lishi uchun, va keshlanmagan keraksiz narsalar. Bundan tashqari, ma'lumotlar bazasi qanchalik murakkab va katta bo'lsa va ularning soni qanchalik ko'p bo'lsa, buni qilish shunchalik qiyin bo'ladi.

Lekin odatda men sichqonchani tashlab, shunchaki kod yozaman. Aytaylik, siz "HR" sxemasida qaysi jadvallar (va qaysi xususiyatlar bilan) mavjudligini aniqlashingiz kerak. Ko'pgina DBMSlarda kerakli natijaga information_schema dan ushbu oddiy so'rov yordamida erishish mumkin:

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

Ma'lumotlar bazasidan ma'lumotlar bazasiga, bunday ma'lumot jadvallarining mazmuni har bir ma'lumotlar bazasining imkoniyatlariga qarab farqlanadi. Va, masalan, MySQL uchun, xuddi shu ma'lumotnomadan ushbu DBMSga xos jadval parametrlarini olishingiz mumkin:

select table_name
     , storage_engine -- Π˜ΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌΡ‹ΠΉ "Π΄Π²ΠΈΠΆΠΎΠΊ" ("MyISAM", "InnoDB" etc)
     , row_format     -- Π€ΠΎΡ€ΠΌΠ°Ρ‚ строки ("Fixed", "Dynamic" etc)
     , ...
  from information_schema.tables
 where schema = 'HR'

Oracle information_schema-ni bilmaydi, lekin unda bor Oracle metama'lumotlari, va hech qanday katta muammolar paydo bo'lmaydi:

select table_name
     , pct_free       -- ΠœΠΈΠ½ΠΈΠΌΡƒΠΌ свободного мСста Π² Π±Π»ΠΎΠΊΠ΅ Π΄Π°Π½Π½Ρ‹Ρ… (%)
     , pct_used       -- ΠœΠΈΠ½ΠΈΠΌΡƒΠΌ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌΠΎΠ³ΠΎ мСста Π² Π±Π»ΠΎΠΊΠ΅ Π΄Π°Π½Π½Ρ‹Ρ… (%)
     , last_analyzed  -- Π”Π°Ρ‚Π° послСднСго сбора статистики
     , ...
  from all_tables
 where owner = 'HR'

ClickHouse ham bundan mustasno emas:

select name
     , engine -- Π˜ΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌΡ‹ΠΉ "Π΄Π²ΠΈΠΆΠΎΠΊ" ("MergeTree", "Dictionary" etc)
     , ...
  from system.tables
 where database = 'HR'

Kassandrada (jadvallar o'rniga ustunlar oilalari va sxemalar o'rniga kalit bo'shliqlari mavjud) shunga o'xshash narsani qilish mumkin:

select columnfamily_name
     , compaction_strategy_class  -- БтратСгия сборки мусора
     , gc_grace_seconds           -- ВрСмя ΠΆΠΈΠ·Π½ΠΈ мусора
     , ...
  from system.schema_columnfamilies
 where keyspace_name = 'HR'

Ko'pgina boshqa ma'lumotlar bazalari uchun siz shunga o'xshash so'rovlarni topishingiz mumkin (hatto Mongoda ham mavjud maxsus tizim to'plami, bu tizimdagi barcha to'plamlar haqidagi ma'lumotlarni o'z ichiga oladi).

Albatta, bu bilan siz nafaqat jadvallar, balki umuman har qanday ob'ekt haqida ma'lumot olishingiz mumkin. Vaqti-vaqti bilan mehribon odamlar turli xil ma'lumotlar bazalari uchun bunday kodni baham ko'rishadi, masalan, "PostgreSQL ma'lumotlar bazalarini hujjatlashtirish funktsiyalari" habra maqolalarida (Ayb, axlat qutisi, sportzal). Albatta, bu butun so'rovlar tog'ini miyamda saqlash va ularni doimiy ravishda yozish juda yoqimli, shuning uchun mening sevimli IDE/muharririmda tez-tez ishlatib turadigan so'rovlar uchun oldindan tayyorlangan parchalar to'plami bor va qolgan narsa: ob'ekt nomlarini shablonga kiriting.

Natijada, ob'ektlarni navigatsiya qilish va qidirishning ushbu usuli ancha moslashuvchan bo'lib, ko'p vaqtni tejaydi va kerakli ma'lumotni hozirda zarur bo'lgan shaklda olish imkonini beradi (masalan, postda tasvirlanganidek). "Ma'lumotlar bazasidan ma'lumotlarni istalgan formatda eksport qilish: IDElar IntelliJ platformasida nima qilishi mumkin").

Ob'ektlar bilan operatsiyalar

Biz kerakli narsalarni topib, o'rganib chiqqanimizdan so'ng, ular bilan foydali narsalarni qilish vaqti keldi. Tabiiyki, barmoqlaringizni klaviaturadan uzmasdan ham.

Hech kimga sir emaski, jadvalni shunchaki o'chirish deyarli barcha ma'lumotlar bazalarida bir xil ko'rinadi:

drop table hr.persons

Ammo stolni yaratish bilan u yanada qiziqarli bo'ladi. Deyarli har qanday DBMS (shu jumladan, ko'p NoSQL) u yoki bu shaklda "jadval yaratishi" mumkin va uning asosiy qismi biroz farq qilishi mumkin (nomi, ustunlar ro'yxati, ma'lumotlar turlari), ammo boshqa tafsilotlar keskin farq qilishi va ma'lumotlarga bog'liq bo'lishi mumkin. ichki qurilma va ma'lum bir ma'lumotlar bazasining imkoniyatlari. Mening sevimli misolim, Oracle hujjatlarida "jadval yaratish" sintaksisi uchun faqat "yalang'och" BNFlar mavjud. 31 sahifani egallaydi. Boshqa DBMSlar oddiyroq imkoniyatlarga ega, ammo ularning har biri jadvallarni yaratish uchun juda ko'p qiziqarli va o'ziga xos xususiyatlarga ega (postgres, mysql, suvarak, kassandra). Boshqa IDE-dan (ayniqsa universal) har qanday grafik "sehrgar" bu barcha qobiliyatlarni to'liq qamrab olishi dargumon va agar imkoni bo'lsa ham, yurak zaiflar uchun tomosha bo'lmaydi. Shu bilan birga, to'g'ri va o'z vaqtida yozilgan bayonot jadval yaratish ularning barchasidan osongina foydalanish, saqlash va ma'lumotlaringizga kirishni ishonchli, optimal va imkon qadar qulay qilish imkonini beradi.

Bundan tashqari, ko'pgina ma'lumotlar bazalari boshqa ma'lumotlar bazalarida mavjud bo'lmagan o'ziga xos turdagi ob'ektlarga ega. Bundan tashqari, biz nafaqat ma'lumotlar bazasi ob'ektlarida, balki DBMSning o'zida ham operatsiyalarni bajarishimiz mumkin, masalan, jarayonni "o'ldirish", xotira maydonini bo'shatish, kuzatishni yoqish, "faqat o'qish" rejimiga o'tish va boshqalar.

Endi biroz chizamiz

Eng keng tarqalgan vazifalardan biri - ma'lumotlar bazasi ob'ektlari bilan diagramma qurish va ular orasidagi ob'ektlar va aloqalarni chiroyli rasmda ko'rishdir. Deyarli har qanday grafik IDE, alohida "buyruqlar qatori" yordam dasturlari, maxsus grafik vositalar va modelerlar buni amalga oshirishi mumkin. Ular siz uchun "iloji boricha" nimanidir chizishadi va siz konfiguratsiya faylidagi bir nechta parametrlar yoki interfeysdagi tasdiqlash qutilari yordamida bu jarayonga ozgina ta'sir qilishingiz mumkin.

Ammo bu muammoni ancha sodda, moslashuvchan va oqlangan va, albatta, kod yordamida hal qilish mumkin. Har qanday murakkablikdagi diagrammalarni yaratish uchun bizda bir nechta maxsus belgilash tillari (DOT, GraphML va boshqalar) mavjud va ular uchun bunday ko'rsatmalarni o'qiy oladigan va ularni turli formatlarda tasvirlashi mumkin bo'lgan to'liq tarqoq ilovalar (GraphViz, PlantUML, Mermaid) mavjud. . Xo'sh, biz allaqachon ob'ektlar va ular orasidagi aloqalar haqida ma'lumot olishni bilamiz.

Mana bu qanday ko'rinishi mumkinligiga kichik misol, PlantUML va PostgreSQL uchun demo ma'lumotlar bazasi (chapda PlantUML uchun kerakli ko'rsatmalarni yaratadigan SQL so'rovi, o'ngda esa natija):

"Ma'lumotlar bazasi kod sifatida" tajribasi

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'

Va agar siz biroz harakat qilsangiz, unda asoslanadi PlantUML uchun ER shabloni haqiqiy ER diagrammasiga juda o'xshash narsani olishingiz mumkin:

SQL so'rovi biroz murakkabroq

-- Π¨Π°ΠΏΠΊΠ°
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'

"Ma'lumotlar bazasi kod sifatida" tajribasi

Agar diqqat bilan qarasangiz, kaput ostida ko'plab vizualizatsiya vositalari ham shunga o'xshash so'rovlardan foydalanadi. To'g'ri, bu so'rovlar odatda chuqurdir Ilovaning o'zi kodiga "o'rnatilgan" va tushunish qiyin, ularning har qanday o'zgartirishlari haqida gapirmasa ham bo'ladi.

Ko'rsatkichlar va monitoring

Keling, an'anaviy murakkab mavzuga o'tamiz - ma'lumotlar bazasining ishlashi monitoringi. Menga "do'stlarimdan biri" aytgan kichik haqiqiy voqeani eslayman. Boshqa bir loyihada ma'lum bir kuchli DBA yashagan va ishlab chiquvchilarning bir nechtasi uni shaxsan bilishgan yoki uni shaxsan ko'rishgan (mish-mishlarga ko'ra, u keyingi binoda ishlagan bo'lsa ham). "X" soatida yirik chakana sotuvchining poduction tizimi yana "o'zini yomon his qila" boshlaganida, u jimgina Oracle Enterprise Manager-dan grafiklarning skrinshotlarini yubordi, unda u "tushunish" uchun qizil marker bilan muhim joylarni diqqat bilan ajratib ko'rsatdi ( Bu, yumshoq qilib aytganda, unchalik yordam bermadi). Va bu "foto karta" asosida men davolanishim kerak edi. Shu bilan birga, hech kim qimmatbaho (so'zning ikkala ma'nosida) korxona menejeriga kira olmadi, chunki tizim murakkab va qimmat, to'satdan "ishlab chiquvchilar biror narsaga qoqilib, hamma narsani buzadilar". Shuning uchun, ishlab chiquvchilar "empirik" tormozlarning joylashuvi va sababini topdilar va yamoqni chiqardilar. Agar DBAdan qoβ€˜rqinchli xat yaqin orada yana kelmasa, hamma yengil nafas olib, hozirgi vazifalariga qaytadi (yangi Xatgacha).

Ammo monitoring jarayoni yanada qiziqarli va do'stona, eng muhimi, hamma uchun ochiq va shaffof ko'rinishi mumkin. Hech bo'lmaganda uning asosiy qismi, asosiy monitoring tizimlariga qo'shimcha sifatida (ular, albatta, foydali va ko'p hollarda almashtirib bo'lmaydigan). Har qanday DBMS o'zining joriy holati va ishlashi haqida ma'lumot almashish uchun bepul va mutlaqo bepul. Xuddi shu "qonli" Oracle DB da ishlash haqida deyarli har qanday ma'lumotni tizim ko'rinishidan, jarayonlar va seanslardan tortib bufer kesh holatigacha olish mumkin (masalan, DBA skriptlari, "Monitoring" bo'limi). Postgresql shuningdek, tizim ko'rinishlarining to'liq to'plamiga ega ma'lumotlar bazasi monitoringi, xususan, har qanday DBA ning kundalik hayotida ajralmas bo'lganlar, masalan pg_stat_activity, pg_stat_ma'lumotlar bazasi, pg_stat_bgwriter. MySQL-da hatto buning uchun alohida sxema mavjud. ishlash_sxema. A In Mongo o'rnatilgan profillovchi ishlash ma'lumotlarini tizim to'plamiga jamlaydi system.profile.

Shunday qilib, maxsus sql so'rovlarini bajara oladigan qandaydir ko'rsatkichlar yig'uvchisi (Telegraf, Metricbeat, Collectd), ushbu ko'rsatkichlarni saqlash (InfluxDB, Elasticsearch, Timescaledb) va vizualizator (Grafana, Kibana) bilan qurollangan holda, siz juda oson bo'lishingiz mumkin. va boshqa tizim miqyosidagi ko'rsatkichlar bilan chambarchas integratsiyalangan moslashuvchan monitoring tizimi (masalan, dastur serveridan, OTdan va boshqalardan olinadi). Masalan, bu pgwatch2 da amalga oshiriladi, u InfluxDB + Grafana kombinatsiyasidan va tizim ko'rinishlari uchun so'rovlar to'plamidan foydalanadi, ularga ham kirish mumkin maxsus so'rovlarni qo'shing.

jami

Va bu oddiy SQL kodidan foydalangan holda bizning ma'lumotlar bazasi bilan nima qilish mumkinligining taxminiy ro'yxati. Ishonchim komilki, siz yana ko'p foydalanishni topishingiz mumkin, sharhlarda yozing. Va biz bularning barchasini qanday avtomatlashtirish (va eng muhimi, nima uchun) haqida gaplashamiz va keyingi safar CI/CD quvuriga qo'shamiz.

Manba: www.habr.com

a Izoh qo'shish