mysql - блокировка строк для запроса выбора?
У меня есть таблица MySQL (таблица-A), которая содержит список уникальных идентификаторов продукта. Приложение, которое использует эту таблицу, использует несколько потоков, каждый из которых выбирает строку таблицы (самая верхнюю), использует API для получения данных о продукте и обновления его в другой таблице (таблица-B). После этого поток удаляет соответствующую строку идентификатора продукта в таблице-A и выбирает другую для работы (цикл до тех пор, пока все строки в таблице-A не будут удалены, пока таблица-B не будет обновлена).
Как предотвратить случайную работу потоков моего приложения в той же строке таблицы-A? Есть ли способ заблокировать строку из выбранных?
Пример: поток приложения 1 выбирает строку 1 из таблицы A. Захват и обновление всех связанных данных в таблицу B из API занимает от 10 до 15 секунд. Пока это происходит, поток-2 выключится и проверит таблицу-A, чтобы выбрать строку для работы. В этом случае я хочу заблокировать только строку-1, чтобы она-нить-2 не видела и не читала, а вместо этого выбирала строку-2.
2 ответа
Собственная блокировка MySQL не обеспечивает эту функциональность. Вы можете использовать столбец для выполнения ваших "замков".
Предполагая, что каждый поток имеет уникальный идентификатор, вы можете создать столбец с именем thread_owner
, по умолчанию 0.
Один поток мог бы получить строку, подобную этой:
UPDATE mytable
SET thread_owner = :my_threadID
WHERE thread_owner = 0
LIMIT 1
Затем выберите строку следующим образом (она может не вернуть ни одной строки, если не было строк, которые нужно обработать):
SELECT *
FROM mytable
WHERE thread_owner = :my_threadID
Затем обработайте его и, наконец, удалите.
Это решение будет работать как на MyISAM, так и на InnoDB.
Однако для InnoDB это может быть медленно, потому что каждый оператор UPDATE пытается заблокировать все строки, где thread_owner = 0, и если вы не уверены, что каждый раз блокируете все строки в одном и том же порядке, это может даже привести к взаимоблокировке. Таким образом, вы можете попробовать явно заблокировать всю таблицу в вашем выражении UPDATE:
LOCK TABLES mytable WRITE;
UPDATE mytable
SET thread_owner = :my_threadID
WHERE thread_owner = 0
LIMIT 1;
UNLOCK TABLES;
Таким образом, MyISAM и InnoDB будут работать одинаково.
Попробуйте с помощью команды "lock table", я использовал 2 окна, чтобы показать вам:
jcho_1> lock table t1 write;
Query OK, 0 rows affected (0.00 sec)
jcho_2> select * from t1;
jcho_2 ждут, чтобы освободить таблицу, чтобы иметь возможность читать, когда jcho_1 сделает это:
jcho_1> unlock tables;
Query OK, 0 rows affected (0.00 sec)
jcho_2 может с его запросом.
jcho_2> select * from t1;
+----------+-------------+--------------+---------------------+
| actor_id | first_name | last_name | last_update |
+----------+-------------+--------------+---------------------+
| 206 | a | b | 0000-00-00 00:00:00 |
| 71 | ADAM | GRANT | 2006-02-15 04:34:33 |
| 132 | ADAM | HOPPER | 2006-02-15 04:34:33 |
...
| 0 | 0 | 0 | 0000-00-00 00:00:00 |
+----------+-------------+--------------+---------------------+
202 rows in set (1 min 27.67 sec)