【发布时间】:2011-02-18 22:36:43
【问题描述】:
关于锁定 innodb 的典型文档太混乱了。我认为有一个“innodb 锁定的傻瓜指南”会很有价值
我将开始,并将所有响应收集为一个 wiki:
- 在应用行级锁定之前需要对该列进行索引。
- 示例:删除 column1=10 的行;除非 column1 被索引,否则将锁定表
【问题讨论】:
关于锁定 innodb 的典型文档太混乱了。我认为有一个“innodb 锁定的傻瓜指南”会很有价值
我将开始,并将所有响应收集为一个 wiki:
【问题讨论】:
以下是我使用 MySQL 支持解决最近一个奇怪的锁定问题(版本 5.1.37)的笔记:
所有经过遍历以到达被更改行的行和索引条目都将被锁定。它涵盖在:
http://dev.mysql.com/doc/refman/5.1/en/innodb-locks-set.html
“锁定读取、UPDATE 或 DELETE 通常会在 SQL 语句处理过程中扫描的每条索引记录上设置记录锁。语句中是否存在将排除该行的 WHERE 条件无关紧要. InnoDB 不记得确切的 WHERE 条件,而只知道扫描了哪些索引范围。 ... 如果您没有适合您的语句的索引并且 MySQL 必须扫描整个表来处理该语句,则表的每一行都会被锁定,这反过来又会阻止其他用户对表的所有插入。”
如果属实,那真是令人头疼。
是的。通常有用的解决方法是:
UPDATE whichevertable 将任何内容设置为primarykey所在的内容(从whichevertable中选择primarykey,其中约束按primarykey排序);
内部选择不需要加锁,更新后要做的工作就更少了。 order by 子句确保更新以主键顺序完成,以匹配 InnoDB 的物理顺序,这是最快的方式。
在涉及大量行的情况下,如您的情况,最好将选择结果存储在添加了标志列的临时表中。然后从没有设置标志的临时表中选择获取每个批次。以 1000 或 10000 的限制运行更新,并在更新后为批次设置标志。这些限制将锁定量保持在可容忍的水平,而选择工作只需完成一次。在每批之后提交以释放锁。
您还可以通过在执行每批更新之前对未索引的列进行选择和来加快这项工作。这会将数据页加载到缓冲池中而不需要锁定。然后锁定将持续更短的时间跨度,因为不会有任何磁盘读取。
这并不总是实用的,但如果它是非常有用的。如果不能批量执行,至少可以尝试先选择先加载数据,前提是它足够小以放入缓冲池中。
如果可能,请使用 READ COMMITTED 事务隔离模式。见:
http://dev.mysql.com/doc/refman/5.1/en/set-transaction.html
要减少锁定需要使用基于行的二进制日志记录(而不是基于默认语句的二进制日志记录)。
两个已知问题:
有时子查询的优化可能不够理想。在这种情况下,它是一个不受欢迎的依赖子查询 - 因此,与本例中的替代方法相比,我提出的使用子查询的建议没有帮助。
删除和更新与 select 语句的查询计划范围不同,因此有时如果不测量结果以准确了解它们在做什么,就很难正确优化它们。
这两者都在逐渐改善。此错误是我们刚刚改进了可用于更新的优化的一个示例,尽管这些更改非常重要,并且仍在通过 QA 以确保它不会产生任何严重的不利影响:
【讨论】: