InnoDB上的MySQL UPDATE操作偶尔会超时

时间:2014-02-21 14:49:15

标签: mysql timeout locking innodb

这些是InnoDB数据库中非常小的表上的简单UPDATE。有时,操作似乎锁定,并且不会超时。然后每个后续UPDATE以超时结束。现在唯一的办法就是让我的ISP重启守护进程。表中的每个字段都用于查询,因此所有字段都被编入索引,包括主要字段。

我不确定导致初始锁定的原因,我的ISP没有提供足够的信息来诊断问题。他们对于让我访问任何设置都很谨慎。

在以前的工作中,我被要求处理类似的信息,但我会做INSERT。我有一个脚本定期从表中运行DELETE个旧记录,因此不需要过滤那么多记录。当SELECT我使用外推技术时,不仅仅有最新的数据是有用的。这种设置坚如磐石,即使在非常繁重的使用情况下也不会挂起。

使用UPDATE和定期INSERT替换DELETE没问题,但它看起来很笨重。有没有人遇到类似的问题并更优雅地修复它?

当前配置

  • max_heap_table_size:16 MiB
  • count(*):4(不是拼写错误,四个记录!)
  • innodb_buffer_pool_size:1 GiB

修改:数据库现在失败了; locations有5条记录。下面的示例错误。

MySQL查询:

UPDATE locations SET x = "43.630181733", y = "-79.882244160", updated = NULL
    WHERE uuid = "6a5c7e9d-400f-c098-68bd-0a0c850b9c86";

MySQL错误:

错误#1205 - 超出锁定等待超时;尝试重新启动交易

locations
Field      Type         Null  Default
uuid       varchar(36)  No
x          double       Yes    NULL
y          double       Yes    NULL
updated    timestamp    No     CURRENT_TIMESTAMP 


Indexes:
Keyname    Type     Cardinality  Field
PRIMARY    PRIMARY  5            uuid
x          INDEX    5            x
y          INDEX    5            y
updated    INDEX    5            updated

3 个答案:

答案 0 :(得分:5)

这是InnoDB的一个已知问题,请参阅MySQL rollback with lost connection。我会欢迎那里提到的像innodb_rollback_on_disconnect这样的东西。你正在发生的事情是你早期断开连接,就像在网络上发生的那样,如果这发生在修改查询的中间,那么执行该操作的线程将挂起但保留对表的锁定。

现在,直接使用Web服务访问InnoDB很容易受到这些断开连接的影响,除了要求他们为您重启服务之外,您无法在FatCow中做任何事情。您使用MyISAM和低优先级的想法是可以的,并且可能不会出现此问题,但如果您想使用InnoDB,则会推荐类似以下的方法。

1)使用存储过程,然后保证事务运行完成,并且在断开连接时不挂起。这是一项很多工作,但是大大提高了可靠性。

2)不要依赖auto commit,理想情况下将其设置为零,并使用BEGIN TRANSACTIONCOMMIT明确地开始和结束每笔交易。

答案 1 :(得分:2)

transaction1> START TRANSACTION;
transaction1> SELECT * FROM t WHERE i > 20 FOR UPDATE;
+------+
| i |
+------+
| 21 |
| 25 |
| 30 |
+------+

transaction2> START TRANSACTION;
transaction2> INSERT INTO t VALUES(26);
transaction2> COMMIT;
transaction1> select * from t where i > 20 FOR UPDATE;

+------+
| i |
+------+
| 21 |
| 25 |
| 26 |
| 30 |
+------+

什么是差距锁?

  1. 间隙锁定是锁定索引记录之间的差距。谢谢 这个间隙锁,当你运行两次相同的查询时,你会得到相同的 结果,无论该表上的其他会话修改如何。

  2. 这使得读取保持一致,从而进行复制 服务器之间一致。如果您执行SELECT * FROM id> 1000 FOR UPDATE两次,你希望得到两次相同的值。

  3. 为了实现这一点,InnoDB锁定了所有索引记录 带有排它锁的WHERE子句和它们之间的间隙 共享间隙锁。

  4. 此锁定不仅影响SELECT ... FOR UPDATE。这是一个带有DELETE语句的示例:

    transaction1 > SELECT * FROM t;
    +------+
    | age |
    +------+
    | 21 |
    | 25 |
    | 30 |
    +------+
    

    启动交易并删除记录25:

    transaction1 > START TRANSACTION;
    transaction1 > DELETE FROM t WHERE age=25;
    

    此时我们假设只有记录25被锁定。然后,我们尝试在第二个会话中插入另一个值:

    transaction2 > START TRANSACTION;
    transaction2 > INSERT INTO t VALUES(26);
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    transaction2 > INSERT INTO t VALUES(29);
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    transaction2 > INSERT INTO t VALUES(23);
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    transaction2 > INSERT INTO t VALUES(31);
    Query OK, 1 row affected (0.00 sec)
    

    在第一个会话上运行delete语句后,不仅受影响的索引记录已被锁定,而且该记录之前和之后的间隙还有一个共享间隙锁,阻止将数据插入其他会话。

答案 2 :(得分:1)

如果您的UPDATE字面意思是:

UPDATE locations SET updated = NULL;

您正在锁定表格中的所有行。如果在对所有行保持锁定时放弃事务,当然所有行都将保持锁定状态。 InnoDB在您的环境中并不“不稳定”,它似乎正在按照您的要求进行操作。您不需要放弃开放交易。