diff --git a/sql/tidb-specific.md b/sql/tidb-specific.md index ae21e0619ea6..da5ce5cb8d54 100644 --- a/sql/tidb-specific.md +++ b/sql/tidb-specific.md @@ -285,6 +285,16 @@ TiDB 在 MySQL 的基础上,定义了一些专用的系统变量和语法用 这个变量用来设置最多可重试次数, 即在一个事务执行中遇到可重试的错误(例如事务冲突、TiKV 繁忙等)时,这个事务可以被重新执行,这个变量值表明最多可重试的次数。 +### tidb_disable_txn_auto_retry + +作用域:SESSION | GLOBAL + +默认值:0 + +这个变量用来设置是否禁用显式事务自动重试,设置为 1 时,不会自动重试,如果遇到冲突需要在应用层重试。 +是否需要禁用自动重试,请参考[自动重试的风险](./transaction-isolation.md#乐观事务注意事项) + + ## Optimizer Hint TiDB 在 MySQL 的 Optimizer Hint 语法上,增加了一些 TiDB 专有的 Hint 语法, 使用这些 Hint 的时候,TiDB 优化器会尽量使用指定的算法,在某些场景下会比默认算法更优。 diff --git a/sql/transaction-isolation.md b/sql/transaction-isolation.md index b9342b7e8bc5..9816d1025a72 100644 --- a/sql/transaction-isolation.md +++ b/sql/transaction-isolation.md @@ -77,6 +77,52 @@ MySQL 的可重复读隔离级别并非 snapshot 隔离级别,MySQL 可重复 retry-limit = 10 ``` +## 乐观事务注意事项 + +因为 TiDB 使用乐观锁机制,通过显式的 BEGIN 语句创建的事务,在遇到冲突后自动重试可能会导致最终结果不符合预期。 + +比如下面这两个例子: + +| Session1 | Session2 | +| ---------------- | ------------ | +| `begin;` | `begin;` | +| `select balance from t where id = 1;` | `update t set balance = balance -100 where id = 1;` | +| | `update t set balance = balance -100 where id = 2;` | +| // 使用 select 的结果决定后续的逻辑 | `commit;` | +| `if balance > 100 {` | | +| `update t set balance = balance + 100 where id = 2;` | | +| `}` | | +| `commit;` // 自动重试 | | + +| Session1 | Session2 | +| ---------------- | ------------ | +| `begin;` | `begin;` | +| `update t set balance = balance - 100 where id = 1;` | `delete t where id = 1;` | +| | `commit;` | +| // 使用 affected_rows 的结果决定后续的逻辑 | | +| `if affected_rows > 100 {` | | +| `update t set balance = balance + 100 where id = 2;` | | +| `}` | | +| `commit;` // 自动重试 | | + +因为 TiDB 自动重试机制会把事务第一次执行的所有语句重新执行一遍,当一个事务里的后续 +语句是否执行取决于前面语句执行结果的时候,自动重试无法保证最终结果符合预期。 +这种情况下,需要在应用层重试整个事务。 + +通过配置全局变量 `tidb_disable_txn_auto_retry` 可以关掉显式事务的重试。 + +``` +set @@global.tidb_disable_txn_auto_retry = 1; + +``` + +这个变量不会影响 auto_commit = 1 的单语句的隐式事务,仍然会自动重试。 + +关掉显示事务重试后,如果出现事务冲突,commit 语句会返回错误,错误信息会包含 + `try again later` 这个字符串,应用层可以用来判断遇到的错误是否是可以重试的。 + +如果事务执行过程中包含了应用层的逻辑,建议在应用层添加显式事务的重试,并关闭自动重试。 + ## 语句回滚 在事务内部执行一个语句,遇到错误时,该语句不会生效。