MySQL / InnoDB'de bir işlem için zaman sınırı belirleme


11

Bu , iki işlemin önemsiz bir durumda (her ikisinin de tek bir satırda çalıştığı) sıralı olarak gerçekleşmesini nasıl zorlayacağımı bilmek istediğim bu ilgili sorudan fırladı . Bir cevap aldım - SELECT ... FOR UPDATEher iki işlemin de ilk satırı olarak kullan - ancak bu bir soruna yol açar: İlk işlem asla gerçekleştirilmez veya geri alınmazsa, ikinci işlem süresiz olarak engellenir. innodb_lock_wait_timeoutDeğişken ikinci işlem yapmaya çalışıyorum müşteri "Maalesef, tekrar deneyin" anlattı olacaktı ... ama bildiğim kadarıyla söyleyebilirim, bir sonraki sunucu yeniden başlatma kadar tekrar çalışırdım bundan sonra saniye sayısını ayarlar. Yani:

  1. Kuşkusuz ROLLBACKbir işlem sonsuza dek sürüyorsa zorlamak için bir yol olmalı ? Bu tür işlemleri öldürmek için bir daemon kullanmaya başvurmalıyım ve eğer öyleyse, böyle bir daemon nasıl olurdu?
  2. Bir bağlantı işlem tarafından wait_timeoutveya interactive_timeoutişlem ortasında kesilirse, işlem geri alınır mı? Bunu konsoldan test etmenin bir yolu var mı?

Açıklama : innodb_lock_wait_timeoutişlemin vazgeçmeden önce kilidin serbest bırakılmasını bekleyeceği saniye sayısını ayarlar; istediğim bir kilidi serbest bırakmaya zorlamanın bir yoludur .

Güncelleme 1 : innodb_lock_wait_timeoutİkinci işlemin birincisi tarafından engellenmediğinden emin olmak için neden yeterli olmadığını gösteren basit bir örnek :

START TRANSACTION;
SELECT SLEEP(55);
COMMIT;

Varsayılan ayarı ile innodb_lock_wait_timeout = 50bu işlem 55 saniye sonra hatasız olarak tamamlanır. Ve satırdan UPDATEönce bir eklerseniz , aynı satıra SLEEPçalışan başka bir istemciden ikinci bir işlem başlatırsanız SELECT ... FOR UPDATE, uykuya dalan işlem değil, zaman aşımına uğrayan ikinci işlemdir.

Aradığım şey, bu işlemin huzurlu uykusuna son vermeye zorlamanın bir yoludur.

Güncelleme 2 : Hobodave'ın yukarıdaki örneğin ne kadar gerçekçi olduğu konusundaki endişelerine yanıt olarak, alternatif bir senaryo: Bir DBA canlı bir sunucuya bağlanıyor ve çalışıyor

START TRANSACTION
SELECT ... FOR UPDATE

burada ikinci satır uygulamanın sık sık yazdığı bir satırı kilitler. Ardından DBA kesintiye uğrar ve işlemi sonlandırmayı unutarak yürür. Uygulama, satır kilidi açılana kadar durur. Bu hatanın bir sonucu olarak uygulamanın takıldığı süreyi en aza indirmek istiyorum.


Sorunuzu, ilk sorunuzla tamamen çakışacak şekilde güncellediniz. Sen devlet kalın "ilk hareket tamamlanmış veya sonra ikinci işlem süresiz engellenecektir geri alındı asla demektir." Bunu en son güncellemenizle onaylamıyorsunuz: "zaman aşımına uğrayan ikinci işlem, uykuya dalan değil." -- ciddi anlamda?!
hobodave

İfadem daha açık olabilirdi. "Süresiz olarak engellendi" derken, ikinci işlemin "işlemi yeniden başlatmayı deneyin" şeklinde bir hata mesajıyla zaman aşımına uğrayacağını; ama bunu yapmak sonuç vermez, çünkü yine zaman aşımına uğrardı. Böylece ikinci işlem engellenir - asla tamamlanmaz, çünkü zaman aşımına uğrar.
Trevor Burnham

@Trevor: İfadeniz tamamen açıktı. Son derece kötü bir form olan tamamen farklı bir şey sormak için sorunuzu güncellediniz.
hobodave

Soru her zaman aynı olmuştur: İlk işlem asla gerçekleştirilmediğinde veya geri alınmadığında ikinci işlemin süresiz olarak engellenmesi bir sorundur . Tamamlanması birkaç saniyeden ROLLBACKfazla sürerse ilk işleme zorlamak istiyorum n. Bunu yapmanın bir yolu var mı?
Trevor Burnham

1
@TrevorBurnham Ayrıca neden MYSQLbu senaryoyu önlemek için bir yapılandırma yok merak ediyorum . Çünkü istemcilerin sorumsuzluğu nedeniyle sunucu asmak kabul edilemez. Sorunuzu anlamak için herhangi bir zorluk bulamadım, ayrıca çok alakalı.
Dinoop paloli

Yanıtlar:


10

Bu iş parçacığının yarısından fazlası ServerFault hakkında nasıl soru sorulacağıyla ilgili gibi görünüyor. Sorunun mantıklı olduğunu ve oldukça basit olduğunu düşünüyorum: Durdurulan bir işlemi otomatik olarak nasıl geri alırsınız?

Bir bağlantı, tüm bağlantıyı öldürmek istiyorsanız, wait_timeout / interactive_timeout'u ayarlamaktır. Bkz. Https://stackoverflow.com/questions/9936699/mysql-rollback-on-transaction-with-lost-disconnected-connection .


3

Sorunuz burada ServerFault üzerinde sorulduğundan, özellikle bir sistem yöneticisinin ve / veya bir DBA'nın uzmanlık sahibi olacağı bilgi alanında bir MySQL sorununa bir MySQL çözümü aradığınızı varsaymak mantıklıdır. bölümünde sorularınız ele alınacaktır:

İlk işlem asla gerçekleştirilmez veya geri alınmazsa, ikinci işlem süresiz olarak engellenir

Hayır, olmayacak. Bence anlamıyorsun innodb_lock_wait_timeout. Tam olarak ihtiyacınız olanı yapar.

Kılavuzda belirtildiği gibi bir hata ile geri dönecektir:

ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
  • Tanım gereği, bu belirsiz değildir . Uygulamanız tekrar tekrar bağlanıyor ve engelleniyorsa , uygulamanız işlemi değil "süresiz olarak engelliyor". İkinci işlem innodb_lock_wait_timeoutsaniyeler boyunca çok kesin bir şekilde engellenir.

Varsayılan olarak, işlem geri alınmaz. Bu hatayı, yeniden denemeyi veya geri almayı nasıl başaracağınıza karar vermek uygulama kodunuzun sorumluluğundadır.

Otomatik geri alma istiyorsanız, bu kılavuzda da açıklanmaktadır:

Geçerli işlem geri alınmaz. (Tüm işlemin geri alınması için sunucuyu --innodb_rollback_on_timeoutseçenekle başlatın .


RE: Sayısız güncelleme ve yorumlarınız

İlk olarak, yorumlarınızda, süresiz olarak engelleyen ilk işlemi zaman aşımına uğratmanın bir yolunu istediğinizi söylemek istediğinizi söylediniz . Bu, orijinal sorunuzdan anlaşılmaz ve "İlk işlem asla gerçekleştirilmez veya geri alınmazsa, ikinci işlem süresiz olarak engellenir" ile çakışıyor.

Yine de bu soruya da cevap verebilirim. MySQL protokolünün "sorgu zaman aşımı" yoktur. Bu, ilk engellenen işlemin zaman aşımına uğramayacağı anlamına gelir. Tamamlanana kadar beklemeniz veya oturumu öldürmeniz gerekir. Oturum öldürüldüğünde, sunucu işlemi otomatik olarak geri alır.

Diğer tek alternatif, uygulamanızın N saniye sonra sorguyu yapan iş parçacığını / çatalı öldürmesine izin verecek, engellemeyen G / Ç kullanan bir mysql kütüphanesi kullanmak veya yazmak olacaktır . Böyle bir kütüphanenin uygulanması ve kullanımı ServerFault'un kapsamı dışındadır. Bu StackOverflow için uygun bir sorudur .

İkincisi, yorumlarınızda aşağıdakileri söylediniz:

Aslında, işlemin MySQL'in sonunda uzun zaman aldığı bir işlemden ziyade, istemci uygulamasının bir işlem sırasında askıda kaldığı (örneğin, sonsuz bir döngüye yakalandığı) bir senaryo ile sorumla daha fazla ilgileniyordum.

Bu, orijinal sorunuzda hiç belirgin değildi ve yine de değil. Bu, ancak yorumda bu oldukça önemli tidbit'i paylaştıktan sonra fark edilebilir.

Bu aslında çözmeye çalıştığınız problemse, yanlış forumda sorduğunuzdan korkuyorum. MySQL'in sağlayamadığı ve bu topluluğun kapsamı dışında olan bir programlama çözümü gerektiren bir uygulama düzeyinde programlama sorunu tanımladınız. En son cevabınız "Bir Ruby programının sonsuz döngü içinde olmasını nasıl önleyebilirim?" Bu soru bu topluluk için konu dışıdır ve StackOverflow'da sorulmalıdır .


Maalesef, işlem bir kilit beklerken doğru olsa da (öneri adı ve dokümanı olarak innodb_lock_wait_timeout), ortaya koyduğum durumda böyle değil: müşterinin bir değişiklik yapmadan önce askıda kaldığı veya çöktüğü COMMITveya ROLLBACK.
Trevor Burnham

@Trevor: Sadece yanılıyorsun. Sonunda test etmek önemsizdir. Sonunda test ettim. Lütfen intikamını geri al.
hobodave

@hobo, sadece hakkın onu sevmesi gerektiği anlamına gelmiyor.
Chris S

Chris, nasıl haklı olduğunu açıklayabilir misin? İlk işlemi çözmek için hayır COMMITveya ROLLBACKmesaj gönderilirse ikinci işlem nasıl gerçekleşir? Hobodave, belki de test kodunuzu sunmanız faydalı olabilir.
Trevor Burnham

2
@Trevor: Biraz anlam ifade etmiyorsun. İşlemleri serileştirmek istiyorsun, değil mi? Evet, diğer sorunuzda bunu söylediniz ve bunu bu soruda tekrarlayın. İlk işlem tamamlanmadıysa, ikinci işlemin nasıl tamamlanmasını beklersiniz? Kilidin bu satırda serbest bırakılmasını beklemesini açıkça söylediniz. Bu nedenle beklemek zorundadır. Bu konuda ne anlamıyorsun?
hobodave

1

Benzer bir durumda ekibim pt-kill kullanmaya karar verdi ( https://www.percona.com/doc/percona-toolkit/2.1/pt-kill.html ). (Diğer şeylerin yanı sıra) çok uzun süredir çalışmakta olan sorguları rapor edebilir veya öldürebilir. Daha sonra her X dakikada bir cronda çalıştırmak mümkündür.

Sorun, beklenmedik bir şekilde uzun (örneğin belirsiz) yürütme süresi olan bir sorgu, okuma kilidi ile tabloları yıkamayı deneyen bir yedekleme yordamını kilitlemişti. çünkü uygulamada herhangi bir hatayla sonuçlanmayan bir kilit beklemiyorlar, bu da sonuçta yöneticilere hiçbir uyarı yapılmaması ve uygulamanın durdurulması ile sonuçlandı.

Düzeltme açık bir şekilde ilk sorguyu düzeltmekti, ancak durumun gelecekte yanlışlıkla olmasını önlemek için, belirli bir süreden daha uzun süren işlemleri / sorguları otomatik olarak öldürmek (veya raporlamak) mümkün olsaydı harika olurdu, Sorunun hangi yazarı soruyor gibi görünüyor. Bu pt-kill ile yapılabilir.


0

Basit bir çözüm, gerekli timeoutzamandan daha fazla zaman alan sorguları öldürmek ve innodb_rollback_on_timeoutbununla birlikte seçeneği kullanmak için saklı bir yordam olması olacaktır.


-2

Bir süre Google'da dolaştıktan sonra, işlem başına zaman sınırı belirlemenin doğrudan bir yolu yok gibi görünüyor . İşte bulabildiğim en iyi çözüm:

Komuta

SHOW PROCESSLIST;

size şu anda çalıştırılmakta olan tüm komutların ve başladıkları andan itibaren geçen sürenin (saniye cinsinden) bir tablo verir. Bir istemci komut vermeyi bıraktığında, Sleepkomutun Timeson komutunun tamamlanmasından bu yana geçen saniye sayısı olacak şekilde komut verdikleri açıklanır . Böylece manuel olarak girebilirsiniz ve herhangi bir sorgunun veritabanınızda 5 saniyeden fazla sürmemesi gerektiğinden eminseniz, 5 saniye boyunca değeri KILLolan her şey Time. Örneğin, id 3 olan işlemin Timesütununda 12 değeri varsa,

KILL 3;

KILL sözdizimi belgeleri , bu kimliğe sahip iş parçacığı tarafından yürütülen bir işlemin geri alınacağını düşündürmektedir (bunu test etmedim, ancak dikkatli olun).

Ancak bunu nasıl otomatik hale getirebilir ve her 5 saniyede bir tüm fazla mesai komut dosyalarını nasıl öldürebilirsiniz? Aynı sayfadaki ilk yorum bize bir ipucu veriyor, ancak kod PHP'de; biz bir yapamaz gibi görünüyor SELECTüzerinde SHOW PROCESSLISTMySQL istemcisi içinden. Yine de, her $MAX_TIMEsaniye çalıştırdığımız bir PHP arka plan programımız olduğunu varsayarsak , şöyle görünebilir:

$result = mysql_query("SHOW FULL PROCESSLIST");
while ($row=mysql_fetch_array($result)) {
  $process_id=$row["Id"];
    if ($row["Time"] > $MAX_TIME) {
      $sql="KILL $process_id";
      mysql_query($sql);
  }
}

Bunun , yalnızca hatalı çalışanlar için değil, tüm boş istemci bağlantılarını yeniden bağlanmaya zorlamanın yan etkisi olacağını unutmayın .

Birinin daha iyi bir cevabı varsa, duymak isterim.

Düzenleme:KILL Bu şekilde kullanmak için en az bir ciddi risk vardır . Yukarıdaki komut dosyasını KILL10 saniye boyunca kullanılmayan her bağlantı için kullandığınızı varsayalım . Bir bağlantı 10 saniye boşta kaldıktan sonra, ancak KILLyayınlanmadan önce bağlantısı kesilen kullanıcı bir START TRANSACTIONkomut verir. O zaman onlar KILLed. Bir gönderiyorlar UPDATEve sonra, iki kez düşünerek, bir sorun çıkarıyorlar ROLLBACK. Ancak, KILLorijinal işlemlerini geri aldıkları için güncelleme hemen geçti ve geri alınamaz! Test senaryosu için bu gönderiye bakın .


2
Bu yorum, bu cevabın gelecekteki okuyucularına yöneliktir. Lütfen kabul edilen cevap olsa bile bunu yapmayın.
hobodave

Tamam, aşağıya inip gizli bir yorum bırakmak yerine, bunun neden kötü bir fikir olacağını açıklamaya ne dersiniz? Orijinal PHP betiğini yayınlayan kişi, sunucusunun askıda kalmasını engellediğini söyledi; aynı hedefe ulaşmak için daha iyi bir yol varsa bana gösterin.
Trevor Burnham

6
Yorum gizli değil. Gelecekteki tüm okuyucular için "çözümünüzü" kullanma girişiminde bulunmamak bir uyarıdır. Uzun süren işlemleri otomatik olarak öldürmek tek taraflı olarak korkunç bir fikirdir. Bu olmalıdır yapılması asla . Açıklama budur. Öldürme oturumları bir kural değil istisna olmalıdır. Kök neden senin yapmacık sorunun kullanıcı hatasıdır. Satırları kilitleyen ve sonra "uzaklaşan" DBA'lar için teknik çözümler oluşturmazsınız. Onları kovuyorsun.
hobodave

-2

Hobodave'nin bir yorumda önerdiği gibi, bazı istemcilerde (görünüşe göre mysqlkomut satırı yardımcı programı olmasa da ) bir işlem için bir zaman sınırı ayarlamak mümkündür. İşte Ruby'de ActiveRecord kullanarak bir gösteri:

require 'rubygems'
require 'timeout'
require 'active_record'

Timeout::timeout(5) {
  Foo.transaction do
    Foo.create(:name => 'Bar')
    sleep 10
  end
}

Bu örnekte, işlem 5 saniye sonra zaman aşımına uğrar ve otomatik olarak geri alınır . ( Hobodave'in yorumuna yanıt olarak güncelleme: Veritabanının yanıt vermesi 5 saniyeden uzun sürerse, işlem en kısa sürede geri alınır - daha erken değil.) Tüm işlemlerinizin nsaniyeler sonra zaman aşımına uğramasını istiyorsanız , ActiveRecord etrafında bir paket oluşturabilirsiniz. Bunun Java, .NET, Python, vb. İçindeki en popüler kütüphaneler için de geçerli olduğunu tahmin ediyorum, ancak henüz test etmedim. (Varsa, lütfen bu yanıta bir yorum gönderin.)

ActiveRecord'daki KILLişlemler, komut satırından yapılan işlemlerin aksine, a verilirse güvenli olma avantajına sahiptir . Bkz. Https://dba.stackexchange.com/questions/1561/mysql-client-believes-theyre-in-a-transaction-gets-killed-wreaks-havoc .

Diğer yanıtıma gönderdiğim gibi bir komut dosyası dışında, sunucu tarafında bir maksimum işlem süresini zorunlu kılmak mümkün görünmüyor.


ActiveRecord'un eşzamanlı (engelleme) G / Ç kullandığını görmediniz. Bu komut dosyasının yaptığı tek şey Ruby uyku komutunu kesmek. Foo.createKomutunuz 5 dakika boyunca bloke olursa , Zaman Aşımı komutu öldürmez. Bu, sorunuzda verilen örnekle uyumsuz. A SELECT ... FOR UPDATE, diğer tüm sorgularda olduğu gibi uzun süre kolayca engellenebilir.
hobodave

Hemen hemen tüm mysql istemci kitaplıklarının engelleme G / Ç kullandığına dikkat edilmelidir, bu nedenle cevabımda, engellemeyen G / Ç kullanılana ait olanı kullanmanız veya yazmanız gerektiği önerisi. İşte Timeout'un
hobodave

Aslında, işlemin MySQL'in sonunda uzun zaman aldığı bir işlemden ziyade, istemci uygulamasının askıda kaldığı (örneğin, sonsuz bir döngüye yakalandığı) bir senaryo ile sorumla daha fazla ilgileniyordum. Ama teşekkürler, bu iyi bir nokta. Not etmek için soruyu güncelledim.)
Trevor Burnham

Hak talebinde bulunduğunuz sonuçları çoğaltamıyorum. Kullanmak için gist güncelledim ActiveRecord::Base#find_by_sql: gist.github.com/856100 Yine, AR engelleme I / O kullanıyor olmasıdır.
hobodave

@hobodave Evet, bu düzenleme hatalıydı ve düzeltildi. Açık olmak gerekirse: timeoutYöntem, MySQL veritabanı bir sorguya yanıt verene kadar işlemi geri alır. Bu nedenle timeoutyöntem, istemci tarafında uzun işlemleri veya nispeten kısa sorgulardan oluşan uzun işlemleri önlemek için uygundur, ancak tek bir sorgunun suçlu olduğu uzun işlemler için uygun değildir.
Trevor Burnham
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.