MySQL'de iyimser kilitleme nasıl doğru şekilde uygulanır


13

MySQL'de iyimser kilitleme nasıl doğru şekilde uygulanır?

Ekibimiz aşağıda # 4 yapmamız gerektiği sonucuna varmıştır, aksi takdirde başka bir iş parçacığının kaydın aynı sürümünü güncelleyebilme riski vardır, ancak bunun bunu yapmanın en iyi yolu olduğunu doğrulamak istiyoruz.

  1. Tabloda, örneğin sütun adı = "sürüm" için iyimser kilitleme kullanmak istediğiniz bir sürüm alanı oluşturun
  2. Seçimlerde, sürüm sütununu eklediğinizden ve sürümü not ettiğinizden emin olun
  3. Kayda yapılan bir sonraki güncellemede, güncelleme ifadesi "burada sürüm = X" değerini vermelidir; burada X, # 2'de aldığımız sürümdür ve bu güncelleme ifadesi sırasında sürüm alanını X + 1 olarak ayarlamalıdır
  4. SELECT FOR UPDATEGüncellemeye çalıştığımız kayıtta kimlerin değişiklik yapabileceğini serileştirebilmemiz için güncelleyeceğimiz bir kayıt üzerinde bir işlem gerçekleştirin .

Açıklığa kavuşturmak için, aynı zaman penceresinde aynı kaydı seçen iki iş parçacığının, kaydı aynı anda denemeye ve güncellemeye çalışırlarsa, kaydın aynı sürümünü birbirlerinin üzerine yazmasını engellemeye çalışıyoruz. # 4 yapmazsak, her iki iş parçacığı da aynı anda kendi işlemlerini girerse (ancak henüz güncellemelerini yayınlamamışsa), güncellemeye gittiklerinde, GÜNCELLEME'yi kullanacak ikinci iş parçacığının ... burada sürüm = X eski veriler üzerinde çalışacaktır.

Sürüm alanları / iyimser kilitleme kullansak da güncelleme yaparken bu kötümser kilitlemeyi yapmamız gerektiğini düşünüyor muyuz?


Sorun ne? Sürüm numarasını UPDATE ürününüzle artırırsınız, ikinci sürüm UPDATE başarısız olur çünkü sürüm numarası okunduğu zamankiyle aynı değildir.
12:12

Emin misin? İşlem yalıtım düzeyini, diğer iş parçacıklarının güncellemesini göreceğiniz belirli bir ayara ayarlamadığınız sürece belirsizdir. Her ikisini de aynı anda girerseniz, ikinci iş parçacığı güncellemeyi yapmaya gittiğinde OLD verilerini çok iyi görebilir. MySQL, Oracle'ın söylediği kadar ACID arenasında sağlam değildir, bu nedenle kirli okumaları / güncellemeleri önleyecek MySQL'de iyimser kilitleme uygulamak için en iyi uygulama yolunu arar.
BestPractices

Ama sonra işlem taahhütte yine de başarısız olacak, değil mi?
AndreKR

Endikasyonları, bu durumla başa çıkmak için güncelleme için bir seçim yapmak isteyeceği yönündedir
BestPractices

@BestPractices ihtiyacınız ya SELECT ... FOR UPDATE ikisi değil, sürüm sıra veya iyimser kilitleme. Yanıttaki ayrıntılara bakın.
Craig Ringer

Yanıtlar:


17

Geliştiriciniz yanlış. Sen gerek ya SELECT ... FOR UPDATE ya satır, ikisini birden sürüm.

Deneyin ve görün. Açık üç MySQL oturumları (A), (B)ve (C)aynı veritabanına.

In (C)konuyla:

CREATE TABLE test(
    id integer PRIMARY KEY,
    data varchar(255) not null,
    version integer not null
);
INSERT INTO test(id,data,version) VALUES (1,'fred',0);
BEGIN;
LOCK TABLES test WRITE;

İkisinde de (A)ve (B)sorunu bir UPDATEo değişen testleri ve setleri satır sürümü, winnerhangi hangi oturumu görebilmeniz için, her metni:

-- In (A):

BEGIN;
UPDATE test SET data = 'winnerA',
            version = version + 1
WHERE id = 1 AND version = 0;

-- in (B):

BEGIN;
UPDATE test SET data = 'winnerB',
            version = version + 1
WHERE id = 1 AND version = 0;

Şimdi de (C), UNLOCK TABLES;Kilidi açmak için.

(A)ve (B)satır kilidi için yarışacak. Bunlardan biri kazanacak ve kilidi alacak. Diğeri kilitte bloke olur. Kilidi alan kazanan sırayı değiştirmeye devam edecektir. (A)Kazanan varsayalım , artık değiştirilmiş satırı (hala taahhüt edilmediğinden diğer işlemler tarafından görülemez) bir ile görebilirsiniz SELECT * FROM test WHERE id = 1.

Şimdi COMMITkazanan oturumda, diyelim (A).

(B)kilidi alır ve güncellemeye devam eder. Ancak, sürüm artık eşleşmediğinden, satır sayısı sonucu tarafından bildirildiği gibi hiçbir satırı değiştirmeyecektir. Sadece birinin UPDATEherhangi bir etkisi vardı ve istemci uygulaması hangisinin UPDATEbaşarılı ve hangisinin başarısız olduğunu açıkça görebiliyor . Başka kilitleme gerekmez.

Burada pastebin'deki oturum günlüklerine bakın . Kullandığım mysql --prompt="A> "oturumları arasındaki farkı söylemek kolay yapmak vb. Çıktıyı zaman serisine eklenmiş olarak kopyaladım ve yapıştırdım, bu yüzden tamamen ham çıktı değil ve kopyalayıp yapıştırarak hatalar yapabilirdim. Görmek için kendiniz test edin.


Eğer olsaydı değil bir satır sürüm alanını eklendi, daha sonra size gerekir SELECT ... FOR UPDATEgüvenilir bir sipariş sağlamak mümkün.

Bu konuda düşünüyorsanız, bir SELECT ... FOR UPDATEolup tamamen gereksiz hemen bir yapıyorsan UPDATEgelen yeniden kullanarak verileri olmadan SELECTsize sürüm satır kullanıyorsanız veya. UPDATEZaten bir kilit alacak. Birisi okuma ve sonraki yazma arasındaki satırı güncellerse, sürümünüz artık eşleşmez, bu nedenle güncellemeniz başarısız olur. İyimser kilitleme böyle çalışır.

Amacı SELECT ... FOR UPDATE:

  • Kilitlenmeleri önlemek için kilit sırasını yönetmek; ve
  • Bir satırdaki verileri okumak istediğinizde bir satır kilidinin süresini uzatmak için, uygulamada değiştirin ve SERIALIZABLEyalıtım veya satır sürümü kullanmadan orijinaline dayanan yeni bir satır yazın .

Hem iyimser kilitleme (sıra sürümlendirme) hem de kullanmanız gerekmez SELECT ... FOR UPDATE. Birini veya diğerini kullanın.


Teşekkürler Craig. Haklıydınız, geliştirici yanılmıştı. Bu testi yaptığınız için teşekkürler.
BestPractices

SQL sunucusu ne olacak? İşlem yalıtım düzeyinden bağımsız olarak güncellenen satırda her zaman bir kilit var mı?
plalx

@plalx Peki, belgeler ne diyor? Bunun gibi interaktif bir test yaparsanız ne olur?
Craig Ringer

@CraigRinger, B A işleminden önce ancak A güncellemesinden sonra kilidi alırsa ne olur?
MengT

1
@MengT Yapamaz, bu yüzden bir kilit.
Craig Ringer

0
UPDATE tbl SET owner = $me,
               id = LAST_INSERT_ID(id)
    WHERE owner = ''
    LIMIT 1;
$id = SELECT LAST_INSERT_ID();
Do some stuff (arbitrarily long time)...;
UPDATE  tbl SET owner = '' WHERE id = $id;

Kilit gerekmez (tablo değil, işlem değil) veya hatta istenir:

  • GÜNCELLEME atomiktir
  • LAST_INSERT_ID () oturuma özgüdür, dolayısıyla iş parçacığı için güvenlidir.
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.