【发布时间】:2017-07-21 17:15:16
【问题描述】:
我有一张简单的桌子
CREATE TABLE test (
col INT,
data TEXT,
KEY (col)
);
还有一个简单的交易
START TRANSACTION;
SELECT * FROM test WHERE col = 4 FOR UPDATE;
-- If no results, generate data and insert
INSERT INTO test SET col = 4, data = 'data';
COMMIT;
我试图确保此事务的两个副本同时运行不会导致重复行和死锁。我也不想产生为col = 4 多次生成data 的成本。
我试过了:
-
SELECT ..(没有FOR UPDATE或LOCK IN SHARE MODE):两个事务都看到没有带有
col = 4的行(没有获取锁)并且都生成data并插入带有col = 4的行的两个副本。 -
SELECT .. LOCK IN SHARE MODE两个事务都在
col = 4上获取共享锁,生成data并尝试使用col = 4插入一行。两个事务都等待对方释放它们的共享锁,以便它可以INSERT,从而产生ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction。 -
SELECT .. FOR UPDATE我预计一个事务的
SELECT将成功并获得col = 4的排他锁,而另一个事务的SELECT将阻塞等待第一个事务。相反,
SELECT .. FOR UPDATE的两个查询都会成功,并且事务会像SELECT .. LOCK IN SHARE MODE一样进入死锁状态。col = 4的独占锁似乎不起作用。
我怎样才能写这个事务而不导致重复行和没有死锁?
【问题讨论】:
-
不要这样做。请设置
UNIQUE索引并改用INSERT ... ON DUPLICATE KEY或INSERT IGNORE。 -
@tadman 如果我这样做
INSERT .. ON DUPLICATE KEY UPDATE,那么这两个事务都会产生生成data的成本,这是不必要的。如果我执行INSERT IGNORE ..,那么 MySQL 在运行INSERT时遇到的 all 错误将被忽略(不仅仅是重复的键),这非常草率。 -
tx_isolation的值是多少? -
@RickJames
REPEATABLE READ(SERIALIZABLE只会使SELECT ..版本的行为与SELECT .. LOCK IN SHARE MODE相同)
标签: mysql sql innodb mariadb deadlock