MySQL InnoDB. Дана таблица со счетчиком.
create table foo
(
entity_id int unsigned not null
primary key,
count int unsigned not null
)
Сначала с помощью select ... for update проверяется существование записи по primary, и, если записи нет, то происходит insert, иначе update с увеличением счетчика на "1". В случае, если записи нет, при "одновременных" запросах закономерно получаю ошибку дубликата (уровень изоляции read committed).
session1> set session transaction isolation level read committed;
Query OK, 0 rows affected (0.00 sec)
session1> start transaction;
Query OK, 0 rows affected (0.00 sec)
session2> set session transaction isolation level read committed;
Query OK, 0 rows affected (0.00 sec)
session2> start transaction;
Query OK, 0 rows affected (0.00 sec)
session1> select * from foo where entity_id = 8 for update;
Empty set (0.00 sec)
session2> select * from foo where entity_id = 8 for update;
Empty set (0.00 sec)
session1> insert into foo (entity_id, count) values (8, 1);
Query OK, 1 row affected (0.00 sec)
session1> commit;
Query OK, 0 rows affected (0.01 sec)
session2> insert into foo (entity_id, count) values (8, 1);
ERROR 1062 (23000): Duplicate entry '8' for key 'foo.PRIMARY'
Можно ли решить указанным ниже способом? Если произойдет ошибка дублирующегося ключа, то будет ли установлена эксклюзивная блокировка на эту запись?
insert into foo (entity_id, count)
values (8, 1)
on duplicate key update count = count + 1;
Если так, то выходит, что `select ... for update` для решения задачи не нужен в обоих случаях (когда записи нет и когда запись есть)? Оптимальнее сразу выполнять запрос ниже без предварительного `select ... for update` в явно открытой транзакции?