- 关于 TiDB
- 快速上手
- 部署标准集群
- 数据迁移
- 运维操作
- 监控与告警
- 故障诊断
- 性能调优
- 系统调优
- 软件调优
- SQL 性能调优
- SQL 性能调优概览
- 理解 TiDB 执行计划
- SQL 优化流程
- 控制执行计划
- 教程
- 同城多中心部署
- 两地三中心部署
- 同城两中心部署
- 读取历史数据
- 使用 Stale Read 功能读取历史数据(推荐)
- 使用系统变量
tidb_snapshot
读取历史数据
- 最佳实践
- Placement Rules 使用文档
- Load Base Split 使用文档
- Store Limit 使用文档
- TiDB 工具
- 功能概览
- 适用场景
- 工具下载
- TiUP
- 文档地图
- 概览
- 术语及核心概念
- TiUP 组件管理
- FAQ
- 故障排查
- TiUP 命令参考手册
- 命令概览
- TiUP 命令
- TiUP Cluster 命令
- TiUP Cluster 命令概览
- tiup cluster audit
- tiup cluster check
- tiup cluster clean
- tiup cluster deploy
- tiup cluster destroy
- tiup cluster disable
- tiup cluster display
- tiup cluster edit-config
- tiup cluster enable
- tiup cluster help
- tiup cluster import
- tiup cluster list
- tiup cluster patch
- tiup cluster prune
- tiup cluster reload
- tiup cluster rename
- tiup cluster replay
- tiup cluster restart
- tiup cluster scale-in
- tiup cluster scale-out
- tiup cluster start
- tiup cluster stop
- tiup cluster template
- tiup cluster upgrade
- TiUP DM 命令
- TiUP DM 命令概览
- tiup dm audit
- tiup dm deploy
- tiup dm destroy
- tiup dm disable
- tiup dm display
- tiup dm edit-config
- tiup dm enable
- tiup dm help
- tiup dm import
- tiup dm list
- tiup dm patch
- tiup dm prune
- tiup dm reload
- tiup dm replay
- tiup dm restart
- tiup dm scale-in
- tiup dm scale-out
- tiup dm start
- tiup dm stop
- tiup dm template
- tiup dm upgrade
- TiDB 集群拓扑文件配置
- DM 集群拓扑文件配置
- TiUP 镜像参考指南
- TiUP 组件文档
- TiDB Operator
- Dumpling
- TiDB Lightning
- TiDB Data Migration
- Backup & Restore (BR)
- TiDB Binlog
- TiCDC
- sync-diff-inspector
- TiSpark
- 参考指南
- 架构
- 监控指标
- 安全加固
- 权限
- SQL
- SQL 语言结构和语法
- SQL 语句
ADD COLUMN
ADD INDEX
ADMIN
ADMIN CANCEL DDL
ADMIN CHECKSUM TABLE
ADMIN CHECK [TABLE|INDEX]
ADMIN SHOW DDL [JOBS|QUERIES]
ADMIN SHOW TELEMETRY
ALTER DATABASE
ALTER INDEX
ALTER INSTANCE
ALTER PLACEMENT POLICY
ALTER TABLE
ALTER USER
ANALYZE TABLE
BACKUP
BEGIN
CHANGE COLUMN
CHANGE DRAINER
CHANGE PUMP
COMMIT
CREATE [GLOBAL|SESSION] BINDING
CREATE DATABASE
CREATE INDEX
CREATE PLACEMENT POLICY
CREATE ROLE
CREATE SEQUENCE
CREATE TABLE LIKE
CREATE TABLE
CREATE USER
CREATE VIEW
DEALLOCATE
DELETE
DESC
DESCRIBE
DO
DROP [GLOBAL|SESSION] BINDING
DROP COLUMN
DROP DATABASE
DROP INDEX
DROP PLACEMENT POLICY
DROP ROLE
DROP SEQUENCE
DROP STATS
DROP TABLE
DROP USER
DROP VIEW
EXECUTE
EXPLAIN ANALYZE
EXPLAIN
FLASHBACK TABLE
FLUSH PRIVILEGES
FLUSH STATUS
FLUSH TABLES
GRANT <privileges>
GRANT <role>
INSERT
KILL [TIDB]
LOAD DATA
LOAD STATS
MODIFY COLUMN
PREPARE
RECOVER TABLE
RENAME INDEX
RENAME TABLE
REPLACE
RESTORE
REVOKE <privileges>
REVOKE <role>
ROLLBACK
SELECT
SET DEFAULT ROLE
SET [NAMES|CHARACTER SET]
SET PASSWORD
SET ROLE
SET TRANSACTION
SET [GLOBAL|SESSION] <variable>
SHOW [BACKUPS|RESTORES]
SHOW ANALYZE STATUS
SHOW [GLOBAL|SESSION] BINDINGS
SHOW BUILTINS
SHOW CHARACTER SET
SHOW COLLATION
SHOW [FULL] COLUMNS FROM
SHOW CONFIG
SHOW CREATE PLACEMENT POLICY
SHOW CREATE SEQUENCE
SHOW CREATE TABLE
SHOW CREATE USER
SHOW DATABASES
SHOW DRAINER STATUS
SHOW ENGINES
SHOW ERRORS
SHOW [FULL] FIELDS FROM
SHOW GRANTS
SHOW INDEX [FROM|IN]
SHOW INDEXES [FROM|IN]
SHOW KEYS [FROM|IN]
SHOW MASTER STATUS
SHOW PLACEMENT
SHOW PLACEMENT FOR
SHOW PLACEMENT LABELS
SHOW PLUGINS
SHOW PRIVILEGES
SHOW [FULL] PROCESSSLIST
SHOW PROFILES
SHOW PUMP STATUS
SHOW SCHEMAS
SHOW STATS_HEALTHY
SHOW STATS_HISTOGRAMS
SHOW STATS_META
SHOW STATUS
SHOW TABLE NEXT_ROW_ID
SHOW TABLE REGIONS
SHOW TABLE STATUS
SHOW [FULL] TABLES
SHOW [GLOBAL|SESSION] VARIABLES
SHOW WARNINGS
SHUTDOWN
SPLIT REGION
START TRANSACTION
TABLE
TRACE
TRUNCATE
UPDATE
USE
WITH
- 数据类型
- 函数与操作符
- 聚簇索引
- 约束
- 生成列
- SQL 模式
- 表属性
- 事务
- 垃圾回收 (GC)
- 视图
- 分区表
- 临时表
- 字符集和排序
- Placement Rules in SQL
- 系统表
mysql
- INFORMATION_SCHEMA
- Overview
ANALYZE_STATUS
CLIENT_ERRORS_SUMMARY_BY_HOST
CLIENT_ERRORS_SUMMARY_BY_USER
CLIENT_ERRORS_SUMMARY_GLOBAL
CHARACTER_SETS
CLUSTER_CONFIG
CLUSTER_HARDWARE
CLUSTER_INFO
CLUSTER_LOAD
CLUSTER_LOG
CLUSTER_SYSTEMINFO
COLLATIONS
COLLATION_CHARACTER_SET_APPLICABILITY
COLUMNS
DATA_LOCK_WAITS
DDL_JOBS
DEADLOCKS
ENGINES
INSPECTION_RESULT
INSPECTION_RULES
INSPECTION_SUMMARY
KEY_COLUMN_USAGE
METRICS_SUMMARY
METRICS_TABLES
PARTITIONS
PLACEMENT_RULES
PROCESSLIST
REFERENTIAL_CONSTRAINTS
SCHEMATA
SEQUENCES
SESSION_VARIABLES
SLOW_QUERY
STATISTICS
TABLES
TABLE_CONSTRAINTS
TABLE_STORAGE_STATS
TIDB_HOT_REGIONS
TIDB_HOT_REGIONS_HISTORY
TIDB_INDEXES
TIDB_SERVERS_INFO
TIDB_TRX
TIFLASH_REPLICA
TIKV_REGION_PEERS
TIKV_REGION_STATUS
TIKV_STORE_STATUS
USER_PRIVILEGES
VIEWS
METRICS_SCHEMA
- UI
- CLI
- 命令行参数
- 配置文件参数
- 系统变量
- 存储引擎
- 遥测
- 错误码
- 通过拓扑 label 进行副本调度
- 常见问题解答 (FAQ)
- 版本发布历史
- 术语表
TiDB 事务概览
TiDB 支持分布式事务,提供乐观事务与悲观事务两种事务模式。TiDB 3.0.8 及以后版本,TiDB 默认采用悲观事务模式。
本文主要介绍涉及事务的常用语句、显式/隐式事务、事务的隔离级别和惰性检查,以及事务大小的限制。
常用的变量包括 autocommit
、tidb_disable_txn_auto_retry
、tidb_retry_limit
以及 tidb_txn_mode
。
变量 tidb_disable_txn_auto_retry
和 tidb_retry_limit
仅适用于乐观事务,不适用于悲观事务。
常用事务语句
开启事务
要显式地开启一个新事务,既可以使用 BEGIN
语句,也可以使用 START TRANSACTION
语句,两者效果相同。
语法:
BEGIN;
START TRANSACTION;
START TRANSACTION WITH CONSISTENT SNAPSHOT;
START TRANSACTION WITH CAUSAL CONSISTENCY ONLY;
如果执行以上语句时,当前 Session 正处于一个事务的中间过程,那么系统会先自动提交当前事务,再开启一个新的事务。
与 MySQL 不同的是,TiDB 在执行完上述语句后即会获取当前数据库快照,而 MySQL 的 BEGIN
和 START TRANSACTION
是在开启事务后的第一个从 InnoDB 读数据的 SELECT
语句(非 SELECT FOR UPDATE
)后获取快照,START TRANSACTION WITH CONSISTENT SNAPSHOT
是语句执行时获取快照。因此,TiDB 中的 BEGIN
、START TRANSACTION
和 START TRANSACTION WITH CONSISTENT SNAPSHOT
都等效为 MySQL 中的 START TRANSACTION WITH CONSISTENT SNAPSHOT
。
提交事务
COMMIT
语句用于提交 TiDB 在当前事务中进行的所有修改。
语法:
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]
语句会显式地开启一个事务,同时也会禁用自动提交,使隐式事务变成显式事务。直到执行 COMMIT
或 ROLLBACK
语句时才会恢复到此前默认的自动提交状态。
对于 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 IGNORE
和INSERT ON DUPLICATE KEY UPDATE
不会生效。
语句回滚
TiDB 支持语句执行失败后的原子性回滚。如果语句报错,则所做的修改将不会生效。该事务将保持打开状态,并且在发出 COMMIT
或 ROLLBACK
语句之前可以进行其他修改。
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 同时支持乐观事务与悲观事务,其中乐观事务是悲观事务的基础。由于乐观事务是先将修改缓存在私有内存中,因此,TiDB 对于单个事务的容量做了限制。
TiDB 中,单个事务的总大小默认不超过 100 MB,这个默认值可以通过配置文件中的配置项 txn-total-size-limit
进行修改,最大支持 1 TB。单个事务的实际大小限制还取决于服务器剩余可用内存的大小,执行事务时 TiDB 进程的内存消耗相对于事务大小会存在一定程度的放大,最大可能达到提交事务大小的 2 到 3 倍以上。
在 4.0 以前的版本,TiDB 限制了单个事务的键值对的总数量不超过 30 万条,从 4.0 版本起 TiDB 取消了这项限制。
通常,用户会开启 TiDB Binlog 将数据向下游进行同步。某些场景下,用户会使用消息中间件来消费同步到下游的 binlog,例如 Kafka。
以 Kafka 为例,Kafka 的单条消息处理能力的上限是 1 GB。因此,当把 txn-total-size-limit
设置为 1 GB 以上时,可能出现事务在 TiDB 中执行成功,但下游 Kafka 报错的情况。为避免这种情况出现,请用户根据最终消费者的限制来决定 txn-total-size-limit
的实际大小。例如:下游使用了 Kafka,则 txn-total-size-limit
不应超过 1 GB。
因果一致性事务
因果一致性事务只在启用 Async Commit 特性和一阶段提交特性时生效。关于这两个特性的启用情况,请参见 tidb_enable_async_commit
系统变量介绍和 tidb_enable_1pc
系统变量介绍。
TiDB 支持开启因果一致性的事务。因果一致性的事务在提交时无需向 PD 获取时间戳,所以提交延迟更低。开启因果一致性事务的语法为:
START TRANSACTION WITH CAUSAL CONSISTENCY ONLY;
默认情况下,TiDB 保证线性一致性。在线性一致性的情况下,如果事务 2 在事务 1 提交完成后提交,逻辑上事务 2 就应该在事务 1 后发生。
因果一致性弱于线性一致性。在因果一致性的情况下,只有事务 1 和事务 2 加锁或写入的数据有交集时(即事务 1 和事务 2 存在数据库可知的因果关系时),才能保证事务的提交顺序与事务的发生顺序保持一致。目前暂不支持传入数据库外部的因果关系。
采用因果一致性的两个事务有以下特性:
有潜在因果关系的事务之间的逻辑顺序与物理提交顺序一致
假设事务 1 和事务 2 都采用因果一致性,并先后执行如下语句:
事务 1 | 事务 2 |
---|---|
START TRANSACTION WITH CAUSAL CONSISTENCY ONLY | START TRANSACTION WITH CAUSAL CONSISTENCY ONLY |
x = SELECT v FROM t WHERE id = 1 FOR UPDATE | |
UPDATE t set v = $(x + 1) WHERE id = 2 | |
COMMIT | |
UPDATE t SET v = 2 WHERE id = 1 | |
COMMIT |
上面的例子中,事务 1 对 id = 1
的记录加了锁,事务 2 的事务对 id = 1
的记录进行了修改,所以事务 1 和事务 2 有潜在的因果关系。所以即使用因果一致性开启事务,只要事务 2 在事务 1 提交成功后才提交,逻辑上事务 2 就必定比事务 1 晚发生。因此,不存在某个事务读到了事务 2 对 id = 1
记录的修改,但却没有读到事务 1 对 id = 2
记录的修改的情况。
无因果关系的事务之间的逻辑顺序与物理提交顺序不保证一致
假设 id = 1
和 id = 2
的记录最初值都为 0,事务 1 和事务 2 都采用因果一致性,并先后执行如下语句:
事务 1 | 事务 2 | 事务 3 |
---|---|---|
START TRANSACTION WITH CAUSAL CONSISTENCY ONLY | START TRANSACTION WITH CAUSAL CONSISTENCY ONLY | |
UPDATE t set v = 3 WHERE id = 2 | ||
UPDATE t SET v = 2 WHERE id = 1 | ||
BEGIN | ||
COMMIT | ||
COMMIT | ||
SELECT v FROM t WHERE id IN (1, 2) |
在本例中,事务 1 不读取 id = 1
的记录。此时事务 1 和事务 2 没有数据库可知的因果关系。如果使用因果一致性开启事务,即使物理时间上事务 2 在事务 1 提交完成后才开始提交,TiDB 也不保证逻辑上事务 2 比事务 1 晚发生。
此时如果有一个事务 3 在事务 1 提交前开启,并在事务 2 提交后读取 id = 1
和 id = 2
的记录,事务 3 可能读到 id = 1
的值为 2 但是 id = 2
的值为 0。
不加锁的读取不产生因果关系
假设事务 1 和事务 2 都采用因果一致性,并先后执行如下语句:
事务 1 | 事务 2 |
---|---|
START TRANSACTION WITH CAUSAL CONSISTENCY ONLY | START TRANSACTION WITH CAUSAL CONSISTENCY ONLY |
UPDATE t SET v = 2 WHERE id = 1 | |
SELECT v FROM t WHERE id = 1 | |
UPDATE t set v = 3 WHERE id = 2 | |
COMMIT | |
COMMIT |
如本例所示,不加锁的读取不产生因果关系。事务 1 和事务 2 产生了写偏斜的异常,如果他们有业务上的因果关系,则是不合理的。所以本例中,使用因果一致性的事务 1 和事务 2 没有确定的逻辑顺序。