MySQL: İşlemler ve Kilitleme Tabloları


110

Veritabanı bütünlüğünü sağlamak ve bir SELECT ve UPDATE'in senkronize olduğundan ve başka hiçbir bağlantının buna müdahale etmediğinden emin olmak için işlemlerle kilitleme tabloları arasında biraz kafam karıştı. Yapmam gerek:

SELECT * FROM table WHERE (...) LIMIT 1

if (condition passes) {
   // Update row I got from the select 
   UPDATE table SET column = "value" WHERE (...)

   ... other logic (including INSERT some data) ...
}

Başka hiçbir sorgunun karışmayacağından ve aynı şeyi gerçekleştirmediğinden emin olmam gerekiyor SELECT(bu bağlantı satırı güncellemeyi bitirmeden önce 'eski değeri' okumak.

Bunu LOCK TABLES tablebir seferde yalnızca 1 bağlantının yaptığından emin olmak için varsayılan olarak yapabileceğimi ve işim bittiğinde kilidini açabileceğimi biliyorum , ancak bu abartılı gibi görünüyor. Bunu bir işlemde sarmalamak aynı şeyi yapar mı (başka bir bağlantının başka bir bağlantı hala işlenirken aynı işlemi denememesini sağlamak)? Ya da bir olur SELECT ... FOR UPDATEveya SELECT ... LOCK IN SHARE MODEdaha iyi?

Yanıtlar:


173

Tabloları kilitlemek, diğer DB kullanıcılarının kilitlediğiniz satırları / tabloları etkilemesini engeller. Ancak kilitler kendi içlerinde mantığınızın tutarlı bir durumda ortaya çıkmasını SAĞLAMAZ.

Bir bankacılık sistemi düşünün. Çevrimiçi olarak bir fatura ödediğinizde, işlemden etkilenen en az iki hesap vardır: Paranın çekildiği hesabınız. Ve paranın transfer edildiği alıcının hesabı. Ve işlem için tahsil edilen tüm hizmet ücretlerini mutlu bir şekilde yatıracakları bankanın hesabı. Bankaların olağanüstü derecede aptal olduğu göz önüne alındığında (bugünlerde herkesin bildiği gibi), sistemlerinin şu şekilde çalıştığını varsayalım:

$balance = "GET BALANCE FROM your ACCOUNT";
if ($balance < $amount_being_paid) {
    charge_huge_overdraft_fees();
}
$balance = $balance - $amount_being paid;
UPDATE your ACCOUNT SET BALANCE = $balance;

$balance = "GET BALANCE FROM receiver ACCOUNT"
charge_insane_transaction_fee();
$balance = $balance + $amount_being_paid
UPDATE receiver ACCOUNT SET BALANCE = $balance

Artık kilit ve işlem olmadan, bu sistem çeşitli yarış koşullarına karşı savunmasızdır; bunlardan en büyüğü, hesabınızda veya alıcının hesabında paralel olarak gerçekleştirilen birden çok ödemedir. Kodunuz bakiyeniz alınmış olsa da, huge_overdraft_fees () ve ne yapmıyor olsa da, başka bir ödemenin aynı tür kodu paralel olarak çalıştırması tamamen mümkündür. Bakiyenizi alacaklar (örneğin, 100 $), işlemlerini yapacaklar (ödediğiniz 20 $ 'ı ve sizi mahvettikleri 30 $' ı çıkaracaklar) ve şimdi her iki kod yolunun iki farklı bakiyesi var: 80 $ ve 70 $. Hangisinin en son bitirdiğine bağlı olarak, hesabınızda bu iki bakiyeden birini elde edersiniz, kazanmanız gereken 50 $ yerine (100 $ - 20 $ - 30 $). Bu durumda, "lehinize banka hatası"

Şimdi, kilit kullandığınızı varsayalım. Fatura ödemeniz (20 $) önce boruyu vurur, böylece kazanır ve hesap kaydınızı kilitler. Artık özel kullanımınız var ve 20 $ 'ı bakiyeden düşebilir ve yeni bakiyeyi huzur içinde yazabilirsiniz ... ve hesabınız beklendiği gibi 80 $ ile biter. Ama ... uhoh ... Alıcının hesabını güncellemeye çalışıyorsun ve o kilitlendi ve kodun izin verdiğinden daha uzun süre kilitlendi, işleminiz zaman aşımına uğradı ... Aptal bankalarla uğraşıyoruz, bu yüzden uygun hata yapmak yerine işlem, kod sadece bir çeker exit()ve 20 dolarınız bir elektron dalgasına dönüşür. Şimdi 20 doların tükendi ve alıcıya hala 20 dolar borçlusun ve telefonunuz yeniden ele geçirildi.

Yani ... işlemleri girin. Bir işleme başlıyorsunuz, hesabınızdan 20 $ borçlandırıyorsunuz, alıcıya 20 $ yatırmaya çalışıyorsunuz ... ve bir şeyler tekrar patlıyor. Ancak bu sefer, exit()kod sadece yapabilir rollbackve puf yerine, 20 dolarınız sihirli bir şekilde hesabınıza geri eklenir.

Sonunda, şuna indirgeniyor:

Kilitler, uğraştığınız herhangi bir veritabanı kaydına başkalarının karışmasını engeller. İşlemler, herhangi bir "sonraki" hatanın yaptığınız "önceki" şeylere müdahale etmesini önler. Hiçbiri sonunda her şeyin yolunda gideceğini garanti edemez. Ama birlikte yaparlar.

yarının dersinde: The Joy of Deadlocks.


4
Ben de / hala kafam karıştı. Diyelim ki alıcı hesabın başlangıç ​​için içinde 100 dolar var ve hesabımızdan 20 dolarlık fatura ödemesini ekliyoruz. İşlemlerle ilgili anladığım kadarıyla, herhangi bir işlem içi işlem, veri tabanını işlemin başlangıcındaki durumda görür. yani: biz değiştirene kadar, alıcı hesabın 100 doları vardır. Yani ... 20 $ eklediğimizde aslında 120 $ 'lık bir denge belirleriz. Fakat işlemimiz sırasında birisi alıcı hesabını 0 $ 'a düşürürse ne olur? Bu bir şekilde engellendi mi? Yine sihirli bir şekilde 120 dolar mı alıyorlar? Bu yüzden mi kilitlere ihtiyaç var?
Russ

Evet, kilitlerin devreye girdiği yer burasıdır. Uygun bir sistem kaydı yazma kilitleyecektir, böylece işlem devam ederken başka hiç kimse kaydı güncelleyemez. Paranoyak bir sistem, kayda koşulsuz bir kilit koyar, böylece hiç kimse "eski" dengeyi okuyamaz.
Marc B

1
Temel olarak işlemlere kod yolunuzdaki şeyleri güvence altına almak olarak bakın. Güvenli şeyleri "paralel" kod yollarında kilitler. Çıkmazlar ortaya çıkana kadar ...
Marc B

1
@MarcB, Öyleyse, yalnızca işlemleri kullanmak zaten kilitlerin yerinde olduğunu garanti ediyorsa neden açıkça kilitleme yapmak zorundayız? İşlemler tek başına yetersiz olduğu için açıkça kilitleme yapmamız gereken bir durum olacak mı?
Pacerier

2
Bu cevap doğru değildir ve yanlış sonuçlara yol açabilir. Bu ifade: "Kilitler, uğraştığınız herhangi bir veritabanı kaydına başkalarının müdahale etmesini önler. İşlemler," sonraki "hataların, yaptığınız" önceki "şeylere müdahale etmesini önler. Tek başına, işlerin iyi sonuç vereceğini garanti edemez. sonunda. Ama birlikte yaparlar. " - kovulmanıza neden olur, bu son derece yanlış ve aptalca Makalelere bakın: en.wikipedia.org/wiki/ACID , en.wikipedia.org/wiki/Isolation_(database_systems) ve dev.mysql.com/doc/refman/5.1/ tr /…
Nikola Svitlica

14

Bir istemek SELECT ... FOR UPDATEya SELECT ... LOCK IN SHARE MODEdediğin gibi bir hareket içinde, normalde SELECTleri beri, bir işlemde olup olmadığını olursa olsun, bir tablo kilitlenmez. Hangisini seçeceğiniz, işleminiz devam ederken diğer işlemlerin o satırı okuyup okumasını isteyip istemediğinize bağlıdır.

http://dev.mysql.com/doc/refman/5.0/en/innodb-locking-reads.html

START TRANSACTION WITH CONSISTENT SNAPSHOTbaşka işlemler gelip bu satırı değiştirebileceğinden, sizin için hile yapmayacaktır. Bu, aşağıdaki bağlantının hemen üstünde belirtilmiştir.

Diğer oturumlar aynı anda aynı tabloyu güncellerse [...] tabloyu veritabanında hiç var olmayan bir durumda görebilirsiniz.

http://dev.mysql.com/doc/refman/5.0/en/innodb-consistent-read.html


7

İşlem kavramları ve kilitler farklıdır. Ancak işlem, ACID ilkelerini takip etmesine yardımcı olmak için kilitler kullandı. Siz okuma / yazma sırasında aynı anda masaya başkalarının da okumasını / yazmasını engellemek istiyorsanız, bunu yapmak için bir kilide ihtiyacınız var. Veri bütünlüğünden ve tutarlılığından emin olmak istiyorsanız, işlemleri daha iyi kullanmışsınızdır. Kilitlerle yapılan işlemlerde karışık izolasyon seviyeleri kavramlarını düşünüyorum. Lütfen işlemlerin izolasyon seviyelerini araştırın, SERİLEŞTİRİN istediğiniz seviye olmalıdır.


Bu doğru cevap olmalı. Kilitleme, yarış koşullarını önlemek içindir ve işlemler, bağımlı verilerle birden çok tabloyu güncellemek içindir. Bu işlemlere rağmen tamamen farklı iki kavram kilit kullanır.
Blue Water

6

Birden çok iş parçacığı aynı tabloyu güncellerken bir yarış durumuna neden olan a'yı denerken IF NOT EXISTS ...ve ardından bir gerçekleştirirken benzer bir sorun yaşadım INSERT.

Sorunun çözümünü burada buldum: Standart SQL'de sorgular MEVCUT DEĞİLSE INSERT yazılır

Bunun sorunuzu doğrudan yanıtlamadığının farkındayım, ancak aynı kontrol ve tek bir ifade olarak ekleme ilkesi çok yararlıdır; güncellemenizi gerçekleştirmek için bunu değiştirebilmelisiniz.


2

Kilitle ve işlemle kafanız karıştı. RMDB'de iki farklı şey var. Kilit, işlem veri izolasyonuna odaklanırken eşzamanlı işlemleri önler. Açıklama ve bazı zarif çözümler için bu harika makaleye göz atın .


1
Kilitler, başkalarının üzerinde çalıştığınız kayıtlara müdahale etmesini engeller, kısa ve öz bir şekilde yaptıklarını açıklar ve işlemler, daha önceki hataların (başkalarının paralel olarak değişiklik yapmaları) yaptığınız önceki şeylere müdahale etmesini önler (birisinin bir şey yapması durumunda geri dönmeye izin vererek) paralel olarak) hemen hemen işlemleri özetliyor ... bu konuları kavrayışında ne kafa karıştırıyor?
steviesama

1

Bir

START TRANSACTION WITH CONSISTENT SNAPSHOT;

başlamak için ve a

COMMIT;

ile bitmek için.

Depolama motorunuz işlemleri destekliyorsa , arada yaptığınız her şey veritabanınızın diğer kullanıcılarından izole edilir (bu InnoDB'dir).


1
Seçtiği tablo dışında, onu özel olarak kilitlemediği sürece (veya GÜNCELLEME gerçekleşene kadar) diğer oturumlara kilitlenmeyecektir; bu, diğer oturumların gelip onu SEÇME ile GÜNCELLEME arasında değiştirebileceği anlamına gelir.
Alison R.

MySQL belgelerinde CONSISTENT SNAPSHOT İLE START TRANSACTION'ı okuduktan sonra, aynı satırı güncellemekten başka bir bağlantıyı gerçekte nerede kilitlediğini göremiyorum. Anladığım kadarıyla, tablonun işlemin başında başladığını görmesi. Dolayısıyla, devam eden başka bir işlem varsa, zaten bir satır almışsa ve onu güncellemek üzereyse, 2. işlem, güncellenmeden önceki satırı görmeye devam edecektir. Bu nedenle potansiyel olarak diğer işlemin yapmak üzere olduğu aynı satırı güncellemeye çalışabilir. Bu doğru mu yoksa devam eden bir şeyi mi kaçırıyorum?
Ryan

1
@Ryan Herhangi bir kilitleme yapmaz; haklısın. Kilitleme (veya değil), yaptığınız işlemlerin türüne göre belirlenir (SEÇ / GÜNCELLEME / SİL).
Alison R.

4
Anlıyorum. Kendi işlem okuma tutarlılığınızı sağlar, ancak diğer kullanıcıların sizden hemen önce bir satırı değiştirmesini engellemez.
Martin Schapendonk
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.