Eşzamanlı tablo tabanlı kuyruğu uygulamanın en iyi yolu


11

MySQL içinde işlenecek bağlantıların bir sırasını temsil eden bir tablo var. Bağlantılar harici bir uygulama tarafından tek tek işlenir ve sonunda silinir. Bu yüksek hacimli bir kuyruk ve birkaç sunucuya yayılmış işleme uygulaması birden çok örneğim var.

Her kaydın yalnızca bir uygulama tarafından seçilmesini nasıl sağlayabilirim? Kaydı işaretlemenin / kilitlemenin bir yolu var mı?

Şu anda, iki veya daha fazla aynı bağlantıyı almaktan kaçınmak için, her örneğin yalnızca belirli bir kayıt kümesini (kimliklerinin MODuna dayanarak) almasına izin veriyorum, ancak bu, kuyruk işlemeyi artırmanın şeffaf bir yolu değil sadece yeni örnekler ekleyerek hızlandırın.


Benim mantram: "Sıraya alma, sadece yap". Yani, bir görevi kuyruğa atmak yerine, görevi yapmak için bir işlem başlatın.
Rick James

Yanıtlar:


8

Birincisi: MySQL, özellikle çok dinamikse, bunu uygulamak için olabilecek en kötü yazılım parçalarından biridir. Bunun nedeni, MEMORY ve MyISAM gibi motorların yalnızca tam tablo kilitleri olmasına rağmen, InnoDB gibi daha uygun motorların daha yüksek yazma cezasına sahip olması (ACID özelliklerini sağlamak için) ve uzamsal ve geçici olarak yakın olan (belleğe ayarlanmış kayıtlara erişmek için optimize edilmiş) ). Ayrıca MySQL için iyi bir değişiklik bildirim sistemi yoktur - bir yoklama olarak uygulanmalıdır. Orada daha o görev için optimize edilmiş yazılım kısımlarını onlarca .

Bunu söyledikten sonra, performans / verimlilik gereksinimleri çok yüksek değilse bu tür erişimi başarıyla uyguladım. Birçok insan, iş mantığının sadece küçük bir kısmı için ayrı bir teknoloji parçası sunmayı ve sürdürmeyi göze alamaz.

SELECT FOR UPDATEaradığınız şey serileştirme. Bir UPDATE / DELETE, çalışan bir MYSQL işlemi sırasında her zaman satırı kilitleyecek olsa da, işlem devam ederken büyük bir işlemden kaçınmak isteyebilirsiniz, bu nedenle:

START TRANSACTION;
SELECT * FROM your_table WHERE state != 'PROCESSING' 
  ORDER BY date_added ASC LIMIT 1 FOR UPDATE;
if (rows_selected = 0) { //finished processing the queue, abort}
else {
UPDATE your_table WHERE id = $row.id SET state = 'PROCESSING'
COMMIT;

// row is processed here, outside of the transaction, and it can take as much time as we want

// once we finish:
DELETE FROM your_table WHERE id = $row.id and state = 'PROCESSING' LIMIT 1;
}

MySQL, satır seçerken biri hariç tüm eşzamanlı seçimleri kilitlemeye özen gösterir. Bu, aynı anda çok sayıda kilitli bağlantıya yol açabileceğinden, ilk işlemi olabildiğince küçük tutun ve bir seferde 1 satırdan fazlasını işlemeye çalışın.


Teşekkürler. Performansın daha büyük bir kilitten faydalanabileceğini düşünüyor musunuz (LIMIT değerini 10 olarak değiştirerek)?
Miguel E

@MiguelE Genel olarak, evet, işleme için ne kadar fazla zaman harcar ve diğer işlemlerle daha az çarpışma olasılığınız o kadar iyi olur. Ancak bazı durumlarda değişebilir - aynı zamanda ters etki yaratabilir (daha fazla işlem kilitlenir). Her zaman önce test edin. Tablonun yeterince endekslenmesi de önemlidir, aksi takdirde bazı yalıtım modlarında tam bir masa kilidi ile sonuçlanabilir.
jynus

1
İşlemin askıya alınması ve bir zaman aşımı mekanizması uygulamak istemeniz durumunda satırı işlemeye başladığınız tarihi takip etmek iyi bir fikir olabilir.
Julian

3

Bu makalede açıkladığım gibi , MySQL 8 hem SKIP LOCKED hem de NO WAIT için destek sağladı.

SKIP LOCKED, iş kuyruklarını (toplu iş kuyrukları olarak da bilinir) uygulamak için kullanışlıdır, böylece diğer eşzamanlı işlemler tarafından zaten kilitlenmiş olan kilitleri atlayabilirsiniz.

NO WAIT, eşzamanlı bir işlem de kilitlemekle ilgilendiğimiz kilitleri serbest bırakana kadar beklemekten kaçınmak için yararlıdır. NO WAIT olmadan, kilitler serbest bırakılıncaya (ya da kilitleri tutan işlem tarafından kesin ya da serbest bırakma zamanında) ya da kilit edinme zaman aşımına uğramasını beklememiz gerekir. Bu nedenle, NO WAIT değeri olan bir kilit zaman aşımı işlevi görür 0.

SKIP LOCK ve NO WAIT hakkında daha fazla bilgi için bu makaleye göz atın .


0

Çevrimdışı DBCC denetimleri (yedekleme geri yükleme yapan iki sunucu ve sonra bir DBCC checkdb) ile benzer bir şey yaptım. Bir sunucu dün 31 sunucunun tüm yedeklerini toplar ve bunları bir kuyruğa, daha sonra da bu sunucuya ve başka bir kuyruğa alır. Çok fazla sunucu olmasa da, yöntem aynı kalmalıdır: Uygulama sunucusunun bir tarih / saat alanını ve o uygulama sunucusunun adı veya daha iyi ama sayısal kimliğiyle "uygulama sunucusu" alanını güncelleyen kuyruğa karşı bir güncelleme sorgusu çalıştırmasını sağlayın. Bu bir kilide neden olur veya başka bir sunucudan sonraki satırı alan bir kilit varsa, engellenir ve diğer uygulamanın bir sonraki satırı almasını bekler. Daha sonra uygulamanın, uygulama alanı için en son kaydı kuyruğundan geri almasını ve istediğiniz bilgiyi almasını istersiniz. MySQL Kullanımı '

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.