Aynı değerde bir satırın güncellenmesi gerçekten satırın güncellenmesini sağlıyor mu?


28

Performansla ilgili bir sorum var. Diyelim ki Michael adında bir kullanıcı var. Aşağıdaki sorguyu alın:

UPDATE users
SET first_name = 'Michael'
WHERE users.id = 123

Sorgu aslında aynı değere güncellense bile güncellemeyi gerçekleştirecek mi? Eğer öyleyse, bunun olmasını nasıl önlerim?


1
Neden bir açıklama yürütüyorsunuz ve aynı anda çalıştırmamasını bekliyorsunuz?
Max Vernon,

@MaxVernon Ruby on Rails'in ORM kaydı güncellemediğinden, PostgreSQL'in de aynı şeyi yapıp yapmadığını merak ediyordum.
OneSneakyMofo

1
Ruby on Rails'in bunu yapıp yapmadığını öneririm, muhtemelen satırın bir güncellemeye ihtiyacı olup olmadığını görmek için önce bir seçim yapıyordur.
Max Vernon,

Yanıtlar:


35

Nedeniyle MVCC modelinin Postgres arasında ve SQL kurallarına göre, bir UPDATEyeni satır sürümünü yazıyor her için de geçerli satırda WHEREmaddesi.

Bu does doğrudan ve dolaylı olarak, performansı üzerinde az ya da çok önemli bir etkiye sahiptir. "Boş güncellemeler", diğer güncellemelerle aynı satır başına maliyete sahiptir. Diğer güncellemeler gibi (varsa) tetikleyicileri tetiklerler, WAL tarafından kaydedilmeleri gerekir ve masayı şişiren ve daha VACUUMsonra diğer güncellemeler gibi daha sonra daha fazla çalışmaya neden olan ölü sıralar üretirler .

Endeksler girdileri ve tost sütunları dahil sütunların hiçbiri değiştirilir olabilir aynı kalır, ama bu herhangi bir güncellenen satır için geçerlidir. İlgili:

Neredeyse her zaman bu tür güncellemeleri dışlamak iyi bir fikirdir (gerçek bir ihtimal olduğunda olabilir). Sorunuzda bir tablo tanımı yapmadınız (bu her zaman iyi bir fikirdir). first_nameNULL olabileceğini varsaymalıyız (bu bir "ilk isim" için şaşırtıcı olmaz), bu nedenle sorgunun NULL güvenli karşılaştırma kullanması gerekir :

UPDATE users
SET    first_name = 'Michael'
WHERE  id = 123
AND   first_name IS DISTINCT FROM 'Michael';

Eğer first_name IS NULLgüncellemeden önce, bir test sadece first_name <> 'Michael'NULL olarak değerlendirirsiniz ve bu güncelleme gelen satır hariç olarak. Sinsi hata. Eğer sütun olduğunu tanımlanmışNOT NULL bu biraz daha ucuz olduğu için, olsa da, basit eşitliği denetimini kullanın.

İlgili:


1
Indexes entries and TOASTed columns where none of the involved columns are changed can stay the sameAncak satırın yeni konumuna işaret edecek şekilde güncellenmeleri gerekmez mi?
dvtan

1
@ dtgq: Dizinin eski konuma işaret etmeye devam edebileceği SICAK güncellemelerde değil ve yığın getirileri, canlı bağlantıyı elde etmek için SICAK zincirini geçmelidir. Yukarıda daha fazla açıklamaya bağlantılar ekledim.
Erwin Brandstetter

1
MVCC, noop güncellemesini yeni bir demet yazmak için çağırırsa ne olur?
jberryman

@jberryman: Anladığımdan emin değilim. Her iki durumda da, lütfen sorunuzu yeni bir soru olarak sorun . Bağlam için her zaman buna bağlantı kurabilirsiniz. Ve buraya geri dönmek (ve dikkatimi çekmek) için bir yorum bırakabilirsiniz.
Erwin Brandstetter

2
@jberryman: Projenin bu şekilde ilerlediği nedenleri aslında bilmiyorum . Bu uzun zaman önce kuruldu. Ancak her sırayı eşitlik açısından kontrol etmenin ve değişmeyen satırlar için ayrı bir kod yoluna sahip olmanın gereksiz olduğunu düşünürüm. İşlem kimliklerinin ele alınması daha karmaşık olurdu - özel kasa rollback, anlık görüntü işleme, kilit yönetimi, WAL, vb. Değil
Erwin Brandstetter

4

ORM, Ruby on Rail'in Ruby teklifi gibi, bir kaydı değiştirilmiş (veya değil) olarak işaretleyen ertelenmiş bir uygulama ve daha sonra gerektiğinde veya arandığında, değişikliği veritabanına gönderin.

PostgreSQL bir ORM değil, bir veritabanıdır. Yeni bir değerin sorgunuzdaki güncellenmiş değerle aynı olup olmadığını kontrol etmek zaman aldıysa performansı düşürürdü.

Bu nedenle, yeni değerle aynı olup olmadığına bakılmaksızın değeri güncelleyecektir.

Bunu önlemek istiyorsanız, cevabında önerilen Max Vernon gibi bir kod kullanabilirsiniz.


2

Sadece wheremaddeye ekleyebilirsin :

UPDATE users
SET first_name = 'Michael'
WHERE users.id = 123
    AND (first_name <> 'Michael' OR first_name IS NULL);

Eğer first_nametanımlanırsa NOT NULL, OR first_name IS NULLparça çıkarılabilir.

Kondisyon:

(first_name <> 'Michael' OR first_name IS NULL)

ayrıca (Erwin'in cevabında) olduğu gibi daha zarif bir şekilde yazılabilir:

first_name IS DISTINCT FROM 'Michael'

Sütunun NULL olup olmadığını bilmemek, sinsi bir hataya neden olabilir.
Erwin Brandstetter 16:15

1
@ErwinBrandstetter Cevabı güncelliyordum - yorum ve cevabını gördüm!
ypercubeᵀᴹ

düzenleme, @ypercube - ve NULL@erwin
Max Vernon

1

Veri tabanı açısından

Sorunuzun cevabı evet. Güncelleme gerçekleşecek. Veri tabanı önceki değeri kontrol etmez, sadece yeni değeri belirler.

Bu bellekte gerçekleştiğinden (ve yalnızca bir taahhüt verdikten sonra veri dosyalarına yazılacağından), performans bir sorun olmaz.

ORM perspektifinden

Normalde, veritabanının tek bir sırasını temsil eden bir Nesneye sahip olacaksınız (bundan daha karmaşık olabilir, fakat basitleştirelim). Bu nesne bellekte (uygulama sunucusu düzeyinde) yönetilir ve bu nesnenin yalnızca en son işlenen sürümü aslında veritabanına belirli bir noktada erişir.

Bu farklı davranışları açıklayabilir.

Şimdi bir kargo gemisini bir 3D yazıcıyla karşılaştıramayalım. 3D yazıcıları kargo gemileri kullanarak gönderebilmeniz, aralarında herhangi bir karşılaştırma olabileceği anlamına gelmez.

Keyfini çıkarın!

Umarım bu bazı kavramları netleştirmiştir.


4
Performans olduğu ve sorunu. Her güncelleme diske (günlük ve tablo) yazılmalıdır.
ypercubeᵀᴹ

Kullandığınız gerçek RDBMS'ye bağlı olacaktır. Ancak çoğu, her güncellemeyi yapmaz, ancak bellekte sahip oldukları son taahhüt edilen bloğu taahhüt eder. Veritabanında hiçbir zaman tek bir satır okumaz veya yazmazsınız. Blokları okuyup / yazıyor ve aynı yere yeni bir blok koymak için yıkayana kadar bellekte tutuyorsunuz. Hafızadayken, bir satırdaki her değişiklik diske yazılmayacak, ancak “hafıza yazıcısı” işlemi bu hafıza bloğunu bir veri dosyasına atmak için işaret edildiğinde sadece blok içeriğine yazılacak. Öyleyse, hayır ... Uygulamanız çok uzun süre engellenmeden blokta tutulmadığı sürece sorun değil.
Silvarion,

1
Soru Postgres ile ilgili, herhangi bir keyfi DBMS ile ilgili değil. Ve güncellemelerin hepsinin birer birer yazılması gerekmemekle birlikte, veritabanına yazılan her yazı günlüğe yazılmalıdır. Kalıcı depolamada bir değişiklik yazılmazsa, DBMS sistem çökmesine nasıl dayanır?
ypercubeᵀᴹ

Evet, kontrol noktaları sırasında hafızadan ve hafızada yazıyor. Çok fazla sayıda eşzamanlı kullanıcınız yoksa, hiç sorun olmamalıdır. Günlükleri de gruplar halinde yazılır. Sanırım sunucular hakkında konuşuyoruz. 5400 RPM HDD'ye sahip bir dizüstü bilgisayarda Postgres veritabanı hakkında konuşuyorsanız, evet ... her zaman performans sorunlarınız olur. Yani, son cevap ilk olacaktı ... Çok fazla şeye bağlı.
Silvarion
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.