有没有选择方法而不会导致锁定MySQL?


126

查询:

SELECT COUNT(online.account_id) cnt from online;

但是联机表也会被事件修改,因此经常可以通过运行看到锁show processlist

MySQL中是否有任何语法可以使select语句不引起锁定?

而且我在上面已经忘记了它在MySQL从数据库中。

在我添加到my.cnf:transaction-isolation = READ-UNCOMMITTED slave后会遇到错误:

错误“无法进行二进制日志记录。消息:InnoDB中的事务级别“ READ-UNCOMMITTED”对于查询中的二进制模式“ STATEMENT”不安全

那么,是否有兼容的方法可以做到这一点?


3
对于其他遇到此问题并难以使用表锁的人:mySQL在内部如何使用锁取决于存储引擎。阅读下面@zombat的答案。
西蒙·佛斯伯格

Answers:


169

找到标题为“ MYSQL WITH NOLOCK”的文章

https://web.archive.org/web/20100814144042/http://sqldba.org/articles/22-mysql-with-nolock.aspx

在MS SQL Server中,您可以执行以下操作:

SELECT * FROM TABLE_NAME WITH (nolock)

和MYSQL等效的是

SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED ;
SELECT * FROM TABLE_NAME ;
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ ;

编辑

迈克尔·米尔Michael Mior)建议以下内容(来自评论)

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED ;
SELECT * FROM TABLE_NAME ;
COMMIT ;

54
只是给将来的读者一个注释,您可能希望消除它们SESSION,从而使事务级别仅适用于下一个事务。然后,只需将上面的第三条语句替换为COMMIT。在这种情况下,这将是无事可做的,但是具有结束事务并将其重置为默认隔离级别的副作用。
Michael Mior

3
请注意,该链接已失效... :(
longda 2012年

5
抱歉,但由于我在这里没有提到InnoDB和MyISAM之间非常重要的区别,因此我不得不对此答案表示反对。如上面@omg所述,这将适用于InnoDB,但不适用于MyISAM表。
西蒙·佛斯伯格

4
@Craig这当然是不准确的说的MyISAM是不是在SELECT查询发出READ锁-有锁,反对InnoDB的那些锁表锁,阻止所有请求写锁执行过程中的所有后续查询。最初的问题似乎与InnoDB有关,并且MyISAM也不存在隔离级别- 语句状态的文档SET TRANSACTION指出:“此语句设置用于InoDB表操作的事务隔离级别。”
syneticon-dj

1
点承认。:-)我实际上是在尝试引用MyISAM与InnoDB的锁定行为。这些隔离基于级别的解决方案并不适用于MyISAM数据,这是不是事务性的,所以它使用一个简单的表锁。MyISAM UPDATE和DELETE必须等待表锁被清除,因此任何后续的SELECT队列都将在写请求之后,直到写操作被阻塞为止。MyISAM没有“脏读”,也没有办法允许大多数写与读同时发生,因此,这里的任何评论“都无法解决MyISAM”毫无意义。我想这就是我的意思。:-)
Craig


16

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED.

5.0版Docs在这里

版本5.1 Docs在这里


2
谢谢,我认为它已经接近了,但是此声明要花多长时间?我将在PHP程序中使用此语句,并且最好在查询完成后自动重置TRANSACTION ISOLATION LEVEL
omg

14

您可能需要阅读MySQL手册的这一页。锁定表的方式取决于表的类型。

MyISAM使用表锁来达到很高的读取速度,但是如果您有一个UPDATE语句正在等待,则将来的SELECTS将排在UPDATE之后。

InnoDB表使用行级锁定,并且您不会在UPDATE之后将整个表锁定。InnoDB还存在其他类型的锁定问题,但您可能会发现它适合您的需求。


1
MyISAM表是否可以使用“ SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED”?
OMG

5
MyISAM表不支持任何形式的事务。事务查询将在MyISAM表上运行,因此您上面提到的查询将执行,但无效。
zombat

1
那么,如何避免在MyISAM情况下SELECT排队?
omg,2009年

6
在MyISAM的情况下,如何避免SELECT排队? 切换到innodb。MyISAM对每个查询使用表级锁。那是主要缺陷。
弗兰克·法默

2

根据表的类型,锁定的执行方式会有所不同,但是SELECT计数也会有所不同。对于MyISAM表,简单的SELECT count(*)FROM表不应锁定该表,因为它访问元数据以提取记录数。Innodb将花费更长的时间,因为它必须抓取快照中的表以对记录进行计数,但是它不会引起锁定。

您至少应该将parallel_insert设置为1(默认值)。然后,如果数据文件中没有要填充的表“间隙”,则将在文件后附加插入,并且MyISAM表可以同时发生SELECT和INSERT。请注意,删除记录会在数据文件中放置一个“间隙”,该间隙将尝试在以后的插入和更新中填充。

如果很少删除记录,则可以将parallel_insert设置为2,并且插入将始终添加到数据文件的末尾。然后可以同时进行选择和插入,但是无论您删除多少记录(所有记录除外),您的数据文件都不会变小。

最重要的是,如果在表上进行了大量更新,插入和选择,则应将其设置为InnoDB。但是,您可以在系统中自由混合表类型。


1

在mysql中启用脏读的另一种方法是添加提示:LOCK IN SHARE MODE

SELECT * FROM TABLE_NAME LOCK IN SHARE MODE; 

“如果将自动提交设置为1,则LOCK IN SHARE MODE和FOR UPDATE子句无效。” ...并且默认为autocommit = 1
MartinZvarík

0

这个参考:

如果使用LOCK TABLES显式获取表锁,则可以在锁定表的同时请求READ LOCAL锁而不是READ锁,以使其他会话能够执行并发插入。


0

SELECT通常不会在InnoDB表上执行您关心的任何锁定。默认的事务隔离级别意味着选择不锁定内容。

当然争执仍然发生。


1
我知道这篇文章很旧,但是这个答案太笼统了,有时是正确的。参见dev.mysql.com/doc/refman/5.0/en/innodb-locks-set.html。肯定为读取获取锁,具体取决于隔离级别。具体而言,在这种情况下,发布者正在处理复制的数据库,并明确声明他可以show processlist用来实际查看锁。因此可以安全地假设实际上已在使用锁。
Craig

1
答案永远是正确的。当然,有一些锁定-innodb内部使用了一些内部互斥锁(例如,innodb缓冲池互斥锁)。大多数用户并不关心或注意到这些锁,它们通常仅在DDL操作期间争用(例如,如果您有16G缓冲池,并在另一个线程中进行“删除表”)。但是默认情况下,它不需要任何行锁。我正是这个意思。答案还是很模糊。
MarkR

1
总是总是?如果将事务隔离级别设置为可序列化,或者select语句使用LOCK IN SHARE MODE并且禁用了自动提交怎么办?我知道许多(大多数/全部?)数据库服务器现在默认情况下使用快照隔离而不是真正的序列化,但是不是偶尔有理由要求强制进行可序列化的读取吗?但是听起来好像您在说在所有远程正常情况下,MySQL中的默认条件不会导致读取锁影响其他线程,所以不必担心您没有遇到的问题吗?我试图撤消我的弃权票,顺便说一句。抱歉...
Craig 2013年

3
我说“不正常”。我的意思是,如果您进行常规选择(没有FOR UPDATE或LOCK IN SHARE MODE),并使用默认的事务隔离级别。有一些更改隔离级别的有效案例,但是我只会在每个会话的基础上执行,而不是默认设置。
MarkR 2013年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.