Cara terbaik untuk mengimplementasikan antrian berdasarkan tabel bersamaan

10

Saya memiliki tabel di MySQL yang mewakili antrian tautan yang akan diproses. Tautan diproses oleh aplikasi eksternal, satu per satu, dan pada akhirnya dihapus. Ini adalah antrian volume tinggi dan saya memiliki beberapa contoh aplikasi pemrosesan, tersebar di beberapa server.

Bagaimana saya bisa memastikan bahwa setiap catatan hanya diambil oleh satu aplikasi? Apakah ada cara untuk menandai / mengunci catatan?

Saat ini, untuk menghindari dua atau lebih mengambil tautan yang sama, saya mengizinkan setiap contoh hanya untuk mengambil satu set catatan tertentu (berdasarkan dalam MOD ID mereka), tetapi ini bukan cara transparan untuk meningkatkan pemrosesan antrian mempercepat hanya dengan menambahkan instance baru.

Miguel E
sumber
Mantra saya: "Jangan mengantri, lakukan saja". Artinya, alih-alih melempar tugas ke antrian, luncurkan proses untuk melakukan tugas tersebut.
Rick James

Jawaban:

7

Pertama: MySQL adalah salah satu perangkat lunak yang paling buruk untuk mengimplementasikannya, khususnya jika sangat dinamis. Alasannya adalah bahwa mesin seperti MEMORY dan MyISAM hanya memiliki kunci tabel penuh sementara mesin yang lebih cocok seperti InnoDB memiliki penalti tulis yang lebih tinggi (untuk menyediakan properti ACID) dan dioptimalkan untuk mengakses catatan yang secara spasial dan sementara ditutup (yang diatur pada memori ). Juga tidak ada sistem pemberitahuan perubahan yang baik untuk MySQL - itu harus diimplementasikan sebagai polling. Ada lusinan perangkat lunak yang lebih dioptimalkan untuk tugas itu .

Karena itu, saya telah melihat berhasil menerapkan akses semacam ini jika persyaratan kinerja / efisiensi tidak terlalu tinggi. Banyak orang tidak mampu memperkenalkan dan memelihara teknologi yang terpisah hanya untuk sebagian kecil dari logika bisnis.

SELECT FOR UPDATEadalah apa yang Anda cari- baca serialisasi. Sementara UPDATE / DELETE akan selalu mengunci baris selama menjalankan transaksi MYSQL, Anda mungkin ingin menghindari transaksi besar saat proses sedang berlangsung, jadi:

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 akan menjaga mengunci semua pilihan bersamaan kecuali satu ketika memilih baris. Karena hal ini dapat menyebabkan banyak koneksi yang terkunci pada saat yang bersamaan, pertahankan transaksi awal sekecil mungkin dan cobalah untuk memproses lebih dari 1 baris sekaligus.

jynus
sumber
Terima kasih. Apakah menurut Anda kinerja dapat memanfaatkan kunci yang lebih besar (dengan mengubah LIMIT menjadi 10)?
Miguel E
@MiguelE Secara umum, ya, semakin banyak waktu yang Anda habiskan untuk memproses dan semakin kecil kemungkinan Anda bertabrakan dengan transaksi lain, semakin baik. Tetapi mungkin tergantung dalam beberapa kasus - itu juga dapat menyebabkan efek sebaliknya (lebih banyak transaksi dikunci). Selalu uji dulu. Penting juga untuk mengindeks tabel secara memadai, atau Anda mungkin berakhir dengan kunci tabel penuh dalam beberapa mode isolasi.
jynus
1
Dan mungkin ide yang baik untuk melacak tanggal Anda mulai memproses baris untuk berjaga-jaga jika prosesnya hang dan Anda ingin menerapkan mekanisme batas waktu.
Julian
3

Seperti yang saya jelaskan dalam artikel ini , MySQL 8 memperkenalkan dukungan untuk SKIP LOCKED dan NO WAIT.

SKIP LOCKED berguna untuk mengimplementasikan antrian pekerjaan (alias antrian batch) sehingga Anda dapat melewati kunci yang sudah dikunci oleh transaksi bersamaan lainnya.

NO WAIT berguna untuk menghindari menunggu sampai transaksi bersamaan melepaskan kunci yang juga ingin kami kunci. Tanpa TANPA TUNGGU, kita harus menunggu sampai kunci dilepaskan (pada saat komit atau waktu rilis oleh transaksi yang saat ini memegang kunci) atau waktu akuisisi kunci habis. Oleh karena itu, NO WAIT bertindak seperti batas waktu kunci dengan nilai 0.

Untuk detail lebih lanjut tentang SKIP LOCK dan TANPA TUNGGU, lihat artikel ini .

Vlad Mihalcea
sumber
0

Saya telah melakukan sesuatu yang mirip dengan pemeriksaan DBCC offline (dua server melakukan pemulihan cadangan dan kemudian DBCC checkdb). Satu server mengumpulkan semua cadangan 31 server kemarin dan memasukkannya ke dalam antrian lalu server itu dan satu lagi tarik dari antrian itu. Meskipun tidak banyak server, metode ini harus tetap sama: Mintalah server aplikasi menjalankan kueri pembaruan terhadap antrian yang memperbarui bidang tanggal / waktu dan bidang "server aplikasi" dengan nama server aplikasi atau ID numerik yang lebih baik. Ini akan menyebabkan kunci atau jika sudah ada kunci dari server lain yang mendapatkan baris berikutnya, itu akan diblokir dan menunggu aplikasi lain selesai mendapatkan baris berikutnya. Anda kemudian ingin aplikasi untuk menarik kembali catatan terbaru dari antrian untuk bidang aplikasi itu dan mendapatkan informasi apa pun yang Anda inginkan dari itu. Menggunakan MySQL '

Chris Woods
sumber