目录
1.START TRANSACTION、COMMIT和ROLLBACK语句
4.SAVEPOINT、ROLLBACK TO SAVEPOINT和RELEASE SAVEPOINT语句
5.LOCK INSTANCE FOR BACKUP和UNLOCK INSTANCE语句
1.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}
这些语句提供对事务使用的控制:
START TRANSACTION或BEGIN启动新事务。
COMMIT提交当前事务,使其修改成为永久性的。
ROLLBACK回滚当前事务,取消其修改改。
SET自动提交禁用或启用当前会话的默认自动提交模式。
默认情况下,MySQL在启用自动提交模式的情况下运行。这意味着,当不在事务内部时,每个语句都是原子语句,就好像它被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结束事务之前,自动提交一直处于禁用状态。然后,自动提交模式将恢复到以前的状态。
START TRANSACTION允许使用多个修饰符来控制事务特性。要指定多个修饰符,请用逗号分隔。
WITH CONSISTENT SNAPSHOT修饰符为具有此功能的存储引擎启动一致读取(快照读)。
这仅适用于InnoDB。其效果与从任何InnoDB表中发出START TRANSACTION后接SELECT相同。(因为Innodb默认可重复读隔离级别)
参见“一致无锁读取”。
WITH CONSISTENT SNAPSHOT修饰符不会更改当前事务隔离级别,因此只有当当前隔离级别允许一致读取时,它才能提供一致的快照。
唯一允许一致读的隔离级别是可重复读取。对于所有其他隔离级别,WITH CONSISTENT SNAPSHOT子句将被忽略。忽略WITH CONSISTENT SNAPSHOT子句时会生成警告。
READ WRITE和READ ONLY修饰符设置事务访问模式。处于事务中时,它们允许或禁止更改使用中的表。
READ ONLY限制防止一个事务修改或锁定其他事务可见的存在事务的表或非事务表;事务仍然可以修改或锁定临时表。
当事务为只读时,MySQL可以对InnoDB表上的查询进行额外的优化。指定READ ONLY可确保在无法自动确定只读状态的情况下应用这些优化。
有关更多信息,请参阅“优化InnoDB只读事务”。
如果未指定访问模式,则应用默认模式。除非默认值已更改,否则它是读/写的。不允许在同一语句中同时指定READ WRITE和READ ONLY。
在只读模式下,仍然可以使用DML语句更改使用TEMPORARY关键字创建的表。不允许使用DDL语句进行更改,就像使用永久表一样。
有关事务访问模式的其他信息,包括更改默认模式的方法,请参阅“SET transaction语句”。
如果启用了READ ONLY系统变量,则使用START TRANSACTION READ WRITE显式启动事务需要CONNECTION_ADMIN权限(或不推荐使用的SUPER权限)。
重要:
许多用于编写MySQL客户端应用程序(如JDBC)的API都提供了自己的启动事务的方法,这些方法可以(有时也应该)用来代替从客户端发送START TRANSACTION语句。
有关更多信息,请参阅“连接器和API”或API文档。
要显式禁用自动提交模式,请使用以下语句:
SET autocommit=0;
通过将autocommit设置为零禁用自动提交模式后,对事务安全表(如InnoDB或NDB的表)的更改不会立即永久化。您必须使用COMMIT将更改存储到磁盘,或者使用ROLLBACK忽略更改。
autocommit是一个会话变量,必须为每个会话设置。
要为每个新连接禁用自动提交模式,请参阅“服务器系统变量”中对自动提交系统变量的描述。
支持将BEGIN和BEGIN WORK作为START TRANSACTION的别名来启动事务。
START TRANSACTION是标准的SQL语法,是启动特别事务的推荐方式,并且允许BEGIN不允许的修饰符。
BEGIN语句不同于BEGIN ... END用法。后者不启动事务。参见BEGIN…END复合语句。
注意:
在所有存储程序(存储过程和函数、触发器和事件)中,解析器将BEGIN[WORK]视为 BEGIN ... END。在此上下文中使用 START TRANSACTION开始事务。
COMMIT和ROLLBACK以及CHAIN和RELEASE子句都支持可选的WORK关键字。CHAIN和RELEASE可用于对事务完成进行额外控制。completion_type系统变量的值决定了默认的完成行为。
请参阅“服务器系统变量”。
AND CHAIN子句使新事务在当前事务结束后立即开始,并且新事务与刚刚终止的事务具有相同的隔离级别。
新事务还使用与刚刚终止的事务相同的访问模式(READ WRITE或READ ONLY)。
RELEASE子句导致服务器在终止当前事务后断开当前客户端会话的连接。包含NO关键字会抑制CHAIN或RELEASE完成,如果completion_type系统变量默认设置为导致链接或发布完成,则这可能很有用。
开始事务会导致提交任何挂起的事务。有关更多信息,请参阅“导致隐性承诺的声明”。
开始事务也会导致使用LOCK TABLES获取的表锁被释放,就好像您已经执行了UNLOCK TABLES。开始事务不会释放使用 FLUSH TABLES WITH READ LOCK.获取的全局读取锁。
为了获得最佳结果,应仅使用由单个事务安全存储引擎管理的表来执行事务。否则,可能会出现以下问题:
如果您使用多个事务安全存储引擎(如InnoDB)中的表,并且事务隔离级别不是SERIALIZABLE,那么当一个事务提交时,使用相同表的另一个正在进行的事务可能只看到第一个事务所做的一些更改。也就是说,混合引擎不能保证事务的原子性,可能会导致不一致。(如果混合引擎事务很少发生,则可以根据需要使用SET TRANSACTION ISOLATION LEVEL按每个事务将隔离级别设置为SERIALIZABLE。)
如果在事务中使用不安全的表,则无论自动提交模式的状态如何,对这些表的更改都会立即存储。
如果在更新事务中的非事务表后发出ROLLBACK语句,则会出现ER_WARNING_NOT_COMPLETE_ROLLBACK警告。将回滚对事务安全表的更改,但不会回滚对非事务安全表所做的更改。
COMMIT时,每个事务都存储在Binlog日志中的一个块中。
不会记录已回滚的事务。(例外:对非事务表的修改无法回滚。如果回滚的事务包括对非事务表格的修改,则会在最后使用ROLLBACK语句记录整个事务,以确保复制对非事务图表的修改。)
请参阅“Binlog日志”。
您可以使用SET TRANSACTION语句更改事务的隔离级别或访问模式。
参见“SET TRANSACTION语句”。
回滚可能是一个缓慢的操作,在用户没有明确要求的情况下(例如,当发生错误时),它可能会隐式发生。因此,SHOW PROCESSLIST在会话的State列中显示Rolling back,不仅用于使用ROLLBACK语句执行的显式回滚,还用于隐式回滚。
注意:
在MySQL 8.0中,BEGIN、COMMIT和ROLLBACK不受--replicate do db或--replicat ignore db rules的影响。
当InnoDB执行事务的完全回滚时,事务设置的所有锁都会被释放。如果事务中的单个SQL语句由于错误(如重复密钥错误)而回滚,则在事务保持活动状态时,将保留该语句设置的锁。
之所以会发生这种情况,是因为InnoDB以这样一种格式存储行锁,使得它以后无法知道哪个锁是由哪个语句设置的。
如果事务中的SELECT语句调用存储函数,而存储函数中的语句失败,则该语句将回滚。如果随后对事务执行ROLLBACK,则整个事务将回滚。
2.无法回滚的语句
某些语句无法回滚。通常,这些语句包括数据定义语言(DDL)语句,例如创建或删除数据库的语句,创建、删除或更改表或存储例程的语句。
您应该将交易设计为不包含此类报表。如果在事务的早期发出一个无法回滚的语句,而随后另一个语句失败,则在这种情况下,无法通过发出ROLLBACK语句来回滚事务的全部效果。
3.导致隐含COMMIT的语句
本节中列出的语句(以及它们的任何同义词)隐式地结束当前会话中的任何活动事务,就好像您在执行该语句之前完成了COMMIT一样。
这些语句中的大多数还会在执行后导致隐式提交。其目的是在其自己的特殊交易中处理每个此类声明。事务控制和锁定语句是例外:如果一个隐式提交发生在执行之前,另一个则不会发生在执行之后。
定义或修改数据库对象的数据定义语言(DDL)语句:
ALTER EVENT, ALTER FUNCTION, ALTER PROCEDURE, ALTER SERVER, ALTER TABLE, ALTER TABLESPACE, ALTER VIEW, CREATE DATABASE, CREATE EVENT, CREATE FUNCTION, CREATE INDEX, CREATE PROCEDURE, CREATE ROLE, CREATE SERVER, CREATE SPATIAL REFERENCE SYSTEM, CREATE TABLE, CREATE TABLESPACE, CREATE TRIGGER, CREATE VIEW, DROP DATABASE, DROP EVENT, DROP FUNCTION, DROP INDEX, DROP PROCEDURE, DROP ROLE, DROP SERVER, DROP SPATIAL REFERENCE SYSTEM, DROP TABLE, DROP TABLESPACE, DROP TRIGGER, DROP VIEW, INSTALL PLUGIN, RENAME TABLE, TRUNCATE TABLE, UNINSTALL PLUGIN.
如果使用了TEMPORARY关键字,CREATE TABLE和DROP TABLE语句不会提交事务。(这不适用于对临时表的其他操作,如ALTER TABLE和CREATE INDEX,它们确实会导致提交。)但是,尽管没有发生隐式提交,但语句也不能回滚,这意味着使用此类语句会违反事务原子性。
例如,如果使用CREATE TEMPORARY TABLE,然后回滚事务,则该表仍然存在。
InnoDB中的CREATE TABLE语句作为单个事务处理。
这意味着用户的ROLLBACK不会撤消用户在该事务期间所做的CREATE TABLE语句。
CREATE TABLE ... SELECT创建非临时表时,SELECT会在执行语句之前和之后导致隐式提交。(CREATE TEMPORARY TABLE…SELECT没有发生提交。)
隐式使用或修改mysql数据库中的表的语句:ALTER USER, CREATE USER, DROP USER, GRANT, RENAME USER, REVOKE, SET PASSWORD.
事务控制和锁定语句:BEGIN,LOCK TABLES,SET autocommit=1(如果值还不是1),START TRANSACTION,UNLOCK TABLES。
UNLOCK TABLES仅在当前任何表已被LOCK TABLS锁定以获取非事务表锁的情况下提交事务。在FLUSH TABLES WITH READ LOCK之后,UNLOCK TABLES不会发生提交,因为后者的语句不会获取表级锁。
事务不能嵌套。这是在发出START TRANSACTION 语句或其同义词之一时对任何当前事务执行隐式提交的结果。
当XA事务处于ACTIVE状态时,不能在该事务中使用导致隐式提交的语句。
BEGIN语句不同于启动BEGIN ... END复合语句。后者不会导致隐式提交。
参见“BEGIN…END复合语句”。
数据加载语句:加载数据。LOAD DATA导致仅对使用NDB存储引擎的表进行隐式提交。
管理声明:ANALYZE TABLE, CACHE INDEX, CHECK TABLE, FLUSH, LOAD INDEX INTO CACHE, OPTIMIZE TABLE, REPAIR TABLE, RESET (but not RESET PERSIST).
复制控制语句:START REPLICA, STOP REPLICA, RESET REPLICA, CHANGE REPLICATION SOURCE TO,CHANGE MASTER TO
在MySQL 8.0.22中,SLAVE关键字被REPLICA替换。
4.SAVEPOINT、ROLLBACK TO SAVEPOINT和RELEASE SAVEPOINT语句
SAVEPOINT identifier
ROLLBACK [WORK] TO [SAVEPOINT] identifier
RELEASE SAVEPOINT identifier
InnoDB支持SQL语句SAVEPOINT、ROLLBACK TO SAVEPOINT、RELEASE SAVEPONT和ROLLBACK的可选WORK关键字。
SAVEPOINT语句设置名称为identifier的命名事务保存点。
如果当前事务具有相同名称的保存点,则会删除旧的保存点并设置新的保存点。
ROLLBACK TO SAVEPOINT语句将事务回滚到命名的保存点,而不终止事务。
当前事务在设置保存点后对行所做的修改将在回滚中撤消,但InnoDB不会释放保存点后存储在内存中的行锁。(对于新插入的行,锁定信息由存储在该行中的事务ID携带;该锁不会单独存储在内存中。在这种情况下,行锁将在撤消中释放。)在命名存储点之后设置的存储点将被删除。
如果ROLLBACK TO SAVEPOINT语句返回以下错误,则表示不存在具有指定名称的存储点:
ERROR 1305 (42000): SAVEPOINT identifier does not exist
InnoDB支持SQL的RELEASE SAVEPOINT语句从当前事务的一组存储点中删除命名的存储点。不会发生提交或回滚。如果保存点不存在,则为错误。
如果执行COMMIT或不命名保存点的ROLLBACK,则会删除当前事务的所有保存点。
当调用存储函数或激活触发器时,会创建一个新的保存点级别。以前级别上的存储点不可用,因此不会与新级别上的保存点冲突。当函数或触发器终止时,它创建的任何存储点都将被释放,并恢复以前的存储点级别。
5.LOCK INSTANCE FOR BACKUP和UNLOCK INSTANCE语句
LOCK INSTANCE FOR BACKUP
UNLOCK INSTANCE
LOCK INSTANCE FOR BACKUP获取一个实例级备份锁,该锁允许在联机备份期间使用DML,同时防止可能导致快照不一致的操作。
执行LOCK INSTANCE FOR BACKUP语句需要BACKUP_ADMIN权限。
从早期版本就地升级到MySQL 8.0时,BACKUP_ADMIN权限会自动授予具有RELOAD权限的用户。
多个会话可以同时持有一个备份锁。
UNLOCK INSTANCE释放当前会话持有的备份锁。如果会话终止,会话所持有的备份锁也会被释放。
LOCK INSTANCE FOR BACKUP可防止创建、重命名或删除文件。REPAIR TABLE TRUNCATE TABLE、OPTIMIZE TABLE和帐户管理语句被阻止。
参见“账户管理语句”。
修改未记录在InnoDB redolog日志中的InnoDB文件的操作也被阻止。
LOCK INSTANCE FOR BACKUP允许只影响用户创建的临时表的DDL操作。
实际上,属于用户创建的临时表的文件可以在保留备份锁的同时创建、重命名或删除。还允许创建binlog日志文件。
当LOCK INSTANCE FOR BACKUP语句对实例有效时,不应发出PURGE BINARY LOGS,因为它从服务器中删除文件违反了备份锁的规则。在MySQL 8.0.28中,这是不允许的。
lock INSTANCE FOR backup获取的备份锁独立于事务锁和FLUSH TABLES tbl_name[,tbl_name]获取的锁。。。WITH READ LOCK,并且允许以下语句序列:
LOCK INSTANCE FOR BACKUP;
FLUSH TABLES tbl_name [, tbl_name] ... WITH READ LOCK;
UNLOCK TABLES;
UNLOCK INSTANCE;
FLUSH TABLES tbl_name [, tbl_name] ... WITH READ LOCK;
LOCK INSTANCE FOR BACKUP;
UNLOCK INSTANCE;
UNLOCK TABLES;
lock_wait_timeout设置定义了LOCK INSTANCE FOR BACKUP 语句在放弃之前等待获取锁的时间量。
6.LOCK TABLE和UNLOCK TABLES语句
LOCK {TABLE | TABLES}
tbl_name [[AS] alias] lock_type
[, tbl_name [[AS] alias] lock_type] ...
lock_type: {
READ [LOCAL]
| [LOW_PRIORITY] WRITE
}
UNLOCK {TABLE | TABLES}
MySQL允许客户端会话显式地获取表锁,以便与其他会话合作访问表,或者防止其他会话在会话需要独占访问表时修改表。会话只能为自己获取或释放锁。一个会话无法获取另一个会话的锁,也无法释放另一会话持有的锁。
锁可以用来模拟事务,或者在更新表时提高速度。这在表锁定限制和条件中有更详细的解释。
LOCK TABLES显式获取当前客户端会话的表锁。可以为基表或视图获取表锁。您必须具有LOCK TABLES权限,以及要锁定的每个对象的SELECT权限。
对于视图锁定,LOCK TABLES将视图中使用的所有基表添加到要锁定的表集中,并自动锁定它们。对于任何被锁定的视图的基础表,LOCK tables检查视图定义器(对于SQL SECURITY definer视图)或调用程序(对于所有视图)是否对这些表具有适当的权限。
如果使用lock TABLES显式锁定表,则触发器中使用的任何表也将隐式锁定,如lock TABLES和触发器中所述。
如果使用lock TABLES显式锁定表,则会隐式打开和锁定与外键约束相关的任何表。对于外键检查,对相关表采用共享只读锁(lock TABLES read)。对于级联更新,在操作中涉及的相关表上采用共享的无写锁(lock TABLES write)。
UNLOCK TABLES显式释放当前会话持有的任何表锁。LOCK TABLES在获取新锁之前隐式释放当前会话持有的任何表锁。
UNLOCK TABLES的另一个用途是释放通过FLUSH TABLES with read lock语句获取的全局读取锁,该语句使您能够锁定所有数据库中的所有表。参见第15.7.8.3节“FLUSH声明”。(如果您有像Veritas这样可以及时获取快照的文件系统,这是一种非常方便的获取备份的方法。)
LOCK TABLE是LOCK TABLES的同义词;UNLOCK TABLE是UNLOCK TABLES的同义词。
表锁只防止其他会话进行不适当的读取或写入。持有WRITE锁的会话可以执行表级操作,如DROP table或TRUNCATE table。对于持有READ锁的会话,不允许执行DROP TABLE和TRUNCATE TABLE操作。
以下讨论仅适用于非临时表。对于TEMPORARY表,允许(但忽略)LOCK TABLES。创建该表的会话可以自由访问该表,而不考虑其他可能生效的锁定。不需要锁,因为没有其他会话可以看到该表。
6.1 表锁获取
要在当前会话中获取表锁,请使用LOCK TABLES语句,该语句将获取元数据锁
(请参阅“元数据锁定”)。
以下锁类型可用:
READ[LOCAL]锁:
持有锁的会话可以读取表(但不能写入表)。
多个会话可以同时获取表的READ锁。
其他会话可以读取该表,而无需显式获取READ锁。
LOCAL修饰符允许其他会话在持有锁时执行不冲突的INSERT语句(并发插入)。
(请参阅“并发插入”。)
但是,如果您要在持有锁的情况下使用服务器外部的进程来操作数据库,则不能使用READ LOCAL。对于InnoDB表,READ LOCAL与READ相同。
[LOW_PRIORITY]写锁:
持有锁的会话可以读取和写入表。
只有持有锁的会话才能访问该表。在解除锁定之前,任何其他会话都无法访问它。
持有WRITE锁时,其他会话对表的锁定请求会被阻止。
LOW_PRIORITY修改器没有效果。在MySQL的早期版本中,它影响了锁定行为,但现在已经不是这样了。它现在已被弃用,使用它会产生警告。请改用不带LOW_PRIORITY的WRITE。
写锁通常比读锁具有更高的优先级,以确保尽快处理更新。
这意味着,如果一个会话获得了READ锁,然后另一个会话请求了WRITE锁,则随后的READ锁请求将等待,直到请求WRITE锁定的会话获得了锁并释放了它。
对于 max_write_lock_count系统变量的小值,可能会出现此策略的例外;
请参阅“元数据锁定”。
如果LOCK TABLES语句由于任何表上的其他会话持有锁而必须等待,则它会阻塞,直到可以获取所有锁为止。
需要锁的会话必须在单个LOCKTABLES语句中获取所需的所有锁。当这样获得的锁被持有时,会话只能访问被锁定的表。例如,在以下语句序列中,由于t2未在LOCK TABLES语句中锁定,因此尝试访问t2时出错:
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
相反,如果使用别名锁定表,则必须使用该别名在语句中引用该表:
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;
6.2 表锁释放
当会话持有的表锁被释放时,它们都会同时被释放。会话可以显式释放其锁,也可以在特定条件下隐式释放锁。
会话可以使用UNLOCK TABLES显式释放锁。
如果会话在已经持有锁的情况下发出LOCK TABLES语句来获取锁,则在授予新锁之前,它的现有锁将被隐式释放。
如果会话开始一个事务(例如,使用START TRANSACTION),则会执行一个隐式的UNLOCK TABLES,从而释放现有的锁。(有关表锁定和事务之间的交互的更多信息,请参阅表锁定与事务的交互。)
如果客户端会话的连接终止,无论是正常还是异常,服务器都会隐式释放会话持有的所有表锁(事务性和非事务性)。如果客户端重新连接,锁定将不再有效。
此外,如果客户端有一个活动事务,则服务器会在断开连接时回滚该事务,如果发生重新连接,则新会话将以启用自动提交的方式开始。
因此,客户端可能希望禁用自动重新连接。在自动重新连接生效的情况下,如果发生重新连接,但丢失了任何表锁或当前事务,则不会通知客户端。
在禁用自动重新连接的情况下,如果连接断开,则发出的下一条语句将出错。
客户端可以检测到错误并采取适当的操作,例如重新获取锁或重做事务。请参阅自动重新连接控制。
注意:
如果对锁定的表使用ALTER TABLE,则该表可能会变为解锁表。例如,如果您尝试第二次ALTER TABLE操作,结果可能是出现错误。表“tbl_name”未使用LOCK TABLES锁定。要处理此问题,请在进行第二次更改之前再次锁定表。另请参阅第“ALTER TABLE的问题”。
6.3 表锁与事务的交互
LOCK TABLE和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 table相同的行为的影响。
例如,START TRANSACTION不会释放全局读取锁。
参见“FLUSH声明”。
其他隐式导致事务被提交的语句不会释放现有的表锁。
有关此类声明的列表,请参阅“导致隐式提交的语句”。
对事务表(如InnoDB表)使用LOCKTABLES和UNLOCK TABLES的正确方法是,以SET autocommit=0(而不是START transaction)开头,后跟LOCKTABLes开始事务,并且在显式提交事务之前不调用UNLOCK TABLES。例如,如果您需要写入表t1并从表t2读取,则可以执行以下操作:
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不会释放表锁。
6.4 表锁和触发器
如果使用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被锁定是由于它们在触发器中使用:
t1被锁定用于根据WRITE锁请求进行写入。
t2被锁定以进行写入,即使该请求是针对READ锁定。这是因为t2被插入到触发器中,所以READ请求被转换为WRITE请求。
t3被锁用于读取,因为它仅从触发器内读取。
t4被锁用于写入,因为它可能在触发器内被更新。
6.5 表锁的限制和条件
您可以安全地使用KILL来终止正在等待表锁的会话。参见“KILL语句”。
在存储的程序中不能使用LOCK TABLES和UNLOCK TABLES。
performance_schema数据库中的表不能用LOCK TABLES锁定,setup_xxx表除外。
LOCK TABLES生成的锁的作用域是一个MySQL服务器。它与NDB Cluster不兼容,后者无法在mysqld的多个实例之间强制执行SQL级别的锁。
您可以改为在API应用程序中强制锁定。
有关更多信息,请参阅“与多个NDB集群节点相关的限制”。
LOCK TABLES语句有效时禁止使用以下语句:CREATE TABLE、CREATE TABLE...LIKE、CREATE VIEW、DROP VIEW和DDL语句。
对于某些操作,必须访问mysql数据库中的系统表。
例如,HELP语句需要服务器端帮助表的内容,CONVERT_TZ()可能需要读取时区表。
服务器会根据需要隐式锁定系统表以进行读取,因此不需要显式锁定它们。这些表按刚才所述进行处理:
mysql.help_category
mysql.help_keyword
mysql.help_relation
mysql.help_topic
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语句执行之间的trans表中插入新行。
在许多情况下,通过使用相对更新(UPDATE customer SET value=value+new_value)或LAST_INSERT_ID()函数,可以避免使用LOCK TABLES。
在某些情况下,您还可以通过使用用户级咨询锁定函数GET_LOCK()和RELEASE_LOCK()来避免锁定表。这些锁保存在服务器中的哈希表中,并使用pthread_mutex_lock()和pthread_mutex_unlock()实现高速。
参见“锁功能”。
有关锁定策略的更多信息,请参阅“内部锁定方法”。
7.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
}
此语句指定事务特征。它采用一个由逗号分隔的一个或多个特征值的列表。每个特征值设置事务隔离级别或访问模式。隔离级别用于InnoDB表上的操作。访问模式指定事务是以读/写模式操作还是以只读模式操作。
此外,SET TRANSACTION可以包括一个可选的GLOBAL或SESSION关键字来指示语句的范围。
7.1 事务隔离级别
要设置事务隔离级别,请使用ISOLATION LEVEL级别子句。
不允许在同一SET TRANSACTION语句中指定多个ISOLATION LEVEL子句。
默认隔离级别为REPEATABLE READ。其他允许的值包括READ COMMITTED、READ UNCOMITTED和SERIALIZABLE。
有关这些隔离级别的信息,请参阅“事务隔离级别”。
7.2 事务访问模式
要设置事务访问模式,请使用READ WRITE或READ ONLY子句。不允许在同一SET TRANSACTION语句中指定多个访问模式子句。
默认情况下,事务以读/写模式进行,允许对事务中使用的表进行读和写操作。可以使用访问模式为READ WRITE的SET TRANSACTION显式指定此模式。
如果事务访问模式设置为只读,则禁止更改表。这可能使存储引擎能够在不允许写入时进行性能改进。
在只读模式下,仍然可以使用DML语句更改使用TEMPORARY关键字创建的表。不允许使用DDL语句进行更改,就像使用永久表一样。
还可以使用START transaction语句为单个事务指定READ WRITE和READ ONLY访问模式。
7.3 事务范围特性
您可以为当前会话或仅为下一个事务全局设置事务特性:
使用GLOBAL关键字:
该语句在全局范围内适用于所有后续会话。
现有会话不受影响。
使用SESSION关键字:
该语句适用于当前会话中执行的所有后续事务。
事务中允许使用该语句,但不影响当前正在进行的事务。
如果在事务之间执行,则该语句将覆盖设置命名特性的下一个事务值的任何先前语句。
没有任何SESSION或GLOBAL关键字:
该语句仅适用于会话中执行的下一个单个事务。
后续事务将恢复为使用命名特性的会话值。
事务中不允许使用该语句:
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
更改全局事务特性需要CONNECTION_ADMIN权限(或不推荐使用的SUPER权限)。任何会话都可以自由更改其会话特性(即使在事务的中间)或其下一个事务的特性(在该事务开始之前)。
要在服务器启动时设置全局隔离级别,请在命令行或配置文件中使用--transaction isolation=level选项。此选项的级别值使用破折号而不是空格,因此允许的值为READ-UNCOMMITTED、READ-COMMITTED、REPEATABLE-READ或SERIALIZABLE。
类似地,要在服务器启动时设置全局事务访问模式,请使用--transaction只读选项。默认值为OFF(读/写模式),但对于只读模式,该值可以设置为ON。
例如,要将隔离级别设置为REPEATABLE READ,将访问模式设置为READ WRITE,请在配置文件的[mysqld]部分使用以下行
[mysqld]
transaction-isolation = REPEATABLE-READ
transaction-read-only = OFF
在运行时,可以使用set transaction语句间接设置全局、会话和下一个事务范围级别的特性,如前所述。也可以使用set语句直接设置它们,为transaction_inisolation和transaction_read_only系统变量赋值:
SET TRANSACTION允许可选的GLOBAL和SESSION关键字用于设置不同作用域级别的事务特性。
用于为transaction_inisolation和transaction_read_only系统变量赋值的SET语句具有用于在不同作用域级别设置这些变量的语法。
下表显示了每个SET TRANSACTION和变量赋值语法设置的特征作用域级别。
事务特性的SET TRANSACTION语法
语法 | 事务影响的范围 |
---|---|
SET GLOBAL TRANSACTION | Global |
SET SESSION TRANSACTION | Session |
SET TRANSACTION | Next transaction only |
事务特征的SET语法
语法 | 事务影响的范围 |
---|---|
SET GLOBAL | Global |
SET @@GLOBAL. | Global |
SET PERSIST | Global |
SET @@PERSIST. | Global |
SET PERSIST_ONLY | No runtime effect |
SET @@PERSIST_ONLY. | No runtime effect |
SET SESSION | Session |
SET @@SESSION. | Session |
SET | Session |
SET @@ | Next transaction only |
可以在运行时检查事务特征的全局值和会话值:
SELECT @@GLOBAL.transaction_isolation, @@GLOBAL.transaction_read_only;
SELECT @@SESSION.transaction_isolation, @@SESSION.transaction_read_only;
8.XA事务
InnoDB存储引擎支持XA事务。
MySQL XA实现基于X/Open CAE文档分布式事务处理:XA规范。本文档由The Open Group发布,可在http://www.opengroup.org/public/pubs/catalog/c193.htm “XA事务的限制”描述了当前XA实现的限制。
在客户端,没有特殊要求。MySQL服务器的XA接口由以XA关键字开头的SQL语句组成。MySQL客户端程序必须能够发送SQL语句并理解XA语句接口的语义。它们不需要与最近的客户端库链接。旧的客户端库也可以使用。
在MySQL连接器中,MySQL连接器/J 5.0.0及更高版本通过一个为您处理XA SQL语句接口的类接口直接支持XA。
XA支持分布式事务,即允许多个独立的事务资源参与全局事务的能力。事务性资源通常是RDBMS,但也可以是其他类型的资源。
全局事务涉及多个操作,这些操作本身是事务性的,但所有操作都必须作为一个组成功完成,或者作为一个小组回滚。
本质上,这将ACID属性“向上”扩展,以便多个ACID事务可以作为具有ACID属性的全局操作的组件一起执行。(与非分布式事务一样,如果您的应用程序对读取现象敏感,则可能首选SERIALIZABLE。可重复读取可能不足以用于分布式事务。)
分布式事务的一些示例:
应用程序可以充当将消息服务与RDBMS相结合的集成工具。该应用程序确保处理消息发送、检索和处理的事务(也涉及事务数据库)都发生在全局事务中。您可以将其视为“事务性电子邮件”
应用程序执行涉及不同数据库服务器的操作,如MySQL服务器和Oracle服务器(或多个MySQL服务器),其中涉及多个服务器的操作必须作为全局事务的一部分进行,而不是作为每个服务器本地的独立事务进行。
银行将账户信息保存在RDBMS中,并通过自动柜员机(ATM)分配和接收资金。有必要确保ATM操作正确地反映在帐户中,但这不能单独使用RDBMS来完成。全球交易管理器集成ATM和数据库资源,以确保金融交易的整体一致性。
使用全局事务的应用程序涉及一个或多个资源管理器和一个事务管理器:
资源管理器(RM)提供对事务性资源的访问。数据库服务器是一种资源管理器。必须能够提交或回滚RM管理的事务。
事务管理器(TM)协调作为全局事务一部分的事务。它与处理这些事务的RM进行通信。全局事务中的单个事务是全局事务的“分支”。全局事务及其分支通过稍后描述的命名方案来标识。
XA的MySQL实现使MySQL服务器能够充当资源管理器,在全局事务中处理XA事务。连接到MySQL服务器的客户端程序充当事务管理器。
要执行全局事务,必须知道涉及哪些组件,并使每个组件达到可以提交或回滚的程度。根据每个组件报告的成功能力,它们都必须作为一个原子组进行提交或回滚。也就是说,要么所有的组件都必须提交,要么所有组件都必须回滚。要管理全局事务,必须考虑到任何组件或连接网络可能出现故障。
执行全局事务的过程使用两阶段提交(2PC)。这发生在全局事务的分支执行的操作之后。
在第一阶段,准备好所有分支。也就是说,TM告诉他们做好承诺的准备。通常,这意味着管理分支的每个RM都会在稳定的存储中记录该分支的操作。分支指示它们是否能够做到这一点,并且这些结果用于第二阶段。
在第二阶段,TM告诉RM是提交还是回滚。如果所有分支在准备好时都表示能够提交,则会告知所有分支进行提交。如果任何分支在准备时表示无法提交,则会通知所有分支回滚。
在某些情况下,全局事务可能使用单阶段提交(1PC)。例如,当事务管理器发现全局事务仅由一个事务资源(即单个分支)组成时,可以告知该资源同时准备和提交。
8.1 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值由客户端提供,或由MySQL服务器生成。xid值由一到三部分组成:
xid: gtrid [, bqual [, formatID ]]
gtrid是全局事务标识符,bqual是分支限定符,formatID是标识gtrid和bqual值使用的格式的数字。如语法所示,bqual和formatID是可选的。如果未给定,则默认bqual值为“”。如果未给定,则默认的formatID值为1。
gtrid和bqual必须是字符串文字,每个文字的长度最多为64字节(而不是字符)。gtrid和bqual可以通过多种方式指定。您可以使用带引号的字符串('ab')、十六进制字符串(X'6162'、0x6162)或位值(b'nnn')。
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事务的所有以下XA语句都必须使用与XA START语句中给定的相同的xid值来指定。如果使用这些语句中的任何一个,但指定的xid值与某些现有XA事务不对应,则会发生错误。
从MySQL 8.0.31开始,当服务器使用--replicate do db或--replicade ignore db运行时,默认数据库不会过滤XA START、XA BEGIN、XA END、XA COMMIT和XA ROLLBACK语句。
一个或多个XA事务可以是同一全局事务的一部分。给定全局事务中的所有XA事务都必须在xid值中使用相同的gtrid值。因此,gtrid值必须是全局唯一的,这样给定的XA事务是哪个全局事务的一部分就不会有歧义。对于全局事务中的每个XA事务,xid值的bqual部分必须不同。(bqual值不同的要求是当前MySQL XA实现的限制。它不是XA规范的一部分。)
XA RECOVER语句返回MySQL服务器上处于PREPARED状态的XA事务的信息。(请参阅第15.3.8.2节“XA事务状态”。)无论是哪个客户端启动的,输出都包括服务器上每个这样的XA事务的一行。
XA RECOVER需要XA_RECOVER_ADMIN权限。此特权要求防止用户发现未完成的已准备XA事务的XID值,而不是自己的事务。它不会影响XA事务的正常提交或回滚,因为启动它的用户知道它的XID。
XA RECOVER输出行如下所示(例如xid值由部分“abc”、“def”和7组成):
mysql> XA RECOVER;
+----------+--------------+--------------+--------+
| formatID | gtrid_length | bqual_length | data |
+----------+--------------+--------------+--------+
| 7 | 3 | 3 | abcdef |
+----------+--------------+--------------+--------+
输出列具有以下含义:
formatID是事务xid的formatID部分
gtrid_length是xid的gtrid部分的长度(以字节为单位)
bqual_length是xid的bqual部分的长度(以字节为单位)
数据是xid的gtrid和bqual部分的串联
XID值可能包含不可打印的字符。XA RECOVER允许使用可选的CONVERT XID子句,以便客户端可以请求十六进制的XID值。
8.2 XA事务状态
XA事务通过以下状态进行:
1.使用XA START启动XA事务并将其置于ACTIVE状态。
2.对于ACTIVE XA事务,发出组成该事务的SQL语句,然后发出XA END语句。XA END将事务置于IDLE状态。
3.对于IDLE XA事务,您可以发出XA PREPARE语句或XA COMMIT ... ONE PHASE 语句:
XA PREPARE将事务置于PREPARED状态。
此时,XA RECOVER语句在其输出中包括事务的xid值,因为XA RECOVERE列出了所有处于PREPARED状态的XA事务。
XA COMMIT ... ONE PHASE 准备并提交事务。由于事务终止,XA RECOVER未列出xid值。
4.对于PREPARED XA事务,可以发出XA COMMIT语句来提交和终止事务,或者发出XA ROLLBACK来回滚和终止事务。
下面是一个简单的XA事务,它将一行作为全局事务的一部分插入到表中:
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)
这里有一个简单的XA事务,它将一行插入到表中,作为全局事务的一部分。
在MySQL 8.0.28及更早版本中,在给定客户端连接的上下文中,XA事务和本地(非XA)事务是互斥的。
例如,如果发出XA START以开始XA事务,则在提交或回滚XA事务之前,无法启动本地事务。相反,如果使用START TRANSACTION启动了本地事务,则在事务提交或回滚之前,不能使用XA语句。
MySQL 8.0.29及更高版本支持分离的XA事务,由 xa_detach_on_prepare系统变量启用(默认为on)。
在执行XA PREPARE之后,独立的事务将与当前会话断开连接(并且可以由另一个连接提交或回滚)。
这意味着当前会话可以自由启动新的本地事务或XA事务,而不必等待准备好的XA事务被提交或回滚。
当XA事务被分离时,连接对其准备的任何XA事务都没有特殊的了解。如果当前会话在另一个连接已经提交或回滚给定的XA事务(即使是它准备的事务)之后尝试提交或回滚,则该尝试将被拒绝,并出现无效的XID错误(ER_XAER_NOTA),因为请求的XID不再存在。行动
注意:
独立的XA事务不能使用临时表。
当独立的XA事务被禁用( xa_detach_on_prepare设置为OFF)时,XA事务将保持连接,直到它被原始连接提交或回滚,如前面针对MySQL 8.0.28和更早版本所述。对于组复制中使用的MySQL服务器实例,不建议禁用独立的的XA事务;有关详细信息,请参阅服务器实例配置。
如果XA事务处于ACTIVE状态,则不能发出任何导致隐式提交的语句。这将违反XA合约,因为您无法回滚XA事务。尝试执行这样的语句会引发以下错误:
ERROR 1399 (XAE07): XAER_RMFAIL: The command cannot be executed
when global transaction is in the ACTIVE state
“导致隐性提交的语句”中列出了适用上述备注的声明。
8.3 XA事务的限制
XA事务支持仅限于InnoDB存储引擎。
对于“外部XA”,MySQL服务器充当资源管理器,客户端程序充当事务管理器。
对于“内部XA”,MySQL服务器中的存储引擎充当RM,服务器本身充当TM。
内部XA支持受到单个存储引擎功能的限制。内部XA是处理涉及多个存储引擎的XA事务所必需的。内部XA的实现要求存储引擎支持表处理程序级别的两阶段提交,而目前这仅适用于InnoDB。
对于XA START,JOIN和RESUME子句是可识别的,但不起作用。
对于XA END,SUSPEND[FOR MIGRATE]子句已被识别,但无效。
对于全局事务中的每个XA事务,xid值的bqual部分都不同的要求是当前MySQL XA实现的限制。它不是XA规范的一部分。
XA事务分两部分写入binlog日志。
当发出XA PREPARE时,事务的第一部分直到XA PREPARE都是使用初始GTID写入的。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事务表。
使用XA事务存在以下限制:
在MySQL 8.0.30之前,XA事务对于binlog日志的意外停止没有完全的弹性。
如果在服务器执行XA PREPARE、XA COMMIT、XA ROLLBACK或XA COMMIT,ONE PHASE语句的过程中出现意外停止,服务器可能无法恢复到正确的状态,从而使服务器和二进制日志处于不一致的状态。
在这种情况下,binlog日志可能包含未应用的额外XA事务,或者未应用的XA事务。此外,如果启用了GTID,恢复后@@GLOBAL.GTID_EXECUTED可能无法正确描述已应用的事务。请注意,如果在XA PREPARE之前、在XA PREPREPARE和XA COMMIT(或XA ROLLBACK)之间或在XA COMMIT或XA ROLLBACK之后发生意外停止,则服务器和二进制日志将正确恢复并保持一致状态。
从MySQL 8.0.30开始,这不再是一个问题;服务器将XA PREPARE实现为两阶段操作,它维护存储引擎和服务器之间的准备操作的状态,并在存储引擎和二进制日志之间强制执行顺序,以便在状态在服务器节点上保持一致和持久之前不进行广播。
您应该注意,当同一事务XID用于顺序执行XA事务,并且在 XA COMMIT ... ONE PHASE处理过程中发生中断时,可能无法再同步binlog日志和存储引擎之间的状态。
如果刚才描述的一系列事件发生在存储引擎中准备好prepared该事务之后,而XA COMMIT语句仍在执行,则可能会发生这种情况。这是一个已知的问题。
不支持将复制筛选器或binlog日志筛选器与XA事务结合使用。
表的筛选可能会导致复制副本上的XA事务为空,并且不支持空的XA事务。
此外,由于副本的连接元数据存储库和应用程序元数据存储库存储在InnoDB表中(在MySQL 8.0中成为默认值),数据引擎事务的内部状态会在经过过滤的XA事务之后发生变化,并且可能与复制事务上下文状态不一致。
每当XA事务受到复制筛选器的影响时,无论事务是否因此为空,都会记录错误ER_XA_REPLICATION_FILTERS。
如果事务不为空,则复制副本可以继续运行,但您应该采取措施停止对XA事务使用复制筛选器,以避免潜在的问题。如果事务为空,则复制副本将停止。在这种情况下,复制副本可能处于未确定的状态,在这种状态下,复制过程的一致性可能会受到损害。特别是,副本的副本上的gtid_executed集可能与源上的不一致。
要解决此问题,请隔离源并停止所有复制,然后检查整个复制拓扑的GTID一致性。撤消生成错误消息的XA事务,然后重新启动复制。
XA事务被认为对于基于语句的复制是不安全的。如果在源上并行提交的两个XA事务在复制副本上以相反的顺序准备,则可能会发生无法安全解决的锁依赖关系,并且复制可能会失败,导致复制副本出现死锁。这种情况可能发生在单线程或多线程复制副本上。
当设置binlog_format=STATION时,XA事务内的DML语句将发出警告。
当设置binlog_format=MIXED或binlog_foormat=ROW时,XA事务中的DML语句将使用基于行的复制进行记录,并且不存在潜在问题。