MySQL 语句:事务和锁定声明
关于
MySQL 通过诸如“SET autocommit”,“START TRANSACTION”,“COMMIT”和“ROLLBACK”之类的语句支持本地事务(在给定的 Client 端会话内)。
START TRANSACTION,COMMIT 和 ROLLBACK 语句
START TRANSACTION
[transaction_characteristic [, transaction_characteristic] ...]
transaction_characteristic: {
WITH CONSISTENT SNAPSHOT
| READ WRITE
| READ ONLY
}
BEGIN [WORK]
COMMIT [WORK] [AND [NO] CHAIN] [[NO] RELEASE]
ROLLBACK [WORK] [AND [NO] CHAIN] [[NO] RELEASE]
SET autocommit = {0 | 1}
这些语句可控制transactions的使用:
- “START TRANSACTION” 或 “BEGIN” 开始新 Transaction。
- “COMMIT” 提交当前事务,使其更改永久生效。
- “ROLLBACK” 回滚当前事务,取消其更改。
- “SET autocommit” 禁用或启用当前会话的默认自动提交模式。
默认情况下:MySQL 在启用autocommit模式的情况下运行。这意味着,当不在事务内时,每个语句都是原子的,就像被 START TRANSACTION 和 COMMIT 包围一样。您不能使用 ROLLBACK 撤消效果;但是,如果在语句执行期间发生错误,则会回滚该语句。
关于“禁用自动提交”:
- 隐式禁用自动提交:
- 要隐式禁用单个语句系列的自动提交模式,请使用“START TRANSACTION”语句:
START TRANSACTION; SELECT @A:=SUM(salary) FROM table1 WHERE type=1; UPDATE table2 SET summary=@A WHERE type=1; COMMIT;
- 使用 START TRANSACTION 时,自动提交保持禁用状态,直到您以 COMMIT 或 ROLLBACK 结束事务。然后,自动提交模式将恢复为之前的状态。
- 显式禁用自动提交:
- 要显式禁用自动提交模式,请使用以下语句:
SET autocommit=0;
- 通过将 autocommit 变量设置为零来禁用自动提交模式之后,对事务安全表(例如 InnoDB 或 NDB 的表)的更改不会立即变为永久不变。您必须使用“COMMIT”将更改存储到磁盘上,或使用“ROLLBACK”忽略更改。
- autocommit 是会话变量,必须为每个会话设置。要为每个新连接禁用自动提交模式,请更改服务器系统变量的 autocommit 系统变量。
关于“transaction_characteristic”:
- START TRANSACTION 允许几个修饰符来控制 TransactionFeature。要指定多个修饰符,请用逗号分隔:
- “WITH CONSISTENT SNAPSHOT”修饰符为具有此功能的存储引擎启动“一致性读取”(consistent read)。【这仅适用于InnoDB】
- 效果与在任何 InnoDB 表中发出 START TRANSACTION,然后发出 SELECT 的效果相同。
- “WITH CONSISTENT SNAPSHOT”修饰符不会更改当前事务 isolation level,因此仅当当前隔离级别为允许一致读取的级别时,它才会提供一致的快照。允许一致读取的唯一隔离级别是“REPEATABLE READ”。对于所有其他隔离级别,将忽略“WITH CONSISTENT SNAPSHOT”子句。(从 MySQL 5.7.2 开始,忽略“WITH CONSISTENT SNAPSHOT”子句会生成警告)
- “READ WRITE”和“READ ONLY”修饰符设置事务访问模式。它们允许或禁止更改事务中使用的 table。
- “READ WRITE”:如果未指定访问模式,则应用默认模式。除非更改了默认值,否则它是读/写的。不允许在同一语句中同时指定“READ WRITE”和“READ ONLY”。
- “READ ONLY”:限制防止事务修改或锁定其他事务可见的事务表和非事务表。
- 当已知事务为只读时,MySQL 会对 InnoDB 表上的查询启用额外的优化。指定“READ ONLY”可以确保在无法自动确定只读状态的情况下应用这些优化。
- 【事务仍然可以修改或锁定临时表:可以使用 DML 语句而非 DDL 语句进行更改】
关于“BEGIN [WORK]”:
- 支持“BEGIN”和“BEGIN WORK”作为“START TRANSACTION”的别名以启动事务。 “START TRANSACTION”是标准的 SQL 语法,是启动临时事务的推荐方法,并且允许“BEGIN”所不允许的修饰符。
- BEGIN 语句不同于使用 BEGIN 关键字来启动“BEGIN ... END”复合语句。后者不开始 Transaction。
- 在所有存储的程序(存储的过程和函数,触发器和事件)中,解析器将“BEGIN [WORK]”视为“BEGIN ... END”块的开头。在这种情况下,请改用“START TRANSACTION”开始 Transaction。
关于“COMMIT、ROLLBACK”:
- COMMIT 和 ROLLBACK 支持可选的“WORK”关键字,及“CHAIN”和“RELEASE”子句(CHAIN 和 RELEASE 可用于对事务完成进行附加控制)。“completion_type”系统变量的值确定默认的完成行为。
- “AND CHAIN”子句使新事务在当前事务结束时立即开始,并且新事务具有与刚刚终止的事务相同的隔离级别。新事务还使用与刚刚终止的事务相同的访问模式(“READ WRITE”或“READ ONLY”)。
- “RELEASE”子句使服务器在终止当前事务之后断开当前 Client 端会话的连接。
- 包含“NO”关键字将禁止“CHAIN”和“RELEASE”完成,如果将“completion_type”系统变量设置为默认导致链接或释放完成,则这很有用。
关于“事务的使用”:
- 开始事务将导致任何未决事务被提交。
- 开始事务也会导致用“LOCK TABLES”获取的 table 锁被释放,就好像您已执行“UNLOCK TABLES”一样。开始事务不会释放通过“FLUSH TABLES WITH READ LOCK”获取的全局读取锁。
- 为了获得最佳结果,应该仅使用由单个事务安全存储引擎 Management 的 table 来执行事务。否则,可能会出现以下问题:
- 如果您使用来自多个事务安全存储引擎(例如InnoDB)的 table,并且事务隔离级别不是“SERIALIZABLE”,则当一个事务提交时,使用相同 table 的另一个正在进行的事务可能只会看到一些由第一个 Transaction 所做的更改。也就是说,使用混合引擎无法保证事务的原子性,并且可能导致不一致。
- (如果不经常使用混合引擎事务,则可以根据需要使用设置 Transaction 隔离级别将每个事务的隔离级别设置为“SERIALIZABLE”。)
- 如果您在事务中使用不安全事务的 table,则无论自动提交模式的状态如何,都将立即存储对这些 table 的更改。
- 如果在更新事务中的非事务 table 之后发出“ROLLBACK”语句,则会出现“ER_WARNING_NOT_COMPLETE_ROLLBACK”警告。回滚对事务安全 table 的更改,但不回滚对非事务安全 table 的更改。
- 如果您使用来自多个事务安全存储引擎(例如InnoDB)的 table,并且事务隔离级别不是“SERIALIZABLE”,则当一个事务提交时,使用相同 table 的另一个正在进行的事务可能只会看到一些由第一个 Transaction 所做的更改。也就是说,使用混合引擎无法保证事务的原子性,并且可能导致不一致。
- 每个事务都在 COMMIT 上以一个块的形式存储在二进制日志中。回滚的事务不会记录。(exception :无法回滚对非事务处理 table 的修改,如果回滚的事务包括对非事务处理 table 的修改,则整个事务都将在末尾使用ROLLBACK 语句记录下来,以确保对非事务处理 table 的修改被复制)。
- 您可以使用“SET TRANSACTION”语句更改事务的隔离级别或访问模式。
- 回滚可能是一个缓慢的操作,可能会隐式发生,而无需用户明确要求(例如,发生错误时)。因此,“SHOW PROCESSLIST”在会话的 State 列中显示 Rolling back,不仅用于使用 ROLLBACK 语句执行的显式回滚,而且还用于隐式回滚。
- (在 MySQL 5.7 中,BEGIN,COMMIT 和 ROLLBACK 不受“--replicate-do-db”或“--replicate-ignore-db”规则的影响。)
无法回滚的语句
某些语句无法回滚。通常,这些语句包括数据定义语言(DDL)语句,例如创建或删除数据库的语句,创建,删除或更改 table 或存储例程的语句。
- 您应设计您的 Transaction 不包含此类声明。如果您在无法回滚的事务中早期发出了一个语句,然后又有另一个语句失败,则在这种情况下,通过发出 ROLLBACK 语句无法回滚事务的全部效果。
导致隐式提交的语句
这些语句大多数会在执行后导致隐式提交。目的是在自己的特殊事务中处理每个这样的语句,因为它无论如何都不能回滚。
- 事务控制和锁定语句是例外:如果隐式提交发生在执行之前,则另一个不会发生。【???】
- 定义或修改数据库对象的数据定义语言(DDL 语句):“DROP INDEX”,“DROP PROCEDURE”,“DROP SERVER”,“DROP TABLE”,“DROP TRIGGER”,“DROP VIEW”,“INSTALL PLUGIN”,“RENAME TABLE”,“TRUNCATE TABLE”,“UNINSTALL PLUGIN”。
- “ALTER FUNCTION”,“CREATE FUNCTION”和“DROP FUNCTION”与存储函数一起使用时,也会引起隐式提交,但与用户定义的函数一起使用时不会。(“ALTER FUNCTION”只能与存储函数一起使用)
- 如果使用“TEMPORARY”关键字,则“CREATE TABLE”和“DROP TABLE”语句不提交事务。(这不适用于确实导致提交的临时表,例如“ALTER TABLE”和“CREATE INDEX”上的其他操作)。但是,尽管没有隐式提交发生,但是语句也不能回滚,这意味着使用此类语句会导致事务性违反原子性。例如,如果您使用创建临时 table 然后回滚该事务,则该 table 仍然存在。
- InnoDB 中的“CREATE TABLE”语句作为单个事务处理。
- 创建非临时表时,“CREATE TABLE ... SELECT”会在执行语句之前和之后导致隐式提交。(“CREATE TEMPORARY TABLE ... SELECT”没有提交)
- 隐式使用或修改mysql数据库中表的语句:“ALTER USER”,“CREATE USER”,“DROP USER”,“GRANT”,“RENAME USER”,“REVOKE”,“SET PASSWORD”。
- Transaction 控制和锁定语句:“BEGIN”,“LOCK TABLES”,“SET autocommit = 1”(如果该值尚未为 1),“START TRANSACTION”,“UNLOCK TABLES”。
- 仅当当前已使用“LOCK TABLES”锁定任何 table 以获取非事务性 table 锁时,“UNLOCK TABLES”才会提交事务。 “FLUSH TABLES WITH READ LOCK”的“UNLOCK TABLES”不会发生提交,因为其不获取 table 级锁。
- 事务不能嵌套:这是您发出“START TRANSACTION”语句或其同义词之一时对任何当前事务执行的隐式提交的结果。
- 当事务处于 ACTIVE 状态时,不能在 XA 事务中使用导致隐式提交的语句。【???】
- “BEGIN”语句不同于使用 BEGIN 关键字来启动“BEGIN ... END”复合语句。后者不会导致隐式提交。
- 数据加载语句:“LOAD DATA”。
- LOAD DATA 仅对使用 NDB 存储引擎的 table 引起隐式提交。
- 管理类声明:“ANALYZE TABLE”,“CACHE INDEX”,“CHECK TABLE”,“FLUSH”,“LOAD INDEX INTO CACHE”,“OPTIMIZE TABLE”,“REPAIR TABLE”,“RESET”。
- 复制控制语句:“START SLAVE”,“STOP SLAVE”,“RESET SLAVE”,“CHANGE MASTER TO”。
SAVEPOINT,ROLLBACK TO SAVEPOINT 和 RELEASE SAVEPOINT 语句
SAVEPOINT identifier
ROLLBACK [WORK] TO [SAVEPOINT] identifier
RELEASE SAVEPOINT identifier
InnoDB支持 SQL 语句“SAVEPOINT”,“ROLLBACK TO SAVEPOINT”,“RELEASE SAVEPOINT”和“ROLLBACK”的可选“WORK”关键字。
- “SAVEPOINT”语句设置名称为 identifier 的命名事务保存点。
- 如果当前事务具有相同名称的保存点,则将删除旧的保存点并设置一个新的保存点。
- “ROLLBACK TO SAVEPOINT”语句将事务回滚到指定的保存点,而不会终止该事务。设置保存点后,当前事务对行所做的修改将在回滚中撤消,但是 InnoDB 不会不释放保存点之后存储在内存中的行锁。(对于新插入的行,锁信息由该行中存储的事务 ID 携带;锁不会单独存储在内存中。在这种情况下,该行锁将在撤消中释放。)在比命名保存点晚的时间被删除。
- 如果语句返回以下错误,则表示不存在具有指定名称的保存点:
ERROR 1305 (42000): SAVEPOINT identifier does not exist
- 如果语句返回以下错误,则表示不存在具有指定名称的保存点:
- “RELEASE SAVEPOINT”语句从当前事务的保存点集中删除命名的保存点。没有提交或回滚发生。如果保存点不存在,则发生错误。
- 如果执行未命名保存点的“COMMIT”或“ROLLBACK”,则当前事务的所有保存点都将被删除。
- 当调用存储功能或激活触发器时,将创建一个新的保存点级别。先前级别上的保存点将不可用,因此不会与新级别上的保存点发生冲突。函数或触发器终止时,将释放它创建的所有保存点,并还原先前的保存点级别。
LOCK TABLES 和 UNLOCK TABLES 语句
LOCK TABLES
tbl_name [[AS] alias] lock_type
[, tbl_name [[AS] alias] lock_type] ...
lock_type: {
READ [LOCAL]
| [LOW_PRIORITY] WRITE
}
UNLOCK TABLES
MySQL 使 Client 端会话能够显式地获取 table 锁,以便与其他会话协作访问 table,或者防止其他会话在会话需要互斥访问期间修改 table。
- 会话只能为其自身获取或释放锁。一个会话无法获取另一会话的锁,也不能释放另一会话持有的锁。
NOTE:
- “LOCK TABLES”:
- 在更新 table 时,锁可用于模拟事务或提高速度。
- LOCK TABLES 明确获取当前 Client 端会话的 table 锁。可以为基本 table 或视图获取 table 锁。您必须具有 LOCK TABLES 特权和 SELECT 特权才能锁定每个对象。
- 对于视图锁定,LOCK TABLES 将视图中使用的所有基本 table 添加到要锁定的 table 集中,并自动锁定它们。【从 MySQL 5.7.32 开始,LOCK TABLES检查视图定义器是否对基于视图的 table 具有适当的特权】
- 如果使用 LOCK TABLES 显式锁定 table,则触发器中使用的任何 table 也会隐式锁定。
- “UNLOCK TABLES”:
- UNLOCK TABLES 明确释放当前会话持有的所有 table 锁。 LOCK TABLES 在获取新锁之前隐式释放当前会话持有的所有 table 锁。
- UNLOCK TABLES 的另一个用途是释放通过“FLUSH TABLES WITH READ LOCK”语句获取的全局读取锁定,这使您可以锁定所有数据库中的所有 table。【???】
- table 锁只能防止其他会话进行不适当的读取或写入。
- 持有“WRITE”锁的会话可以执行 table 级操作,例如“DROP TABLE”或“TRUNCATE TABLE”。
- 对于持有“READ”锁的会话,不允许“DROP TABLE”和“TRUNCATE TABLE”操作。
以下讨论仅适用于非 TEMPORARY 表。
- TEMPORARY 表允许“LOCK TABLES”(但被忽略)。可以通过创建 table 的会话自由访问该 table,而不考虑可能执行了哪些其他锁定。无需锁定,因为没有其他会话可以看到该 table。
表锁的获取
要获取当前会话中的 table 锁,请使用“LOCK TABLES”语句,该语句获取元数据锁。
可以使用以下锁定类型:
- “READ [LOCAL]”锁定:
- 持有锁的会话可以读取表(但不能写入表)。
- 多个会话可以同时获取该表的 READ 锁。
- 其他会话可以在不显式获取 READ 锁的情况下读取表。
- “LOCAL”修饰符使其他会话可以在保持锁的同时执行不冲突的 INSERT语句 (并发插入)。但是,如果您要在持有锁的同时使用服务器外部的进程来操作数据库,则不能使用 READ LOCAL。
- 对于 InnoDB 表,“READ LOCAL”与“READ”相同。
- “[LOW_PRIORITY] WRITE”锁定:
- 持有锁的会话可以读写表。
- 只有持有锁的会话才能访问该表。在释放锁之前,没有其他会话可以访问它。
- 保持 WRITE 锁定时,其他会话对表的锁定请求将阻止。
- “LOW_PRIORITY”修饰符无效。在以前的 MySQL 版本中,它影响了锁定行为,但现在不再如此。现在已弃用它,并且使用它会产生警告。使用 WRITE 而不使用 LOW_PRIORITY。
NOTE:
- WRITE 锁通常具有比 READ 锁更高的优先级,以确保尽快处理更新。这意味着,如果一个会话获得 READ 锁,然后另一个会话请求 WRITE 锁,则随后的 READ 锁请求将 await,直到请求 WRITE 锁的会话已获得并释放了该锁。
- 如果“LOCK TABLES”语句由于在任何表上其他会话持有的锁而必须 await,则它将阻塞直到可以获取所有锁为止。
- 需要锁的会话必须在一个“LOCK TABLES”语句中获取它需要的所有锁。在保留由此获得的锁的同时,会话只能访问锁定的表。
- 例如,在下面的语句序列中,尝试访问t2时发生错误,因为它未锁定在LOCK TABLES语句中:
mysql> LOCK TABLES t1 READ; mysql> SELECT COUNT(*) FROM t1; +----------+ | COUNT(*) | +----------+ | 3 | +----------+ mysql> SELECT COUNT(*) FROM t2; ERROR 1100 (HY000): Table 't2' was not locked with LOCK TABLES
- INFORMATION_SCHEMA数据库中的表是一个例外。即使会话持有通过“LOCK TABLES”获得的表锁,也可以在不显式锁定它们的情况下访问它们。
- 您不能在使用相同名称的单个查询中多次引用锁定的表。请改用别名,并为表和每个别名获取单独的锁:
mysql> LOCK TABLE t WRITE, t AS t1 READ; mysql> INSERT INTO t SELECT * FROM t; ERROR 1100: Table 't' was not locked with LOCK TABLES mysql> INSERT INTO t SELECT * FROM t AS t1;
- 对于第一个 INSERT,会发生错误,因为锁定表有两个对相同名称的引用。第二个 INSERT 成功,因为对该表的引用使用了不同的名称。
- 如果您的语句通过别名引用表,则必须使用相同的别名锁定表。如果不指定别名,则无法锁定表:
mysql> LOCK TABLE t READ; mysql> SELECT * FROM t AS myalias; ERROR 1100: Table 'myalias' was not locked with LOCK TABLES
- 相反,如果使用别名锁定 table,则必须在该语句中使用该别名引用该 table:
mysql> LOCK TABLE t AS myalias READ; mysql> SELECT * FROM t; ERROR 1100: Table 't' was not locked with LOCK TABLES mysql> SELECT * FROM t AS myalias;
- “LOCK TABLES”或“UNLOCK TABLES”应用于分区表时,始终锁定或解锁整个表。这些语句不支持分区锁修剪。
表锁的释放
释放会话持有的表锁时,它们都将同时释放。会话可以显式释放其锁,也可以在某些条件下隐式释放锁:
- 会话可以使用“UNLOCK TABLES”显式释放其锁。
- 如果会话发出“LOCK TABLES”语句以在已经持有锁的同时获取锁,则在授予新锁之前隐式释放其现有锁。
- 如果会话开始事务(例如,使用“START TRANSACTION”),则执行隐式“UNLOCK TABLES”,这将导致释放现有的锁。
关于 Client 端使用锁:
- 如果 Client 端会话的连接终止,无论是正常连接还是异常连接,服务器都会隐式释放该会话持有的所有表锁(事务性和非事务性)。
- 如果 Client 端重新连接,则锁定将不再有效。此外,如果 Client 端有活动的事务,则服务器在断开连接时会回滚事务,如果发生重新连接,则新会话将从启用自动提交开始。因此,Client 端可能希望禁用自动重新连接。启用自动重新连接后,如果发生重新连接,则不会通知 Client 端,但是任何表锁或当前事务都将丢失。
- 在禁用自动重新连接的情况下,如果连接断开,则下一条发出的语句将发生错误。Client 端可以检测到错误并采取适当的措施,例如重新获得锁或重做事务。
- 如果在锁定表上使用“ALTER TABLE”,则它可能会被解锁。
- 例如,如果您尝试第二次“ALTER TABLE”操作,则结果可能是错误“Table 'tbl_name' was not locked with LOCK TABLES”。要处理此问题,请在第二次更改之前再次锁定表。
表锁与事务的交互
“LOCK TABLES”和“UNLOCK TABLES”与事务的交互作用如下:
- “LOCK TABLES”不是事务安全的,它在尝试锁定表之前隐式提交任何活动的事务。
- “UNLOCK TABLES”隐式提交任何活动事务,但前提是已使用“LOCK TABLES”来获取表锁。
- 例如,在以下语句集中,UNLOCK TABLES 释放全局读取锁,但由于没有有效的表锁而不提交事务:
FLUSH TABLES WITH READ LOCK; START TRANSACTION; SELECT ... ; UNLOCK TABLES;
- 开始事务(例如,以“START TRANSACTION”)隐式提交任何当前事务并释放现有的表锁。
- “FLUSH TABLES WITH READ LOCK”获取全局读锁定,而不是表锁定,因此就表锁定和隐式提交而言,它与“LOCK TABLES”和“UNLOCK TABLES”不会具有相同的行为。
- 例如,“START TRANSACTION”不会释放全局读取锁定。
- 其他隐式导致事务提交的语句不会释放现有的表锁。
- 在事务吧(例如 InnoDB 表)上使用“LOCK TABLES”和“UNLOCK TABLES”的正确方法是:
- 先以“SET autocommit = 0”(不是“START TRANSACTION”);【???禁用自动提交】
- 后跟“LOCK TABLES”来开始事务,并且在明确提交事务之前不要调用“UNLOCK TABLES”。
- 例如,如果您需要写入 tablet1 并从 tablet2 读取,则可以执行以下操作:
SET autocommit=0; LOCK TABLES t1 WRITE, t2 READ, ...; ... do something with tables t1 and t2 here ... COMMIT; UNLOCK TABLES;
- 调用“LOCK TABLES”时,InnoDB 在内部采用自己的表锁,而 MySQL 则采用自己的表锁。 InnoDB 在下一次提交时释放其内部表锁,但是要让 MySQL 释放其表锁必须调用“UNLOCK TABLES”。【!?】
- 不应该使用“autocommit = 1”,因为 InnoDB 会在调用“LOCK TABLES”之后立即释放其内部表锁,并且死锁很容易发生。“autocommit = 1”使InnoDB 根本不获取内部表锁,以帮助旧应用程序避免不必要的死锁。【??】
- “ROLLBACK”不释放表锁。
LOCK TABLES 与 Triggers
如果使用“LOCK TABLES”显式锁定表,则触发器中使用的任何表也将隐式锁定:
- 锁定与通过“LOCK TABLES”语句显式获取的锁定时间相同。
- 触发器中使用的表上的锁取决于该表是否仅用于读取:如果是这样,则读锁就足够了;否则,将使用写锁。
- 如果使用“LOCK TABLES”显式锁定了表以进行读取,但是由于可能在触发器中对其进行了修改,则需要将其锁定以进行写入,则将采用写锁定,而不是读锁定。(也就是说,由于表在触发器中的出现而需要的隐式写锁定导致将表的显式读锁定请求转换为写锁定请求)
示例:假设您使用以下语句锁定两个表 t1 和 t2:
LOCK TABLES t1 WRITE, t2 READ;
如果 t1 或 t2 有任何触发器,则触发器中使用的表也将被锁定。假设 t1 的触发器定义如下:
CREATE TRIGGER t1_a_ins AFTER INSERT ON t1 FOR EACH ROW
BEGIN
UPDATE t4 SET count = count+1
WHERE id = NEW.id AND EXISTS (SELECT a FROM t3);
INSERT INTO t2 VALUES(1, 2);
END;
“LOCK TABLES”语句的结果是 t1 和 t2 被锁定,因为它们出现在该语句中;而 t3 和 t4 被锁定,因为它们在触发器中使用:
- 根据 WRITE 锁定请求,t1 被锁定以进行写入。
- t2 被锁定以进行写入,即使请求是 READ 锁定。发生这种情况是因为在触发器中插入了 t2,因此 READ 请求被转换为 WRITE 请求。
- t3 被锁定以进行读取,因为它只能从触发器内读取。
- t4 已被锁定,无法写入,因为它可能会在触发器中更新。
表锁定的限制和条件
- 您可以安全地使用“KILL”终止正在 await 表锁的会话。
- “LOCK TABLES”和“UNLOCK TABLES”不能在存储的程序中使用。
- “performance_schema”数据库中的表不能用“LOCK TABLES”锁定,但“setup_xxx”表除外。
- 当“LOCK TABLES”语句生效时,以下语句被禁止:“CREATE TABLE”,“CREATE表... LIKE”,“CREATE VIEW”,“DROP VIEW”以及有关存储的函数、过程和事件的 DDL 语句。
- 对于某些操作,必须访问 mysql 数据库中的系统表(例如,HELP 语句需要服务器端帮助表的内容,而 CONVERT_TZ() 则可能需要读取时区表)服务器隐式锁定系统表以在需要时进行读取,因此您无需显式锁定它们。
- 这些表如前所述:
mysql.help_category mysql.help_keyword mysql.help_relation mysql.help_topic mysql.proc mysql.time_zone mysql.time_zone_leap_second mysql.time_zone_name mysql.time_zone_transition mysql.time_zone_transition_type
- 如果要使用“LOCK TABLES”语句在所有这些表上显式放置 WRITE 锁,则该表必须是唯一的一个锁;没有其他表可以使用同一语句锁定。
- 通常,不需要锁定表,因为所有单个“UPDATE”语句都是原子的;没有其他会话可以干扰任何其他当前正在执行的 SQL 语句。但是,在某些情况下,锁定表可能会带来好处:
- 如果要在一组 MyISAM表上运行许多操作,则锁定要使用的表要快得多。锁定 MyISAM表可以加快对其的插入,更新或删除,因为 MySQL 直到调用“UNLOCK TABLES”时才会刷新锁定表的键高速缓存。通常,在每个 SQL 语句之后刷新键缓存。
- 锁定表的不利之处在于,没有会话可以更新 READ 锁定的表(包括持有锁的表),没有会话可以访问 WRITE 锁定的表,除了持有该锁的表之外。
- 如果要将表用于非事务性存储引擎,则要确保没有其他会话修改 SELECT 和 UPDATE 之间的表,则必须使用“LOCK TABLES”。【??】
- 此处显示的示例要求“LOCK TABLES”安全执行:
LOCK TABLES trans READ, customer WRITE; SELECT SUM(value) FROM trans WHERE customer_id=some_id; UPDATE customer SET total_value=sum_from_previous_statement WHERE customer_id=some_id; UNLOCK TABLES;
- 如果没有"LOCK TABLES",则另一个会话可能会在执行 SELECT 和 UPDATE 语句之间在transt able 中插入新行。
- 如果要在一组 MyISAM表上运行许多操作,则锁定要使用的表要快得多。锁定 MyISAM表可以加快对其的插入,更新或删除,因为 MySQL 直到调用“UNLOCK TABLES”时才会刷新锁定表的键高速缓存。通常,在每个 SQL 语句之后刷新键缓存。
- 在许多情况下,可以通过使用相对更新(“UPDATE customer SET value=value+new_value”)或“LAST_INSERT_ID()”函数来避免使用“LOCK TABLES”。【?】
- 在某些情况下,您还可以通过使用用户级咨询锁定功能“GET_LOCK()”和“RELEASE_LOCK()”来避免锁定表。这些锁保存在服务器的哈希表中,并以“pthread_mutex_lock()”和“pthread_mutex_unlock()”高速实现。【???】
XA 事务【分布式事务标准】
什么是 XA
InnoDB 存储引擎支持 XA 事务。 MySQL XA 实现基于 X/Open CAE 文档 《分布式事务处理:XA 规范》。本文档由 The Open Group 发布,可在 http://www.opengroup.org/public/pubs/catalog/c193.htm 获得。 第 13.3.7.3 节“ XATransaction 限制”中描述了当前 XA 实现的局限性。 XA 用于协调分布式 事务 的标准接口,允许多个数据库参与事务,同时保持 ACID 的合规性。
XA 是由 X/Open 组织提出的分布式事务的规范。 XA 规范主要定义了(全局)事务管理器(TM)和(局部)资源管理器(RM)之间的接口。
- 主流的关系型数据库产品都是实现了 XA 接口的。
- XA 接口是双向的系统接口,在事务管理器 (TM)以及一个或多个资源管理器(RM)之间形成通信桥梁。
- XA之所以需要引入事务管理器,是因为在分布式系统中,从理论上讲两台机器理论上无法达 到一致的状态,需要引入一个单点进行协调。
- 由全局事务管理器管理和协调的事务,可以跨越多个资源(如数据库或JMS队列)和进程。全局事务管理器一般使用 XA 二阶段提交协议 与数据库进行交互。
分布式事务模型
XA 分布式事务模型构成:由一个或多个“资源管理器”(Resource Manager),一个“事务管理器”(Transaction Manager),以及一个“应用程序”(Application Program)组成。如图:
- 资源管理器(RM):提供访问事务资源的方法。通常一个数据库就是一个资源管理器。【资源管理还必须具有管理事务提交或回滚的能力】
- 事务管理器(TM):协调参与全局事务中的各个事务。与参与全局事务中的每个资源管理器进行通信。
- 全局事务中的各个事务是全局事务的“分支”。全局事务及其分支由后面描述的命名方案标识。
- 应用程序(AP):定义事务的边界,指定全局事务中的操作。
- mysql 在执行分布式事务(外部XA)的时候,mysql服务器相当于xa事务资源管理器,与mysql链接的客户端相当于事务管理器。
分布式事务原理
分段提交:分布式事务通常采用 2PC 协议,全称“Two Phase Commitment Protocol”。该协议主要为了解决在分布式数据库场景下,所有节点间数据一致性的问题。分布式事务通过 2PC 协议将提交分成两个阶段:
- “prepare”;准备阶段。
- 准备所有分支:TM 告诉他们准备提交。通常,这意味着管理分支的每个 RM 在稳定的存储中记录分支的动作。分支指示它们是否能够执行此操作,并将这些结果用于第二阶段。
- 【即,所有的参与者准备执行事务并锁住需要的资源。参与者 ready 时,向“Transaction Manager”报告已准备就绪。】
- “commit/rollback”:提交阶段。
- TM 告诉 RM,是提交还是回滚。如果所有分支在准备就绪时都表示可以提交,则将告知所有分支进行提交。如果在准备就绪时指示任何分支将无法提交,则将告知所有分支回滚。
- 【当“Transaction Manager”确认所有参与者都 ready 后,向所有参与者发送 commit 命令。】
- 在某些情况下,全局事务可能使用一阶段提交(1PC)。例如,当事务管理器发现全局事务仅由一个事务资源(即单个分支)组成时,可以告知该资源同时准备和提交。
MySQL 对分布式事务的支持
MySQL 的 XA 事务分为“外部XA”和“内部XA”:
- “外部XA”:用于跨多 MySQL 实例的分布式事务,需要应用层作为协调者。【通俗的说就是比如我们在PHP中写代码,那么PHP书写的逻辑就是协调者】
- 应用层负责决定提交还是回滚,崩溃时的悬挂事务。MySQL 数据库 外部XA 可以用在分布式数据库代理层,实现对 MySQL 数据库的分布式事务支持,例如:开源的代理工具,网易的DDB,淘宝的TDDL 等等。
- “内部XA”:用于同一实例下跨多引擎事务,由“Binlog”作为协调者。【???】
- 比如在一个存储引擎提交时,需要将提交信息写入二进制日志,这就是一个分布式 内部XA 事务,只不过二进制日志的参与者是 MySQL 本身。Binlog作为 内部XA 的协调者,在binlog中出现的内部xid,在crash recover时,由binlog负责提交。(这是因为,binlog不进行prepare,只进行commit,因此在binlog中出现的内部xid,一定能够保证其在底层各存储引擎中已经完成prepare)
MySQL 中启用 XA:
- XA 分布式事务支持默认情况下处于启用状态。如果不使用此功能,则可以禁用“innodb_support_xa”配置选项,从而避免了每个事务额外的 fsync 的性能开销。
- 从 MySQL 5.0.3 开始支持 XA分布式事务,且只有 InnoDB 存储引擎支持。MySQL Connector/J 从5.0.0版本之后开始直接提供对XA的支持。
- 从 MySQL 5.7.10 开始,不允许禁用“innodb_support_xa”,因为它会使复制不安全,并阻止与“二进制日志”组提交相关的性能提升。 MySQL 8.0 中删除了“innodb_support_xa”配置选项。
NOTE:
- 在 Client 端,没有特殊要求。 MySQL 服务器的 XA 接口包含以 XA 关键字开头的 SQL 语句。 MySQL Client 端程序必须能够发送 SQL 语句并理解 XA 语句接口的语义。他们不需要链接到最新的 Client 端库。较旧的 Client 端库也将起作用。
- 在 MySQL 连接器中,MySQL Connector/J 5.0.0 和更高版本通过可为您处理 XA SQL 语句接口的类接口直接支持 XA。
- XA 支持分布式事务,即允许多个单独的事务资源参与全局事务的能力。事务性资源通常是 RDBMS,但可能是其他类型的资源。
- 全局事务涉及几个本身具有事务性的操作,但是所有操作必须要么作为一个组成功完成,要么全部作为一个组回滚。从本质上讲,这将 ACID 属性扩展到“一个级别”,以便可以将多个 ACID 事务作为具有 ACID 属性的全局操作的组成部分一起执行。
- (与非分布式事务一样,如果您的应用程序对读取现象敏感,则“SERIALIZABLE”可能是首选。“REPEATABLE READ”可能不足以进行分布式事务。)
XA 事务 SQL 语句
要在 MySQL 中执行 XA 事务,请使用以下语句:
XA {START|BEGIN} xid [JOIN|RESUME]
XA END xid [SUSPEND [FOR MIGRATE]]
XA PREPARE xid
XA COMMIT xid [ONE PHASE]
XA ROLLBACK xid
XA RECOVER [CONVERT XID]
- 对于“XA START”,可以识别“JOIN”和“RESUME”子句,但无效果。
- 对于“XA END”,可以识别“SUSPEND [FOR MIGRATE]”子句,但无效果。
每个 XA 语句都以“XA”关键字开头,并且大多数都需要“xid”值。
“xid”是 XA 事务标识符。它指示该语句适用于哪个事务。
- xid 值由 Client 端提供,或由 MySQL 服务器生成。
xid 的值由一到三部分组成:
xid: gtrid [, bqual [, formatID ]]
其中:“gtrid”是全局事务标识符,“bqual”是分支限定符,“formatID”是标识“gtrid”和“bqual”值所使用的格式的数字。
- 如语法所示,“bqual”和“formatID”是可选的。如果未指定:“bqual”默认值为“''”,“formatID”默认值为“1”。
- “gtrid”和“bqual”必须是字符串 Literals,每个字符串的长度最多为 64 个字节(不是字符)。
- “gtrid”和“bqual”可以通过几种方式指定。您可以使用带引号的字符串(“'ab'”),十六进制字符串(“X'6162'”,“0x6162”)或位值(“b'nnnn'”)。
- “formatID”是无符号整数。
- “gtrid”和“bqual”值由 MySQL 服务器的基础 XA 支持例程以字节为单位进行解释。但是,在解析包含 XA 语句的 SQL 语句时,服务器将使用某些特定的字符集。为了安全起见,将“gtrid”和“bqual”写为十六进制字符串。
- xid 值通常由事务管理器生成。一个 TM 生成的值必须不同于其他 TM 生成的值。给定的 TM 必须能够识别“XA RECOVER”语句返回的值列表中自己的 xid 值。
“XA START xid”:使用给定的 xid 值开始 XA 事务。
- 每个 XA 事务必须具有唯一的_xid 值,因此该值当前不能被另一个 XA 事务使用。
- 使用“gtrid”和“bqual”值评估唯一性。
- 必须使用与“XA START”语句中给定的相同值 xid 来指定用于 XA 事务的所有以下 XA 语句。如果使用这些语句中的任何一条,但是指定的 xid 值与某些现有 XA 事务不对应,则将发生错误。
一个或多个 XA 事务可以是同一全局事务的一部分。给定全局事务中的所有 XA 事务必须在 xid 值中使用相同的“gtrid”值。因此,“gtrid”值必须是全局唯一的,以使给定 XA 事务属于哪个全局事务没有歧义。 xid 值的“bqual”部分对于全局事务中的每个 XA 事务必须不同。(“bqual”值必须不同,这是当前 MySQL XA 实现的限制。它不是 XA 规范的一部分)
“XA RECOVER”语句返回 MySQL 服务器上处于“PREPARED”状态的 XA 事务的信息。
- 无论服务器是由哪个 Client 端启动的,输出都会为服务器上的每个此类 XA 事务包含一行。
“XA RECOVER”输出行看起来像这样(例如 xid 由 'abc','def'和 7 组成的值):
mysql> XA RECOVER;
+----------+--------------+--------------+--------+
| formatID | gtrid_length | bqual_length | data |
+----------+--------------+--------------+--------+
| 7 | 3 | 3 | abcdef |
+----------+--------------+--------------+--------+
输出列具有以下含义:
- formatID 是 Transaction 的 formatID 部分 xid
- gtrid_length 是 xid 的 gtrid 部分的字节长度
- bqual_length 是 xid 的 bqual 部分的字节长度
- data 是 xid 的 gtrid 和 bqual 部分的串联
XID 值可能包含不可打印的字符。从 MySQL 5.7.5 开始,“XA RECOVER”允许使用可选的“CONVERT XID”子句,以便 Client 端可以请求十六进制的 XID 值。
XA 事务 状态
XA 事务通过以下状态进行:
- 使用“XA START”启动 XA 事务并将其置于“ACTIVE”状态。【启动】
- 对于“ACTIVE” XA 事务,请发出构成该事务的 SQL 语句,然后发出“XA END”语句。“XA END”将事务置于“IDLE”状态。【空闲】
- 对于“IDLE” XA 事务,使用:
- “XA PREPARE”将事务置于“PREPARED”状态。【二阶段提交:准备阶段】
- 此时的“XA RECOVER”语句将在其输出中包含事务的 xid 值,因为“XA RECOVER”列出了处于“PREPARED”状态的所有 XA 事务。
- “XA COMMIT ... ONE PHASE”准备并提交事务。xid 的值不会被“XA RECOVER”列出,因为事务终止。【一阶段提交】
- “XA PREPARE”将事务置于“PREPARED”状态。【二阶段提交:准备阶段】
- 对于“PREPARED” XA 事务,您可以发出“XA COMMIT”语句来提交和终止该事务,或发出“XA ROLLBACK”来回滚并终止该事务。【二阶段提交:提交阶段】
示例:这是一个简单的 XA 事务,它作为全局事务的一部分在 table 中插入一行:
mysql> XA START 'xatest';
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO mytable (i) VALUES(10);
Query OK, 1 row affected (0.04 sec)
mysql> XA END 'xatest';
Query OK, 0 rows affected (0.00 sec)
mysql> XA PREPARE 'xatest';
Query OK, 0 rows affected (0.00 sec)
mysql> XA COMMIT 'xatest';
Query OK, 0 rows affected (0.00 sec)
NOTE:
- 在给定的 Client 端连接的上下文中,XA 事务和本地(非 XA)事务是互斥的。
- 例如,如果已发出“XA START”以开始 XA 事务,则在 XA 事务已提交或回滚之前无法启动本地事务。相反,如果本地事务以“START TRANSACTION”开始,则在提交或回滚该事务之前,不能使用 XA 语句。
- 如果 XA 事务处于“ACTIVE”状态,则不能发出任何导致隐式提交的语句。这将违反 XAContract,因为您无法回滚 XATransaction。如果您尝试执行这样的语句,则会收到以下错误:
ERROR 1399 (XAE07): XAER_RMFAIL: The command cannot be executed when global transaction is in the ACTIVE state
XA 事务 限制【???】
- XA 事务支持仅限于 InnoDB 存储引擎。
- “外部 XA”与“内部 XA”:
- 对于“外部 XA”,MySQL 服务器充当资源管理器,而 Client 端程序充当事务管理器。【跨多 MySQL 实例的分布式事务】
- 对于“内部 XA”,MySQL 服务器中的存储引擎充当资源管理器,而服务器本身充当事务管理器。【同一实例下跨多引擎事务】
- 内部 XA 支持受单个存储引擎功能的限制。需要内部 XA 来处理涉及多个存储引擎的 XA 事务。内部 XA 的实现要求存储引擎在 table 处理程序级别支持两阶段提交,并且当前仅对 InnoDB 如此。
- 对于全局事务中的每个 XA 事务,xid 值的“bqual”部分必须不同,这一要求是当前 MySQL XA 实现的限制。它不是 XA 规范的一部分。
- 在 MySQL 5.7.7 之前,XA 事务与复制不兼容。
- 这是因为处于“PREPARED”状态的 XA 事务将在清理服务器关闭或 Client 端断开连接时回滚。
- 类似地,如果服务器异常关闭然后再次启动,则处于“PREPARED”状态的 XA 事务仍将处于“PREPARED”状态,但是该事务的内容无法写入二进制日志。
- 在这两种情况下,都无法正确复制 XA 事务。
- 在 MySQL 5.7.7 和更高版本中,行为有所变化,并且 XA 事务分为两部分写入二进制日志:
- 发出“XA PREPARE”时,使用初始 GTID 写入直到“XA PREPARE”的事务的第一部分。 “XA_prepare_log_event”用于标识二进制日志中的此类事务。
- 发出“XA COMMIT”或“XA ROLLBACK”时,使用第二个 GTID 写入仅包含“XA COMMIT”或“XA ROLLBACK”语句的事务的第二部分。
- 请注意,由“XA_prepare_log_event”标识的事务的初始部分不必紧跟其“XA COMMIT”或“XA ROLLBACK”,这会导致任何两个 XA 事务的交错二进制日志记录。 XA 事务的两个部分甚至可以出现在不同的二进制日志文件中。
- 这意味着处于“PREPARED”状态的 XA 事务现在将一直保留,直到发出明确的“XA COMMIT”或“XA ROLLBACK”语句为止,从而确保 XA 事务与复制兼容。
- 在复制从属服务器上,准备好 XA 事务后,它立即与从属服务器应用程序线程分离,并且可以由该从属服务器上的任何线程提交或回滚。这意味着同一 XA 事务可以在不同线程上的不同状态下出现在“events_transactions_current”表中。“events_transactions_current”表显示线程上最近监视的事务事件的当前状态,并且在线程空闲时不更新此状态。因此,XA 事务在被另一个线程处理后,仍可以在原始应用程序线程的“PREPARED”状态下显示。
- 为了肯定地标识仍处于“PREPARED”状态且需要恢复的 XA 事务,请使用“XA RECOVER”语句而不是 Performance Schema 事务表。
在 MySQL 5.7.7 和更高版本中使用 XA 事务存在以下限制:
- XA 事务不能完全抵抗二进制日志的意外中断。如果服务器在执行“XA PREPARE”,“XA COMMIT”,“XA ROLLBACK”或“XA COMMIT ... ONE PHASE”语句的过程中意外中断,则服务器可能无法恢复到正确的状态,从而使服务器和二进制日志处于不一致的状态。
- 在这种情况下,二进制日志可能包含未应用的额外 XA 事务,或者错过了已应用的 XA 事务。另外,如果启用了 GTID,则恢复“@@GLOBAL.GTID_EXECUTED”后可能无法正确描述已应用的事务。
- 请注意,如果在“XA PREPARE”之前,“XA PREPARE”和“XA COMMIT”(或XA ROLLBACK”)之间或“XA COMMIT”(或XA ROLLBACK”)之后发生意外的停止,则服务器和二进制日志将被正确恢复并进入一致状态。
- 不支持将复制过滤器或二进制日志过滤器与 XA 事务结合使用。table 的筛选可能会导致 XA 事务在复制从属服务器上为空,并且不支持空的 XA 事务。同样,使用复制从属服务器上的设置“master_info_repository=TABLE”和“relay_log_info_repository=TABLE”(在 MySQL 8.0 中成为默认设置),在过滤 XA 事务后,数据引擎事务的内部状态会更改,并且可能与复制事务上下文状态不一致。
- 每当 XA 事务受到复制过滤器的影响时,无论该事务是否为空,都会记录错误“ER_XA_REPLICATION_FILTERS”。如果事务不为空,则复制从属服务器可以 continue 运行,但是您应该采取步骤中止对 XA 事务使用复制筛选器,以避免潜在的问题。如果事务为空,则复制从站停止。在这种情况下,复制从设备可能处于不确定状态,在该状态下复制过程的一致性可能会受到影响。特别是,从站的从站上的“gtid_executed”设置可能与主站上的“gtid_executed”不一致。要解决这种情况,请隔离主服务器并停止所有复制,然后检查整个复制拓扑中的 GTID 一致性。撤消生成错误消息的 XA 事务,然后重新启动复制。
- 在 MySQL 5.7.19 之前,“FLUSH TABLES WITH READ LOCK”与 XA 事务不兼容。
- 对于基于语句的复制,XA 事务被认为是不安全的。如果正在从服务器上以相反的 Sequences 准备在主服务器上并行提交的两个 XA 事务,则会发生无法安全解决的锁定依赖关系,并且复制可能会因从服务器上的死锁而失败。对于单线程或多线程复制从属,可能会发生这种情况。设置“binlog_format=STATEMENT”时,将对 XA 事务中的 DML 语句发出警告。设置“binlog_format=MIXED”或“binlog_format=ROW”时,将使用基于行的复制来记录 XA 事务中的 DML 语句,并且不存在潜在的问题。
SET TRANSACTION 语句
SET [GLOBAL | SESSION] TRANSACTION
transaction_characteristic [, transaction_characteristic] ...
transaction_characteristic: {
ISOLATION LEVEL level
| access_mode
}
level: {
REPEATABLE READ
| READ COMMITTED
| READ UNCOMMITTED
| SERIALIZABLE
}
access_mode: {
READ WRITE
| READ ONLY
}
该语句指定 transaction 的 Feature。它采用由逗号分隔的一个或多个 Feature 值的列表。每个 Feature 值设置事务 isolation level 或访问模式。
- 隔离级别用于 InnoDB 表上的操作。
- 访问模式指定事务是以读/写还是只读模式运行。
- 此外,SET TRANSACTION 可以包含可选的“GLOBAL”或“SESSION”关键字以指示语句的范围。
Transaction 隔离级别
要设置事务隔离级别,请使用“ISOLATION LEVEL level”子句。不允许在同一“SET TRANSACTION”语句中指定多个“ISOLATION LEVEL”子句。
- 默认隔离级别为“REPEATABLE READ”。其他允许的值为“READ COMMITTED”,“READ UNCOMMITTED”和“SERIALIZABLE”。
- 见:“InnoDB:InnoDB 事务模型 之 事务隔离级别”
Transaction 访问模式
要设置事务访问模式,请使用“READ WRITE”或“READ ONLY”子句。不允许在同一“SET TRANSACTION”语句中指定多个访问模式子句。
- “READ WRITE”:(默认模式)允许对事务中使用的表进行读写。
- 可以使用“SET TRANSACTION”和“READ WRITE”的访问模式来明确指定此模式。
- “READ ONLY”:禁止更改 table。
- 这可以使存储引擎能够进行性能改进,这在不允许写入的情况下是可能的。
- 在只读模式下,仍然可以使用 DML 语句更改用 TEMPORARY 关键字创建的 table。与永久 table 一样,不允许使用 DDL 语句进行更改。
也可以使用“START TRANSACTION”语句为单个事务指定“READ WRITE”和“READ ONLY”访问模式。
Transaction Feature 范围
可以为当前会话或仅针对下一个事务全局设置事务 Feature:
- 使用“GLOBAL”关键字:
- 该声明适用于所有后续会话。
- 现有会话不受影响。
- 使用“SESSION”关键字:
- 该声明适用于当前会话中执行的所有后续事务。
- 该语句在事务中是允许的,但不会影响当前正在进行的事务。
- 如果在事务之间执行,则该语句将覆盖设置命名特征的下一个事务值的任何前面的语句。【???】
- 没有任何“SESSION”或“GLOBAL”关键字:
- 该语句仅适用于会话中执行的下一个单个事务。
- 随后的事务将恢复为使用命名 Feature 的会话值。
- 事务中的如下声明是不允许的:【???】
mysql> START TRANSACTION; Query OK, 0 rows affected (0.02 sec) mysql> SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; ERROR 1568 (25001): Transaction characteristics can't be changed while a transaction is in progress
NOTE:
- 更改全局事务 Feature 需要“SUPER”特权。任何会话都可以自由更改其会话 Feature(即使在事务中间),也可以更改其下一个事务的 Feature(在该事务开始之前)。
- 要在服务器启动时设置全局隔离级别,请在命令行或选项文件中使用“--transaction-isolation=level”选项。
- 【此选项的 level 值使用破折号而不是空格,因此允许的值为“READ-UNCOMMITTED”,“READ-COMMITTED”,“REPEATABLE-READ”或“SERIALIZABLE”。】
- 要在服务器启动时设置全局事务访问模式,请使用“--transaction-read-only”选项。默认值为“OFF”(读/写模式),但对于只读模式为“ON”。
- 在运行时,可以使用“SET TRANSACTION”语句间接设置全局,会话和下一个事务作用域级别的 Feature。也可以使用“SET”语句直接设置它们,以将值分配给“transaction_isolation”和“transaction_read_only”系统变量:
- 【在 MySQL 5.7.20 之前,请使用“tx_isolation”和“tx_read_only”而不是 transaction_isolation 和 transaction_read_only。】
- “SET TRANSACTION”允许使用可选的“GLOBAL”和“SESSION”关键字来设置不同范围级别的 TransactionFeature。
- 用于为“transaction_isolation”和“transaction_read_only”系统变量分配值的“SET”语句具有用于在不同作用域级别设置这些变量的语法。
- 语法如下:
TransactionFeature 的“SET TRANSACTION”语法 Syntax 受影响的 Feature 范围 SET GLOBAL TRANSACTION transaction_characteristic Global SET SESSION TRANSACTION transaction_characteristic Session SET TRANSACTION transaction_characteristic 仅下一个 Transaction
TransactionFeature 的“SET”语法 Syntax 受影响的 Feature 范围 SET GLOBAL var_name = value Global SET @@GLOBAL.var_name = value SET SESSION var_name = value Session SET @@SESSION.var_name = value SET var_name = value SET @@var_name = value 仅下一个 Transaction
- 可以在运行时检查事务 Feature 的全局和会话值:
SELECT @@GLOBAL.transaction_isolation, @@GLOBAL.transaction_read_only; SELECT @@SESSION.transaction_isolation, @@SESSION.transaction_read_only;