Найкращий спосіб впровадити сумісну чергу на основі таблиці


11

У мене в MySQL є таблиця, яка представляє чергу посилань, які підлягають обробці. Посилання обробляються зовнішнім додатком, по одному, і видаляються врешті-решт. Це черга з великим обсягом, і у мене є кілька примірників програми обробки, розповсюдженої на декілька серверів.

Як я можу гарантувати, що кожен запис вибирається лише одним додатком? Чи є спосіб позначити / заблокувати запис?

Наразі, щоб уникнути двох і більше підбирань одного і того ж посилання, я дозволяю кожному екземпляру лише збирати певний набір записів (заснований на МОД їхнього ідентифікатора), але це не прозорий спосіб збільшити обробку черги швидкості, просто додаючи нові екземпляри.


Моя мантра: "Не в черзі, просто роби це". Тобто, замість того, щоб кидати завдання в чергу, запустіть процес для виконання завдання.
Рік Джеймс

Відповіді:


8

По-перше: MySQL - це одна з найгірших можливих програм для її реалізації, особливо якщо вона дуже динамічна. Причина полягає в тому, що такі двигуни, як MEMORY та MyISAM, мають лише блокування з повним столом, тоді як більш підходящі двигуни, такі як InnoDB, мають більш високу штрафну кількість запису (для надання властивостей ACID) та оптимізовані для доступу до записів, які є просторово і часово близькими (ті встановлені в пам'яті ). Існує також непогана система сповіщень про зміни для MySQL - вона повинна бути реалізована як опитування. Є десятки програм програмного забезпечення, більш оптимізованих для цього завдання .

Сказавши це, я успішно реалізував такий доступ, якщо вимоги до продуктивності / ефективності не дуже високі. Багато людей не можуть дозволити собі впровадити та підтримувати повний окремий фрагмент технології лише для невеликої частини бізнес-логіки.

SELECT FOR UPDATEце те, що ви шукаєте - прочитана серіалізація. У той час як UPDATE / DELETE завжди блокує рядок під час запущеної транзакції MYSQL, ви можете уникнути великої транзакції, поки процес триває, так:

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 піклується про блокування всіх одночасно вибраних, крім одного, під час вибору рядків. Оскільки це може призвести до безлічі заблокованих з'єднань одночасно, зберігайте початкову транзакцію якомога менше і намагайтеся обробляти більше, ніж 1 ряд одночасно.


Дякую. Як ви вважаєте, на користь продуктивності може бути більший замок (змінивши LIMIT на 10)?
Мігель Е

@MiguelE Загалом, так, чим більше часу ви витрачаєте на обробку і тим менше шансів зіткнутися з іншими транзакціями, тим краще. Але це може залежати в деяких випадках - це також може спричинити зворотний ефект (більше транзакцій блокується). Завжди перевіряйте це спочатку. Також важливо адекватно індексувати таблицю, інакше у деяких режимах ізоляції ви можете отримати повне блокування таблиці.
jynus

1
І, напевно, було б гарною ідеєю відстежувати дату, коли ви почали обробляти рядок на випадок, якщо процес зависне, і ви хочете реалізувати механізм тайм-ауту.
Джуліан

3

Як я пояснив у цій статті , MySQL 8 представив підтримку як SKIP LOCKED, так і НЕ ЧАКАЙТЕ.

SKIP LOCKED корисний для реалізації черг завдань (також пакетних черг), так що ви можете пропускати через блоки, які вже заблоковані іншими одночасними транзакціями.

НЕ ЧАКАЙТЕ корисно, щоб уникнути очікування, поки паралельна транзакція не звільнить замки, які ми також зацікавлені в блокуванні. Без ЧАКУВАННЯ, нам або доведеться чекати, поки блокування будуть звільнені (під час фіксації або випуску транзакції, яка в даний час містить блокування), або вичерпано час придбання блокування. Тому NO WAIT діє як час очікування блокування зі значенням 0.

Більш детально про SKIP LOCK та NO WAIT ознайомтесь із цією статтею .


0

Я робив щось подібне з офлайн-перевірками DBCC (два сервери, які виконують резервне копіювання, а потім DBD checkdb). Один сервер збирає всі резервні копії 31 сервера вчора і ставить їх у чергу, а потім цей сервер та інший витяг із цієї черги. Хоча на серверах не так багато, метод повинен залишатися тим самим. Попросіть сервер додатків виконати запит оновлення проти черги, оновлюючи поле дати / часу та поле "сервер додатків" з іменем цього сервера додатків або ще кращим числовим ідентифікатором. Це призведе до блокування або якщо вже є замок з іншого сервера, який отримує наступний рядок, він буде заблокований і чекати, коли інший додаток закінчить отримання наступного рядка. Потім ви захочете, щоб програма витягнула останню запис із черги для цього поля додатків і отримала з неї будь-яку інформацію. Використання MySQL '

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.