【问题标题】:Locking a table for inserts from a MySQL InnoDB Stored Procedure为 MySQL InnoDB 存储过程中的插入锁定表
【发布时间】:2017-01-15 11:52:46
【问题描述】:

我正在编写一个存储过程。在其中我有以下 mysql 插入语句:

INSERT INTO `Address`(`countryCode`, `addressLine1`, `addressLine2`, `postcode`, `region`, `city`) VALUES (country, addressLine1, addressLine2, postcode, region, city);
INSERT INTO `UserAddress`(`username`, `creationDate`, `addressId`) VALUES (username,NOW(),(SELECT addressId FROM Address ORDER BY addressId DESC LIMIT 1));

如您所见,我正在对地址表执行插入操作,然后在下一行的子查询中使用自动递增的“addressId”。现在,如果在这两个语句之间,另一个事务将向 Address 表中插入一些内容,我会将错误的 addressId 插入到 UserAddress 表中。我的猜测是我需要在执行这些语句时锁定地址表。

经过长时间的搜索,我找到了以下代码来锁定地址表:

SELECT addressId FROM Address FOR UPDATE;

这也适用于新插入的行吗?如果没有,会怎样?

【问题讨论】:

  • 您是否总是将两行插入在一起?如果是这样,为什么将它们插入单独的表中?我认为你的设计有问题。
  • 如果你真的需要这样做,我可能会考虑使用事务。但正如@GurV 所说,您可能遇到了设计问题。
  • @GurV 这是一个设计问题吗?这个想法是用户可以有多个地址。此外,一个地址可以是多个用户。此外,地址具有创建日期。我需要在 Address 和 User 表之间有一个表,对吗?
  • 代码,如所写,仅处理 1:1。它不允许 1:many、many:1 和 many:many。您需要 IODKU、唯一键和/或它们的映射表。
  • 用户和地址之间存在多对多关系。中间的表是 UserAddress。我的代码仍然可以这样做吗?

标签: mysql stored-procedures concurrency innodb


【解决方案1】:

这正是他们发明LAST_INSERT_ID的原因

INSERT INTO `Address`(`countryCode`, `addressLine1`, `addressLine2`, `postcode`, `region`, `city`) VALUES (country, addressLine1, addressLine2, postcode, region, city);
INSERT INTO `UserAddress`(`username`, `creationDate`, `addressId`) VALUES (username,NOW(),LAST_INSERT_ID());

获取最后一个 id 的任务不需要锁或事务,甚至不需要存储过程。只需在您的应用中执行此操作即可。

【讨论】:

  • 那是并发安全的吗?太棒了!
  • 很高兴能帮上忙
  • LAST_INSERT_ID() 是本地连接;因此,“货币安全”。
  • 但我不同意不需要“交易”。没有这样的,如果服务器在两个自动提交的INSERTs 之间崩溃,您可以获得Address 存储但不是UserAddress
  • 是的,@RickJames 对交易有意见。不过,在我的回答中,我纯粹是在获取最后一个插入的 ID 的上下文中谈论。
【解决方案2】:

理想情况下,您应该在Transaction 中执行此操作,方法是使用“START TRANSACTION;”和“提交”语法。您还需要将autocommit 设置为 0(默认启用)。 Here 是事务和自动提交的文档。

只要您的更新包含在事务中并设置了适当的isolation level,其他事务所做的更改就不会影响您的事务。

【讨论】:

  • 将我的每个存储过程都包装在一个 START TRANSACTION 中是不是很好的设计;'和 'COMMIT 在过程中?
  • 除了需要隔离的位之外,不是所有的东西,就像你在问题中提到的那样。
  • 这样想... 在这个: 查询1:从您的银行帐户中取出一些钱;查询 2:将这笔钱存入您的储蓄账户。如果服务器在查询 1 和查询 2 之间崩溃怎么办?这是需要BEGIN; query 1; query 2; COMMIT; 以便both 成功或都不 的经典示例。如果你的应用有类似的东西,你就需要事务。
猜你喜欢
  • 2013-11-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-09
相关资源
最近更新 更多