有什么方法可以在不导致 MySQL 锁定的情况下进行选择吗?

质疑:

SELECT COUNT(online.account_id) cnt from online;

但是在线表也会被一个事件修改,所以我经常可以通过运行 show processlist看到锁。

MySQL 中是否有任何语法可以使 select 语句不导致锁?

我忘了在上面提到它在 MySQL 从数据库中。

在我加入 my.cnf:transaction-isolation = READ-UNCOMMITTED之后 奴隶会遇到错误:

错误‘二进制日志记录不可能。消息: InnoDB 中的事务级别‘ READ-UNCOMMITTED’对于查询时的 blog 模式‘ STATEMENT’是不安全的

那么,有没有一种兼容的方法可以做到这一点呢?

274782 次浏览

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

MyISAM 使用表锁来实现非常高的读取速度,但是如果您有一个 UPDATE 语句在等待,那么未来的 SELECTS 将在 UPDATE 后面排队。

InnoDB 表使用行级锁定,并且不会在 UPDATE 后面锁定整个表。与 InnoDB 相关的还有其他类型的锁定问题,但是您可能会发现它适合您的需要。

来自 这个的参考文献:

如果显式获取表锁 使用锁定表,您可以请求一个 读取本地锁而不是读取 将其他会话锁定到 执行并发插入 把桌子锁上。

如果该表是 InnoDB,请参阅 http://dev.mysql.com/doc/refman/5.1/en/innodb-consistent-read.html——它对 SELECT 使用一致读(无锁模式) ,如果设置了 inodb _ LOCK _ safe _ FOR _ binlog 选项且事务的隔离级别没有设置为 SERIALIZABLE,则 SELECT“不指定 FOR UPDATE 或 LOCK IN SHARE MODE。因此,对从选定表中读取的行不设置锁”。

使用

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED.

版本5.0文档是 给你

版本5.1文件为 给你

引用链接文档: 设置事务特征(如上所示) ..。

没有任何 SESSION 或 GLOBAL 关键字:

  • 该语句仅适用于下一个执行的事务 在会议期间。

  • 后续事务恢复为使用命名的 性格。

  • 在事务中不允许使用该语句

根据表的类型,锁定的执行方式不同,但 SELECT 计数也不同。对于 MyISAM 表,一个简单的 SELECT count (*) FROM 表不应该锁定表,因为它访问元数据来获取记录计数。Innodb 将花费更长的时间,因为它必须在快照中获取表来计算记录,但这不会导致锁定。

您至少应该将 while _ insert 设置为1(默认值)。然后,如果数据文件中没有要填充的“空白”,则将向文件添加插入,并且 SELECT 和 INSERT 可以与 MyISAM 表同时发生。请注意,删除记录会在数据文件中留下一个“空白”,这个空白将尝试用以后的插入和更新来填充。

如果您很少删除记录,那么您可以将 current _ insert 设置为2,插入将始终添加到数据文件的末尾。然后可以同时进行选择和插入,但是无论删除多少记录(除了所有记录) ,数据文件永远不会变小。

底线是,如果对一个表进行了大量的更新、插入和选择,那么应该将其设置为 InnoDB。不过,您可以在系统中自由地混合表类型。

找到一篇题为“ MYSQL WITHNOLOCK”的文章

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

在 MSSQLServer 中,您将执行以下操作:

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 ;

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

当然,争论仍然存在。

在 mysql 中启用脏读的另一种方法是添加提示: 锁定共享模式

SELECT * FROM TABLE_NAME LOCK IN SHARE MODE;

这里有一个可供选择的编程解决方案,可能适用于其他使用 MyISAM IF (重要)的用户,因为您不关心在查询过程中是否发生了更新。正如我们所知道的,MyISAM 可能导致表级别锁定,特别是如果您有一个将被锁定的更新挂起,然后这个更新后面的其他选择查询也会被锁定。

所以这个方法不会阻止锁,但是它会做很多很小的锁,例如,不会挂起一个需要在很短时间内响应的网站。

这里的想法是,我们基于一个快速的索引获取一个范围,然后我们只从该查询进行匹配,因此它是在较小的批处理中。然后我们把名单向下移动到下一个范围,检查它们是否匹配。

示例在 Perl 中使用了一些伪代码,可以从高到低遍历。


# object_id must be an index so it's fast
# First get the range of object_id, as it may not start from 0 to reduce empty queries later on.


my ( $first_id, $last_id ) = $db->db_query_array(
sql => q{ SELECT MIN(object_id), MAX(object_id) FROM mytable }
);


my $keep_running = 1;
my $step_size    = 1000;
my $next_id      = $last_id;


while( $keep_running ) {


my $sql = q{
SELECT object_id, created, status FROM
( SELECT object_id, created, status FROM mytable AS is1 WHERE is1.object_id <= ? ORDER BY is1.object_id DESC LIMIT ? ) AS is2
WHERE status='live' ORDER BY object_id DESC
};


my $sth = $db->db_query( sql => $sql, args => [ $step_size, $next_id ] );


while( my ($object_id, $created, $status ) = $sth->fetchrow_array() ) {


$last_id = $object_id;
        

## do your stuff


}


if( !$last_id ) {
$next_id -= $step_size; # There weren't any matched in the range we grabbed
} else {
$next_id = $last_id - 1; # There were some, so we'll start from that.
}


$keep_running = 0 if $next_id < 1 || $next_id < $first_id;
    

}