【问题标题】:MySQL Select for Update and insertMySQL 选择更新并插入
【发布时间】:2018-02-25 10:54:30
【问题描述】:

我正在运行一个多线程应用程序,并且在不同线程尝试访问特定表中的相同记录时遇到问题。

线程 A - 想要表“LastOperations”中的最后一条记录,因此按 ID desc LIMIT 1 执行 Select For Update 订单。 - 根据 LastOperations 表中的最后一条记录创建一条新记录并插入回其中。

线程 B - 想要表“LastOperations”中的最后一条记录,因此执行与线程 A 相同的活动。

两个线程是相同的代码。只是使用不同的数据进行操作。此表上一次最大活动线程数为 4-6。

问题 如果线程 A 锁定最后一条 id 为 1000 的记录,线程 B 将获取 999 条记录并将新条目添加到表中。 ID 字段是一个自增字段,也是主键。

假设线程 1 购买原材料 A,线程 b 购买原材料 B。两者都应从同一帐户中扣除最新的条目。发生的事情是一个线程从帐户中获取较旧的条目,导致帐户余额错误。

关于我缺少什么的任何建议? DB 端需要进行任何更改吗?

【问题讨论】:

  • 真正的问题似乎是您自己生成了ID。让数据库为你做这件事
  • 您的问题不清楚您要做什么。两个线程真的在做同样的事情吗?您指出线程 B 执行与线程 A 相同的活动。
  • ID 由 DB 自动递增。
  • 假设线程 1 购买原材料 A,线程 b 购买原材料 B。两者都应从同一个帐户中扣除最新的条目。发生的事情是一个线程从帐户中获取较旧的条目,导致帐户余额错误。
  • 您能提供更多信息吗?两个线程都在更新基础表吗?如果是这样,那么我相信在更新完成之前应该有一个表锁。否则数据库可能会变得非常混乱。另外,您能否提供您正在使用的事务隔离级别?

标签: java mysql multithreading select insert-update


【解决方案1】:

我相信您遇到的问题是Transaction Isolation Level。线程 A 没有完成创建它的记录。所以,当线程 B 从“LastOperations”中获取最新记录为 999 时,999 确实是表中的最后一条记录。

您可以将事务隔离级别更改为 READ UNCOMMITTED(如果您可以控制 MySQL 实例),但这样做有风险。如果允许脏读,那么线程 A 生成的完整记录可能不会在数据库中最终确定。 (因此,“脏读”中的“脏”。)

【讨论】:

  • 在数据库端完成。仍然面临问题。
  • 同意脏读部分。帐户主要是每个人都需要的表格...这已成为故障点
  • 您的语句是否在事务中的这些线程中执行?也就是说,您正在执行的语句是否以BEGIN TRANSACTION 后跟COMMITROLLBACK 为界?
  • 是的。使用事务。线程负责启动和提交/回滚
【解决方案2】:

不要混淆历史和当前。有两张桌子:

  • History 列出所有操作;它主要是INSERTed 进入。
  • Current 列出了当前余额。主要是UPDATEd

当一个动作发生时:

BEGIN;
SELECT ... FROM Current ... FOR UPDATE;
INSERT INTO History ...;
UPDATE Current ...;
COMMIT;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-17
    • 2012-02-07
    • 2013-05-24
    • 1970-01-01
    相关资源
    最近更新 更多