Kami memantau basis data PostgreSQL - siapa yang harus disalahkan dan apa yang harus dilakukan

Saya sudah berbicara tentang bagaimana kita "menangkap" masalah PostgreSQL menggunakan pemantauan log massal pada ratusan server secara bersamaan. Tapi selain log, DBMS ini juga memberi kita banyak alat untuk menganalisis kondisinya - itu adalah dosa untuk tidak menggunakannya.

Benar, jika Anda hanya melihatnya dari konsol, Anda dapat dengan cepat berputar tanpa manfaat apa pun, karena jumlah data yang tersedia bagi kami melebihi semua batas yang masuk akal.


Oleh karena itu, agar situasi tetap terkendali, kami mengembangkan add-on untuk Zabbix yang memberikan metrik, membentuk layar, dan membuat aturan pemantauan yang seragam untuk semua server dan database.

Artikel hari ini adalah tentang kesimpulan apa yang bisa ditarik dengan mengamati secara dinamis berbagai metrik basis server PostgreSQL, dan di mana masalahnya mungkin disembunyikan.

Status hubungan


Hal pertama yang dimulai dari semua pembongkaran pada topik "apa yang terjadi pada basis data / buruk" adalah memantau status ringkasan pg_stat_activity :



Pada grafik kiri kita melihat semua koneksi yang menunggu sesuatu, di sebelah kanan - yang merupakan sesuatu melakukan. Tergantung pada versi PG, status koneksi ditentukan oleh pg_stat_activity.state/wait_eventdan / atau teks dari permintaan itu sendiri.

Apa yang harus dicari :

  • Terlalu sedikitidle - pada beberapa titik aplikasi Anda mungkin tidak memiliki cukup koneksi yang sudah terbuka ke database, dan ketika Anda mencoba untuk membuka yang lain, Anda akan berakhir menunggu proses untuk menginisialisasi untuk melayani koneksi baru.
  • 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)'

    Dalam kebanyakan kasus, akan ada jumlah autovacuum / autoanalyze yang bekerja pada saat yang sama, yang salah satunya adalah penggunaan sumber daya server untuk kasus "asing". Jika ini penting untuk Anda - putar autovacuum_max_workersdan autovacuum_naptime, tapi matikan sepenuhnya - Anda tidak boleh .

    Tetapi jika pada saat yang sama mulai tumbuh wait, danmaintenance , ini adalah kesempatan untuk melihat apakah seseorang telah memutuskan untuk mengeluarkan DBA atau kode pengembang, misalnya, memblokir setengah peluang aplikasi fungsional.

Karena penting bagi kami untuk menghapus tidak hanya banyak metrik, tetapi juga untuk melakukannya seefisien mungkin, kami mencoba memotret beberapa dari mereka secara bersamaan dalam kerangka satu permintaan:

Koneksi dan status kunci
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;

Kunci


Karena kami menyentuh tentang pemblokiran pemantauan pada paragraf sebelumnya, perlu dicatat bahwa PostgreSQL suka overlay kanan dan kiri:



Kami paling tertarik pada dua jenis:

  • Exclusive - biasanya terjadi ketika mengunci pada catatan tertentu.
  • AccessExclusive - saat melakukan operasi pemeliharaan di atas meja.

Tapi jangan lupa bahwa jumlah kunci bukan karet :
Baik kunci penasihat dan reguler disimpan di area memori bersama, yang ukurannya ditentukan oleh parameter konfigurasi max_locks_per_transactiondan max_connections. Penting agar memori ini memadai, karena jika tidak server tidak akan dapat mengeluarkan kunci apa pun . Dengan demikian, jumlah kunci yang direkomendasikan yang dapat dikeluarkan oleh server biasanya terbatas pada puluhan atau ratusan ribu, tergantung pada konfigurasi server.
Biasanya, situasi ini muncul jika aplikasi Anda "mengalir" dan sumber daya tidak dirilis: koneksi ke database, konteks transaksi, atau kunci penasihat . Karena itu, perhatikan dinamika keseluruhan.

Transaksi per detik (TPS)


Untuk mendapatkan informasi tentang perubahan dalam konteks database saat ini, Anda dapat menggunakan tampilan sistem pg_stat_database . Tetapi jika ada banyak database di server, akan lebih mudah untuk melakukan ini segera untuk mereka semua, terhubung kepostgres .

TPS & tupel
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;

Saya ingin menekankan secara terpisah - jangan abaikan output dari nilai maksimal metrik!



Dalam grafik ini, kita dapat dengan jelas melihat situasi peningkatan puncak tiba-tiba dalam jumlah committransaksi yang dilakukan ( ). Ini bukan satu-satu sesuai dengan beban di server dan transaksi dapat dengan kompleksitas yang berbeda-beda, tetapi pertumbuhan 4 kali lipat jelas menunjukkan bahwa server harus memiliki cadangan kinerja tertentu untuk bertahan dari puncak seperti itu tanpa masalah.

Nah, rollback ( rollback) dari transaksi adalah kesempatan untuk memeriksa apakah aplikasi Anda secara sadar dieksekusi ROLLBACK, atau apakah server secara otomatis melakukan ini sebagai akibat dari kesalahan.

Jumlah operasi pada catatan


Pertama, perhatikan catatan yang kita kurangi dari indeks / tabel:



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

Jika Anda mengamati puncak yang tajamtuples.ratio , Anda dapat yakin bahwa beberapa permintaan tidak efisien dari kategori yang dijelaskan dalam artikel tentang resep untuk perawatan mereka akan menemukan Anda di log .

Namun, meski ratioidealnya sama dengan 1, tetapi puncaknya jatuhreturned/fetched - juga jangan berharap bagus. Biasanya ini dapat berarti bahwa ada beberapa masalah dalam rencana, seperti:

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

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

Karena kami mulai memeriksa apa yang sedang dibaca di sana, mari kita lihat bagaimana itu terjadi. Yaitu, berapa banyak catatan yang kita baca berdasarkan indeks, dan seberapa banyak hasilnya Seq Scan:



Jelas bahwa di sini setiap pertumbuhan indikator yang tidak direncanakan harus menimbulkan kecurigaan. Misalnya, jika karena alasan tertentu Anda perlu membaca sepiring penuh catatan 10 juta setiap malam, maka penampilan puncak seperti itu di siang hari adalah alasan pembongkaran.

Serta setiap sisipan / pembaruan / penghapusan masal-anomali massal:



Menggunakan cache data


Untuk memahami bagaimana pengoreksian ulang rekaman yang benar-benar memperburuk kehidupan server, mari kita lihat bagaimana server bekerja dengan halaman data dan perbandingannyablock.read/hit . Dalam dunia yang ideal, server tidak boleh "membaca" dari disk ( shared readpada node rencana) sama sekali tidak ada, semuanya harus sudah ada dalam memori ( shared hit), karena mengakses disk selalu lambat .

Pada kenyataannya, ini tidak sepenuhnya benar, dan merupakan alasan untuk analisis menyeluruh permintaan sekitar waktu puncak:



Permintaan / Transaksi Terpanjang


Untuk MVCC, permintaan jangka panjang dan transaksi dalam sistem sibuk adalah bencana kinerja. Detail dan gambar tentang ini bisa dibaca di sini , dan di sini - bagaimana Anda masih bisa bertahan dalam kondisi seperti itu.



Menangkap penjahat seperti itu membantu kita pg_stat_activity.query_start/xact_start.

Seperti yang ditunjukkan oleh pengalaman kami, representasi visual dari metrik ini sudah cukup untuk secara kasar menunjukkan di mana "menggali" lebih lanjut:

  • mencari kebocoran sumber daya dalam aplikasi
  • optimalkan permintaan yang gagal
  • letakkan perangkat keras yang lebih produktif
  • ... atau pastikan bahwa beban ditempatkan dengan benar pada waktunya

All Articles