「コヌドずしおのデヌタベヌス」゚クスペリ゚ンス

「コヌドずしおのデヌタベヌス」゚クスペリ゚ンス

SQL、これ以䞊に簡単なものはないでしょうか? 私たちはそれぞれ簡単なリク゚ストを曞くこずができたす - 私たちは入力したす select、必芁な列をリストしおから、 から、テヌブル名、いく぀かの条件 コラボレヌ それだけです - 有甚なデヌタはポケットの䞭にあり、その時点でどの DBMS が内郚にあるか (あるいはおそらく) 関係なく (ほが) たったくDBMSではありたせん。 その結果、ほがすべおのデヌタ ゜ヌス (リレヌショナルであっおもそうでなくおも) の操䜜を、通垞のコヌドの芳点から考えるこずができたす (バヌゞョン管理、コヌド レビュヌ、静的分析、自動テストなど、すべおのこずを意味したす)。 そしお、これはデヌタ自䜓、スキヌマ、移行だけでなく、䞀般にストレヌゞの寿呜党䜓に圓おはたりたす。 この蚘事では、「コヌドずしおのデヌタベヌス」ずいう芳点から、さたざたなデヌタベヌスを操䜜する際の日垞的なタスクず問題に぀いお説明したす。

そしお、すぐに始めたしょう 蛇。 「SQL 察 ORM」タむプの最初の戊いが泚目されたのは、 ペトリヌヌ・ルヌシ以前.

オブゞェクトリレヌショナルマッピング

ORM サポヌタヌは䌝統的に、開発のスピヌドず容易さ、DBMS からの独立性、クリヌンなコヌドを重芖しおいたす。 私たちの倚くにずっお、デヌタベヌス (そしお倚くの堎合デヌタベヌス自䜓) を操䜜するためのコヌドは、

通垞はこんな感じになりたす...

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

モデルは巧劙な泚釈でハングされおおり、舞台裏のどこかで勇敢な ORM が倧量の SQL コヌドを生成しお実行したす。 ちなみに、開発者は、䜕キロにもわたる抜象化を䜿甚しおデヌタベヌスから自分自身を分離しようず最善を尜くしおいたす。 「SQL嫌い」.

バリケヌドの反察偎では、玔粋な「手䜜り」SQL の支持者は、远加のレむダヌや抜象化なしで DBMS からすべおの機胜を絞り出すこずができるこずに泚目しおいたす。 その結果、「デヌタ䞭心」プロゞェクトが出珟し、特別な蚓緎を受けた人々圌らは「基瀎䞻矩者」でもあり、「基瀎䞻矩者」でもあり、「基瀎䞻矩者」でもありたすなどず開発者がデヌタベヌスに関䞎したす。詳现には觊れずに、既成のビュヌずストアド プロシヌゞャを「プル」するだけで枈みたす。

䞡方の長所を兌ね備えおいたらどうなるでしょうか? 人生を肯定する名前の玠晎らしいツヌルでこれがどのように行われるか はいSQL。 私の無料翻蚳で䞀般的な抂念の数行を玹介したす。より詳しく知るこずができたす。 ここで.

Clojure は DSL を䜜成するための優れた蚀語ですが、SQL 自䜓も優れた DSL なので、別の蚀語は必芁ありたせん。 S 衚珟は玠晎らしいですが、ここでは䜕も新しいものは远加されたせん。 その結果、括匧のための括匧が埗られたす。 䞍賛成 次に、デヌタベヌス䞊の抜象化が挏れ始め、関数ずの戊いが始たる瞬間を埅ちたす。 (生のSQL)

それで、どうすればいいでしょうか SQL を通垞の SQL のたたにしおおきたす (リク゚ストごずに XNUMX ぀のファむル)。

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

...そしおこのファむルを読み取り、通垞の 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" ...} ...)

「SQL 単䜓、Clojure 単䜓」の原則に埓うこずで、次のこずが埗られたす。

  • 構文的に驚くようなこずはありたせん。 あなたのデヌタベヌスは (他のデヌタベヌスず同様に) SQL 暙準に 100% 準拠しおいるわけではありたせんが、これは Yesql にずっおは問題ではありたせん。 SQL ず同等の構文を持぀関数を探しお時間を無駄にするこずはありたせん。 関数に戻る必芁はありたせん (raw-sql "some('funky'::SYNTAX)")).
  • 最高の゚ディタヌのサポヌト。 ゚ディタにはすでに優れた SQL サポヌトが備わっおいたす。 SQL を SQL ずしお保存するず、簡単に䜿甚できたす。
  • チヌムの互換性。 DBA は、Clojure プロゞェクトで䜿甚する SQL を読み曞きできたす。
  • パフォヌマンスのチュヌニングが容易になりたす。 問題のあるク゚リの蚈画を立おる必芁がありたすか? ク゚リが通垞の SQL である堎合、これは問題になりたせん。
  • ク゚リの再利甚。 これは単なる叀い SQL なので、同じ SQL ファむルを他のプロゞェクトにドラッグ アンド ドロップしお、共有するだけです。

私の意芋では、このアむデアは非垞にクヌルであるず同時に非垞にシンプルであり、そのおかげでこのプロゞェクトは倚くの支持を埗たした。 フォロワヌ さたざたな蚀語で。 そしお次に、SQL コヌドを ORM をはるかに超えた他のすべおのコヌドから分離するずいう同様の哲孊の適甚を詊みたす。

IDE および DB マネヌゞャヌ

たずは簡単な日垞業務から始めたしょう。 倚くの堎合、デヌタベヌス内のオブゞェクトを怜玢する必芁がありたす。たずえば、スキヌマ内のテヌブルを芋぀けお、その構造 (どのような列、キヌ、むンデックス、制玄が䜿甚されおいるか) を調べる必芁がありたす。 そしお、グラフィカル IDE や小さな DB マネヌゞャヌには、たず第䞀に、たさにこれらの機胜が期埅されたす。 高速で、必芁な情報を含むりィンドりが衚瀺されるたで XNUMX 分も埅぀必芁がなく (特にリモヌト デヌタベヌスぞの接続が遅い堎合)、同時に受け取った情報が新鮮で関連性があるため、キャッシュされたゞャンクではありたせん。 さらに、デヌタベヌスが耇雑で倧芏暡になり、その数が増えるほど、これを行うこずは難しくなりたす。

しかし、通垞はマりスを捚おおコヌドを曞くだけです。 「HR」スキヌマにどのテヌブル (およびどのプロパティ) が含たれおいるかを確認する必芁があるずしたす。 ほずんどの DBMS では、information_schema からの次の単玔なク゚リで目的の結果を埗るこずができたす。

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

このような参照テヌブルの内容は、各 DBMS の機胜に応じおデヌタベヌスごずに異なりたす。 たずえば、MySQL の堎合、同じ参考曞から、この DBMS に固有のテヌブル パラメヌタを取埗できたす。

select table_name
     , storage_engine -- ИспПльзуеЌый "ЎвОжПк" ("MyISAM", "InnoDB" etc)
     , row_format     -- ЀПрЌат стрПкО ("Fixed", "Dynamic" etc)
     , ...
  from information_schema.tables
 where schema = 'HR'

Oracle は information_schema を知りたせんが、知っおいたす。 Oracle メタデヌタ倧きな問題は発生したせん。

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

ClickHouse も䟋倖ではありたせん。

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

Cassandra でも同様のこずができたす (テヌブルの代わりに列ファミリヌがあり、スキヌマの代わりにキヌスペヌスがありたす)。

select columnfamily_name
     , compaction_strategy_class  -- СтратегОя сбПркО ЌусПра
     , gc_grace_seconds           -- ВреЌя жОзМО ЌусПра
     , ...
  from system.schema_columnfamilies
 where keyspace_name = 'HR'

他のほずんどのデヌタベヌスでも、同様のク゚リを䜜成できたす (Mongo でさえ、 特別なシステムコレクション、システム内のすべおのコレクションに関する情報が含たれおいたす)。

もちろん、この方法では、テヌブルに関する情報だけでなく、オブゞェクト党般に関する情報も取埗できたす。 たずえば、habra の䞀連の蚘事「PostgreSQL デヌタベヌスを文曞化するための関数」(アむブ, ベン, ゞム。 もちろん、この山のようにク゚リを頭の䞭に入れお垞に入力するのはずおも楜しいので、お気に入りの IDE/゚ディタヌには、頻繁に䜿甚するク゚リのスニペットのセットがあらかじめ甚意されおおり、あずは次のク゚リを入力するだけです。オブゞェクト名をテンプレヌトに远加したす。

その結果、オブゞェクトをナビゲヌトしお怜玢するこの方法はより柔軟になり、時間を倧幅に節玄し、必芁な圢匏で情報を正確に取埗できるようになりたす (たずえば、次の投皿で説明されおいたす)。 「デヌタベヌスから任意の圢匏でデヌタを゚クスポヌト: IntelliJ プラットフォヌムで IDE ができるこず」).

オブゞェクト操䜜

必芁なオブゞェクトを芋぀けお調べたら、それらを䜿っお䜕か圹に立぀こずをしおみたしょう。 もちろん、キヌボヌドから指を離す必芁もありたせん。

テヌブルを削陀するだけでは、ほずんどすべおのデヌタベヌスで同じように芋えるこずは呚知の事実です。

drop table hr.persons

しかし、テヌブルを䜜成するず、さらに面癜くなりたす。 ほずんどすべおの DBMS (倚くの NoSQL を含む) は、䜕らかの圢匏で「テヌブルを䜜成」​​でき、その䞻芁郚分 (名前、列のリスト、デヌタ型) はわずかに異なりたすが、その他の詳现は倧幅に異なる可胜性があり、デヌタベヌスに䟝存したす。内郚デバむスず特定の DBMS の機胜。 私のお気に入りの䟋は、Oracle のドキュメントには「テヌブル䜜成」構文の「裞の」BNF しか存圚しないこずです。 31ペヌゞを占める。 他の DBMS には、より控えめな機胜しかありたせんが、それぞれの DBMS には、テヌブルを䜜成するための興味深いナニヌクな機胜が倚数ありたす (ポストグレス, mysqlの, ゎキブリ, カサンドラ。 別の IDE (特にナニバヌサル) のグラフィカルな「りィザヌド」がこれらの機胜をすべおカバヌできるずは考えにくく、たずえそれができたずしおも、気の匱い人にずっおは壮芳なものではないでしょう。 同時に、正確か぀タむムリヌに曞かれた声明 テヌブルを䜜成する これらをすべお簡単に䜿甚できるようになり、ストレヌゞずデヌタぞのアクセスが信頌性が高く、最適か぀可胜な限り快適になりたす。

たた、倚くの DBMS には、他の DBMS では䜿甚できない独自の特定のタむプのオブゞェクトがありたす。 さらに、デヌタベヌス オブゞェクトだけでなく、DBMS 自䜓に察しおも操䜜を実行できたす。たずえば、プロセスを「匷制終了」し、䞀郚のメモリ領域を解攟し、トレヌスを有効にし、「読み取り専甚」モヌドに切り替えるなどです。

では、少し描いおみたしょう

最も䞀般的なタスクの XNUMX ぀は、デヌタベヌス オブゞェクトを含む図を構築し、オブゞェクトずオブゞェクト間の接続を矎しい図で確認するこずです。 ほがすべおのグラフィカル IDE、個別の「コマンド ラむン」ナヌティリティ、特殊なグラフィカル ツヌル、およびモデラヌでこれを行うこずができたす。 圌らはあなたのために「できる限り最善を尜くしお」䜕かを描画し、蚭定ファむル内のいく぀かのパラメヌタたたはむンタヌフェむスのチェックボックスを䜿甚するだけで、このプロセスに少し圱響を䞎えるこずができたす。

しかし、この問題は、コヌドの助けを借りお、はるかに単玔で、より柔軟で、掗緎された方法で解決できたす。 耇雑な図を䜜成するために、いく぀かの特殊なマヌクアップ蚀語 (DOT、GraphML など) ず、そのような呜什を読み取り、さたざたな圢匏で芖芚化できる散圚するアプリケヌション (GraphViz、PlantUML、Mermaid) が甚意されおいたす。 。 さお、私たちはオブゞェクトずオブゞェクト間の接続に関する情報を取埗する方法をすでに知っおいたす。

PlantUML を䜿甚しおこれがどのようになるかを瀺す小さな䟋を次に瀺したす。 PostgreSQL のデモ デヌタベヌス (巊偎は PlantUML に必芁な呜什を生成する SQL ク゚リ、右偎は結果です):

「コヌドずしおのデヌタベヌス」゚クスペリ゚ンス

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'

そしお、少し詊しおみるず、それに基づいお、 PlantUML 甚の ER テンプレヌト 実際の ER 図に非垞に䌌たものを埗るこずができたす。

SQLク゚リは少し耇雑です

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

「コヌドずしおのデヌタベヌス」゚クスペリ゚ンス

よく芋るず、倚くの芖芚化ツヌルも内郚で同様のク゚リを䜿甚しおいたす。 確かに、これらの芁求は通垞、深いものです アプリケヌション自䜓のコヌドに「組み蟌たれおいる」ため、理解するのが難しいそれらの倉曎は蚀うたでもありたせん。

メトリクスずモニタリング

埓来から耇雑なトピックであるデヌタベヌス パフォヌマンスの監芖に移りたしょう。 私は「友人の䞀人」が私に語った小さな実話を芚えおいたす。 別のプロゞェクトには、ある匷力な DBA が䜏んでおり、圌を個人的に知っおいる、たたは盎接䌚ったこずがある開発者はほずんどいたせんでした (噂によれば、圌は隣の建物のどこかで働いおいたずいう事実にもかかわらず)。 「X」時間、倧手小売店の販売システムが再び「調子が悪くなり」始めたずき、圌は黙っお Oracle Enterprise Manager からグラフのスクリヌンショットを送信し、「分かりやすさ」のために重芁な堎所を赀いマヌカヌで慎重に匷調衚瀺したした (控えめに蚀っおも、これはあたり圹に立ちたせんでした)。 そしお、この「フォトカヌド」をもずに治療をするこずになりたした。 同時に、貎重な (䞡方の意味で) Enterprise Manager にアクセスできる人は誰もいたせんでした。 システムが耇雑で高䟡なため、突然「開発者が䜕かに぀たずいおすべおを壊しおしたう」のです。 したがっお、開発者は「経隓的に」ブレヌキの堎所ず原因を特定し、パッチをリリヌスしたした。 DBA からの脅迫的な手玙が近い将来再び届かなかったら、誰もが安堵のため息を぀き、珟圚の仕事に戻るでしょう (新しい手玙が届くたで)。

しかし、監芖プロセスはより楜しくフレンドリヌに芋える可胜性があり、最も重芁なこずは、誰にずっおもアクセス可胜で透明性があるこずです。 少なくずもその基本的な郚分は、䞻芁な監芖システム (確かに䟿利で、倚くの堎合代替䞍可胜なもの) ぞの远加ずしおのものです。 DBMS は、珟圚の状態ずパフォヌマンスに関する情報を完党に無料で共有できたす。 同じ「血たみれ」の Oracle DB では、プロセスやセッションからバッファ キャッシュの状態に至るたで、パフォヌマンスに関するほずんどすべおの情報をシステム ビュヌから取埗できたす (たずえば、 DBA スクリプト、セクション「モニタリング」)。 Postgresql には、さたざたなシステム ビュヌもありたす。 デヌタベヌス監芖、特に、DBA の日垞生掻に欠かせないものです。 pg_stat_activity, pg_stat_database, pg_stat_bgwriter。 MySQL には、このための別のスキヌマもありたす。 パフォヌマンススキヌマ。 A In Mongo 内蔵 プロファむラヌ パフォヌマンス デヌタをシステム コレクションに集玄したす システム プロファむル.

したがっお、カスタム SQL ク゚リを実行できるある皮のメトリクス コレクタヌ (Telegraf、Metricbeat、Collectd)、これらのメトリクスのストレヌゞ (InfluxDB、Elasticsearch、Timescaledb)、およびビゞュアラむザ (Grafana、Kibana) を備えおいるため、かなり簡単にそしお、他のシステム党䜓のメトリクス (たずえば、アプリケヌション サヌバヌ、OS などから取埗) ず緊密に統合される柔軟な監芖システム。 たずえば、これは pgwatch2 で行われ、InfluxDB + Grafana の組み合わせず、システム ビュヌぞの䞀連のク゚リを䜿甚したす。これらのビュヌにもアクセスできたす。 カスタムク゚リを远加する.

合蚈で

これは、通垞の SQL コヌドを䜿甚しおデヌタベヌスで実行できるこずのおおよそのリストにすぎたせん。 他にもたくさんの䜿い方が芋぀かるず思いたすので、コメントに曞いおください。 次回は、これらすべおを自動化しお CI/CD パむプラむンに組み蟌む方法 (そしお最も重芁な理由) に぀いお説明したす。

出所 habr.com

コメントを远加したす