نحن نراقب قاعدة بيانات PostgreSQL - من يقع عليه اللوم وماذا يفعل

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

صحيح ، إذا نظرت إليها فقط من وحدة التحكم ، يمكنك الانتقال بسرعة كبيرة دون أي فائدة ، لأن كمية البيانات المتاحة لنا تتجاوز جميع الحدود المعقولة.


لذلك ، من أجل أن يبقى الموقف قابلاً للتحكم ، قمنا بتطوير إضافة لـ Zabbix تقدم المقاييس وتشكل الشاشات وتضع قواعد مراقبة موحدة لجميع الخوادم وقواعد البيانات عليها.

مقالة اليوم حول الاستنتاجات التي يمكن استخلاصها من خلال ملاحظة المقاييس المختلفة لقواعد خادم PostgreSQL في ديناميكيات ، وأين قد تكون المشكلة مخفية.

حالة الإتصال


أول ما تبدأ به جميع عمليات التفكيك حول موضوع "ما حدث لقاعدة البيانات / كان سيئًا" هو مراقبة الحالة الموجزة لـ pg_stat_activity :



على الرسم البياني الأيسر نرى جميع الاتصالات التي تنتظر شيئًا ، على اليمين - وهي شيء فعل. بناءً على إصدار PG ، يتم تحديد حالة الاتصال من خلال pg_stat_activity.state/wait_eventو / أو نص الطلب نفسه.

ما الذي تبحث عنه :

  • قليلidle جدًا - في مرحلة ما قد لا يحتوي تطبيقك على اتصالات كافية مفتوحة بالفعل لقاعدة البيانات ، وعندما تحاول فتح اتصال آخر ، ستجد نفسك تنتظر بدء التهيئة لخدمة اتصال جديد.
  • idle «» , max_connections.
  • idle in transaction — , - pgbouncer. .

    , , idle_in_transaction_session_timeout.
  • wait — - «» . — .

    , «» pg_terminate_backend(pid).
  • active ( max-) , «». - (, « ») , , …

    — , «» .
  • maintenance — , - :

    query ~* E'^(\\s*(--[^\\n]*\\n|/\\*.*\\*/|\\n))*(autovacuum|VACUUM|ANALYZE|REINDEX|CLUSTER|CREATE|ALTER|TRUNCATE|DROP)'

    في معظم الحالات ، سيكون هناك عدد من الفراغ التلقائي / التحليل التلقائي الذي يعمل في نفس الوقت ، والذي يتمثل ضرره فقط في استخدام موارد الخادم للحالات "الدخيلة". إذا كان هذا أمر بالغ الأهمية بالنسبة لك - تويست autovacuum_max_workersو autovacuum_naptime، ولكن تحويلها تماما قبالة - لا يجب عليك .

    ولكن إذا بدأ في الوقت نفسه في النمو wait،maintenance وهي فرصة لمعرفة ما إذا كان شخص ما قد قرر طرح رمز DBA أو المطور ، على سبيل المثال ، حظر نصف فرصة التطبيقات الوظيفية.

نظرًا لأنه من المهم بالنسبة لنا إزالة ليس فقط الكثير من المقاييس ، ولكن أيضًا للقيام بذلك بأكبر قدر ممكن من الكفاءة ، فإننا نحاول تصوير بعضها بشكل متزامن في إطار طلب واحد:

حالة الاتصال والقفل
WITH event_types(wait_event_type) AS(
  VALUES
    ('lwlock')
  , ('lock')
  , ('bufferpin')
  , ('client')
  , ('extension')
  , ('ipc')
  , ('timeout')
  , ('io')
)
, events(wait_event) AS(
  VALUES
    ('walwritelock')
  , ('wal_insert')
  , ('buffer_content')
  , ('buffer_io')
  , ('lock_manager')
  , ('relation')
  , ('extend')
  , ('page')
  , ('tuple')
  , ('transactionid')
  , ('virtualxid')
  , ('speculative token')
  , ('object')
  , ('userlock')
  , ('advisory')
  , ('clientread')
  , ('datafileextend')
  , ('datafileread')
  , ('datafilewrite')
  , ('slruread')
  , ('slruwrite')
)
, states(state) AS(
  VALUES
    ('running')
  , ('maintenance')
  , ('waiting')
  , ('transaction')
  , ('idle')
)
, stats AS(
  SELECT
    pid
  , datname
  , state
  , lower(wait_event_type) wait_event_type
  , lower(wait_event) wait_event
  , query
  FROM
    pg_stat_activity
  WHERE
    pid <> pg_backend_pid()
)
, dbs AS(
  SELECT
    datname
  FROM
    pg_database db
  WHERE
    NOT db.datistemplate
)
  SELECT
    date_part('epoch', now())::integer ts
  , coalesce(s.qty, 0) val
  , dbs.datname dbname
  , states.state
  , true total
  FROM
    dbs
  CROSS JOIN
    states
  NATURAL LEFT JOIN
    (
      SELECT
        datname
      , CASE
          WHEN query ~* E'^(\\s*(--[^\\n]*\\n|/\\*.*\\*/|\\n))*(autovacuum|VACUUM|ANALYZE|REINDEX|CLUSTER|CREATE|ALTER|TRUNCATE|DROP)' THEN
            'maintenance'
          WHEN wait_event IS NOT NULL AND
            wait_event <> 'clientread' AND
            state = 'active' THEN
            'waiting'
          WHEN state = 'active' THEN
            'running'
          WHEN state = 'idle' THEN
            'idle'
          WHEN state IN ('idle in transaction', 'idle in transaction (aborted)') THEN
            'transaction'
          WHEN state = 'fastpath function call' THEN
            'fastpath'
          ELSE
            'disabled'
        END state
      , count(*) qty
      FROM
        stats
      GROUP BY
        1, 2
    ) s
UNION
  SELECT
    date_part('epoch', now())::integer ts
  , coalesce(t.qty, 0) val
  , dbs.datname dbname
  , event_types.wait_event_type
  , false total
  FROM
    dbs
  CROSS JOIN
    event_types
  NATURAL LEFT JOIN
    (
      SELECT
        datname
      , wait_event_type
      , count(*) qty
      FROM
        stats
      WHERE
        wait_event_type IS NOT NULL
      GROUP BY
        1, 2
    ) t
UNION
  SELECT
    date_part('epoch', now())::integer ts
  , coalesce(e.qty, 0) val
  , dbs.datname dbname
  , events.wait_event
  , false total
  FROM
    dbs
  CROSS JOIN
    events
  NATURAL LEFT JOIN
    (
      SELECT
        datname
      , wait_event
      , count(*) qty
      FROM
        stats
      WHERE
        wait_event IS NOT NULL
      GROUP BY
        1, 2
    ) e;

أقفال


نظرًا لأننا تطرقنا إلى حظر المراقبة في الفقرة السابقة ، تجدر الإشارة إلى أن PostgreSQL يحب تراكبها يمينًا ويسارًا:



نحن مهتمون أكثر بنوعين منهم:

  • Exclusive - يحدث عادة عند قفل سجل معين.
  • AccessExclusive - عند تنفيذ عمليات الصيانة على الطاولة.

ولكن لا تنس أن إجمالي عدد الأقفال ليس من المطاط :
يتم تخزين كل من الأقفال الاستشارية والعادية في منطقة الذاكرة المشتركة ، والتي يتم تحديد حجمها بواسطة معلمات التكوين max_locks_per_transactionو max_connections. من المهم أن تكون هذه الذاكرة كافية ، وإلا فلن يتمكن الخادم من إصدار أي قفل . وبالتالي ، فإن عدد عمليات التأمين الموصى بها التي يمكن أن يصدرها الخادم عادة ما يقتصر على عشرات أو مئات الآلاف ، اعتمادًا على تكوين الخادم.
عادةً ما ينشأ هذا الموقف إذا لم يتم تحرير "تدفقات" التطبيق والموارد الخاصة بك: اتصالات بقاعدة البيانات أو سياقات المعاملات أو أقفال استشارية . لذلك ، انتبه إلى الديناميكيات الكلية.

المعاملات في الثانية (TPS)


للحصول على معلومات حول التغييرات في سياق قاعدة البيانات الحالية ، يمكنك استخدام طريقة عرض النظام pg_stat_database . ولكن إذا كان هناك العديد من قواعد البيانات على الخادم ، فمن المناسب القيام بذلك على الفور لجميعها ، والاتصال بهاpostgres .

TPS & tuples
SELECT
  extract(epoch from now())::integer ts
, datname dbname
, pg_stat_get_db_tuples_returned(oid) tup_returned
, pg_stat_get_db_tuples_fetched(oid) tup_fetched
, pg_stat_get_db_tuples_inserted(oid) tup_inserted
, pg_stat_get_db_tuples_updated(oid) tup_updated
, pg_stat_get_db_tuples_deleted(oid) tup_deleted
, pg_stat_get_db_xact_commit(oid) xact_commit
, pg_stat_get_db_xact_rollback(oid) xact_rollback
FROM
  pg_database
WHERE
  NOT datistemplate;

أريد التأكيد بشكل منفصل - لا تهمل ناتج القيم القصوى للمقاييس!



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

حسنًا ، يعد التراجع ( rollback) للمعاملة فرصة للتحقق مما إذا كان تطبيقك يتم تنفيذه بوعي ROLLBACK، أو إذا كان الخادم يقوم بذلك تلقائيًا نتيجة لخطأ ما.

عدد العمليات على السجلات


أولاً ، انتبه إلى السجلات التي نطرحها من الفهارس / الجداول:



  • tuples.returned — , «» .
  • tuples.fetched — , « » Rows Removed by Filter, «» .
  • tuples.ratio — , , 1, — . , , , .

إذا لاحظت ذروة حادةtuples.ratio ، يمكنك التأكد من أنك ستجد بعض الطلبات غير الفعالة من الفئة الموضحة في المقالة حول وصفات علاجها بجوار السجل .

ومع ذلك ، حتى لو كان ratioيساوي 1 بشكل مثالي ، ولكن الذروة هبطتreturned/fetched - لا تتوقع جيدًا أيضًا. عادة هذا يمكن أن يعني أن هناك نوع من المشاكل في الخطة ، مثل:

Hash Join
  - Hash
    - Seq Scan on BIG_TABLE
  - Index Scan ...

Merge Join
  - Index Scan on BIG_INDEX
  - Index Scan ...

منذ أن بدأنا في التحقق مما تتم قراءته هناك ، دعنا نرى كيف يحدث ذلك. أي ، كم عدد السجلات التي نقرأها بواسطة الفهارس ، ومقدار النتيجة Seq Scan: من



الواضح أن أي نمو غير مخطط للمؤشرات هنا يجب أن يسبب الشك. على سبيل المثال ، إذا احتجت لسبب ما لقراءة لوحة كاملة من 10 ملايين سجل كل ليلة ، فإن ظهور مثل هذه الذروة أثناء النهار هو سبب التفكيك.

بالإضافة إلى أي إدخالات / تحديثات / حذف شاذة الكتلة:



استخدام ذاكرة التخزين المؤقت للبيانات


لفهم الكيفية التي تؤدي بها المراجعة الجماعية للسجلات إلى تفاقم عمر الخادم ، دعنا نلقي نظرة على عمل الخادم مع صفحات البيانات والنسبةblock.read/hit . في عالم مثالي ، يجب ألا يقوم الخادم "بالقراءة" من القرص ( shared readعلى عقدة الخطة) لا شيء على الإطلاق ، يجب أن يكون كل شيء موجودًا بالفعل في الذاكرة ( shared hit) ، نظرًا لأن الوصول إلى القرص يكون دائمًا بطيئًا .

في الواقع ، هذا ليس صحيحًا تمامًا ، وهو سبب التحليل الشامل للطلبات في وقت الذروة:



أطول طلب / معاملة


بالنسبة لـ MVCC ، تعد الاستعلامات والمعاملات طويلة المدى في الأنظمة المشغولة كارثة للأداء. يمكن قراءة التفاصيل والصور حول هذا هنا ، وهنا - كيف يمكنك البقاء على قيد الحياة في مثل هذه الظروف.



التقاط مثل هذه الأوغاد يساعدنا pg_stat_activity.query_start/xact_start.

كما تظهر تجربتنا ، فإن التمثيل البصري لهذه المقاييس يكفي بالفعل لتمثيل الأماكن التي "حفر" بشكل أكبر:

  • ابحث عن تسرب الموارد في التطبيق
  • تحسين الطلبات الفاشلة
  • وضع أجهزة أكثر إنتاجية
  • ... أو تأكد من أن الحمل متباعد بشكل صحيح في الوقت المناسب

All Articles