- Postgres veritabanı çok miktarda RAM kullanır. Sonuç kümeleri oluştururken indeks eşleştirme, tablodan ilgili satırları alma, tuple birleştirme/filtreleme/toplama/sıralama gibi adımlardan geçer ve tüm bu adımlar belleğe dayanır
- Postgres'in bellek kullanımını optimize etmek için, mevcut RAM'den azami ölçüde yararlanırken farklı türde bellek tahsislerini verimli biçimde dengelemek ve işletim sisteminin aşırı bellek kullanımı nedeniyle süreçleri sonlandırmasını önlemek gerekir
Sharing is Caring
- Postgres ile ilişkili RAM'in en büyük bölümü
shared_buffers olarak adlandırılır ve en sık erişilen tüm tablo ve indekslerin satırlarını temsil eder. Bu, kullanım sıklığına göre puan veren bir sezgisel yöntemle desteklenir
shared_buffers, Postgres başlatılırken ayrılan sabit bir değerdir ve beklenmedik bellek sorunlarına katkıda bulunmaz
- Varsayılan değer 128MB'dir
- Ancak işletim sistemi bunu önceden tahsis edilmiş bellek olarak görmeyebilir; bu nedenle örneğin RAM miktarına kadar çok yüksek bir değer vermek riskli olabilir
- Üretim sistemlerinde
shared_buffers için en yaygın önerilen değer, kullanılabilir RAM'in %25'idir. Bu, donanıma göre ölçeklendiği için çoğu sistem için iyi bir başlangıç noktasıdır
- Benchmark sonuçları, %25 tavsiyesinin genelde yeterli olduğunu ancak veritabanının kullanım biçimine göre değişebileceğini gösterir
- Örneğin raporlama sistemleri, karmaşık geçici sorgular nedeniyle düşük önbellek isabet oranlarına sahip olabilir ve hatta daha düşük ayarlarda biraz daha iyi performans gösterebilir
pg_buffercache uzantısı kullanılarak paylaşılan tamponlara ayrılan tablo ve indeksler tam olarak görülebilir. Tamponlarda kullanılan sayfa sayısına bakarak shared_buffers değeri ayarlanabilir
- Tampon önbelleği %100 kullanılmıyorsa ayar çok yüksek olabilir; bu durumda instance boyutu veya
shared_buffers değeri düşürülebilir
- %100 doluysa ve çok sayıda tablonun yalnızca bir kısmı önbellekteyse, azalan getiri noktasına kadar kademeli olarak daha yüksek değerler vermek faydalı olabilir
- Postgres 16'daki yeni
pg_stat_io görünümü de shared_buffers ayarına yardımcı olabilir. İsabet oranı ile istemci backend okuma/yazmalarını görebilirsiniz
- Okuma/yazma oranı 1'e yakınsa, Postgres'in aynı sayfaları
shared_buffers içinde sürekli döndürdüğünü gösterebilir. Bu tür thrashing'i azaltmak için shared_buffers artırılmalıdır
- Sistem RAM'inin %50'sini aşmaya başlıyorsanız instance boyutunu artırmayı düşünmelisiniz. Çünkü Postgres'in kullanıcı oturumları ve ilgili sorgular için de belleğe ihtiyacı vardır
Working Memory
- Postgres'in gerçekten işi yapmak için kullandığı belleğin diğer yarısı,
work_mem parametresiyle kontrol edilen çalışma belleğidir
- Varsayılan değer 4MB'dir ve kullanıcıların sorgu çalıştırma hızını artırmak için ilk değiştirdiği değerlerden biridir
- Ancak işletim sistemi "bellek yetersiz" mesajı nedeniyle Postgres'i sonlandırıyorsa
work_mem değerini artırmak isteyebilirsiniz; fakat bu yalnızca sorunu kötüleştirir. Postgres'in kullandığı RAM miktarını artırır ve bu tür sonlandırmalarla karşılaşma olasılığını yükseltir
- Pek çok kişi "çalışma belleği"ni, Postgres'in sorgu üzerinde çalışırken yaptığı tüm işlemler için ayrılmış tek bir tahsis olarak yorumlar; fakat gerçekte durum bundan farklıdır
- Her adımın (node) kendine ait ayrı bir
work_mem örneği tahsis edilir. Örneğin varsayılan work_mem değeri 4MB ise, 4 node gerektiren bir sorgu en fazla 16MB RAM tüketebilir
- Yoğun bir sunucuda bu tür sorguların aynı anda 100 tane çalışması, yalnızca sonuç hesaplaması için 1.6GB'a kadar RAM kullanabileceği anlamına gelir. Daha karmaşık sorgular, yürütme için gereken node sayısına bağlı olarak daha da fazla RAM isteyebilir
- Sorgunun yürütme planını görmek için
EXPLAIN komutu kullanıldığında, Postgres'in sorguyu nasıl çalıştırdığını ve çıktıyı üretmek için gerekli tüm node'ları görebilirsiniz
pg_stat_statements uzantısıyla birlikte kullanıldığında, en aktif sorguları ayırabilir ve work_mem kaynaklı toplam bellek kullanımını tahmin edebilirsiniz
work_mem çok düşük ayarlanırsa, RAM'e sığmayan satırlar veya ara sonuçlar diske taşar ve çok daha yavaş çalışır
- Diske yazılmış tüm geçici dosyaların toplam boyutu ve sayısını görmek için
pg_stat_database görünümüne bakabilir, ortalama boyut makulse work_mem değerini bu miktar kadar artırabilirsiniz
- Oturum başına kullanılabilir RAM miktarını kabaca görmek için şu formül kullanılabilir:
(toplam RAM'in %80'i - shared_buffers) / (max_connections)
- Örneğin 16GB RAM, 4GB paylaşılan tampon ve 100 maksimum bağlantı varsa, oturum başına yaklaşık 88MB kullanılabilir
work_mem için iyi bir ayar elde etmek üzere bu değeri sorgu planı node'larının ortalama sayısına bölebilirsiniz
Ongoing Maintenance
- Postgres RAM kullanımında ayarlanabilir son bölüm, çalışma belleğine benzer ancak özellikle bakım işleriyle ilgili olan ve
maintenance_work_mem adlı benzer bir parametreyle kontrol edilen kısımdır
- Varsayılan değer 64MB'dir ve
VACUUM, CREATE INDEX, ALTER TABLE ADD FOREIGN KEY gibi işlemler için ayrılan RAM miktarını belirtir
- Oturum başına tek bir işle sınırlı olduğu ve çok sayıda eşzamanlı bakım işi olasılığı düşük olduğu için daha yüksek değerler kullanmak genelde yeterince güvenli kabul edilir
- Bu bakım işlemleri çok fazla bellek kullanabilir ve tamamen RAM'de çalışabildiklerinde çok daha hızlı tamamlanabilir; bu yüzden 1GB veya 2GB'a ayarlamak oldukça yaygındır
- Buradaki önemli uyarı, daha sonra yeniden kullanılmak üzere ölü tuple'ları işaretleyen Postgres autovacuum sürecidir
- Autovacuum,
autovacuum_max_workers sınırına kadar arka plan işleri başlatır ve her biri maintenance_work_mem değerinin tam bir örneğini kullanabilir
- Bol miktarda boş RAM'e sahip çoğu sunucu için 1GB bakım çalışma belleği güvenlidir; ancak RAM kısıtlıysa daha dikkatli olmak gerekir
- Özellikle autovacuum worker'larını sınırlamak için ayrıca
autovacuum_work_mem parametresi vardır
- Postgres autovacuum worker'ları 1GB'tan fazla kullanamaz; bu yüzden
autovacuum_work_mem değerini bunun üstüne ayarlamanın bir etkisi olmaz
Session Pooling
- Bellek tüketimini azaltmanın en kolay yolu, olası tahsislere mantıksal bir sınır koymaktır
- Postgres şu anda süreç tabanlı bir motordur; dolayısıyla her kullanıcı oturumuna thread yerine fiziksel bir süreç atanır
- Bu nedenle her bağlantı belirli bir RAM ek yükü getirir ve context switching'e katkıda bulunur
- Sonuç olarak yaygın öneri,
max_connections değerini kullanılabilir CPU thread sayısının 4 katından fazla yapmamaktır. Bu, aktif oturumların CPU'lar arasında taşınması için harcanan zamanı azaltır ve oturumların topluca tüketebileceği RAM miktarını doğal olarak sınırlar
- Tüm oturumlar sorgu çalıştırıyorsa ve her node bir
work_mem tahsisini temsil ediyorsa, teorik azami çalışma belleği kullanımı connections * nodes * work_mem olur
- Sorgu karmaşıklığını azaltmak her zaman mümkün olmayabilir, ancak bağlantı sayısını azaltmak genellikle mümkündür
- Uygulama sürekli belirli bir yüksek sayıda oturum açıyorsa veya birden fazla bağımsız mikroservis Postgres'e dayanıyorsa, bunu yapmak söylendiği kadar kolay olmayabilir
work_mem * max_connections * 5 formülü, tüm bağlantıların aktif olduğu varsayımıyla, temel sorguları işlemek için Postgres instance'ının kullanıcı oturumlarına ayırabileceği azami RAM miktarına dair kaba bir tahmindir
- Sunucuda bu değer için yeterli RAM yoksa, etkenlerden birini azaltmayı veya RAM'i artırmayı düşünmek gerekir
- Sorgu başına ortalama 5 node varsayımı uygulamanız için geçerli olmayabilir; bu nedenle sorgu yürütme planlarını daha iyi anladıktan sonra gerektiği gibi ayarlanmalıdır
- Bir sonraki adım, PgBouncer gibi bir bağlantı havuzlayıcı kullanmaktır
- Bu, istemci bağlantılarını veritabanından ayırır ve maliyetli Postgres oturumlarını istemciler arasında yeniden kullanır
- Doğru yapılandırıldığında, yüzlerce istemci uygulamayı etkilemeden onlarca Postgres bağlantısını paylaşabilir
- PgBouncer'ın bu şekilde 1000'den fazla bağlantıyı 40-50 bağlantıya çoklayarak süreç ek yükünden kaynaklanan toplam bellek tüketimini ciddi biçimde azalttığı görülmüştür
Reducing Bloat
- Bellek kullanımını izlemenin en zor yönü muhtemelen tablo şişmesi (
bloat) olacaktır
- Postgres, veriyi depolama sisteminde temsil etmek için çok sürümlü eşzamanlılık denetimi (MVCC) kullanır
- Yani bir tablo satırı her değiştirildiğinde Postgres, tablonun bir yerinde satırın başka bir kopyasını oluşturur ve bunu yeni bir sürüm numarasıyla işaretler
- Postgres'in
VACUUM süreci, eski satır sürümlerini "kullanılmayan" alan olarak işaretleyerek yeni satır sürümlerinin yerleştirilmesine olanak tanır
- Postgres'te bu yeniden kullanılabilir tahsisleri sürekli bulup tabloların sınırsız büyümesini önleyen bir autovacuum arka plan süreci bulunur
- Ancak bazen, özellikle büyük sistemlerde, bunun varsayılan yapılandırması yeterli olmayabilir ve bu bakım işlemleri geride kalabilir
- Sonuç olarak tablolar, canlı satırlardan daha fazla ölü satır içerebilir ve eski verilerle "şişmiş" hale gelebilir
- Bir tablo aşırı derecede şişmişse bunun paylaşılan tamponlar üzerindeki etkisini düşünmek gerekir
- Her sayfa yalnızca tek bir canlı satır ve birkaç ölü satır içeriyorsa, belirli bir sorgu için 10 satır gerektiğinde 10 sayfanın
shared_buffers içine alınması gerekir; bu da başka amaçlar için kullanılabilecek önemli miktarda belleği boşa harcar
- Bu satırlara talep özellikle yüksekse, kullanım sıklığı onları shared buffer içinde tutar ve önbellek verimliliğini ciddi biçimde düşürür
- İnternette tablo şişmesini tahmin eden pek çok sorgu dolaşsa da, tablo sayfalarının gerçekte nasıl göründüğünü ayrıntılı biçimde görmenin tek yolu
pgstattuple uzantısını kullanmaktır
free_percent %30'dan büyükse, autovacuum ayarlarını daha agresif hâle getirmek gerekebilir. Bu değer %30'un çok üstündeyse, şişmeyi tamamen gidermek daha iyi olabilir
- Günümüzde bunun için desteklenen tek yöntem, tabloyu esasen yeniden oluşturan
VACUUM FULL komutunu kullanmaktır. Bu, tüm canlı satırları yeni bir konuma taşır ve eski şişmiş kopyayı atar
- Bu süreç çalıştığı süre boyunca özel erişim kilidi atar; bu yüzden neredeyse her durumda bir tür kesinti gerekir
- Buna alternatif olarak, Tembo'nun desteklediği
pg_repack uzantısı vardır
- Bu komut satırı aracı, özel kilit olmadan tamamen çevrimiçi şekilde şişmeyi gidermek için tabloları yeniden düzenleyebilir
- Araç, Postgres çekirdeğinin dışında yer aldığı ve tablo ile indeks depolamasını değiştirdiği için genellikle ileri seviye kullanım olarak değerlendirilir
- Kullanmadan önce üretim dışı bir ortamda yeterli test yapılması önerilir
- Sayfa başına satır sayısını en üst düzeye çıkarmak için sütun sırasını yeniden düzenleyerek bir tür sütun tetrisi yapmak da mümkündür
- Bu muhtemelen oldukça ileri seviye bir optimizasyon sayılır; ancak tabloları bu şekilde yeniden kurma esnekliğine sahip ortamlarda uygulanabilir bir stratejidir
The Balancing Act
- Tüm bu parametreleri ve kaynakları doğru biçimde yapılandırmak hem sanat hem bilimdir
- Paylaşılan tamponların gerçek kullanımını nasıl ölçeceğimizi ve çalışma belleğinin çok düşük olup olmadığını nasıl anlayacağımızı gördük
- Ancak çoğu durumda olduğu gibi mevcut donanım veya bütçe sınırlıysa ne olacak? İşte "sanat" gerektiren kısım burasıdır
- Belleğin kısıtlı olduğu durumlarda, daha fazla
work_mem için yer açmak amacıyla shared_buffers biraz azaltılabilir. Hatta ikisini birden azaltmak gerekebilir
- Uygulamanın çok sayıda oturuma ihtiyaç duyması durumunda, eşzamanlı oturumların geniş RAM tahsisleri biriktirmesini önlemek için
work_mem değerini düşürmek veya bağlantı havuzu kullanmak daha mantıklı olabilir
- Geçmişte her şey için yeterli RAM olduğu varsayımıyla
maintenance_work_mem artırıldıysa, bunu azaltmak daha mantıklı olabilir. Düşünülmesi gereken çok şey var
- Düşük bellekli instance'larda yukarıdaki öneriler bile yeterli olmayabilir. Bu durumlarda bellek kullanımını en üst düzeye çıkarıp kaynak tükenmesini önlemek için şu işlem sırasını izlemek yararlı olur:
- Bir bağlantı havuzlayıcı ekleyin ve
max_connections değerini düşürün. Bu, azami kaynak tüketimini azaltmanın en hızlı ve kolay yoludur
pg_stat_statements tarafından raporlanan en sık sorgularda EXPLAIN kullanarak ortalamayı değil sorguların azami node sayısını bulun. Ardından work_mem değerini (toplam RAM'in %80'i - shared_buffers) / (max_connections * azami plan node sayısı) değerinin altında olacak şekilde ayarlayın
maintenance_work_mem ve autovacuum_work_mem değerlerini varsayılan 64MB'a geri çekin. Bakım işleri fazla yavaşsa ve daha fazla RAM kullanılabiliyorsa 8MB'lık adımlarla artırmayı değerlendirin
pg_buffercache uzantısını kullanarak shared_buffers içinde tutulan tablo miktarını inceleyin. Her tablo ve indeksi dikkatle gözden geçirip veri arşivleme, sorguları daha az bilgi kullanacak şekilde değiştirme gibi yöntemlerle bunu azaltmanın yolları olup olmadığını değerlendirin. Bu, etkin şekilde şişmiş tablolarda kullanılan sayfaları sıkıştırmak için VACUUM FULL veya pg_repack kullanımını da içerebilir
- Eğer
pg_buffercache, shared_buffers alanının dolu olduğunu ve etkin sayfaları çıkarmadan daha fazla küçültülemeyeceğini gösteriyorsa, en etkin sayfaları önceliklendirmek için usagecount sütununu kullanın. Bu sütunun değeri 1-5 arasındadır; dolayısıyla 3-5 kez kullanılmış sayfalara odaklanarak shared_buffers değerini performansa büyük etki etmeden azaltabilirsiniz
- Son olarak daha güçlü donanım sağlayın. Veritabanı mevcut iş yükü için gerçekten daha fazla RAM'e ihtiyaç duyuyorsa ve yukarıdaki parametreleri azaltmak sistem performansını fazla bozacaksa, genellikle yükseltme yapmak daha mantıklıdır
Henüz yorum yok.