【问题标题】:Is there a way to GUARANTEE non-blocking reads in MySQL?有没有办法保证 MySQL 中的非阻塞读取?
【发布时间】:2010-12-09 19:49:44
【问题描述】:

我已经尝试了明显的“SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED”,但是当更新正在进行时(当与某些复杂的更新事务同时运行时,我的简单存储过程仍然会被阻塞)在 PRIMARY KEY 上执行我不想修改)——最终会遇到死锁和锁定超时。

当然,必须有一种方法来保证非阻塞读取......我认为这就是 READ-UNCOMMITTED 的目的。但我错了……这是一个 MySQL 错误吗?有解决办法吗?

我知道 READ-UNCOMMITTED 的所有危险和学术上不健全的属性,但这没关系,对于我的特定应用程序,这里偶尔会出现幻影或缺失行,实际上没什么大不了的,但延迟或错误由读锁引起的问题要严重得多。

数据库中的所有表都是 InnoDB。 服务器版本为 5.0.67。平台是 Linux 32 位。

更新这是一个简化的“hello world”版本的问题描述(我的实际查询过于复杂和丑陋,无法发布):

控制台 1:

mysql> create table t1(a int primary key) engine=innodb; 查询正常,0 行受影响(0.20 秒) mysql> 插入 t1 值 (1),(2),(3); 查询正常,3 行受影响(0.03 秒) 记录:3 重复:0 警告:0 mysql>开始事务; 查询正常,0 行受影响(0.00 秒) mysql> 插入 t1 值 (4); 查询正常,1 行受影响(0.01 秒) mysql> 更新 t1 设置 a=5 其中 a=4; 查询正常,1 行受影响(0.00 秒) 匹配行:1 更改:1 警告:0

CONSOLE 2(在单独的窗口中,不要关闭 CONSOLE 1)

mysql> select max(a) from t1;
+--------+
|最大(一)|
+--------+
| 3 |
+--------+
一组中的 1 行(0.00 秒)

mysql> set @test = (select max(a) from t1);
ERROR 1205 (HY000): 超过锁定等待超时;尝试重启事务

【问题讨论】:

  • 您有索引来“覆盖”您的查询工作负载吗?
  • Mitch:作为我调查重点的简单查询的索引足以涵盖 WHERE 条件,但不能涵盖所有返回的列。
  • 您的读取在这里是非阻塞的,它是阻塞的插入和更新。如果没有主从设置或类似设置,或者按照 James 的建议修改长期运行的 UPDATE,则无法解决此问题。
  • 本:你的假设不正确。我已经通过“显示引擎 innodb 状态”深入了解了这一点,而且绝对是这样,我的读取不是非阻塞的(即读取实际上是被阻塞的)。我们所读到的关于 InnoDB 的所有内容都是错误的 :(

标签: sql mysql


【解决方案1】:

终于明白了:

“这是一个 MySQL 错误吗?” --> 是的,我称之为错误。其他人可能称之为限制或“陷阱”。我将其称为 BUG,因为很明显,在没有锁定的情况下检索此数据的理论基础和实际能力已被一种主要是语法变通方法的存在所证明。

“有解决方法吗?” --> 是的。

重写

set @test = (select max(a) from t1);

这样

select max(a) from t1 into @test;

当另一个事务没有运行时产生相同的结果;并在另一个事务正在运行时产生预期的结果(值立即成功检索,而不是死在锁上)。

【讨论】:

    【解决方案2】:

    如果你想确保你的更新永远不会被你的选择阻塞,那么我建议有两个数据库,主数据库是插入/更新发生的地方,通过复制将数据发送到从属数据库,你在哪里做你的选择。

    这应该会限制选择的任何问题,因为复制非常快,因此您的选择可以随心所欲地复杂,并且永远不会影响更新。

    不幸的是,即使您刚刚进行了行锁定,您可能仍然会遇到问题,因为一个查询在尝试从该表中读取时正在写入该表。

    更新:在你投反对票之前,他最近刚刚提出了查询和错误消息,所以现在他可以帮助解决问题,所以我的回答没有错,因为他开始了.

    【讨论】:

    • 我的问题是选择被更新阻止,而不是你上面所说的相反。事实证明,我的选择很简单,但更新很复杂。无论如何,复制非常昂贵(不仅仅是硬件,主要是管理时间!)所以我当然希望有更好的方法。另外我怀疑基于语句的复制不会解决任何问题,因为相同的更新会在从属服务器上运行。
    • 如果您正在进行插入,那么除非插入完成,否则 select Max(...) 将如何找到新行?你是对的,但如果问题是插入阻塞选择,复制将无济于事。设置复制后,管理时间非常短。你能展示一个选择和插入/更新的示例吗?
    • James:找到新行不是问题。我不太关心最新的未提交行是否包含在结果集中。我想要的只是一些结果集,无论是否有未提交的行,它都会比锁定超时更好。实际的选择是 SET @LastImport = (select MAX(IMPORT_FILE_DATE) from idx_datafeed_file_commit);冲突的插入/更新太难看,无法在这里发布,我必须想出一个“hello world”版本。
    • 感谢您的帮助...如果您不一直坚持我必须发布查询,我就不会创建简化的“hello world”示例,这会使问题和解决方法变得更加容易最终确定。
    • 你应该发布解决方案,顺便说一句,以防其他人被卡住。
    【解决方案3】:

    默认情况下,InnoDB SELECT 通常是非锁定的(这样它们可以运行尽可能多的 DML 语句)。因此,似乎除了正常的 SELECT 和 DML 语句之外还有其他事情正在发生。

    也许您正在执行 INSERT...SELECT 语句或类似的操作?你能发布你的存储过程吗?

    【讨论】:

    • 这不是 INSERT...SELECT;只是一个常规的 INSERT 后跟 UPDATES,它持有另一个会话的锁。
    猜你喜欢
    • 2012-04-13
    • 1970-01-01
    • 2011-07-15
    • 2023-04-05
    • 1970-01-01
    • 1970-01-01
    • 2022-08-24
    • 1970-01-01
    • 2020-04-15
    相关资源
    最近更新 更多