Mysql 添加主键时死锁增加。为什么?
首先,一点必要的背景知识(请耐心听我说)。我是一名web应用程序的开发人员,使用MySQL实现持久性。我们通过为每个数据表创建审计跟踪表来实现审计日志记录。例如,我们可以为客户实体提供以下表定义:Mysql 添加主键时死锁增加。为什么?,mysql,sql,database,Mysql,Sql,Database,首先,一点必要的背景知识(请耐心听我说)。我是一名web应用程序的开发人员,使用MySQL实现持久性。我们通过为每个数据表创建审计跟踪表来实现审计日志记录。例如,我们可以为客户实体提供以下表定义: -- Data table definition. CREATE TABLE my_database.customers ( CustomerId INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, FirstName VARCHAR(255) NOT N
-- Data table definition.
CREATE TABLE my_database.customers (
CustomerId INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
FirstName VARCHAR(255) NOT NULL,
LastName VARCHAR(255) NOT NULL,
-- More data columns, removed for simplicity.
...
);
-- Audit table definition in separate schema.
CREATE TABLE my_database_audittrail.customers (
CustomerId INT(11) DEFAULT NULL,
FirstName VARCHAR(255) DEFAULT NULL,
LastName VARCHAR(255) DEFAULT NULL,
-- More data columns.
...
-- Audit meta data columns.
ChangeTime DATETIME NOT NULL,
ChangeByUser VARCHAR(255) NOT NULL
);
如您所见,审计表只是数据表加上一些元数据的副本。请注意,审核表没有任何键。例如,当我们更新客户时,我们的ORM会生成类似以下内容的SQL:
-- Insert a copy of the customer entity, before the update, into the audit table.
INSERT INTO my_database_audittrail.customers (
CustomerId,
FirstName,
LastName,
...
ChangeTime,
ChangeByUser)
)
SELECT
CustomerId,
FirstName,
LastName,
...
NOW(),
@ChangeByUser
FROM my_database.customers
WHERE CustomerId = @CustomerId;
-- Then update the data table.
UPDATE
my_database.customers
SET
FirstName = @FirstName,
LastName = @LastName,
...
WHERE CustomerId = @CustomerId;
CREATE TABLE my_database_audittrail.customers (
__auditId INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
CustomerId INT(11) DEFAULT NULL,
FirstName VARCHAR(255) DEFAULT NULL,
LastName VARCHAR(255) DEFAULT NULL,
...
ChangeTime DATETIME NOT NULL,
ChangeByUser VARCHAR(255) NOT NULL
);
这已经足够有效了。但是,最近出于各种原因,我们需要向审计表中添加一个主键列,将审计表定义更改为类似于以下内容:
-- Insert a copy of the customer entity, before the update, into the audit table.
INSERT INTO my_database_audittrail.customers (
CustomerId,
FirstName,
LastName,
...
ChangeTime,
ChangeByUser)
)
SELECT
CustomerId,
FirstName,
LastName,
...
NOW(),
@ChangeByUser
FROM my_database.customers
WHERE CustomerId = @CustomerId;
-- Then update the data table.
UPDATE
my_database.customers
SET
FirstName = @FirstName,
LastName = @LastName,
...
WHERE CustomerId = @CustomerId;
CREATE TABLE my_database_audittrail.customers (
__auditId INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
CustomerId INT(11) DEFAULT NULL,
FirstName VARCHAR(255) DEFAULT NULL,
LastName VARCHAR(255) DEFAULT NULL,
...
ChangeTime DATETIME NOT NULL,
ChangeByUser VARCHAR(255) NOT NULL
);
ORM在更新数据表时生成的SQL没有被修改。这一变化似乎大大增加了僵局的风险。所讨论的系统是一个web应用程序,具有许多夜间批处理作业。死锁的增加并没有体现在我们的网络用户对系统的日常使用中。然而,夜间批处理作业确实会受到死锁的影响,因为它们在一些数据库表上进行大量的工作。我们的“解决方案”是添加一个死锁重试策略(几乎没有争议),虽然这似乎很好,但我非常想理解为什么上述更改会增加死锁的风险那么多(如果我们能够以某种方式解决问题)
进一步资料:
- 我们的夜间批处理作业对数据表进行插入、更新和删除。仅对审核表执行插入
- 我们在out数据库事务上使用可重复读取隔离级别
- 在此更改之前,我们在运行夜间批处理作业时没有看到任何死锁
*** WAITING FOR THIS LOCK TO BE GRANTED:
TABLE LOCK table `my_database_audittrail`.`customers` trx id 24972756464 lock mode AUTO-INC waiting
我的印象是,为了避免在不同的事务中使用相同的自动增量值,自动增量是在任何事务之外处理的?但我想我们介绍的主键上的自动增量属性似乎是问题所在?这是推测
在具有索引的表中插入或更新不仅会锁定数据页,还会锁定索引页,包括更高级别的索引。当多个线程同时影响记录时,它们可能会锁定索引的不同部分
这通常不会出现在单记录插入中。但是,正在更新多个记录的两条语句可能会开始获取索引上的锁,并发现它们彼此处于死锁状态。重试可能足以解决此问题。另一方面,似乎“太多”可能同时运行,你可能想考虑每晚更新工作是如何安排的。 < P>这是猜测。
在具有索引的表中插入或更新不仅会锁定数据页,还会锁定索引页,包括更高级别的索引。当多个线程同时影响记录时,它们可能会锁定索引的不同部分
这通常不会出现在单记录插入中。但是,正在更新多个记录的两条语句可能会开始获取索引上的锁,并发现它们彼此处于死锁状态。重试可能足以解决此问题。另一方面,似乎“太多”可能同时运行,您可能想考虑每晚更新工作是如何安排的。
插入到具有自动增量列的表中时,MySQL使用不同的策略来获取自动增量列的值,这取决于插入的类型,在insert语句和MySQL如何配置以处理自动增量列上,insert可能会导致一个完整的表锁
使用“简单插入”,即MySQL可以事先确定要插入到表中的行数的插入(例如插入到表中(col1,col2)值(val1,val2);)自动增量列值是使用自动增量计数器上的轻量级锁获取的。一旦获得自动增量值,就会释放此轻量锁,这样就不必等到实际插入完成。也就是说,没有桌子锁 但是,对于“批量插入”,MySQL无法确定之前插入的行数(例如,插入到表中(col1,col2)从表2中选择col1,col2,其中…;),将创建一个表锁以获取自动增量列值,并且在插入完成之前不会放弃以上是MySQL的默认配置。MySQL可以配置为在大容量插入时不使用表锁,但这可能会导致自动增量列在主列和从列上具有不同的值(如果设置了复制),因此可能是可行的选项,也可能不是可行的选项。插入具有自动增量列的表时,MySQL使用不同的策略获取自动递增列的值,具体取决于插入的类型、insert语句以及MySQL处理自动递增列的配置方式,insert可能会导致完整的表锁 使用“简单插入”,即MySQL可以事先确定要插入到表中的行数的插入(例如插入到表中(col1,col2)值(val1,val2);)自动增量列值是使用自动增量计数器上的轻量级锁获取的。一旦获得自动增量值,就会释放此轻量锁,这样就不必等到实际插入完成。也就是说,没有桌子锁 但是,对于“批量插入”,MySQL无法确定之前插入的行数(例如,插入到表中(col1,col2)从表2中选择col1,col2,其中…;)会为