Pengalaman "Pangkalan Data sebagai Kod".

Pengalaman "Pangkalan Data sebagai Kod".

SQL, apa yang lebih mudah? Setiap daripada kita boleh menulis permintaan mudah - kita menaip pilih, senaraikan lajur yang diperlukan, kemudian dari, nama jadual, beberapa syarat dalam di mana dan itu sahaja - data berguna ada dalam poket kami, dan (hampir) tidak kira DBMS mana yang berada di bawah hud pada masa itu (atau mungkin bukan DBMS sama sekali). Akibatnya, bekerja dengan hampir mana-mana sumber data (berkaitan dan tidak begitu) boleh dipertimbangkan dari sudut pandangan kod biasa (dengan semua yang tersirat - kawalan versi, semakan kod, analisis statik, autoujian dan itu sahaja). Dan ini terpakai bukan sahaja pada data itu sendiri, skema dan migrasi, tetapi secara umum untuk keseluruhan hayat storan. Dalam artikel ini kita akan bercakap tentang tugas harian dan masalah bekerja dengan pelbagai pangkalan data di bawah lensa "pangkalan data sebagai kod".

Dan mari kita mulakan dari ORM. Pertempuran pertama jenis "SQL vs ORM" diperhatikan kembali pra-Petrine Rus'.

Pemetaan objek-hubungan

Penyokong ORM secara tradisinya menghargai kepantasan dan kemudahan pembangunan, kebebasan daripada DBMS dan kod bersih. Bagi kebanyakan kita, kod untuk bekerja dengan pangkalan data (dan selalunya pangkalan data itu sendiri)

selalunya nampak macam ni...

@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 ini digantung dengan anotasi yang bijak, dan di sebalik tabir ORM yang berani menjana dan melaksanakan banyak kod SQL. Ngomong-ngomong, pembangun cuba sedaya upaya untuk mengasingkan diri mereka daripada pangkalan data mereka dengan abstraksi kilometer, yang menunjukkan beberapa "SQL benci".

Di sisi lain penghadang, penganut SQL "buatan tangan" tulen mencatatkan keupayaan untuk memerah semua jus keluar dari DBMS mereka tanpa lapisan dan abstraksi tambahan. Akibatnya, projek "berpusatkan data" muncul, di mana orang yang terlatih khas terlibat dalam pangkalan data (mereka juga "ahli asas", mereka juga "ahli asas", mereka juga "tukang bas", dll.), dan pemaju hanya perlu "menarik" pandangan yang sudah siap dan prosedur tersimpan, tanpa pergi ke butiran.

Bagaimana jika kita mempunyai yang terbaik dari kedua-dua dunia? Bagaimana ini dilakukan dalam alat yang hebat dengan nama yang mengesahkan kehidupan Yesql. Saya akan memberikan beberapa baris daripada konsep umum dalam terjemahan percuma saya, dan anda boleh membiasakannya dengan lebih terperinci di sini.

Clojure ialah bahasa yang menarik untuk mencipta DSL, tetapi SQL itu sendiri ialah DSL yang hebat, dan kami tidak memerlukan yang lain. Ungkapan-S bagus, tetapi ia tidak menambah apa-apa yang baharu di sini. Akibatnya, kami mendapat kurungan demi kurungan. Tidak setuju? Kemudian tunggu masa apabila abstraksi ke atas pangkalan data mula bocor dan anda mula berjuang dengan fungsi tersebut (raw-sql)

Jadi apa yang perlu saya lakukan? Mari tinggalkan SQL sebagai SQL biasa - satu fail setiap permintaan:

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

... dan kemudian baca fail ini, mengubahnya menjadi fungsi Clojure biasa:

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

Dengan mematuhi prinsip "SQL dengan sendirinya, Clojure dengan sendirinya", anda mendapat:

  • Tiada kejutan sintaksis. Pangkalan data anda (seperti mana-mana yang lain) tidak 100% mematuhi standard SQL - tetapi ini tidak penting untuk Yesql. Anda tidak akan membuang masa untuk memburu fungsi dengan sintaks setara SQL. Anda tidak perlu kembali ke fungsi (raw-sql "beberapa('funky'::SYNTAX)")).
  • Sokongan editor terbaik. Editor anda sudah mempunyai sokongan SQL yang sangat baik. Dengan menyimpan SQL sebagai SQL anda boleh menggunakannya dengan mudah.
  • Keserasian pasukan. DBA anda boleh membaca dan menulis SQL yang anda gunakan dalam projek Clojure anda.
  • Penalaan prestasi yang lebih mudah. Perlu membina rancangan untuk pertanyaan bermasalah? Ini tidak menjadi masalah apabila pertanyaan anda adalah SQL biasa.
  • Menggunakan semula pertanyaan. Seret dan lepaskan fail SQL yang sama ke dalam projek lain kerana ia hanyalah SQL lama biasa - kongsi sahaja.

Pada pendapat saya, idea itu sangat keren dan pada masa yang sama sangat mudah, berkat projek itu telah mendapat banyak keuntungan pengikut dalam pelbagai bahasa. Dan seterusnya kami akan cuba menerapkan falsafah yang sama untuk memisahkan kod SQL daripada segala-galanya yang jauh di luar ORM.

Pengurus IDE & DB

Mari kita mulakan dengan tugas harian yang mudah. Selalunya kita perlu mencari beberapa objek dalam pangkalan data, sebagai contoh, mencari jadual dalam skema dan mengkaji strukturnya (apa lajur, kunci, indeks, kekangan, dll. yang digunakan). Dan daripada mana-mana IDE grafik atau pengurus DB kecil, pertama sekali, kami menjangkakan kebolehan ini dengan tepat. Supaya ia pantas dan anda tidak perlu menunggu setengah jam sehingga tetingkap dengan maklumat yang diperlukan ditarik (terutama dengan sambungan perlahan ke pangkalan data jauh), dan pada masa yang sama, maklumat yang diterima adalah segar dan relevan, dan bukan sampah sarap. Lebih-lebih lagi, lebih kompleks dan lebih besar pangkalan data dan lebih banyak bilangannya, lebih sukar untuk melakukan ini.

Tetapi biasanya saya membuang tetikus dan hanya menulis kod. Katakan anda perlu mengetahui jadual (dan sifat yang mana) terkandung dalam skema "HR". Dalam kebanyakan DBMS, hasil yang diingini boleh dicapai dengan pertanyaan mudah ini daripada information_schema:

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

Dari pangkalan data ke pangkalan data, kandungan jadual rujukan tersebut berbeza-beza bergantung pada keupayaan setiap DBMS. Dan, sebagai contoh, untuk MySQL, dari buku rujukan yang sama anda boleh mendapatkan parameter jadual khusus untuk DBMS ini:

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

Oracle tidak mengetahui information_schema, tetapi ia mempunyai Metadata Oracle, dan tiada masalah besar timbul:

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

ClickHouse tidak terkecuali:

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

Sesuatu yang serupa boleh dilakukan dalam Cassandra (yang mempunyai columnfamilies dan bukannya jadual dan ruang kekunci dan bukannya skema):

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

Untuk kebanyakan pangkalan data lain, anda juga boleh membuat pertanyaan yang serupa (malah Mongo mempunyai koleksi sistem khas, yang mengandungi maklumat tentang semua koleksi dalam sistem).

Sudah tentu, dengan cara ini anda boleh mendapatkan maklumat bukan sahaja mengenai jadual, tetapi mengenai sebarang objek secara umum. Dari semasa ke semasa, orang baik berkongsi kod sedemikian untuk pangkalan data yang berbeza, sebagai contoh, dalam siri artikel habra "Fungsi untuk mendokumentasikan pangkalan data PostgreSQL" (Ayb, ben, Gim). Sudah tentu, menyimpan segunung pertanyaan ini dalam kepala saya dan sentiasa menaipnya adalah satu keseronokan, jadi dalam IDE/editor kegemaran saya, saya mempunyai set coretan yang telah disediakan terlebih dahulu untuk pertanyaan yang kerap digunakan, dan yang tinggal hanyalah menaip nama objek ke dalam templat.

Akibatnya, kaedah menavigasi dan mencari objek ini adalah lebih fleksibel, menjimatkan banyak masa, dan membolehkan anda mendapatkan maklumat yang tepat dalam bentuk yang kini diperlukan (seperti, contohnya, diterangkan dalam siaran "Mengeksport data daripada pangkalan data dalam sebarang format: perkara yang boleh dilakukan oleh IDE pada platform IntelliJ").

Operasi dengan objek

Selepas kami menemui dan mengkaji objek yang diperlukan, sudah tiba masanya untuk melakukan sesuatu yang berguna dengannya. Sememangnya, juga tanpa mengambil jari anda dari papan kekunci.

Bukan rahsia lagi bahawa hanya memadamkan jadual akan kelihatan sama dalam hampir semua pangkalan data:

drop table hr.persons

Tetapi dengan penciptaan jadual ia menjadi lebih menarik. Hampir mana-mana DBMS (termasuk banyak NoSQL) boleh "membuat jadual" dalam satu bentuk atau yang lain, dan bahagian utamanya akan berbeza sedikit (nama, senarai lajur, jenis data), tetapi butiran lain boleh berbeza secara mendadak dan bergantung pada peranti dalaman dan keupayaan DBMS tertentu. Contoh kegemaran saya ialah dalam dokumentasi Oracle hanya terdapat BNF "telanjang" untuk sintaks "buat jadual" menduduki 31 muka surat. DBMS lain mempunyai keupayaan yang lebih sederhana, tetapi setiap DBMS juga mempunyai banyak ciri menarik dan unik untuk mencipta jadual (postgres, mysql, lipas, cassandra). Tidak mungkin mana-mana "wizard" grafik dari IDE lain (terutama yang universal) akan dapat menampung sepenuhnya semua kebolehan ini, dan walaupun boleh, ia tidak akan menjadi tontonan bagi mereka yang lemah hati. Pada masa yang sama, kenyataan bertulis yang betul dan tepat pada masanya buat jadual akan membolehkan anda menggunakan kesemuanya dengan mudah, menjadikan storan dan akses kepada data anda boleh dipercayai, optimum dan seselesa mungkin.

Selain itu, banyak DBMS mempunyai jenis objek khusus mereka sendiri yang tidak tersedia dalam DBMS lain. Selain itu, kita boleh melakukan operasi bukan sahaja pada objek pangkalan data, tetapi juga pada DBMS itu sendiri, sebagai contoh, "membunuh" proses, membebaskan beberapa kawasan memori, membolehkan pengesanan, beralih kepada mod "baca sahaja", dan banyak lagi.

Sekarang mari kita lukis sedikit

Salah satu tugas yang paling biasa ialah membina gambar rajah dengan objek pangkalan data dan melihat objek dan hubungan antara mereka dalam gambar yang cantik. Hampir mana-mana IDE grafik, utiliti "baris arahan" berasingan, alat grafik khusus dan pemodel boleh melakukan ini. Mereka akan menarik sesuatu untuk anda "sebaik mungkin," dan anda boleh mempengaruhi sedikit proses ini hanya dengan bantuan beberapa parameter dalam fail konfigurasi atau kotak pilihan dalam antara muka.

Tetapi masalah ini boleh diselesaikan dengan lebih mudah, lebih fleksibel dan elegan, dan sudah tentu dengan bantuan kod. Untuk mencipta gambar rajah dengan sebarang kerumitan, kami mempunyai beberapa bahasa penanda khusus (DOT, GraphML dll), dan bagi mereka serakan keseluruhan aplikasi (GraphViz, PlantUML, Mermaid) yang boleh membaca arahan tersebut dan menggambarkannya dalam pelbagai format . Nah, kita sudah tahu cara mendapatkan maklumat tentang objek dan hubungan antara mereka.

Berikut ialah contoh kecil tentang rupa ini, menggunakan PlantUML dan pangkalan data demo untuk PostgreSQL (di sebelah kiri ialah pertanyaan SQL yang akan menghasilkan arahan yang diperlukan untuk PlantUML, dan di sebelah kanan ialah hasilnya):

Pengalaman "Pangkalan Data sebagai Kod".

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'

Dan jika anda mencuba sedikit, maka berdasarkan Templat ER untuk PlantUML anda boleh mendapatkan sesuatu yang hampir sama dengan gambar rajah ER sebenar:

Pertanyaan SQL adalah sedikit lebih rumit

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

Pengalaman "Pangkalan Data sebagai Kod".

Jika anda melihat dengan teliti, di bawah tudung banyak alat visualisasi juga menggunakan pertanyaan yang serupa. Benar, permintaan ini biasanya mendalam "berkabel" ke dalam kod aplikasi itu sendiri dan sukar untuk difahami, apatah lagi sebarang pengubahsuaian daripadanya.

Metrik dan pemantauan

Mari kita beralih kepada topik tradisional yang kompleks - pemantauan prestasi pangkalan data. Saya masih ingat kisah benar kecil yang diceritakan kepada saya oleh "salah seorang kawan saya." Pada projek lain terdapat DBA yang berkuasa tertentu, dan beberapa pemaju mengenalinya secara peribadi, atau pernah melihatnya secara peribadi (walaupun fakta bahawa, menurut khabar angin, dia bekerja di suatu tempat di bangunan sebelah) . Pada jam "X", apabila sistem poduction peruncit besar mula "berasa buruk" sekali lagi, dia secara senyap menghantar tangkapan skrin graf daripada Pengurus Perusahaan Oracle, di mana dia dengan teliti menyerlahkan tempat kritikal dengan penanda merah untuk "kefahaman" ( ini, secara ringkasnya, tidak banyak membantu). Dan berdasarkan "kad foto" ini saya terpaksa merawat. Pada masa yang sama, tiada siapa yang mempunyai akses kepada Pengurus Perusahaan (dalam kedua-dua erti kata) yang berharga, kerana sistem ini rumit dan mahal, tiba-tiba "pembangun tersandung pada sesuatu dan memecahkan segala-galanya." Oleh itu, pemaju "secara empirik" menemui lokasi dan punca brek dan mengeluarkan tampalan. Sekiranya surat ancaman daripada DBA tidak tiba lagi dalam masa terdekat, maka semua orang akan menarik nafas lega dan kembali kepada tugas semasa mereka (sehingga Surat baharu).

Tetapi proses pemantauan boleh kelihatan lebih menyeronokkan dan mesra, dan yang paling penting, boleh diakses dan telus untuk semua orang. Sekurang-kurangnya bahagian asasnya, sebagai tambahan kepada sistem pemantauan utama (yang pastinya berguna dan dalam banyak kes tidak boleh diganti). Mana-mana DBMS adalah percuma dan benar-benar percuma untuk berkongsi maklumat tentang keadaan dan prestasi semasanya. Dalam Oracle DB "berdarah" yang sama, hampir semua maklumat tentang prestasi boleh diperoleh daripada paparan sistem, daripada proses dan sesi kepada keadaan cache penimbal (contohnya, Skrip DBA, bahagian "Pemantauan"). Postgresql juga mempunyai banyak pandangan sistem untuk pemantauan pangkalan data, khususnya yang amat diperlukan dalam kehidupan harian mana-mana DBA, seperti pg_stat_activity, pg_stat_database, pg_stat_bgwriter. MySQL juga mempunyai skema yang berasingan untuk ini. skema_prestasi. A In Mongo terbina dalam profiler mengagregatkan data prestasi ke dalam koleksi sistem sistem.profil.

Oleh itu, bersenjatakan beberapa jenis pengumpul metrik (Telegraf, Metricbeat, Collectd) yang boleh melakukan pertanyaan sql tersuai, penyimpanan metrik ini (InfluxDB, Elasticsearch, Timescaledb) dan visualizer (Grafana, Kibana), anda boleh mendapatkan yang agak mudah dan sistem pemantauan fleksibel yang akan disepadukan rapat dengan metrik seluruh sistem yang lain (diperolehi, contohnya, daripada pelayan aplikasi, daripada OS, dsb.). Sebagai contoh, ini dilakukan dalam pgwatch2, yang menggunakan kombinasi InfluxDB + Grafana dan satu set pertanyaan kepada paparan sistem, yang juga boleh diakses tambah pertanyaan tersuai.

Dalam jumlah

Dan ini hanyalah senarai anggaran perkara yang boleh dilakukan dengan pangkalan data kami menggunakan kod SQL biasa. Saya pasti anda boleh mencari banyak lagi kegunaan, tulis dalam komen. Dan kita akan bercakap tentang cara (dan yang paling penting mengapa) untuk mengautomasikan semua ini dan memasukkannya ke dalam saluran paip CI/CD anda pada masa akan datang.

Sumber: www.habr.com

Tambah komen