Mencari tahu mengapa kueri yang berkinerja baik dalam pengembangan dan pengujian meledak dalam produksi terkadang bisa menjadi tantangan. Baca terus untuk mempelajari lebih lanjut tentang beberapa fitur yang dapat memberikan wawasan tentang bagaimana kueri Anda berjalan dalam produksi.
Kueri yang Sedang Berjalan
Ketika klien menghubungkan server PostgreSQL, proses server Postgres utama (secara historis disebut postmaster ) memunculkan proses baru (disebutbackend ) untuk melayani pertanyaan klien. Oleh karena itu, setiap backend menunggu kliennya mengirim kueri, atau mencoba menjalankannya.
Tampilan sistem pg_stat_activity menunjukkan informasi tentang setiap backend yang sedang berjalan. Secara khusus, ini menunjukkan kueri yang sedang dijalankan oleh backend jika aktif, atau kueri terakhir yang dijalankan jika menunggu klien untuk mengirim kueri lain.
Berikut adalah dua backend yang melayani klien yang terhubung ke database testdb
, dengan keduanya secara aktif menjalankan kueri mereka:
testdb=# select usename,datname,state,query from pg_stat_activity where datname='testdb';
-[ RECORD 1 ]-----------------------------------------------------------------------------
usename | postgres
datname | testdb
state | active
query | SELECT pg_sleep(10);
-[ RECORD 2 ]-----------------------------------------------------------------------------
usename | postgres
datname | testdb
state | active
query | select usename,datname,state,query from pg_stat_activity where datname='testdb';
Terkadang kueri mungkin menunggu kunci, dan ini juga muncul inpg_stat_activity. Anda dapat melihat INSERT menunggu kunci relasi di sini:
testdb=# select wait_event_type, wait_event, left(query, 60) from pg_stat_activity where datname='testdb';
-[ RECORD 1 ]---+-------------------------------------------------------------
wait_event_type | Client
wait_event | ClientRead
left | lock table t in access exclusive mode;
-[ RECORD 2 ]---+-------------------------------------------------------------
wait_event_type |
wait_event |
left | select wait_event_type, wait_event, left(query, 60) from pg_
-[ RECORD 3 ]---+-------------------------------------------------------------
wait_event_type | Lock
wait_event | relation
left | insert into t values (1);
Untuk informasi lebih lanjut tentang pg_stat_activity, lihat dokumen.
Meskipun tampilan ini berguna untuk memahami apa yang dilakukan Postgres saat ini, tampilan ini tidak memberikan informasi tentang statistik eksekusi kueri, atau informasi tentang kueri yang telah selesai dieksekusi.
Semua Kueri Berjalan Di Masa Lalu
Untuk itu, ekstensi pg_stat_statements sangat berharga. Ekstensi ini disertakan dalam distribusi inti PostgreSQL dan juga tersedia di layanan terkelola seperti AWS RDS dan GCP SQL.
pg_stat_statements (PSS) adalah "ekstensi" dalam istilah PostgreSQL, dan harus diinstal terlebih dahulu:
- Lihat dokumentasi distro Linux Anda untuk melihat apakah ekstensi sudah diinstal sebelumnya atau memerlukan instalasi paket lain. Misalnya, pada Centos 7 Anda harus
sudo yum install postgresql-contrib
. - Edit file konfigurasi utama postgresql.conf (biasanya di bawah
/etc
, seperti/etc/postgresql/10/main/postgresql.conf
di Debian) dan ubah nilaishared_preload_libraries
ke "pg_stat_statements". Ini adalah daftar nilai yang dipisahkan koma, jadi jika sudah ada sesuatu di sana, tambahkan koma lalu “pg_stat_statements”. - Untuk AWS RDS, Anda harus mengubah grup parameter aktif dan menyetel nilainya.
- Setelah mengedit “shared_preload_libraries”, Anda harus memulai ulang daemon PostgreSQL. Sayangnya tidak ada jalan lain untuk ini. Di AWS RDS, Anda harus memulai ulang instans RDS.
- Setelah restart, server PostgreSQL akan memuat perpustakaan bersama, dan kita dapat menginstal ekstensi dengan menjalankan
CREATE EXTENSION pg_stat_statements
. Anda harus menjadi pengguna super untuk menjalankan perintah ini. - Anda sebenarnya dapat memasang ekstensi di basis data apa pun, namun tetap melihat kueri di semua basis data.
Setelah ekstensi dipasang, Anda dapat menanyakan tampilan yang disebutpg_stat_statements
untuk mendapatkan informasi tentang setiap kueri yang dijalankan sejak ekstensi dipasang.
Angka-angka, seperti waktu yang dibutuhkan untuk mengeksekusi kueri, diakumulasikan sebagai jumlah. Untuk waktu eksekusi kueri saja, beberapa statistik (rata-rata, min, maks, deviasi standar) disajikan. Nilai-nilai ini dapat dihapus menggunakan fungsipg_stat_statements_reset
.
Berikut adalah bagaimana baris dari pg_stat_statements
terlihat seperti:
testdb=# select * from pg_stat_statements where query like '%pg_sleep%' and dbid=42548;
-[ RECORD 1 ]-------+--------------------
userid | 10
dbid | 42548
queryid | 2649515222348904837
query | SELECT pg_sleep($1)
calls | 1
total_time | 10016.782625
min_time | 10016.782625
max_time | 10016.782625
mean_time | 10016.782625
stddev_time | 0
rows | 1
shared_blks_hit | 0
shared_blks_read | 0
shared_blks_dirtied | 0
shared_blks_written | 0
local_blks_hit | 0
local_blks_read | 0
local_blks_dirtied | 0
local_blks_written | 0
temp_blks_read | 0
temp_blks_written | 0
blk_read_time | 0
blk_write_time | 0
Terlepas dari parameter pengidentifikasi (pengguna, basis data, kueri), Anda dapat menemukan banyak hal menarik tentang kueri Anda:
- Berapa lama waktu yang dibutuhkan untuk mengeksekusi biasanya (
mean_time
) - Berapa banyak baris yang dikembalikan rata-rata (
rows
/calls
) - Jumlah data yang dibaca dari cache buffer bersama, dan jumlah data yang dibaca dari disk (
shared_blks_read
menunjukkan jumlah total data yang dibaca kueri, yangshared_blks_hit
berasal dari cache) - Jumlah data yang harus ditulis ke disk secara sinkron karena tekanan cache (
shared_blks_written
) - Jumlah data yang ditulis, sesuai dengan jumlah blok yang disentuh(
shared_blks_dirtied
) - Jumlah waktu yang dihabiskan dalam membaca dan menulis disk (
blk_{read,write}_time
) - File sementara ditulis dan dibaca dari (
temp_blks_{read,written}
) - Tabel sementara ditulis dan dibaca dari (
local_*
)
Waktu baca dan tulis disk hanya tersedia jika parameter konfigurasitrack_io_timing
dihidupkan. Secara default, tidak. Pada sebagian besar sistem Linux modern, tidak masalah untuk mengaktifkan parameter ini. Baca selengkapnya.
Sebaiknya snapshot pg_stat_statements
data terus menerus pada interval reguler untuk melihat tren parameter ini berdasarkan per kueri. Alat sumber terbuka pgmetrics dapat mengekstrak dan mengekspos pg_stat_statements
data sebagai JSON untuk otomatisasi yang lebih mudah.
Kueri Dijalankan Selama Rentang Waktu
Setelah Anda memiliki sistem seperti itu, menjadi mudah untuk melacak kueri yang dieksekusi dalam jangka waktu tertentu. Ini memudahkan untuk men-debug masalah seperti mengapa tugas batch setiap malam membutuhkan waktu lebih lama dari yang diharapkan.
Dengan mengurangi penghitung di antara dua stempel waktu yang diberikan, Anda dapat menemukan angka paling banyak seperti sebelumnya, kecuali untuk min, maks, dan standar deviasi. Ini cukup untuk mengidentifikasi kueri yang dieksekusi dalam rentang waktu dan sumber daya yang digunakan.
Mencatat Kueri Lambat
Cara lain untuk mengidentifikasi kueri dengan cepat yang membutuhkan waktu lebih lama dari yang diharapkan adalah dengan mengaktifkan pencatatan log pernyataan. Anda dapat menentukan durasi ambang batas, dan jika kueri membutuhkan waktu lebih lama dari ini untuk diselesaikan, itu akan dicatat. (Dalam file log PostgreSQL biasa, tidak ada yang terpisah untuk kueri lambat.)
Untuk mengaktifkan fitur ini, edit konfigurasi seperti di bawah ini:
log_min_duration_statement = 1000 # in milliseconds
dan muat ulang Postgres. Anda juga dapat menggunakan ALTER SYSTEM
:
ALTER SYSTEM SET log_min_duration_statement = 1000; -- in milliseconds
Dengan ini, setiap pernyataan (termasuk yang non-DML) yang membutuhkan waktu lebih dari satu detik untuk selesai akan dicatat:
2019-12-02 16:57:05.727 UTC [8040] postgres@testdb LOG: duration: 10017.862 ms statement: SELECT pg_sleep(10);
Waktu aktual yang diambil oleh kueri, serta teks SQL lengkap, dicatat.
Jika Anda memiliki sistem pemantauan log dan dapat melacak jumlah kueri lambat per jam / per hari, ini dapat berfungsi sebagai indikator kinerja aplikasi yang baik.
Rencana Eksekusi Kueri
Setelah Anda menemukan kueri yang menurut Anda harus berjalan lebih cepat, langkah selanjutnya adalah melihat rencana kuerinya. Biasanya, Anda memerlukan paket kueri aktual dari server produksi untuk digunakan. Jika Anda dapat menjalankanEXPLAIN di server produksi yang hebat, jika tidak, Anda harus mengandalkanauto_explain
.
auto_explain
adalah ekstensi PostgreSQL inti lainnya, baik yang sudah diinstal atau tersedia sebagai paket "contrib" untuk distro Anda. Ini juga tersedia di AWSRDS. auto_explain
sedikit lebih mudah dipasang daripada pg_stat_statements
:
- Edit konfigurasi postgres (atau grup parameter RDS)
shared_preload_libraries
untuk menyertakanauto_explain
. - Anda tidak perlu me-restart Postgres, Anda cukup menjalankan:
LOAD 'auto_explain';
. - Anda akan ingin mengonfigurasi pengaturannya, setidaknya yang ini:
auto_explain.log_min_duration = 1000 # seconds
Intinya, setiap kali kueri membutuhkan waktu lebih lama dariauto_explain.log_min_duration
jumlah detik untuk menyelesaikan, auto_explainlog kueri dan rencana eksekusi kuerinya di file log, seperti ini:
2019-12-04 09:23:05.130 UTC [12823] postgres@testdb LOG: duration: 11025.765 ms plan:
Query Text: select pg_sleep(11);
Result (cost=0.00..0.01 rows=1 width=4) (actual time=11025.716..11025.718 rows=1 loops=1)
Output: pg_sleep('11'::double precision)
Itu dapat mencatat rencana dalam format JSON juga, jika Anda memiliki skrip yang dapat memprosesnya:
2019-12-02 17:30:53.676 UTC [8040] postgres@testdb LOG: duration: 10000.230 ms plan:
{
"Query Text": "SELECT pg_sleep(10);",
"Plan": {
"Node Type": "Result",
"Parallel Aware": false,
"Startup Cost": 0.00,
"Total Cost": 0.01,
"Plan Rows": 1,
"Plan Width": 4,
"Actual Startup Time": 10000.205,
"Actual Total Time": 10000.206,
"Actual Rows": 1,
"Actual Loops": 1,
"Output": ["pg_sleep('10'::double precision)"],
"Shared Hit Blocks": 0,
"Shared Read Blocks": 0,
"Shared Dirtied Blocks": 0,
"Shared Written Blocks": 0,
"Local Hit Blocks": 0,
"Local Read Blocks": 0,
"Local Dirtied Blocks": 0,
"Local Written Blocks": 0,
"Temp Read Blocks": 0,
"Temp Written Blocks": 0,
"I/O Read Time": 0.000,
"I/O Write Time": 0.000
},
"Triggers": [
]
}
Di Postgres, tidak ada cara selain auto_explain untuk melihat rencana eksekusi kueri yang telah dijalankan, yang menjadikan auto_explain sebagai alat penting di kotak alat Anda.