### 8.11.1 内部锁定方法

[](<>)[](<>)[](<>)[](<>)[](<>)[](<>)[](<>)[](<>)[](<>)

本节讨论内部锁定；也就是说，在 MySQL 服务器内部执行锁定以管理多个会话对表内容的争用。这种类型的锁定是内部的，因为它完全由服务器执行，不涉及其他程序。有关其他程序对 MySQL 文件执行的锁定，请参阅[第 8.11.5 节，“外部锁定”](external-locking.html).

-   [行级锁定](internal-locking.html#internal-row-level-locking)

-   [表级锁定](internal-locking.html#internal-table-level-locking)

-   [选择锁定类型](internal-locking.html#internal-locking-choices)

#### 行级锁定

MySQL 使用[行级锁定](glossary.html#glos_row_lock)为了`InnoDB`表支持多个会话同时写入访问，使其适用于多用户、高并发和 OLTP 应用程序。

避免[死锁](glossary.html#glos_deadlock)在单个上执行多个并发写入操作时`InnoDB`表，在事务开始时通过发出一个获取必要的锁`选择 ... 更新`预期要修改的每组行的语句，即使数据更改语句在事务中稍后出现。如果事务修改或锁定多个表，则在每个事务中以相同的顺序发出适用的语句。死锁影响性能而不是代表严重错误，因为`InnoDB`自动地[检测到](glossary.html#glos_deadlock_detection)默认情况下出现死锁条件并回滚受影响的事务之一。

在高并发系统上，当多个线程等待同一个锁时，死锁检测会导致速度变慢。有时，禁用死锁检测并依赖[`innodb_lock_wait_timeout`](innodb-parameters.html#sysvar_innodb_lock_wait_timeout)发生死锁时事务回滚的设置。死锁检测可以使用禁用[`innodb_deadlock_detect`](innodb-parameters.html#sysvar_innodb_deadlock_detect)配置选项。

行级锁定的优点：

-   当不同的会话访问不同的行时，更少的锁冲突。

-   回滚的更改更少。

-   可以长时间锁定单行。

#### 表级锁定

MySQL 使用[表级锁定](glossary.html#glos_table_lock)为了`MyISAM`,`记忆`， 和`合并`表，一次只允许一个会话更新这些表。这种锁定级别使这些存储引擎更适合只读、主要读取或单用户应用程序。

这些存储引擎避免[死锁](glossary.html#glos_deadlock)通过始终在查询开始时一次请求所有需要的锁并始终以相同的顺序锁定表。权衡是这种策略降低了并发性；其他想要修改表的会话必须等到当前数据更改语句完成。

表级锁定的优点：

-   需要的内存相对较少（行锁定需要每行或每组锁定的行的内存）

-   在表的大部分上使用时速度很快，因为只涉及一个锁。

-   如果你经常这样做，那就快`通过...分组`对大部分数据进行操作或必须频繁扫描整个表。

    MySQL 授予表写锁如下：

1.  如果表上没有锁，则在其上放置写锁。

2.  否则，将锁请求放入写锁队列。

    MySQL 授予表读锁如下：

3.  如果表上没有写锁，则在其上放置读锁。

4.  否则，将锁请求放入读锁队列。

    表更新的优先级高于表检索。因此，当一个锁被释放时，锁对写锁队列中的请求可用，然后对读锁队列中的请求可用。这确保了对表的更新不会“饿死”，即使有大量的[`选择`](select.html)表的活动。但是，如果一个表有很多更新，[`选择`](select.html)语句等到没有更多更新。

    有关更改读取和写入优先级的信息，请参阅[第 8.11.2 节，“表锁定问题”](table-locking.html).

    您可以通过检查来分析系统上的表锁争用[`Table_locks_immediate`](server-status-variables.html#statvar_Table_locks_immediate)和[`Table_locks_waited`](server-status-variables.html#statvar_Table_locks_waited)状态变量，分别表示可以立即授予表锁请求的次数和必须等待的次数：


```
mysql> SHOW STATUS LIKE 'Table%';
+-----------------------+---------+
| Variable_name         | Value   |
+-----------------------+---------+
| Table_locks_immediate | 1151552 |
| Table_locks_waited    | 15324   |
+-----------------------+---------+
```

Performance Schema 锁定表还提供锁定信息。看[第 27.12.13 节，“性能模式锁定表”](performance-schema-lock-tables.html).

[](<>)[](<>)

这`MyISAM`存储引擎支持并发插入以减少给定表的读取器和写入器之间的争用：如果`MyISAM`table 在数据文件的中间没有空闲块，行总是插入到数据文件的末尾。在这种情况下，您可以自由混合并发[`插入`](insert.html)和[`选择`](select.html)的陈述`MyISAM`没有锁的桌子。也就是说，您可以将行插入到`MyISAM`表同时其他客户端正在读取它。空洞可能是由于表中间的行被删除或更新造成的。如果有空洞，并发插入将被禁用，但在所有空洞都被新数据填充后会自动再次启用。要控制此行为，请使用[`并发插入`](server-system-variables.html#sysvar_concurrent_insert)系统变量。看[第 8.11.3 节，“并发插入”](concurrent-inserts.html).

如果您使用显式获取表锁[`锁定表`](lock-tables.html), 你可以请求一个`阅读本地`锁而不是`读`锁定以使其他会话在您锁定表时执行并发插入。

执行许多[`插入`](insert.html)和[`选择`](select.html)对表的操作`t1`当无法进行并发插入时，您可以将行插入到临时表中`temp_t1`并使用临时表中的行更新真实表：

```
mysql> LOCK TABLES t1 WRITE, temp_t1 WRITE;
mysql> INSERT INTO t1 SELECT * FROM temp_t1;
mysql> DELETE FROM temp_t1;
mysql> UNLOCK TABLES;
```

#### 选择锁定类型

通常，在以下情况下，表锁优于行级锁：

-   该表的大多数语句都是读取。

-   表的语句是读取和写入的混合，其中写入是可以通过一个键读取获取的单行的更新或删除：

    ```
    UPDATE tbl_name SET column=value WHERE unique_key_col=key_value;
    DELETE FROM tbl_name WHERE unique_key_col=key_value;
    ```

-   [`选择`](select.html)结合并发[`插入`](insert.html)陈述，而且很少[`更新`](update.html)要么[`删除`](delete.html)陈述。

-   多次扫描或`通过...分组`在没有任何写入者的情况下对整个表进行操作。

    使用更高级别的锁，您可以通过支持不同类型的锁来更轻松地调整应用程序，因为锁开销低于行级锁。

    行级锁定以外的选项：

-   版本控制（例如 MySQL 中用于并发插入的版本），其中可以有一个写入器同时拥有多个读取器。这意味着数据库或表根据访问开始的时间支持不同的数据视图。其他常用术语是“时间旅行”、“写时复制”或“按需复制”。

-   按需复制在许多情况下优于行级锁定。但是，在最坏的情况下，它可以使用比使用普通锁更多的内存。

-   除了使用行级锁，您还可以使用应用程序级锁，例如由[`GET_LOCK()`](locking-functions.html#function_get-lock)和[`RELEASE_LOCK()`](locking-functions.html#function_release-lock)在 MySQL 中。这些是咨询锁，因此它们仅适用于相互协作的应用程序。看[第 12.15 节，“锁定功能”](locking-functions.html).
