Meningkatkan work_mem dan shared_buffers di Postgres 9.2 secara signifikan memperlambat kueri

39

Saya memiliki instance PostgreSQL 9.2 yang berjalan pada RHEL 6.3, mesin 8-core dengan 16GB RAM. Server didedikasikan untuk basis data ini. Mengingat postgresql.conf default cukup konservatif mengenai pengaturan memori, saya pikir mungkin ide yang baik untuk memungkinkan Postgres menggunakan lebih banyak memori. Yang mengejutkan saya, mengikuti saran di wiki.postgresql.org/wiki/Tuning_Your_PostgreSQL_Server secara signifikan melambat hampir setiap kueri yang saya jalankan tetapi jelas lebih terlihat pada kueri yang lebih kompleks.

Saya juga mencoba menjalankan pgtune yang memberikan rekomendasi berikut dengan lebih banyak parameter yang disetel, tetapi itu tidak mengubah apa pun. Ini menunjukkan shared_buffers dari 1/4 ukuran RAM yang tampaknya sejalan dengan saran di tempat lain (dan pada wiki PG khususnya).

default_statistics_target = 50
maintenance_work_mem = 960MB
constraint_exclusion = on
checkpoint_completion_target = 0.9
effective_cache_size = 11GB
work_mem = 96MB
wal_buffers = 8MB
checkpoint_segments = 16
shared_buffers = 3840MB
max_connections = 80

Saya mencoba mengindeks ulang seluruh database setelah mengubah pengaturan (menggunakan reindex database), tetapi itu tidak membantu. Saya bermain-main dengan shared_buffers dan work_mem. Mengubahnya secara bertahap dari nilai standar yang sangat konservatif (128k / 1MB) secara bertahap menurunkan kinerja.

Saya berlari EXPLAIN (ANALYZE,BUFFERS)pada beberapa pertanyaan dan pelakunya tampaknya Hash Join secara signifikan lebih lambat. Tidak jelas bagi saya mengapa.

Untuk memberikan beberapa contoh spesifik, saya memiliki pertanyaan berikut. Ini berjalan dalam ~ 2100 ms pada konfigurasi default dan ~ 3300 ms pada konfigurasi dengan peningkatan ukuran buffer:

select count(*) from contest c
left outer join contestparticipant cp on c.id=cp.contestId
left outer join teammember tm on tm.contestparticipantid=cp.id
left outer join staffmember sm on cp.id=sm.contestparticipantid
left outer join person p on p.id=cp.personid
left outer join personinfo pi on pi.id=cp.personinfoid
where pi.lastname like '%b%' or pi.firstname like '%a%';

EXPLAIN (ANALYZE,BUFFERS) untuk kueri di atas:

Pertanyaannya adalah mengapa saya mengamati penurunan kinerja ketika saya meningkatkan ukuran buffer? Mesin pasti tidak kehabisan memori. Alokasi jika memori bersama di OS adalah ( shmmaxdan shmall) diatur ke nilai yang sangat besar, itu seharusnya tidak menjadi masalah. Saya juga tidak mendapatkan kesalahan dalam log Postgres. Saya menjalankan autovacuum dalam konfigurasi default, tetapi saya tidak berharap ada hubungannya dengan itu. Semua pertanyaan dijalankan pada mesin yang sama beberapa detik terpisah, hanya dengan konfigurasi yang diubah (dan restart PG).

Sunting: Saya baru saja menemukan satu fakta yang sangat menarik: ketika saya melakukan tes yang sama pada pertengahan 2010 iMac (OSX 10.7.5) saya juga dengan Postgres 9.2.1 dan 16GB RAM, saya tidak mengalami perlambatan. Secara khusus:

set work_mem='1MB';
select ...; // running time is ~1800 ms
set work_mem='96MB';
select ...' // running time is ~1500 ms

Ketika saya melakukan permintaan yang persis sama (yang di atas) dengan data yang persis sama di server saya mendapatkan 2100 ms dengan work_mem = 1MB dan 3200 ms dengan 96 MB.

Mac memiliki SSD sehingga bisa dimengerti lebih cepat, tetapi menunjukkan perilaku yang saya harapkan.

Lihat juga diskusi tindak lanjut tentang kinerja pgsql .

Petr Praus
sumber
1
Sepertinya dalam kasus yang lebih lambat setiap langkah selalu lebih lambat. Apakah pengaturan lain tetap sama?
dezso
1
Mungkin bernilai saat Anda bertanya di forum yang lebih khusus daripada yang umum. Dalam hal ini saya sarankan arsip
Colin 't Hart
1
Oh, dan laporkan kembali dan tolong jawab pertanyaan Anda sendiri jika Anda menemukan jawabannya! (Ini diizinkan, dianjurkan bahkan).
Colin 't Hart
1
Saya bertanya-tanya bagaimana miripnya Postgres dengan Oracle dalam hal ini: Saya ingat sebuah kursus oleh Jonathan Lewis (guru Oracle) di mana ia menunjukkan bahwa mengalokasikan lebih banyak memori untuk beberapa jenis terkadang membuat mereka lebih lambat. Saya lupa spesifik tapi itu ada hubungannya dengan Oracle melakukan sebagian dan kemudian menulisnya untuk penyimpanan sementara dan kemudian menggabungkannya nanti. Entah bagaimana lebih banyak memori membuat proses ini lebih lambat.
Colin 't Hart
2
Pertanyaannya sekarang diposting di pgsql-performance: archives.postgresql.org/pgsql-performance/2012-11/msg00004.php
Petr Praus

Jawaban:

28

Pertama-tama, perlu diingat bahwa work_mem adalah per operasi dan sehingga bisa menjadi sangat cepat. Secara umum jika Anda tidak mengalami masalah dengan jenis yang lambat saya akan meninggalkan work_mem sendirian sampai Anda membutuhkannya.

Melihat rencana permintaan Anda, satu hal yang mengejutkan saya adalah bahwa hit buffer sangat berbeda dengan melihat kedua paket tersebut, dan bahkan pemindaian berurutan lebih lambat. Saya menduga bahwa masalah ini berkaitan dengan caching baca-depan dan memiliki lebih sedikit ruang untuk itu. Apa ini artinya Anda biasing memori untuk menggunakan kembali indeks dan terhadap membaca tabel pada disk.


Pemahaman saya adalah PostgreSQL akan melihat ke cache untuk halaman sebelum membacanya dari disk karena tidak tahu benar apakah cache OS akan berisi halaman itu. Karena halaman-halaman tersebut kemudian tinggal di cache dan karena cache itu lebih lambat daripada cache OS, ini mengubah jenis pertanyaan yang cepat vs jenis-jenis yang lambat. Bahkan membaca rencana, selain dari masalah work_mem, sepertinya semua info permintaan Anda berasal dari cache tetapi itu adalah pertanyaan tentang cache yang mana.

work_mem : berapa banyak memori yang dapat kita alokasikan untuk pengurutan atau operasi gabungan yang terkait. Ini adalah per operasi, bukan per pernyataan atau per back-end, jadi satu query kompleks dapat menggunakan banyak kali jumlah memori ini. Tidak jelas Anda mencapai batas ini, tetapi perlu diperhatikan dan diperhatikan. jika Anda meningkatkan ini terlalu jauh, Anda kehilangan memori yang mungkin tersedia untuk cache baca dan buffer bersama.

shared_buffers : berapa banyak memori yang dialokasikan ke antrian halaman PostgreSQL yang sebenarnya. Sekarang, idealnya kumpulan database Anda yang menarik akan tetap tersimpan dalam memori yang tersimpan di sini dan di buffer baca. Namun, yang dilakukan adalah memastikan bahwa informasi yang paling sering digunakan di semua backend di-cache dan tidak dibilas ke disk. Di Linux, cache ini jauh lebih lambat daripada cache disk OS, tetapi cache ini menawarkan jaminan bahwa cache disk OS tidak dan transparan untuk PostgreSQL. Ini cukup jelas di mana masalah Anda.

Jadi yang terjadi adalah ketika kami memiliki permintaan, kami memeriksa buffer bersama terlebih dahulu sejak PostgreSQL memiliki pengetahuan yang mendalam tentang cache ini, dan mencari halaman-halamannya. Jika mereka tidak ada di sana, kami meminta OS untuk membukanya dari file, dan jika OS telah menyimpan hasilnya, ia mengembalikan salinan yang di-cache (ini lebih cepat dari buffer yang dibagikan, tetapi Pg tidak bisa memastikan apakah itu di-cache atau di disk, dan disk jauh lebih lambat sehingga PostgreSQL biasanya tidak akan mengambil risiko itu). Perlu diingat ini juga mempengaruhi akses halaman acak vs berurutan. Jadi, Anda mungkin mendapatkan kinerja yang lebih baik dengan pengaturan shared_buffers yang lebih rendah.

Perasaan saya adalah bahwa Anda mungkin mendapatkan kinerja yang lebih baik, atau setidaknya lebih konsisten, di lingkungan konkurensi tinggi dengan pengaturan shared_buffer yang lebih besar. Juga perlu diingat bahwa PostgreSQL mengambil memori ini dan menahannya jadi jika Anda memiliki hal lain yang berjalan pada sistem, buffer baca akan menahan file yang dibaca oleh proses lain. Ini adalah topik yang sangat besar dan kompleks. Pengaturan buffer bersama yang lebih besar memberikan jaminan kinerja yang lebih baik tetapi dalam beberapa kasus memberikan kinerja yang lebih sedikit.

Chris Travers
sumber
10

Terlepas dari efek yang tampaknya paradoksal yang meningkat work_mem penurunan kinerja ( @Chris mungkin memiliki penjelasan), Anda dapat meningkatkan fungsi Anda setidaknya dalam dua cara.

  • Tulis ulang dua palsu LEFT JOINdengan JOIN. Itu mungkin membingungkan perencana kueri dan mengarah pada rencana yang lebih rendah.

SELECT count(*) AS ct
FROM   contest            c
JOIN   contestparticipant cp ON cp.contestId = c.id
JOIN   personinfo         pi ON pi.id = cp.personinfoid
LEFT   JOIN teammember    tm ON tm.contestparticipantid = cp.id
LEFT   JOIN staffmember   sm ON sm.contestparticipantid = cp.id
LEFT   JOIN person        p  ON p.id = cp.personid
WHERE (pi.firstname LIKE '%a%'
OR     pi.lastname  LIKE '%b%')
  • Dengan asumsi bahwa pola pencarian Anda yang sebenarnya lebih selektif, gunakan indeks trigram pi.firstnamedan pi.lastnameuntuk mendukung LIKEpencarian yang tidak berlabuh . (Pola yang lebih pendek seperti'%a%' didukung juga tetapi indeks tidak mungkin membantu predikat non-selektif.):

CREATE INDEX personinfo_firstname_gin_idx ON personinfo USING gin (firstname gin_trgm_ops);
CREATE INDEX personinfo_lastname_gin_idx  ON personinfo USING gin (lastname gin_trgm_ops);

Atau satu indeks multikolom:

CREATE INDEX personinfo_name_gin_idx ON personinfo USING gin (firstname gin_trgm_ops, lastname gin_trgm_ops);

Seharusnya membuat pertanyaan Anda sedikit lebih cepat. Anda perlu menginstal modul tambahan pg_trgmuntuk ini. Detail di bawah pertanyaan terkait ini:


Juga, sudahkah Anda mencoba pengaturan work_mem secara lokal - hanya untuk transaksi saat ini ?

SET LOCAL work_mem = '96MB';

Ini menjaga transaksi bersamaan dari juga memakan lebih banyak RAM, mungkin kelaparan satu sama lain.

Erwin Brandstetter
sumber
3
Saya ingin saran work_mem lokal kedua Erwin. Karena work_mem mengubah jenis kueri yang lebih cepat, Anda mungkin perlu mengubahnya untuk beberapa kueri. Yaitu level work_mem rendah yang terbaik untuk kueri yang mengurutkan / menggabungkan sejumlah kecil catatan dengan cara yang kompleks (mis. Banyak yang bergabung) sedangkan level work_mem tinggi adalah yang terbaik untuk kueri yang memiliki beberapa jenis tetapi yang mengurutkan atau menggabungkan sejumlah besar baris sekaligus .
Chris Travers
Sementara itu, saya meningkatkan permintaan (pertanyaannya adalah dari Oktober tahun lalu) tetapi terima kasih :) Pertanyaan ini lebih tentang efek yang tidak terduga daripada permintaan tertentu. Permintaan berfungsi terutama untuk menunjukkan efeknya. Terima kasih atas tipnya pada indeks, saya akan mencobanya!
Petr Praus