تجربة "قاعدة البيانات كرمز"

تجربة "قاعدة البيانات كرمز"

SQL ، ما الذي يمكن أن يكون أسهل؟ يمكن لكل منا أن يكتب طلبًا بسيطًا - نكتبه حدد، قم بسرد الأعمدة المطلوبة ، ثم تبدأ من، اسم الجدول ، شروط قليلة في أين وهذا كل شيء - بيانات مفيدة في جيوبنا ، و (تقريبًا) بغض النظر عن نظام إدارة قواعد البيانات (DBMS) الموجود تحت الغطاء في ذلك الوقت (أو ربما ليس DBMS على الإطلاق). نتيجة لذلك ، يمكن اعتبار العمل مع أي مصدر بيانات تقريبًا (علائقي وغير ذلك) من وجهة نظر الكود العادي (مع كل العواقب - التحكم في الإصدار ، ومراجعة الكود ، والتحليل الثابت ، والاختبارات التلقائية ، وهذا كل شيء). وهذا لا ينطبق فقط على البيانات نفسها والمخططات والترحيلات ، ولكن بشكل عام على كامل عمر التخزين. في هذا المقال سنتحدث عن المهام اليومية ومشاكل العمل مع قواعد البيانات المختلفة ضمن نطاق "قاعدة البيانات كرمز".

ودعنا نبدأ بشكل صحيح ORM. ظهرت المعارك الأولى مثل "SQL vs 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 of hate".

على الجانب الآخر من المتاريس ، يلاحظ أتباع "صناعة يدوية" نقية -SQL القدرة على عصر كل العصير من نظام إدارة قواعد البيانات (DBMS) الخاص بهم دون طبقات إضافية وتجريدية. ونتيجة لذلك ، تظهر المشاريع "المتمحورة حول البيانات" ، حيث يشارك أشخاص مدربون بشكل خاص في قاعدة البيانات (فهم أيضًا "أساسيون" ، وهم أيضًا "أساسيون" ، وهم أيضًا "أوغاد" ، وما إلى ذلك) ، ومطورون فقط يجب "سحب" وجهات النظر الجاهزة والإجراءات المخزنة ، دون الخوض في التفاصيل.

وماذا لو أخذت أفضل ما في العالمين؟ كيف يتم ذلك بأداة رائعة تحمل اسمًا يؤكد الحياة يسقل. سأقدم سطرين من المفهوم العام في ترجمتي المجانية ، ويمكنك التعرف عليه بمزيد من التفصيل هنا.

Clojure هي لغة رائعة لبناء DSL ، لكن SQL نفسها هي DSL رائعة ولا نحتاج إلى لغة أخرى. تعبيرات S رائعة ، لكنها لا تضيف أي شيء جديد هنا. نتيجة لذلك ، نحصل على الأقواس من أجل الأقواس. لا توافق؟ ثم انتظر اللحظة التي يتسرب فيها التجريد فوق قاعدة البيانات ، وستبدأ في القتال مع الوظيفة (خام- sql)

و ما العمل؟ دعنا نترك SQL كملف SQL عادي - ملف واحد لكل طلب:

-- 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 منفصل ، كلوجور منفصل" ، تحصل على:

  • لا مفاجآت نحوية. قاعدة بياناتك (مثل أي قاعدة بيانات أخرى) ليست متوافقة مع SQL بنسبة 100٪ - لكن Yesql لا تهتم. لن تضيع وقتك مطلقًا في البحث عن وظائف ذات بناء جملة مكافئ لـ SQL. لست مضطرًا أبدًا للعودة إلى الوظيفة (raw-sql "بعض ('funky' :: SYNTAX)")).
  • دعم محرر أفضل. يتمتع محررك بالفعل بدعم SQL ممتاز. من خلال تخزين SQL على هيئة SQL ، يمكنك فقط استخدامها.
  • توافق الفريق. يمكن لمسؤولي قواعد البيانات قراءة وكتابة SQL الذي تستخدمه في مشروع Clojure.
  • ضبط أداء أسهل. هل تحتاج إلى بناء خطة لاستعلام إشكالي؟ هذه ليست مشكلة عندما يكون الاستعلام الخاص بك هو SQL العادية.
  • إعادة استخدام الاستعلامات. اسحب ملفات SQL نفسها إلى مشاريع أخرى لأنها مجرد لغة SQL قديمة جيدة - ما عليك سوى مشاركتها.

في رأيي ، الفكرة رائعة جدًا وفي نفس الوقت بسيطة جدًا ، بفضلها اكتسب المشروع الكثير متابعون بلغات مختلفة. وبعد ذلك سنحاول تطبيق فلسفة مماثلة لفصل كود SQL عن أي شيء آخر بعيدًا عن ORM.

مديري IDE & DB

لنبدأ بمهمة يومية بسيطة. غالبًا ما يتعين علينا البحث عن بعض الكائنات في قاعدة البيانات ، على سبيل المثال ، العثور على جدول في المخطط ودراسة هيكله (أي الأعمدة والمفاتيح والفهارس والقيود المستخدمة وما إلى ذلك). ومن أي IDE رسومي أو مدير DB صغير ، أولاً وقبل كل شيء ، نحن ننتظر هذه القدرات ذاتها. بحيث يكون سريعًا ولا يتعين عليه الانتظار نصف ساعة حتى يتم رسم نافذة بها المعلومات الضرورية (خاصةً مع الاتصال البطيء بقاعدة بيانات بعيدة) ، وفي نفس الوقت تكون المعلومات المستلمة حديثة وذات صلة ، و لا غير مرغوب فيه مخبأ. علاوة على ذلك ، كلما كانت قاعدة البيانات أكثر تعقيدًا وأكبر حجمًا وكلما زاد عددها ، زادت صعوبة القيام بها.

لكن عادةً ما أرمي الماوس بعيدًا وأكتب الرمز. لنفترض أنك تريد معرفة الجداول (وما هي الخصائص) المضمنة في مخطط "الموارد البشرية". في معظم نظم إدارة قواعد البيانات (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 مخطط المعلومات ، لكنها تعرفها بيانات تعريف أوراكل، ولا توجد مشاكل كبيرة:

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" (ayb, بن, نادي رياضي). بطبيعة الحال ، فإن الاحتفاظ بكل هذا الجبل من الاستعلامات في رأسي وكتابته باستمرار هو أمر "ممتع للغاية" ، لذلك في IDE / المحرر المفضل لدي مجموعة مُعدة مسبقًا من المقتطفات لطلبات البحث التي يتم استخدامها بشكل متكرر ، وكل ما تبقى هو اكتب أسماء الكائنات في القالب.

نتيجة لذلك ، تكون طريقة التنقل والبحث عن الكائنات أكثر مرونة ، وتوفر الكثير من الوقت ، وتسمح لك بالحصول على المعلومات بالضبط وبالشكل الذي تحتاجه الآن (كما هو موضح ، على سبيل المثال ، في المنشور "تصدير البيانات من قاعدة بيانات بأي تنسيق: ما يمكن أن تفعله IDEs على منصة IntelliJ").

العمليات مع الأشياء

بعد أن وجدنا الأشياء الضرورية ودرسناها ، حان الوقت لعمل شيء مفيد معهم. وبطبيعة الحال ، أيضًا بدون رفع أصابعك عن لوحة المفاتيح.

ليس سراً أن حذف الجدول سيبدو كما هو في جميع قواعد البيانات تقريبًا:

drop table hr.persons

ولكن مع إنشاء الجدول أصبح بالفعل أكثر إثارة للاهتمام. يمكن لأي نظام DBMS تقريبًا (بما في ذلك العديد من NoSQL) "إنشاء جدول" بشكل أو بآخر ، وسيختلف الجزء الرئيسي منه قليلاً (الاسم ، قائمة الأعمدة ، أنواع البيانات) ، لكن التفاصيل الأخرى يمكن أن تختلف بشكل كبير وتعتمد على الجهاز الداخلي وقدرات نظام DBMS معين. المثال المفضل لدي هو أنه في وثائق أوراكل لا يوجد سوى BNFs "عارية" لبناء جملة "إنشاء جدول" تشغل 31 صفحة. يتمتع نظام DBMS الآخر بقدرات أكثر تواضعًا ، ولكن لكل منها أيضًا الكثير من الميزات الفريدة والمثيرة للاهتمام لإنشاء الجداول (بوستجرس, ك, صرصور, كاساندرا). من غير المحتمل أن يكون أي "معالج" رسومي من IDE التالي (بشكل خاص عالميًا) قادرًا على تغطية كل هذه القدرات بالكامل ، وإذا أمكن ، فلن يكون مشهدًا لضعاف القلوب. في الوقت نفسه ، بيان مكتوب بشكل صحيح وفي الوقت المناسب إنشاء الجدول سيسمح لك باستخدامها جميعًا بسهولة ، وجعل التخزين والوصول إلى بياناتك موثوقة ومثالية ومريحة قدر الإمكان.

أيضًا ، تحتوي العديد من أنظمة إدارة قواعد البيانات (DBMS) على أنواعها الخاصة من الكائنات غير المتوفرة في نظم إدارة قواعد البيانات الأخرى. علاوة على ذلك ، يمكننا إجراء عمليات ليس فقط على كائنات قاعدة البيانات ، ولكن أيضًا على نظام إدارة قواعد البيانات نفسه ، على سبيل المثال ، "قتل" العملية ، وتحرير بعض منطقة الذاكرة ، وتمكين التتبع ، والتبديل إلى وضع "القراءة فقط" ، وغير ذلك الكثير.

والآن دعونا نرسم قليلاً

تتمثل إحدى المهام الأكثر شيوعًا في إنشاء رسم تخطيطي باستخدام كائنات قاعدة البيانات ، لرؤية الكائنات والعلاقات بينها في صورة جميلة. يمكن لأي بيئة تطوير متكاملة للرسومات تقريبًا ، وأدوات مساعدة "سطر أوامر" منفصلة وأدوات رسومية متخصصة ومنمذجة القيام بذلك. والذي سيرسم شيئًا ما لك "بأفضل ما في وسعهم" ، ولا يمكنك التأثير بشكل طفيف على هذه العملية إلا بمساعدة عدد قليل من المعلمات في ملف التكوين أو مربعات الاختيار في الواجهة.

ولكن يمكن حل هذه المشكلة بطريقة أبسط وأكثر مرونة وأنيقة ، وبالطبع بمساعدة الكود. لإنشاء مخططات لأي تعقيد ، لدينا العديد من لغات الترميز المتخصصة في آنٍ واحد (DOT ، GraphML ، إلخ) ، وبالنسبة لهم - مجموعة كاملة من التطبيقات (GraphViz ، و PlantUML ، و Mermaid) التي يمكنها قراءة هذه التعليمات وتصورها في صورة مجموعة متنوعة من التنسيقات. حسنًا ، نحن نعرف بالفعل كيفية الحصول على معلومات حول الأشياء والعلاقات فيما بينها.

دعنا نعطي مثالًا صغيرًا لما يمكن أن يبدو عليه باستخدام PlantUML و قاعدة بيانات تجريبية لـ PostgreSQL (على اليسار يوجد استعلام SQL الذي سينشئ التعليمات المطلوبة لـ PlantUML ، وعلى اليمين النتيجة):

تجربة "قاعدة البيانات كرمز"

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'

وإذا حاولت قليلاً ، فعندئذٍ على أساس نموذج ER لـ PlantUML يمكنك الحصول على شيء مشابه جدًا لمخطط 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 ، حيث سلط الضوء بعناية على الأماكن الحرجة بعلامة حمراء من أجل "الوضوح" (هذا ، بعبارة ملطفة ، ساعد قليلاً). وكان لابد من معالجة هذه "البطاقة المصورة". في الوقت نفسه ، لم يتمكن أحد من الوصول إلى مدير المؤسسة الثمين (بكل معاني الكلمة) ، لأن النظام معقد ومكلف ، وفجأة "سوف يعثر المطورون على شيء ما ويكسرون كل شيء." لذلك ، وجد المطورون "تجريبيًا" مكان وسبب الفرامل وأطلقوا التصحيح. إذا لم تأت الرسالة الرهيبة من DBA مرة أخرى في المستقبل القريب ، فقد تنفس الجميع الصعداء وعادوا إلى مهامهم الحالية (حتى الرسالة الجديدة).

لكن يمكن أن تبدو عملية المراقبة أكثر متعة وودية ، والأهم من ذلك أنها يمكن الوصول إليها وشفافة للجميع. على الأقل الجزء الأساسي منه ، كإضافة لأنظمة المراقبة الرئيسية (والتي هي بالتأكيد مفيدة وفي كثير من الحالات لا يمكن الاستغناء عنها). أي نظام DBMS مجاني تمامًا وجاهز لمشاركة المعلومات حول حالته الحالية وأدائه. في قاعدة بيانات Oracle DB نفسها "الدموية" ، يمكن الحصول على أي معلومات أداء تقريبًا من عروض النظام ، من العمليات والجلسات إلى حالة ذاكرة التخزين المؤقت (على سبيل المثال ، مخطوطات ديسيبل، قسم "المراقبة"). يحتوي Postgresql أيضًا على مجموعة من طرق عرض النظام لـ مراقبة تشغيل قاعدة البيانات، على وجه الخصوص ، لا غنى عنه في الحياة اليومية لأي ديسيبل مثل pg_stat_activity, pg_stat_database, pg_stat_bgwriter. في MySQL ، حتى مخطط منفصل مصمم لهذا الغرض. مخطط_الأداء. A B Mongo مدمج المحلل بتجميع بيانات الأداء في مجموعة النظام الملف الشخصي.

وبالتالي ، مسلحًا بنوع من جامع المقاييس (Telegraf ، Metricbeat ، Collectd) ، والذي يمكنه تنفيذ استعلامات sql مخصصة ، وتخزين هذه المقاييس (InfluxDB ، Elasticsearch ، Timescaledb) ومتخيل (Grafana ، Kibana) ، يمكنك الحصول على نظام مراقبة سهل ومرن سيتم دمجه بإحكام مع مقاييس أخرى على مستوى النظام (تم الحصول عليها ، على سبيل المثال ، من خادم التطبيق ، من نظام التشغيل ، وما إلى ذلك). على سبيل المثال ، يتم إجراء ذلك في pgwatch2 ، والتي تستخدم حزمة InfluxDB + Grafana ومجموعة من الاستعلامات لعروض النظام ، والتي يمكن أيضًا أن تكون إضافة طلبات مخصصة.

في المجموع

وهذه ليست سوى قائمة تقريبية لما يمكن عمله بقاعدة بياناتنا باستخدام كود SQL العادي. أنا متأكد من أنه يمكنك العثور على العديد من التطبيقات ، اكتب التعليقات. وسنتحدث عن كيفية (والأهم من ذلك لماذا) لأتمتة كل هذا وإدراجه في خط أنابيب CI / CD في المرة القادمة.

المصدر: www.habr.com

إضافة تعليق