Unexpected Locking for Table with Primary Key & Unique Key

南笙酒味 提交于 2019-12-03 12:20:30
elGEoRgE TheKiLLa

The problem you are experiencing happens because MySQL doesn't just lock the table row for a value you're going to insert, it locks all possible values between the previous id and the next id in order, so, reusing your example bellow:

DROP TABLE IF EXISTS foo;
CREATE TABLE `foo` (
  `i` INT(11) NOT NULL,
  `j` INT(11) DEFAULT NULL,
  PRIMARY KEY (`i`),
  UNIQUE KEY `jk` (`j`) 
) ENGINE=InnoDB DEFAULT CHARSET=latin1 ;
INSERT INTO foo VALUES (5,5), (8,8), (11,11);

Suppose you start with transaction TX1:

START TRANSACTION;
REPLACE INTO foo VALUES(8,8);

Then if you start a transaction TX2, whatever INSERT or REPLACE using an id between 5 and 11 will be locked:

START TRANSACTION;
REPLACE INTO foo VALUES(11,11);

Looks like MySQL uses this kind of locking to avoid the "phantom problem" described here: http://dev.mysql.com/doc/refman/5.0/en/innodb-next-key-locking.html, MySQL uses a "next-key locking", that combines index-row locking with gap locking, this means for us that it will lock a lot of possible ids between the previous and next ids, and will lock prev and next ids as well.

To avoid this try to create a server algorithm that inserts your records so that records inserted in different transactions don't overlap, or at least don't execute all your transactions at the same time so the TX doesn't have to wait one each other.

RolandoMySQLDBA

It seems as if the problem might lie in the fact that InnoDB indexes are weird.

The primary key (clustered) is i and there would be a rowid associated with it.

The unique key on j (nonclustered) has the rowid of i associated with the value of j in the index.

Doing a DELETE followed by an INSERT on the same key value for i should produce an upcoming different rowid for the primary key (clustered) and, likewise, an upcoming different rowid to associate with the value of j (nonclustered).

This would require some bizarre internal locking within MVCC mechanism.

You may need to change your Transaction Isolation Level to Allow Dirty Reads (i.e., not have repeatable reads)

Play some games with tx_isolation variable within a session
Try READ_COMMITTED and READ_UNCOMMITTED

Click here to see syntax for setting Isolation Level in a Session
Click here to see how there was once a bug concerning this within a Session and the warning on how to use it carefully

Otherwise, just permamnently set the following in /etc/my.cnf (Example)

[mysqld]
transaction_isolation=read-committed

Give it a try !!!

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!