SQLProgramlamaKıdemli Veri Tabanı Mühendisi

PostgreSQL mantıksal replikasyon topolojisinde, yayıncı geniş metin sütunları için dışa satır TOAST depolama alanını sıkıştırdığı zaman, abone TOAST'lı sütunlarda güncelleme çatışmalarını çözemez. Hangi özel REPLICA IDENTITY yapılandırması koşulunda bu olur ve REPLICA IDENTITY FULL'a geçmek, esas olarak JSONB yüklerine sahip tablolarda WAL trafiği hacmini nasıl değiştirir?

Hintsage yapay zeka asistanı ile mülakatları geçin

Sorunun yanıtı.

TOAST, 8KB sayfa boyutunu aşan satır verilerini yönetmek için PostgreSQL'de tanıtılmıştır. Büyük sütunlar ayrı fiziksel depolama alanına sıkıştırılarak ele alınır. Mantıksal replikasyon, değişiklikleri WAL aracılığıyla aktarırken, REPLICA IDENTITY ayarı hangi eski tuple değerlerinin dahil edileceğini belirler. Varsayılan REPLICA IDENTITY DEFAULT, yalnızca birincil anahtarı gönderirken, REPLICA IDENTITY FULL eski satır görüntüsünün tamamını gönderir.

Bir tablo JSONB veya TEXT sütunları içeriyorsa ve bu sütunlar yaklaşık 2KB'yi aşıyorsa ve TOAST'a sıkıştırılmışsa, yalnızca TOAST olmayan sütunları değiştiren UPDATE işlemleri, WAL kaydı için dışarıdaki TOAST değerlerini talep etmeyebilir. Mantıksal çözümleme süreci, I/O'yu azaltmak için değişmemiş TOAST işaretçilerini atlayarak, abonenin bu büyük alanlar için NULL veya eksik değerler almasına neden olur.

REPLICA IDENTITY FULL'a geçiş, PostgreSQL'nin, taahhüt sırasında dış depolamadan tüm TOAST edilmiş değerleri açıkça alarak WAL kaydına eski tüm tuple'ı dahil etmesini zorlar. Bu, UPSERT işlemleri için veri tamlığını garanti ederken, geniş JSONB tabloları için WAL hacmini önemli ölçüde artırır—genellikle %300-500 oranında—çünkü her UPDATE, satırın tam ön görüntüsünü kaydetmelidir.

Hayattan bir durum

Bir finansal ticaret platformu, birincil PostgreSQL 15 kümesinden bir veri ambarına düzenleyici raporlama için emir defteri anlık görüntülerini çoğaltması gerekti. market_data tablosu, derinlik bilgilerini içeren büyük JSONB yükleri (10-50KB) ve enstrüman tanımlayıcılarını saklıyordu. Replikasyon, yalnızca birincil anahtar ile REPLICA IDENTITY DEFAULT kullanan pglogical ile gerçekleştirildi. Veri ambarı tarafındaki ETL işlemi, denetim kaydı için delta değişikliklerini hesaplamak üzere eski JSONB değerlerini gerektiriyordu.

Yüksek hacimli ticaret dönemlerinde, emir defteri sık güncellenirken ancak JSONB yükü değişmeden kalırken, mantıksal replikasyon akışı, yalnızca birincil anahtarı ve yeni tuple verilerini içeren UPDATE kayıtları gönderdi. TOAST'lı JSONB eski değerleri, UPDATE talimatı yalnızca updated_at zaman damgası sütununu etkilediği için değişiklik setine dahil edilmedi. ETL işlemi, güncellemeler öncesi JSONB durumuna erişemediğinden, denetim kaydı için kesin fiyat hareketi delta hesaplamaları yapılması imkansız hale geldi, bu da MiFID II uyumluluk gerekliliklerini ihlal etti.

Çözüm 1: REPLICA IDENTITY FULL'a geçiş Bu yaklaşım, yayıcının her UPDATE için eski satır görüntüsünü, TOAST depolama alanından tüm JSONB içeriği dahil ederek WAL'ye yazmasını zorunlu kılardı. Artıları veri tamlığının garanti edilmesi ve hiçbir şema değişikliği gerektirmeden basit bir uygulama olmasını içeriyordu. Ancak, eksileri ciddi idi: 50KB yükler göz önüne alındığında WAL üretimi yaklaşık %400 artacak, bu da birincil veri alanında disk alanı taşmasını ve veri ambarına artan ağ gecikmesini riske atıyordu. Saniyede 10.000 güncelleme işlemekte olan bir tablo için, bu üretim kararlılığı açısından çok riskli bulunuyordu.

Çözüm 2: Uygulama düzeyinde günlükleme ile ayrı bir geçmiş tablosu Ekip, güncellemeden önce eski JSONB değerlerini ayrı bir market_data_history tablosuna kopyalayan bir tetikleyici oluşturmayı düşündü. Artıları, mantıksal replikasyonun bu geçmiş tablosunu ayrı olarak çoğaltabilmesi ve ana tablodaki TOAST eksikliğini önleyebilmesiydi. Ancak, eksileri ana için çift yazma yükü (işlem gecikmesini artırdı), 2 kat hızda büyüyen ek depolama gereksinimleri ve geçmiş kayıtlarının ana tablo değişiklikleriyle ilişkisini sağlamak için işlem kimlikleri ve zaman damgalarını kullanarak ETL mantığında karmaşıklık içeriyordu.

Çözüm 3: JSONB hash dahil bir kaplama indeksi ile REPLICA IDENTITY kullanımı Bu strateji, md5(jsonb_column::text) üzerinde işlevsel bir indeks oluşturmayı ve bu hash'i bir birleşik REPLICA IDENTITY indeksine dahil etmeyi içeriyordu. Artıları, WAL içinde tam yükü göndermeden JSONB içeriğindeki değişiklikleri hash değişimi yoluyla tespit edebilmesiydi. Ancak, eksileri, tam eski değeri (sadece hash'ini) almanın imkansızlığıydı; bu, denetim gerekliliği için tam önceden güncelleme durumunu gösterme açısından yetersizdi ve yüksek döngüsel tablolarda indeks bakım yükü oluşturuyordu.

Ekip, Çözüm 2 (Uygulama düzeyinde günlükleme)'yi, ancak bir değişiklikle seçti. PostgreSQL'in 14+ sürümündeki JSONB kısmi güncelleme optimizasyonunu kullanarak, tam eski satır yerine yalnızca değişen yolları (fark) saklayan bir BEFORE UPDATE tetikleyici uyguladılar. Bu, geçmiş tablosunun büyümesini azalttı ve gerekli tüm ön görüntü verilerinin mevcut olduğundan emin oldu. WAL şişmesini önlemek için ana tabloda REPLICA IDENTITY DEFAULT'ı korudular ve denetim rekonstrüksiyonu için geçmiş tabloyla birleştirme yönlendirmesi yaptılar.

Replikasyon akışı boyutu kararlı kaldı, birincil depolama baskısını önledi. ETL işlemi, mevcut satır durumunu geçmiş tablodan saklanan diffs ile birleştirerek tam denetim izlerini başarılı bir şekilde yeniden oluşturdu. Düzenleyici uyumluluk, yalnızca %15 artış ile (REPLICA IDENTITY FULL için %400) sağlandı ve işlem verimliliği üzerinde minimal etki yarattı.

Adayların sıklıkla kaçırdığı noktalar

Neden PostgreSQL'in mantıksal çözümlemesi, sütun değişse bile TOAST değerlerini atlar?

Pek çok aday, herhangi bir UPDATE'in otomatik olarak tüm TOAST edilmiş değerleri WAL için alacağını varsayıyor. Ancak, PostgreSQL yalnızca yürütücü, veriyi değiştirmek için gerçekten okuduğunda "tuple unTOASTing" işlemi gerçekleştirir. Bir UPDATE, bir başka sütunu değiştirirse (örneğin, SET updated_at = NOW()) ve JSONB sütununu hedef listesinde veya WHERE koşulunda atıfta bulunmazsa, TOAST işaretçisi değişmeden kalır ve dış depolama erişilmez. Bu nedenle, WAL kaydı yalnızca disk üzerindeki tuple ile işaretçisini içerir, gerçek veriyi değil. Mantıksal çözümleme, geçmiş sürümler için yığın veya TOAST tablolarına erişmeden WAL'den tuple'ları yeniden oluşturduğundan, atlanan değer değişiklik akışında NULL olarak görünür.

REPLICA IDENTITY FULL, HOT (Yalnızca Yığın Tuple) güncellemeleri ile nasıl etkileşir?

Adaylar genellikle REPLICA IDENTITY FULL'ın bir tablo için HOT güncellemelerini devre dışı bıraktığını gözden kaçırır. HOT güncellemeleri, PostgreSQL'in, bir indeksli sütun değişmediği sürece, veri sayfasındaki satır sürümlerini birbirine bağlamasına izin verir. REPLICA IDENTITY FULL etkin olduğunda, her UPDATE, replikasyon için eski satır görüntüsünü tam olarak kaydetmelidir; bu da sistemin satırı tam içeriğiyle benzersiz bir şekilde tanımlamasını gerektirir. Bu, mantıksal replikasyonun tam tuple karşılaştırma verilerine ihtiyaç duyması nedeniyle HOT optimizasyonunu kırar ve böylece değişmeyen sütunları değiştirirken her satır sürümü için indeks güncellemelerini zorunlu kılar. Sonuç olarak, bu ayarla tablolar daha yüksek indeks şişmesi ve artan I/O yaşar; bu, yüksek döngüsel tablolarda kritik bir ticarettir.

TOAST sıkıştırması ile PostgreSQL'in WAL sıkıştırması arasındaki fark nedir ve mantıksal replikasyon sırasında nasıl etkileşirler?

Bu soru, derin sistem bilgisi ile yüzeysel anlayış arasındaki ayrımı ortaya çıkarır. TOAST sıkıştırması, büyük sütunlar dış tablolar içinde saklanmadan önce LZ4 veya PGLZ kullanarak satır boyutunu azaltır. WAL sıkıştırması (wal_compression=lz4 ile etkinleştirildiğinde), çökme kurtarma verimliliği için WAL'ye yazılan tam sayfa görüntülerini sıkıştırır. Ancak, REPLICA IDENTITY FULL kullanıldığında, mantıksal çözümlemeye gönderilen eski tuple verileri, WAL kaydı depolama için sıkıştırılmadan önce çıkarılır. Bu nedenle, mantıksal çözücü, alınan TOAST verilerini (eğer alınmışsa) sıkıştırılmamış olarak alırken, fiziksel WAL dosyası tam sayfa görüntüsünün bir parçası olarak sıkıştırılmış olarak depolanabilir; bu da ağ bant genişliği ile disk I/O'yu farklı şekilde etkiler.