Як запитувати та збільшувати значення (лічильник) безпечним способом? (уникайте перегонів)


10

У таблиці, де кожен рядок має лічильник (просто ціле значення), мені потрібно отримати поточне значення та одночасно збільшити його .

Ефективно, я хочу це зробити:

SELECT counter FROM table WHERE id=123
UPDATE table SET counter=counter+1 WHERE id=123

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

Я можу придумати конструкцію, де я впроваджую ручний замок на ряд, але мені цікаво, чи існує простіший спосіб зробити це?


використання транзакцій, можливо?
ypercubeᵀᴹ

Відповіді:


15

Оперативні оновлення працюють прекрасно без вибору раніше! Оскільки окремі оператори є безпечними за визначенням, навіть два запити UPDATE, виконані одночасно, призведуть до того, що рядок збільшується вдвічі.

Якщо ви дійсно хочете вибрати значення для свого сценарію PHP, зробити щось із ним і пізніше хочете оновити це точне значення лічильника, ви можете зробити наступне:

BEGIN;
SELECT `counter` FROM `table` WHERE `id` = 123 FOR UPDATE;
UPDATE `table` SET `counter` = `counter`+1 WHERE `id` = 123;
COMMIT;

Це запускає нову транзакцію, потім вибирає рядки, які потрібно оновити, і блокує їх виключно. Потім ви можете безпечно оновлювати їх, не турбуючись про те, щоб інші клієнти змінили свій вміст або навіть отримали доступ до заблокованих рядків. Нарешті, вам потрібно здійснити зміни.

Ви також повинні прочитати щось про рівні ізоляції . Ви, ймовірно, не хочете такого значення, як READ UNCOMMITTEDрівень ізоляції. Все інше має бути добре для цього випадку використання.


Я читав в інших місцях, що UPDATE table SET counter = counter + 1достатньо атомний? Вам ще потрібні виписки про транзакції, що їх оточують?
CMCDragonkai

@CMCDragonkai Один лише ваш запит є атомним, але якщо ви вибираєте значення раніше, а не використовували FOR UPDATEта трансакції, вибране вами значення може відрізнятися від того, яке було використано в запиті оновлення. Моя комбінація запитів блокує рядок, як тільки вибирається значення, і тому гарантує, що це точне значення лічильника буде використано в запиті оновлення.
GhostGambler

Гаразд, але це потрібно лише в тому випадку, якщо я виконую будь-яку роботу, крім збільшення права? Як відомо, самотній атомний запит на оновлення достатньо хороший, якщо це все, що я хочу зробити?
CMCDragonkai

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