【TiDB 4.0 PCTA 学习笔记】- 2.3.5 How to Use Transactions in TiDB(如何在 TiDB 中使用事务)@3+ 邓喆

课程名称:2.3.5 How to Use Transactions in TiDB(如何在 TiDB 中使用事务)

学习时长:

20分钟

课程收获:

本节课程介绍事务的基本概念,TiDB 分布式事务实现原理,以及使用方法。

课程内容:

TiDB 支持分布式事务,提供乐观事务悲观事务两种事务模型。TiDB 3.0.8 及以后版本,TiDB 默认采用悲观事务模型。

本文主要介绍涉及事务的常用语句、显式/隐式事务、事务的隔离级别和惰性检查,以及事务大小的限制。

常用的变量包括 autocommittidb_disable_txn_auto_retrytidb_retry_limit 以及 tidb_txn_mode

常用事务语句

开启事务

要显式地开启一个新事务,既可以使用 BEGIN 语句,也可以使用 START TRANSACTION 语句,两者效果相同。

语法:

BEGIN;
START TRANSACTION;
START TRANSACTION WITH CONSISTENT SNAPSHOT;

如果执行以上语句时,当前 Session 正处于一个事务的中间过程,那么系统会先自动提交当前事务,再开启一个新的事务。

注意:

与 MySQL 不同的是,TiDB 在执行完上述语句后即会获取当前数据库快照,而 MySQL 的 BEGINSTART TRANSACTION 是在开启事务后的第一个从 InnoDB 读数据的 SELECT 语句(非 SELECT FOR UPDATE)后获取快照,START TRANSACTION WITH CONSISTENT SNAPSHOT 是语句执行时获取快照。因此,TiDB 中的 BEGINSTART TRANSACTIONSTART TRANSACTION WITH CONSISTENT SNAPSHOT 都等效为 MySQL 中的 START TRANSACTION WITH CONSISTENT SNAPSHOT

提交事务

COMMIT 语句用于提交 TiDB 在当前事务中进行的所有修改。

语法:

COMMIT;

建议:

启用乐观事务前,请确保应用程序可正确处理 COMMIT 语句可能返回的错误。如果不确定应用程序将会如何处理,建议改为使用悲观事务

回滚事务

ROLLBACK 语句用于回滚并撤销当前事务的所有修改。

语法:

ROLLBACK;

如果客户端连接中止或关闭,也会自动回滚该事务。

自动提交

为满足 MySQL 兼容性的要求,在默认情况下,TiDB 将在执行语句后立即进行 autocommit(自动提交)。

举例:

mysql> CREATE TABLE t1 (
    ->  id INT NOT NULL PRIMARY KEY auto_increment,
    ->  pad1 VARCHAR(100)
    -> );
Query OK, 0 rows affected (0.09 sec)

mysql> SELECT @@autocommit;
+--------------+
| @@autocommit |
+--------------+
| 1            |
+--------------+
1 row in set (0.00 sec)

mysql> INSERT INTO t1 VALUES (1, 'test');
Query OK, 1 row affected (0.02 sec)

mysql> ROLLBACK;
Query OK, 0 rows affected (0.01 sec)

mysql> SELECT * FROM t1;
+----+------+
| id | pad1 |
+----+------+
|  1 | test |
+----+------+
1 row in set (0.00 sec)

以上示例中,ROLLBACK 语句没产生任何效果。由于 INSERT 语句是在自动提交的情况下执行的,等同于以下单语句事务:

START TRANSACTION;
INSERT INTO t1 VALUES (1, 'test');
COMMIT;

如果已显式地启动事务,则不适用自动提交。以下示例,ROLLBACK 语句成功撤回了 INSERT 语句:

mysql> CREATE TABLE t2 (
    ->  id INT NOT NULL PRIMARY KEY auto_increment,
    ->  pad1 VARCHAR(100)
    -> );
Query OK, 0 rows affected (0.10 sec)

mysql> SELECT @@autocommit;
+--------------+
| @@autocommit |
+--------------+
| 1            |
+--------------+
1 row in set (0.00 sec)

mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)

mysql> INSERT INTO t2 VALUES (1, 'test');
Query OK, 1 row affected (0.02 sec)

mysql> ROLLBACK;
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT * FROM t2;
Empty set (0.00 sec)

autocommit 是一个系统变量,可以基于 Session 或 Global 进行修改

举例:

SET autocommit = 0;
SET GLOBAL autocommit = 0;

显式事务和隐式事务

注意:

有些语句是隐式提交的。例如,执行 [BEGIN|START TRANCATION] 语句时,TiDB 会隐式提交上一个事务,并开启一个新的事务以满足 MySQL 兼容性的需求。详情参见 implicit commit

TiDB 可以显式地使用事务(通过 [BEGIN|START TRANSACTION]/COMMIT 语句定义事务的开始和结束) 或者隐式地使用事务 (SET autocommit = 1)。

在自动提交状态下,使用 [BEGIN|START TRANSACTION] 语句会显式地开启一个事务,同时也会禁用自动提交,使隐式事务变成显式事务。直到执行 COMMITROLLBACK 语句时才会恢复到此前默认的自动提交状态。

对于 DDL 语句,会自动提交并且不能回滚。如果运行 DDL 的时候,正在一个事务的中间过程中,会先自动提交当前事务,再执行 DDL。

惰性检查

执行 DML 语句时,乐观事务默认不会检查主键约束唯一约束,而是在 COMMIT 事务时进行这些检查。

举例:

CREATE TABLE t1 (id INT NOT NULL PRIMARY KEY);
INSERT INTO t1 VALUES (1);
BEGIN OPTIMISTIC;
INSERT INTO t1 VALUES (1); -- MySQL 返回错误;TiDB 返回成功。
INSERT INTO t1 VALUES (2);
COMMIT; -- MySQL 提交成功;TiDB 返回错误,事务回滚。
SELECT * FROM t1; -- MySQL 返回 1 2;TiDB 返回 1。
mysql> CREATE TABLE t1 (id INT NOT NULL PRIMARY KEY);
Query OK, 0 rows affected (0.10 sec)

mysql> INSERT INTO t1 VALUES (1);
Query OK, 1 row affected (0.02 sec)

mysql> BEGIN OPTIMISTIC;
Query OK, 0 rows affected (0.00 sec)

mysql> INSERT INTO t1 VALUES (1); -- MySQL 返回错误;TiDB 返回成功。
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO t1 VALUES (2);
Query OK, 1 row affected (0.00 sec)

mysql> COMMIT; -- MySQL 提交成功;TiDB 返回错误,事务回滚。
ERROR 1062 (23000): Duplicate entry '1' for key 'PRIMARY'
mysql> SELECT * FROM t1; -- MySQL 返回 1 2;TiDB 返回 1。
+----+
| id |
+----+
|  1 |
+----+
1 row in set (0.01 sec)

惰性检查优化通过批处理约束检查并减少网络通信来提升性能。可以通过设置 tidb_constraint_check_in_place = TRUE 禁用该行为。

注意:

  • 本优化仅适用于乐观事务。
  • 本优化仅对普通的 INSERT 语句生效,对 INSERT IGNOREINSERT ON DUPLICATE KEY UPDATE 不会生效。

语句回滚

TiDB 支持语句执行失败后的原子性回滚。如果语句报错,则所做的修改将不会生效。该事务将保持打开状态,并且在发出 COMMITROLLBACK 语句之前可以进行其他修改。

CREATE TABLE test (id INT NOT NULL PRIMARY KEY);
BEGIN;
INSERT INTO test VALUES (1);
INSERT INTO tset VALUES (2);  -- tset 拼写错误,使该语句执行出错。
INSERT INTO test VALUES (1),(2);  -- 违反 PRIMARY KEY 约束,语句不生效。
INSERT INTO test VALUES (3);
COMMIT;
SELECT * FROM test;
mysql> CREATE TABLE test (id INT NOT NULL PRIMARY KEY);
Query OK, 0 rows affected (0.09 sec)

mysql> BEGIN;
Query OK, 0 rows affected (0.00 sec)

mysql> INSERT INTO test VALUES (1);
Query OK, 1 row affected (0.02 sec)

mysql> INSERT INTO tset VALUES (2);  -- tset 拼写错误,使该语句执行出错。
ERROR 1146 (42S02): Table 'test.tset' doesn't exist
mysql> INSERT INTO test VALUES (1),(2);  -- 违反 PRIMARY KEY 约束,语句不生效。
ERROR 1062 (23000): Duplicate entry '1' for key 'PRIMARY'
mysql> INSERT INTO test VALUES (3);
Query OK, 1 row affected (0.00 sec)

mysql> COMMIT;
Query OK, 0 rows affected (0.01 sec)

mysql> SELECT * FROM test;
+----+
| id |
+----+
|  1 |
|  3 |
+----+
2 rows in set (0.00 sec)

以上例子中,INSERT 语句执行失败之后,事务保持打开状态。最后的 INSERT 语句执行成功,并且提交了修改。

事务限制

由于底层存储引擎的限制,TiDB 要求单行不超过 6 MB。可以将一行的所有列根据类型转换为字节数并加和来估算单行大小。

学习过程中参考的其他资料

同学你好,感谢参与 TiDB 4.0 课程的学习!

本篇笔记逻辑清晰、内容丰富,被评选为优质笔记,将额外获得 20 积分,并在 「TiDB 培训」分类下获得“置顶”权益,积分兑换规则将于近期开放,敬请关注!

期待您继续产出优质内容!

此话题已在最后回复的 1 分钟后被自动关闭。不再允许新回复。