2017-01-08 2 views
0

Мы используем MySQL InnoDB. Система поддерживает несколько запросов, которые могут выполняться одновременно. Вот упрощенный пример того, чего мы пытаемся достичь. Предположим, у нас есть таблица, содержащая объекты, представляющие лампочку. Эти лампочки могут быть включены (активированы) пользователями. Пользователи могут добавлять в таблицу лампочки, которые изначально дезактивированы, и только 10 из них могут быть отмечены как активированные. Таким образом, у нас есть предел 10. Поскольку система поддерживает параллелизм, существует риск гонки условия основаны на следующем примере:Как избежать состояния гонки в MySQL InnoDB?

СЕССИЯ 1:

1: Получить количество активных лампы

2: Проверьте количество активированных ламп

3: Если предел еще не достигнут, активируйте лампу и зафиксируйте ее.

4: В противном случае откат.

Проблема в том, что во время шагов 2 и 4 другая сессия могла войти и могла превысить предел.

Это SQL представление того, что мы делаем в данный момент:

  1. SELECT COUNT (ID) AS Всего с ТПС WHERE is_active = 1;
  2. if (всего < 10) {
  3. активировать другую лампочку; } else {
  4. откат; }

Что вы рекомендуете в качестве решения для этого сценария? Мы рассматривали возможность «выбрать для обновления», но если я не ошибаюсь, блокирует выбранные строки, и пользователи могут добавлять луковицы в таблицу, в то время как выбранные строки находятся под блокировкой.

спасибо.

+0

Вам нужно «активировать» лампу или делать _absolutely nothing_? Заметьте, даже сообщите пользователю, что он больше не может «активировать»? (Я чувствую, что постановка проблемы неполна.) –

ответ

0

Одна идея состоит в том, чтобы держать отдельный запирающий таблицу, в которой вы управляете этот предел требования и сделать только атомные обновления для подсчета активных луковиц, как:

update active_bulb_count 
    set active_count = active_count + 1 
    where active_count < limit 
     and <any other conditions needed>; 

Если ни одна строка не обновляется вы знаете предел был встречен , поэтому вы не должны активировать лампу.

Возможно, вы все равно должны рассмотреть возможность настройки своей транзакции для сеанса, который выполняет это действие. Более подробная информация на атомном обновления и транзакции MySQL Atomic UPDATE in InnoDB vs MyISAM

+0

Мы закончили делать что-то похожее на то, что вы предложили. Мы используем таблицу с одной строкой в ​​качестве мьютекса, который работает как опекун и сериализует весь доступ к ресурсам. Я ценю ваш совет и руководство @ Рик Джеймс. Спасибо вам всем. –

+0

@JinIzzraeel - таблица с двумя строками мьютекса обычно требует 2 столбца: 1 для первичного ключа для блокировки, плюс один для статуса (свободный и индикатор того, кто имеет блокировку). Если хотите, запустите новый вопрос с помощью _your specificics_; они, вероятно, будут достаточно разными. –

+0

@ Rick James Оцените свой ответ. Мьютекс одной строки будет действовать как опекун и сериализовать доступ ко всем ресурсам, поскольку блок кода является единственной точкой доступа ко всем запросам. По моему мнению, этого должно быть достаточно для нашего дела. Спасибо. –

0
BEGIN; -- start a "transaction" 
do the 4 steps 
COMMIT; -- or ROLLBACK 

Примечание: Любой SELECTs, что может привести к UPDATE нужно сказать FOR UPDATE.

И/или посмотрите, как сделать больше работы в UPDATE (см. Ответ churd). Или INSERT ... ON DUPLICATE KEY UPDATE ...

Имейте в виду, что INSERT и UPDATE могут сообщать, сколько строк было затронуто. Это можно использовать для того, чтобы «предположить, что задача будет успешной», а затем проверить, было ли это сделано, и принять уклончивое действие, если оно не выполнено.

Смежные вопросы