START TRANSACTION, COMMIT, and ROLLBACK Statements
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在启用自动提交模式的情况下运行 。这意味着,当不在事务内时,每个语句都是原子的,就像它被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
允许使用几个修饰符来控制transaction特征。要指定多个修饰符,请用逗号分隔。
WITH CONSISTENT SNAPSHOT
修饰符为能够使用它的存储引擎启动 consistent read,这仅适用于InnoDB
。效果与从任何InnoDB
table 发出START TRANSACTION
后跟SELECT
相同。WITH CONSISTENT SNAPSHOT
修饰符不会更改当前 transaction isolation level,因此仅当当前隔离 level 是允许一致读取的隔离时,它才会提供一致的快照。允许一致读取的唯一隔离级别是REPEATABLE READ
。对于所有其他隔离级别,将忽略WITH CONSISTENT SNAPSHOT
子句。从 MySQL 5.7.2 开始,忽略WITH CONSISTENT SNAPSHOT
子句时会生成警告。READ WRITE
和READ ONLY
修饰符设置 transaction 访问模式。它们允许或禁止对 transaction 中使用的表进行更改。READ ONLY
限制可防止事务修改或锁定其他事务可见的事务表和非事务表;事务仍然可以修改或锁定临时表。
当已知事务是只读的时, MySQL可以对InnoDB表查询进行额外的优化 。通过指定READ ONLY
可确保在无法自动确定只读状态的情况下应用这些优化。
如果未指定访问模式,则应用默认模式。除非更改了默认值,否则它是读/写的。不允许在同一语句中同时指定READ WRITE
和READ ONLY
。
在只读模式下,仍然可以使用DML语句更改用TEMPORARY
关键字创建的表。与永久表一样,不允许使用DDL语句进行更改。
如果启用了read_only系统变量,则使用START TRANSACTION READ WRITE
显式启动 transaction 需要 SUPER
特权。
重要许多用于编写MySQL客户端应用程序的API(例如JDBC)提供了自己的启动事务的方法,这些方法可以(有时应该)用于代替从客户端发送
START TRANSACTION
语句。
要显式禁用自动提交模式,请使用以下语句:
SET autocommit=0;
通过将autocommit
变量设置为零来禁用自动提交模式后 ,对事务安全表(例如for InnoDB
或 NDB
)的更改不会立即变为永久更改 。您必须使用COMMIT
将更改存储到磁盘或ROLLBACK
忽略更改。
autocommit
是会话变量,必须为每个会话设置。要为每个新连接禁用自动提交模式,请参见第5.1.7节“服务器系统变量”中对autocommit
系统变量 的描述 。
支持BEGIN
和BEGIN WORK
作为START TRANSACTION
的别名来启动 transaction。START TRANSACTION
是标准的SQL语法,是启动临时事务的推荐方法,并且允许使用BEGIN
不允许的修饰符。
BEGIN
语句与用于启动BEGIN ... END
复合语句的BEGIN
关键字的用法不同 。后者不会开始 transaction。
注意
在所有存储的程序(存储的过程和功能,触发器和事件)中,解析器将
BEGIN [WORK]
视为BEGIN ... END
块的开始 。而是在这种情况下使用START TRANSACTION替代
开始事务 。
COMMIT
和ROLLBACK
支持可选的WORK
关键字,CHAIN
和RELEASE
子句也支持。 CHAIN
和RELEASE
可用于对 transaction 完成的附加控制。 completion_type系统变量的 value 确定默认的完成行为。
AND CHAIN
子句使新事务在当前事务结束时立即开始,并且新事务具有与刚刚终止的事务相同的隔离级别。新事务还使用与刚刚终止的事务相同的访问模式(READ WRITE
或READ ONLY
)。RELEASE
子句使服务器在终止当前事务之后断开当前客户端会话的连接。包含NO
关键字会抑制CHAIN
或RELEASE
完成,如果将completion_type系统变量设置为默认情况下导致链接或释放完成,这可能很有用。
开始 transaction 会导致任何挂起的 transaction 被提交。
开始 transaction 也会导致使用 LOCK TABLES
获取的 table 锁被释放,就像你已经执行了 UNLOCK TABLES
一样。开始事务不会释放通过FLUSH TABLES WITH READ LOCK
获取的全局读取锁。
为了获得最佳结果,应仅使用由单个 transaction-safe 存储引擎管理的表来执行 transactions。否则,可能会出现以下问题:
- 如果您使用来自多个事务安全存储引擎(例如
InnoDB
)的表,而事务隔离级别不是SERIALIZABLE
,则当一个事务提交时,使用相同表的另一个正在进行的事务可能仅会看到一些更改。由第一次 transaction 完成。也就是说,使用混合引擎无法保证事务的原子性,并且可能导致不一致。(如果不经常使用混合引擎事务,则可以使用SET TRANSACTION ISOLATION LEVEL
将所需的每个事务基准隔离级别设置为SERIALIZABLE
。) - 如果在事务中使用不安全事务的表,则无论自动提交模式的状态如何,都将立即存储对这些表的更改。
- 如果在更新事务中的非事务表之后发出
ROLLBACK
语句,则会发生ER_WARNING_NOT_COMPLETE_ROLLBACK
警告。将回滚对事务安全表的更改,但不回滚对非事务安全表的更改。
每个 transaction 均以块的形式存储在二进制日志中,在到达 COMMIT
后。回滚的事务不会记录。例外:对非事务性表的修改不能被回滚,如果事务回滚包含修改的非事务表。在最后执行 ROLLBACK语句时,会将整个事务记录到日志中,以确保对非事务表的修改可以复制
(replicated)。
您可以使用SET TRANSACTION语句更改 transactions 的隔离 level 或访问模式。
回滚可能是一种缓慢的操作,可能会在没有用户明确要求的情况下隐式发生(例如,发生错误时)。因此, SHOW PROCESSLIST会在会话的状态字段展示Rolling back。
ROLLBACK
statement不仅用于显示回滚,还用于隐式回滚。
注意
在 MySQL 5.7,BEGIN
,COMMIT
和ROLLBACK
不受--replicate-do-db或--replicate-ignore-db规则的影响。
无法回滚的语句
某些语句无法回滚。通常,这些语句包括数据定义语言(DDL)语句,例如创建或删除数据库的语句,创建,删除或更改表或存储例程的语句。
您应设计您的 transactions不包含此类声明。如果在 transaction 的早期发出一个无法回滚的语句,然后又有另一个语句失败,则在这种情况下,不能通过发出一条ROLLBACK
语句来回滚事务的全部效果 。
导致隐式提交的语句
本章节的语句列表在在当前会话中隐式结束了任一活跃的事务,仿佛你在正在执行的语句中执行了 COMMIT。
这些语句大多数在执行后也会导致隐式提交。目的是在其自己的特殊 transaction 中处理每个这样的语句,因为它无论如何都无法回滚。 事务控制和锁定语句是例外:如果在执行之前发生隐式提交,则之后另一个不会发生。
- 定义或修改数据库对象的数据定义语言(DDL)语句.
ALTER DATABASE ... UPGRADE DATA DIRECTORY NAME
,ALTER EVENT
,ALTER PROCEDURE
,ALTER SERVER
,ALTER TABLE
,ALTER VIEW
,CREATE DATABASE
,CREATE EVENT
,CREATE INDEX
,CREATE PROCEDURE
,CREATE SERVER
,CREATE TABLE
,CREATE TRIGGER
,CREATE VIEW
,DROP DATABASE
,DROP EVENT
,DROP INDEX
,DROP PROCEDURE
,DROP SERVER
,DROP TABLE
,DROP TRIGGER
,DROP VIEW
,INSTALL PLUGIN
,RENAME TABLE
,TRUNCATE TABLE
,UNINSTALL PLUGIN
.
ALTER FUNCTION
, CREATE FUNCTION
and DROP FUNCTION
在与存储函数一起使用时也会导致隐式提交,但和 user-defined functions一起使用时不会(ALTER FUNCTION
can only be used with stored functions.)
如果使用TEMPORARY
关键字,CREATE TABLE
and DROP TABLE
statements 不提交 transaction。 (这不适用于临时表上的其他操作,例如 ALTER TABLE
and CREATE INDEX
,它们会导致 commit.)然而,尽管没有隐式提交发生,但是语句也不能回滚,这意味着使用此类语句会导致事务性原子化,例如,如果你使用 CREATE TEMPORARY TABLE
然后回滚 transaction,table 仍然存在。
InnoDB
中的 CREATE TABLE
语句作为单个 transaction 处理。这意味着用户的ROLLBACK不会撤消用户在 transaction 期间所做的 CREATE TABLE
statements。
在创建非临时表时,CREATE TABLE ... SELECT
会在执行语句之前和之后导致隐式提交。 (No commit occurs for CREATE TEMPORARY TABLE ... SELECT
.)
- 隐式使用或修改 mysql 数据库中的表的语句.
ALTER USER
,CREATE USER
,DROP USER
,GRANT
,RENAME USER
,REVOKE
,SET PASSWORD
. - Transaction-control and locking statements..
BEGIN
,LOCK TABLES
,SET autocommit = 1
(如果 value 不是 1)START TRANSACTION
,UNLOCK TABLES
.
UNLOCK TABLES
仅在当前已使用LOCK TABLES
锁定任一表以获取非事务性表锁时,才提交事务。UNLOCK TABLES
后续 FLUSH TABLES WITH READ LOCK不会发生提交
,因为后一个语句没有获取表级锁。
Transactions 不能嵌套。这是由于在发出START TRANSACTION
语句或其同义词之一时会对任何当前事务执行隐式提交的结果。
当事务处于ACTIVE
状态时,不能在XA事务中使用导致隐式提交的语句 。
BEGIN
语句与BEGIN
用于启动一个BEGIN ... END
复合语句的关键字的用法不同。后者不会导致隐式提交。
- 数据加载 statements.
LOAD DATA
.LOAD DATA
仅对使用NDB
存储引擎的表进行隐式提交。 - Administrative statements.
ANALYZE TABLE
,CACHE INDEX
,CHECK TABLE
,FLUSH
,LOAD INDEX INTO CACHE
,OPTIMIZE TABLE
,REPAIR TABLE
,RESET
. - Replication control statements.
START SLAVE
,STOP SLAVE
,RESET SLAVE
,CHANGE MASTER TO
.
SAVEPOINT,ROLLBACK 到 SAVEPOINT 和 RELEASE SAVEPOINT 语法
SAVEPOINT identifier
ROLLBACK [WORK] TO [SAVEPOINT] identifier
RELEASE SAVEPOINT identifier
InnoDB
支持 SQL statements SAVEPOINT
, ROLLBACK TO SAVEPOINT
, RELEASE SAVEPOINT
和ROLLBACK的可选WORK
关键字。
SAVEPOINT语句用name标识符设置命名 transaction 保存点。如果当前 transaction 具有相同 name 的保存点,则会删除旧保存点并设置新保存点。
ROLLBACK TO SAVEPOINT语句将 transaction 回滚到指定的保存点,而不终止 transaction。设置保存点后当前 transaction 对行所做的修改在回滚中撤消。但是InnoDB
不会释放保存点之后存储在 memory 中的行锁。 (对于新插入的行,锁定信息由存储在行中的 transaction ID 携带;锁不单独存储在 memory 中.在这种情况下,行锁在“撤消”中被释放.)将删除在指定保存点之后设置的保存点。
如果ROLLBACK TO SAVEPOINT语句返回以下错误,则表示不存在具有指定 name 的保存点:
ERROR 1305 (42000): SAVEPOINT identifier does not exist
RELEASE SAVEPOINT
语句从当前事务的保存点集中删除命名的保存点。不会发生提交或回滚。如果保存点不存在则会出错。
如果执行COMMIT
,则删除当前 transaction 的所有保存点,或者不命名保存点的 ROLLBACK
。
当调用存储的 function 或激活触发器时,将创建新的保存点级别。先前级别上的保存点变得不可用,因此不会与新 level 上的保存点冲突。当 function 或触发器终止时,将释放它创建的任何保存点,并恢复先前的保存点 level。
LOCK TABLES and UNLOCK TABLES Statements
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 锁,以便与其他会话协作以访问表,或者在 session 需要对其进行独占访问期间防止其他会话修改表。 session 只能为自己获取或释放锁。一个 session 无法为另一个 session 获取锁定或释放另一个 session 持有的锁定。
锁定可用于模拟 transactions 或在更新表时获得更快的速度。这在 Table-Locking Restrictions and Conditions.中有更详细的解释。
LOCK TABLES
显式获取当前 client session 的 table 锁。可以为基表或视图获取 Table 锁。您必须具有 LOCK TABLES
权限,并且要锁定的每个对象的选择权限。
对于视图锁定, LOCK TABLES
将视图中使用的所有基表添加到要锁定的表集中并自动锁定它们。如果使用 LOCK TABLES
显式锁定 table,则触发器中使用的任何表也将被隐式锁定,如 LOCK TABLES and Triggers.中所述。
UNLOCK TABLES
显式释放当前 session 持有的任何 table 锁。LOCK TABLES
在获取新锁之前隐式释放当前 session 持有的任何 table 锁。
UNLOCK TABLES
的另一个用途是释放使用 FLUSH TABLES WITH READ LOCK
语句获取的 global 读锁,这使您可以锁定所有数据库中的所有表。 See Section 13.7.6.3, “FLUSH Statement”. 如果您有一个文件系统(如Veritas)可以及时拍摄快照,那么这是一种获取备份的非常方便的方法
table 锁仅保护其他会话不适当的读取或写入。持有WRITE
锁的 session 可以执行 table-level 操作,例如DROP TABLE或TRUNCATE TABLE。对于持有READ
锁的会话,不允许DROP TABLE和TRUNCATE TABLE操作。
以下讨论仅适用于非TEMPORARY
表。对于TEMPORARY
table,允许(但忽略) LOCK TABLES,
可以通过创建表的会话自由访问该表,而不考虑可能执行了哪些其他锁定。无需锁定,因为没有其他会话可以看到该表。
表锁定获取
要在当前 session 中获取 table 锁,请使用 LOCK TABLES
语句,该语句获取元数据锁
可以使用以下锁定类型:
READ [LOCAL]
锁:
- 持有锁的 session 可以读取 table(但不能写它)。
- 多个会话可以在同一 time 获取 table 的
READ
锁。 - 其他会话可以在不明确获取
READ
锁的情况下读取 table。 LOCAL
修饰符允许在保持锁定时执行其他会话的非冲突INSERT
statements(并发插入)。 (请参阅第 8.11.3 节,“并发插入” .)但是,如果要在保持锁定时使用服务器外部的进程操作数据库,则不能使用READ LOCAL
。对于InnoDB
表,READ LOCAL
与READ
相同。
[LOW_PRIORITY] WRITE
锁:
- 持有锁的 session 可以读写 table。
- 只有持有锁的 session 才能访问 table。在释放锁之前,没有其他 session 可以访问它。
- 在保持
WRITE
锁定时,其他会话阻止对 table 的请求。 - 该
LOW_PRIORITY
修饰符无效。在以前的MySQL版本中,它影响了锁定行为,但现在不再如此。现在已弃用,使用它会产生警告。使用不带LOW_PRIORITY的WRITE
代替。
WRITE
锁通常具有比READ
锁更高的优先级,以确保尽快处理更新。这意味着如果一个 session 获得READ
锁,然后另一个 session 请求WRITE
锁,则后续READ
锁请求将等待,直到请求WRITE
锁的 session 获得锁并释放它。( max_write_lock_count
系统变量的小值是一个例外,请参阅第 8.11.4 节,“元数据锁定” .)
如果该LOCK TABLES
语句由于任何其他表上的其他会话持有的锁而必须等待,则它将阻塞直到可以获取所有锁为止。
需要锁的 session 必须在单个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
数据库中的表是 exception。即使 session 持有 LOCK TABLES
获得的 table 锁,也可以在不显式锁定的情况下访问它们。
您不能在使用相同名称的单个查询中多次引用锁定的表。请改用别名,并为表和每个别名获取单独的锁:
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
发生错误,因为对于锁定的 table,两次引用同一名字。第二个 INSERT
成功,因为 table 的 references 使用不同的名称。
如果 statements 通过别名引用 table,则必须使用相同的别名锁定 table。如果不指定别名,则无法锁定 table:
mysql> LOCK TABLE t READ;
mysql> SELECT * FROM t AS myalias;
ERROR 1100: Table 'myalias' was not locked with LOCK TABLES
相反,如果使用别名锁定 table,则必须使用该别名在 statements 中引用它:
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
,当应用于分区 table 时,始终锁定或解锁整个 table;这些语句不支持分区锁定修剪。见第 22.6.4 节,“分区和锁定”。
表锁定释放
当释放 session 持有的 table 锁时,它们都在同一时间释放。 session 可以显式释放其锁,或者可以在某些条件下隐式释放锁。
- session 可以使用
UNLOCK TABLES
显式释放其锁。 - 如果 session 在已经持有锁的情况下发出
LOCK TABLES
语句来获取锁,则在授予新锁之前将隐式释放其现有锁。 - 如果 session 开始 transaction(for example,使用START TRANSACTION),则执行隐式
UNLOCK TABLES
,这会导致释放现有锁。
如果 client session 的连接终止,无论是正常还是异常,服务器都会隐式释放 session(transactional 和 nontransactional)所持有的所有 table 锁。如果 client 重新连接,则锁将不再有效。此外,如果 client 具有 active transaction,则服务器在断开连接时回滚 transaction,如果发生重新连接,则新的 session 将以启用自动提交开始。出于这个原因,客户可能希望禁用 auto-reconnect。在 auto-reconnect 生效的情况下,如果发生重新连接,则不会通知 client,但任何 table 锁定或当前 transaction 都将丢失。禁用 auto-reconnect 后,如果连接断开,则发出的下一个语句会发生错误。 client 可以检测错误并采取适当的操作,例如重新获取锁或重做 transaction。
注意
如果在锁定的 table 上使用ALTER TABLE,它可能会解锁。例如,如果您尝试第二次ALTER TABLE操作,结果可能是错误Table 'tbl_name' was not locked with LOCK TABLES
。要处理此问题,请在第二次更改之前再次锁定 table。
Table Locking 和 Transactions 的交互
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
不释放全局读取锁定。- 其他隐式导致事务提交的语句不会释放现有的表锁。有关此类语句的列表,请参见第13.3.3节“导致隐式提交的语句”。
- 将
LOCK TABLES
和UNLOCK TABLES
与 transactional 表(如InnoDB
表)一起使用的正确方法是使用SET autocommit = 0
(不是START TRANSACTION)后跟LOCK TABLES
开始 transaction,并且在显式提交 transaction 之前不要调用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
在下次提交时释放其内部 table 锁,但是为了释放其 table 锁,你必须调用UNLOCK TABLES
。你不应该有autocommit = 1,因为在LOCK TABLES
调用之后InnoDB
会立即释放它的内部 table 锁,并且很容易发生死锁。如果autocommit = 1,InnoDB
根本不会获取内部 table 锁,以帮助旧的 applications 避免不必要的死锁。
- ROLLBACK不释放 table 锁。
锁定表和触发器
如果使用LOCK TABLES
显式锁定 table,则触发器中使用的任何表也将被隐式锁定:
- 这些锁与使用该
LOCK TABLES
语句显式获取的锁在同一时间获取。 - 触发器中使用的表上的锁取决于该表是否仅用于读取。如果是这样,则读锁就足够了。否则,将使用写锁。
- 如果 table 被显式锁定以便使用
LOCK TABLES
进行读取,但需要锁定以进行写入,因为它可能在触发器中被修改,则会执行写入锁定而不是读取锁定。 (也就是说,由于 table 在触发器中的出现而需要隐式写锁定会导致 table 的显式读锁定请求转换为写锁定 request.)
假设您使用以下语句锁定两个表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
被锁定以进行写入,因为它可能在触发器中更新。
Table-Locking 限制和条件
您可以放心地使用KILL
来终止正在等待表锁的会话。
LOCK TABLES
和 UNLOCK TABLES
不能在存储的程序中使用。
performance_schema
数据库中的 表不能用LOCK TABLES
锁定,但 setup_
表除外 。xxx
当 LOCK TABLES
语句生效时,以下语句被禁止:CREATE TABLE
, CREATE TABLE ... LIKE
,CREATE VIEW
, DROP VIEW
and DDL statements on stored functions and procedures and events.
对于某些操作,必须访问mysql
数据库中的系统表。For example, HELP
statement需要服务器端帮助表的内容,而CONVERT_TZ()可能需要读取 time zone 表。服务器隐式锁定系统表以供必要时读取,以便您无需显式锁定它们。这些表如上所述:
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 statements 都是原子的;没有其他 session 可以干扰任何其他当前正在执行的 SQL 语句。但是,在某些情况下,锁定表可能会提供一个优势:
- 如果要在一组
MyISAM
表上运行许多操作,则锁定要使用的表会快得多。锁定MyISAM
表会加快插入,更新或删除它们,因为 MySQL 在调用UNLOCK TABLES
之前不会刷新锁定表的 key 缓存。通常,在每个 SQL 语句之后刷新 key 缓存。 - 锁定表的不利之处在于,没有会话可以更新
READ
锁定表(包括持有锁的那个),没有会话可以访问拥有WRITE
锁的表除了持有lock的之外。 - 如果要将表用于非事务性存储引擎,则必须使用锁表,如果要确保没有其他 session 修改表在
SELECT
和UPDATE之间的。此处显示的 example 需要锁表来安全执行:
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;
如果没有锁表,则执行 SELECT
和UPDATE statements 之间,另一个 session 可能会插入trans
table 中新行。
在许多情况下,您可以通过使用相对更新(UPDATE customer SET value=value+new_value
)或LASTINSERT_ID() function 来避免使用锁表。
在某些情况下,您还可以通过使用用户级咨询锁定功能GET_LOCK()
和RELEASE_LOCK()
来避免锁定表 。这些锁保存在服务器的哈希表中, pthread_mutex_lock()
和pthread_mutex_unlock()
以高速实现。
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
}
此语句指定s transaction 特性。它采用逗号分隔的一个或多个特征值的列表。每个特征 value sets transaction isolation level 或访问模式。隔离 level 用于InnoDB表上的操作。访问模式指定 transactions 是否以 read/write 或 read-only 模式运行。
此外,SET TRANSACTION可以包含可选的GLOBAL
或SESSION
关键字来指示语句的范围。
Transaction 隔离级别
要设置 transaction 隔离 level,请使用ISOLATION LEVEL level
子句。不允许在同一SET TRANSACTION语句中指定多个ISOLATION LEVEL
子句。
默认隔离 level 是 REPEATABLE READ
。其他允许的值为 READ COMMITTED
, READ UNCOMMITTED
, and SERIALIZABLE
.
Transaction 访问模式
要设置 transaction 访问模式,请使用READ WRITE
或READ ONLY
子句。不允许在同一SET TRANSACTION语句中指定多个 access-mode 子句。
默认情况下,transaction 在 read/write 模式下发生,对 transaction 中使用的表允许读写。可以使用SET TRANSACTION显式指定此模式,访问模式为READ WRITE
。
如果 transaction 访问模式设置为READ ONLY
,则禁止对表进行更改。这可以使存储引擎能够在不允许写入时进行性能改进。
在 read-only 模式下,仍然可以使用 DML statements 更改使用TEMPORARY
关键字创建的表。与永久表一样,不允许使用 DDL statements 进行更改。
也可以使用START TRANSACTION语句为单个 transaction 指定READ WRITE
和READ ONLY
访问模式。
Transaction Characteristic Scope
您可以为全局,当前 session 或仅为下一个 transaction 设置 transaction 特性:
- 使用
GLOBAL
关键字:
- 该语句适用于所有后续会话。
- 现有会话不受影响。
- 使用
SESSION
关键字:
- 该语句适用于当前 session 中执行的所有后续 transactions。
- transactions 中允许使用该语句,但不会影响当前正在进行的 transaction。
- 如果在 transactions 之间执行,则该语句将覆盖任何前面的语句,该语句设置指定特征的 next-transaction value。
- 没有任何
SESSION
或GLOBAL
关键字:
- 该语句仅适用于 session 中执行的下一个 transaction。
- 后续 transactions 将恢复使用命名特征的 session value。
- transactions 中不允许使用该语句:
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
对 global transaction 特性的更改需要super特权。任何 session 都可以自由地改变它的 session 特性(即使在 transaction 的中间),或者它的下一个 transaction 的特征(在 transaction 开始之前)。
要在服务器启动时设置 global isolation level,请在命令 line 或选项文件中使用--transaction-isolation=level选项。此选项的level
值使用破折号而不是空格,因此允许的值为READ-UNCOMMITTED,READ-COMMITTED,REPEATABLE-READ或SERIALIZABLE。
同样,要在服务器启动时设置 global transaction 访问模式,请使用--transaction-read-only选项。默认值为OFF
(read/write 模式),但 value 可以设置为ON
以获得只读模式。
For example, 要将隔离 level 设置为 REPEATABLE READ
并将访问模式设置为READ WRITE
,请在选项文件的[mysqld]
部分中使用这些 lines:
[mysqld]
transaction-isolation = REPEATABLE-READ
transaction-read-only = OFF
可以使用SET TRANSACTION语句间接设置 global,session 和 next-transaction 范围级别的特征,如前所述。也可以使用 SET
语句直接设置它们,以便为transaction_isolation和transaction_read_only系统变量赋值:
- SET TRANSACTION允许使用可选的
GLOBAL
和SESSION
关键字来设置不同范围级别的 transaction 特性。 - 用于为transaction_isolation和transaction_read_only系统变量赋值的
SET
语句具有用于在不同范围级别设置这些变量的语法。
下表显示了每个SET TRANSACTION和 variable-assignment 语法设置的特征范围 level。
SET TRANSACTION Transaction 特性的语法
Syntax | Affected Characteristic Scope |
---|---|
SET GLOBAL TRANSACTION | Global |
SET SESSION TRANSACTION | Session |
SET TRANSACTION | Next transaction only |
Transaction 特征的 SET 语法
Syntax | Affected Characteristic Scope |
---|---|
SET GLOBAL | Global |
SET @@GLOBAL. | Global |
SET SESSION | Session |
SET @@SESSION. | Session |
SET | Session |
SET @@ | Next transaction only |
可以在运行时检查 transaction 特性的 global 和 session 值:
SELECT @@GLOBAL.transaction_isolation, @@GLOBAL.transaction_read_only;
SELECT @@SESSION.transaction_isolation, @@SESSION.transaction_read_only;
在 MySQL 5.7.20 之前,使用tx_isolation和tx_read_only而不是transaction_isolation和transaction_read_only。
XA Transactions
略,后续待补。。。。