8.11.1 内部锁定方法
本节讨论内部锁定。也就是说,在 MySQL 服务器内部执行锁定,以 Management 多个会话对 table 内容的争用。这种类型的锁定是内部锁定,因为它完全由服务器执行,并且不涉及其他程序。有关其他程序对 MySQL 文件执行的锁定,请参见第 8.11.5 节“外部锁定”。
Row-Level Locking
MySQL 使用row-level locking到InnoDB
table 来支持多个会话的同时写访问,从而使其适用于多用户,高并发和 OLTP 应用程序。
为避免在单个InnoDB
table 上执行多个并发写操作时出现deadlocks,请在事务开始时通过为预期要修改的每行行发出SELECT ... FOR UPDATE
语句来获取必要的锁,即使数据更改语句稍后在事务中。如果事务修改或锁定了多个 table,请在每个事务中以相同 Sequences 发出适用的语句。死锁会影响性能,而不是代 table 严重的错误,因为InnoDB
自动detects死锁条件并回滚受影响的事务之一。
在高并发系统上,当多个线程 await 相同的锁时,死锁检测会导致速度变慢。有时,禁用死锁检测并在发生死锁时依靠innodb_lock_wait_timeout设置进行事务回滚可能会更有效。可以使用innodb_deadlock_detect配置选项禁用死锁检测。
行级锁定的优点:
-
当不同的会话访问不同的行时,锁冲突减少。
-
回滚更改较少。
-
可以长时间锁定单个行。
Table-Level Locking
MySQL 将table-level locking用于MyISAM
,MEMORY
和MERGE
table,一次只允许一个会话更新这些 table。此锁定级别使这些存储引擎更适合于只读,只读或单用户应用程序。
这些存储引擎通过始终在查询开始时一次请求所有必需的锁并始终以相同 Sequences 锁定 table 来避免deadlocks。权衡是该策略减少了并发性。其他要修改 table 的会话必须 await,直到当前数据更改语句完成为止。
table 级锁定的优点:
-
所需的内存相对较少(行锁定需要锁定每行或每组行的内存)
-
在 table 的大部分上使用时非常快,因为只涉及一个锁。
-
如果您经常对大部分数据执行
GROUP BY
操作,或者必须经常扫描整个 table,则速度很快。
MySQL 授予 table 写锁定,如下所示:
-
如果 table 上没有锁,请在其上放置写锁。
-
否则,将锁定请求放入写锁定队列中。
MySQL 授予 table 读取锁,如下所示:
-
如果 table 上没有写锁,请在其上放置一个读锁。
-
否则,将锁定请求放入读取锁定队列中。
table 更新的优先级高于 table 检索。因此,释放锁时,该锁可用于写锁队列中的请求,然后可用于读锁队列中的请求。这样可以确保即使 table 有大量的SELECT活动,对 table 的更新也不会“饿死”。但是,如果一个 table 有很多更新,则SELECT语句将等到没有更多更新。
有关更改读写优先级的信息,请参阅第 8.11.2 节“table 锁定问题”。
您可以通过检查状态变量Table_locks_immediate和Table_locks_waited来分析系统上的 table 锁争用,这两个变量分别指示可以立即授予 table 锁请求的次数和必须 await 的次数:
mysql> SHOW STATUS LIKE 'Table%';
+-----------------------+---------+
| Variable_name | Value |
+-----------------------+---------+
| Table_locks_immediate | 1151552 |
| Table_locks_waited | 15324 |
+-----------------------+---------+
性能架构锁定 table 还提供锁定信息。参见第 25.12.12 节,“性能模式锁定 table”。
MyISAM
存储引擎支持并发插入,以减少给定 table 的读取器和写入器之间的争用:如果MyISAM
table 在数据文件的中间没有空闲块,则始终在数据文件的末尾插入行。在这种情况下,您可以为MyISAM
table 自由混合并发的INSERT和SELECT语句而无需锁定。也就是说,您可以在其他 Client 端读取MyISAM
table 的同时插入行。从 table 的中间删除或更新的行可能会导致漏洞。如果有孔,将禁用并发插入,但是当所有孔都已填充新数据时,并发插入会自动重新启用。若要控制此行为,请使用concurrent_insert系统变量。参见第 8.11.3 节“并发插入”。
如果您使用LOCK TABLES显式获取 table 锁,则可以请求READ LOCAL
锁而不是READ
锁,以使其他会话在锁定 table 的同时执行并发插入。
要在无法同时插入 tablet1
时对 tablet1
执行许多INSERT和SELECT操作,可以将行插入到临时 tabletemp_t1
中,并使用临时 table 中的行更新实际 table:
mysql> LOCK TABLES t1 WRITE, temp_t1 WRITE;
mysql> INSERT INTO t1 SELECT * FROM temp_t1;
mysql> DELETE FROM temp_t1;
mysql> UNLOCK TABLES;
选择锁定类型
通常,在以下情况下,table 锁优于行级锁:
-
该 table 的大多数语句均为读取。
-
该 table 的语句是读和写的混合,其中写是对单行的更新或删除,可通过一次按键读取来获取:
UPDATE tbl_name SET column=value WHERE unique_key_col=key_value;
DELETE FROM tbl_name WHERE unique_key_col=key_value;
使用高级锁,您可以通过支持不同类型的锁来更轻松地调整应用程序,因为锁开销比行级锁要少。
行级锁定以外的选项:
-
版本控制(例如在 MySQL 中用于并发插入的版本控制),可以同时拥有一个编写者和多个阅 Reader。这意味着数据库或 table 根据访问开始的时间支持数据的不同视图。其他常用术语是“时间旅行”,“写时复制”或“按需复制”。
-
在许多情况下,按需复制优于行级锁定。但是,在最坏的情况下,与使用普通锁相比,它可以使用更多的内存。
-
除了使用行级锁,您还可以使用应用程序级锁,例如 MySQL 中的GET_LOCK()和RELEASE_LOCK()提供的锁。这些是咨询锁,因此它们仅适用于相互协作的应用程序。参见第 12.14 节“锁定功能”。