· kaynak dev.to (home feed)
Postgres'te UPDATE aslında bir insert artı bir delete'tir: MVCC ve tablo şişmesi nasıl çalışır
Dev.to'da yayımlanan bir anlatım, PostgreSQL'in satırları neden hiçbir zaman yerinde güncellemediğini, xmin ve xmax takibinin MVCC'yi nasıl çalıştırdığını ve bu tasarımın her ikincil indexe nasıl bir maliyet yüklediğini açıklıyor.
Eski satıra hiç dokunmayan bir UPDATE
Bir veritabanı güncellemesine dair sezgisel zihinsel model — satırı bul, sütuna git, baytların üzerine yaz — PostgreSQL'in çalışma şekli değil. Dev.to'da yayımlanan ayrıntılı bir anlatıma göre Postgres bir satırı asla yerinde değiştirmez. Bunun yerine her UPDATE iki işleme bölünür: satırın yeni bir fiziksel kopyası sanki bir INSERT'miş gibi yazılır ve orijinal satır diskte olduğu gibi bırakılır ama süresi dolmuş olarak işaretlenir; bu da bir tür soft delete'e denk düşer.
Yazar bunu somut bir örnekle izliyor. 100 numaralı transaction'ın eklediği bir satır xmin 100 ve xmax 0 olacak şekilde (0,1) fiziksel adresine yerleşiyor. 105 numaralı transaction daha sonra bir sütunu değiştirdiğinde Postgres (0,2) konumuna xmin 105 taşıyan tamamen yeni bir tuple yazıyor, ardından eski tuple'a damga vuruyor: xmax'ı 105 oluyor ve t_ctid alanı (0,2)'deki yerine ileriye işaret edecek şekilde yeniden bağlanıyor. Satırın her iki sürümü artık aynı anda aynı disk sayfasında duruyor.
8KB'lık bir sayfanın içinde ne var
Anlatım mekanizmayı depolama düzenine dayandırıyor. Tablolar sayfa adı verilen sabit 8KB'lık bloklara bölünür. Her sayfa 24 baytlık bir başlık, üstten aşağı büyüyen 4 baytlık satır işaretçileri dizisi ve alttan yukarıya büyüyen asıl tuple verisini taşır; aralarında boş alan kalır.
Her tuple'ın önünde, çoğu SQL kullanıcısının hiç görmediği metadata'yı taşıyan 23 baytlık bir başlık bulunur: sürümü oluşturan transaction ID'si olan t_xmin; onu silen veya yerine geçiren transaction ID'si olan t_xmax (hâlâ canlıyken sıfırdır); tuple'ın kendi fiziksel konumu ya da daha yeni bir sürüme işaret eden ileri işaretçisi olan t_ctid; ve commit durumunu ile Heap-Only Tuple özelliklerini kaydeden infomask bit bayrakları.
Okuma kilitleri yerine snapshot'lar
Bu görünüşte savurgan tasarımın kazandırdığı şey, kilitleme olmadan eşzamanlılıktır. 105 numaralı transaction'dan önce başlamış uzun süreli bir transaction — makale rapor çalıştıran 102 numaralı transaction'ı kullanıyor — eski tuple'ı değerlendirir ve xmin'in transaction'ın snapshot'ı başlamadan önce commit edildiğini, buna karşılık 105'in xmax'ının kendisine henüz görünmediğini görür. Dolayısıyla eski sürümü satır üzerinde hiçbir paylaşımlı kilit almadan temizçe okur.
105 commit ettikten sonra başlayan bir transaction ise tersini görür: eski tuple'ın xmax'ı commit edilmiş bir transaction'a aittir, yani o sürüm ölüdür. İleri işaretçiyi yeni tuple'a kadar izler ve güncellenmiş veriyi okur. Geri almalar da aynı ucuzlukla ele alınır. 105 numaralı transaction geri alınmış olsaydı, Postgres sadece abort'u pg_xact commit kaydına yazardı; bu da yeni kopyayı anında görünmez, eski kopyayı tekrar canlı yapardı — uygulanacak bir undo log yoktur.
İkincil index sorunu
Bu tasarımın maliyeti indexlemede ortaya çıkıyor. İkincil indexlerin primary key'e referans verdiği MySQL'in InnoDB'sinin aksine Postgres indexleri heap tuple'larına doğrudan fiziksel işaretçiler (ctid) saklar. Makale beş indexli bir tablo çiziyor; güncellemeler naif biçimde her yeni satır sürümü için index girdileri oluştursaydı, tek bir sütunu değiştirmek — indexlenmiş değerlerden dördü hiç değişmemiş olsa bile — beş B-Tree'ye de yazma gerektirirdi. Makale uyarıyor: büyük ve yazma yoğun bir tabloda bu, şişen index yazmaları, B-Tree sayfa bölünmeleri ve ağır disk I/O'su demektir.
Heap-Only Tuple'ların (HOT) köreltmek için tasarlandığı sorun tam da budur: bir güncellemenin indexlere dokunması gerekmediğinde Postgres'in bundan nasıl kaçındığı, anlatımın adım adım yaklaşığı mekanizmadır.
Neden önemli
Bir UPDATE'in fiziksel olarak bir insert artı bir süre dolması olduğunu anlamak, Postgres'in birçok günlük davranışını açıklar. Ölü satır sürümleri alan geri kazanılana kadar birikir; yazma yoğun tabloların şişmesinin ve vacuum gibi rutin bakımın önemli olmasının nedeni budur. Sık güncellenen tablolardaki indexlerin neden zamanla bozulup büyüdüğünü açıklar. Ve pratik kararları — kaç index oluşturulacağı, hangi sütunların indexleneceği, satırların ne sıklıkla yeniden yazılacağı — doğrudan fiziksel sonuçları olan kararlar olarak yeniden çerçeveler. Dev.to yazısı, Postgres'teki eşzamanlılık garantilerinin bedava olmadığının yararlı bir hatırlatıcısıdır: bunlar disk sayfalarıyla ödenir ve faturanın bu şekilde kalem kalem düzenlendiğini bilmek, performans sorunlarını teşhis etmeyi çok daha kolaylaştırır.
- #postgresql
- #databases
- #mvcc
- #performance
- #storage