<commit_before>---
title: TiDB 悲观事务模式
category: reference
---

# TiDB 悲观事务模式

TiDB 默认使用乐观事务模式，存在事务提交时因为冲突而失败的问题。为了保证事务的成功率，需要修改应用程序，加上重试的逻辑。
悲观事务模式可以避免这个问题，应用程序无需添加重试逻辑，就可以正常执行。

> **注意：**
>
> 悲观事务模式目前是一个**试验性**的特性，**不建议在生产环境中使用**。

## 悲观事务模式的行为

悲观事务的行为和 MySQL 基本一致（不一致的地方详见[已知的局限](#已知的局限)）：

- `SELECT FOR UPDATE` 会读取已提交的最新数据，并对读取到的数据加悲观锁。

- `UPDATE`/`DELETE`/`INSERT` 语句都会读取已提交的最新的数据来执行，并对修改的数据加悲观锁。

- 当一行数据被加了悲观锁以后，其他尝试修改这一行的写事务会被阻塞，等待悲观锁的释放。

- 当一行数据被加了悲观锁以后，其他尝试读取这一行的事务不会被阻塞，可以读到已提交的数据。

- 事务提交或回滚的时候，会释放所有的锁。

- 如果并发事务出现死锁，会被死锁检测器检测到，并返回和 MySQL 相同的 DEADLOCK 错误。

- 乐观事务和悲观事务可以共存，事务可以任意指定使用乐观模式或悲观模式来执行。

## 悲观事务的启用方法

因为悲观事务目前还是一个试验性特性，默认是关闭的，启用悲观事务首先需要在 TiDB 的配置文件里添加：

```
[pessimistic-txn]
enable = true
```

`enable` 设置为 `true` 以后，默认的事务模式仍然是乐观事务模式，要进入悲观事务模式有以下三种方式：

- 执行 `BEGIN PESSIMISTIC;` 语句开启的事务，会进入悲观事务模式。
可以通过写成注释的形式 `BEGIN /*!90000 PESSIMISTIC */;` 来兼容 MySQL 语法。

- 执行 `set @@tidb_txn_mode = 'pessimistic';`，使这个 session 执行的所有显式事务（即非 autocommit 的事务）都会进入悲观事务模式。

- 在配置文件设置中默认启用悲观事务模式，使除了自动提交的单语句事务之外的所有事务都会进入悲观事务模式。

    ```
    [pessimistic-txn]
    enable = true
    default = true
    ```

在配置了默认启用悲观事务的情况下，可以用以下两种方式使事务进入乐观事务模式：

- 执行 `BEGIN OPTIMISTIC;` 语句开启的事务，会进入乐观事务模式。
可以通过写成注释的形式 `BEGIN /*!90000 OPTIMISTIC */;` 来兼容 MySQL 语法。

- 通过执行 `set @@tidb_txn_mode = 'optimistic';`，使当前的 session 执行的事务进入乐观事务模式。

## 启用方式的优先级

- 优先级最高的是 `BEGIN PESSIMISTIC;` 和 `BEGIN OPTIMISTIC;` 语句。

- 其次是 session 变量 `tidb_txn_mode`。

- 最后是配置文件里的 `default`，当使用普通的 `BEGIN` 语句，且 `tidb_txn_mode` 的值为空字符串 `''` 时，根据 `default` 来决定启用悲观事务还是乐观事务。

## 相关配置参数

相关配置都在 `[pessimistic-txn]` 类别下，除了前面介绍过的 `enable` 和 `default`，还可配置以下参数：

- `ttl`

    ```
    ttl = "30s"
    ```

    `ttl` 是悲观事务的锁超时时间，默认值 30 秒，可以在 15 秒到 60 秒之间配置，超出这个范围会报错。如果事务的执行时间超过了 `ttl`，事务会失败。设置过大，会存在 `tidb-server` 宕机时，残留的悲观锁长时间阻塞写的问题，设置的过小，会存在事务来不及执行完，就被其他事务 rollback 的问题。

- `max-retry-count`

    ```
    max-retry-count = 256
    ```

    悲观事务会在事务内自动重试单个语句，这个配置指定单个语句最大重试次数，设置这个参数是为了避免极端情况下，出现无限重试，无法停止。
正常情况下不需要修改这个配置。

## 已知的局限

- 不支持 GAP Lock 和 Next Key Lock 在悲观事务内通过范围条件来更新多行数据的时候，其他的事务可以在这个范围内插入数据而不会被阻塞。

- 不支持 SELECT LOCK IN SHARE MODE。<commit_msg>reference/transactions: apply updates to v3.0<commit_after>---
title: TiDB 悲观事务模式
category: reference
---

# TiDB 悲观事务模式

TiDB 默认使用乐观事务模式，存在事务提交时因为冲突而失败的问题。为了保证事务的成功率，需要修改应用程序，加上重试的逻辑。
悲观事务模式可以避免这个问题，应用程序无需添加重试逻辑，就可以正常执行。

## 悲观事务模式的行为

悲观事务的行为和 MySQL 基本一致（不一致的地方详见[已知的局限](#已知的局限)）：

- `SELECT FOR UPDATE` 会读取已提交的最新数据，并对读取到的数据加悲观锁。

- `UPDATE`/`DELETE`/`INSERT` 语句都会读取已提交的最新的数据来执行，并对修改的数据加悲观锁。

- 当一行数据被加了悲观锁以后，其他尝试修改这一行的写事务会被阻塞，等待悲观锁的释放。

- 当一行数据被加了悲观锁以后，其他尝试读取这一行的事务不会被阻塞，可以读到已提交的数据。

- 事务提交或回滚的时候，会释放所有的锁。

- 如果并发事务出现死锁，会被死锁检测器检测到，并返回和 MySQL 相同的 DEADLOCK 错误。

- 乐观事务和悲观事务可以共存，事务可以任意指定使用乐观模式或悲观模式来执行。

## 悲观事务的使用方法

进入悲观事务模式有以下三种方式:

- 执行 `BEGIN PESSIMISTIC;` 语句开启的事务，会进入悲观事务模式。
可以通过写成注释的形式 `BEGIN /*!90000 PESSIMISTIC */;` 来兼容 MySQL 语法。

- 执行 `set @@tidb_txn_mode = 'pessimistic';`，使这个 session 执行的所有显式事务（即非 autocommit 的事务）都会进入悲观事务模式。

- 执行 `set @@global.tidb_txn_mode = 'pessimistic';`，使之后整个集群所有新创建的 session 都会进入悲观事务模式执行显示事务。

在配置了 `global.tidb_txn_mode` 为 `pessimistic` 之后，默认进入悲观事务模式，但是可以用以下两种方式使事务进入乐观事务模式：

- 执行 `BEGIN OPTIMISTIC;` 语句开启的事务，会进入乐观事务模式。
可以通过写成注释的形式 `BEGIN /*!90000 OPTIMISTIC */;` 来兼容 MySQL 语法。

- 执行 `set @@tidb_txn_mode = 'optimistic';`，使当前的 session 执行的事务进入乐观事务模式。

`BEGIN PESSIMISTIC;` 和 `BEGIN OPTIMISTIC;` 语句的优先级高于 `tidb_txn_mode` 系统变量。使用这两个语句开启的事务，会忽略系统变量。

如果想要禁用悲观事务特性，可以修改配置文件，在 `[pessimistic-txn]` 类别下添加 `enable = false`.

## 相关配置参数

相关配置都在 `[pessimistic-txn]` 类别下，除了前面介绍过的 `enable`，还可配置以下参数：

- `ttl`

    ```
    ttl = "40s"
    ```

    `ttl` 是悲观事务的锁超时时间，默认值 40 秒，可以在 15 秒到 120 秒之间配置，超出这个范围会报错。如果事务的执行时间超过了 `ttl`，事务会失败。设置过大，会存在 `tidb-server` 宕机时，残留的悲观锁长时间阻塞写的问题，设置的过小，会存在事务来不及执行完，就被其他事务 rollback 的问题。

- `max-retry-count`

    ```
    max-retry-count = 256
    ```

    悲观事务会在事务内自动重试单个语句，这个配置指定单个语句最大重试次数，设置这个参数是为了避免极端情况下，出现无限重试，无法停止。
正常情况下不需要修改这个配置。

## 已知的局限

- 不支持 GAP Lock 和 Next Key Lock 在悲观事务内通过范围条件来更新多行数据的时候，其他的事务可以在这个范围内插入数据而不会被阻塞。

- 不支持 SELECT LOCK IN SHARE MODE。
{'fraction_non_alphanumeric': 0.12748508946322068, 'fraction_numerical': 0.011182902584493042, 'mean_word_length': 5.708333333333333, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 6, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5146759', 'n_tokens_mistral': 3136, 'n_tokens_neox': 3423, 'n_words': 286}