事务是由一组SQL语句组成的逻辑处理单元,事务具有以下4个属性,通常简称为事务的ACID属性。
原子性(Atomicity,或称不可分割性)
一致性(Consistency)
隔离性(Isolation,又称独立性)
持久性(Durability)
1、用BEGIN、COMMIT、ROLLBACK来实现
2、调整MySQL的事务提交模式
BEGIN 或 START TRANSACTION 显式地开启一个事务;
COMMIT 也可以使用 COMMIT WORK,不过二者是等价的。COMMIT 会提交事务,并使已对数据库进行的所有修改成为永久性的;
ROLLBACK 也可以使用 ROLLBACK WORK,不过二者是等价的。回滚会结束用户的事务,并撤销正在进行的所有未提交的修改;
SAVEPOINT identifier,SAVEPOINT 允许在事务中创建一个保存点,一个事务中可以有多个 SAVEPOINT;
RELEASE SAVEPOINT identifier 删除一个事务的保存点,当没有指定的保存点时,执行该语句会抛出一个异常;
ROLLBACK TO identifier 把事务回滚到标记点;
SET TRANSACTION 用来设置事务的隔离级别。InnoDB 存储引擎提供事务的隔离级别有READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ 和 SERIALIZABLE。
正常提交事务
mysql> CREATE TABLE runoob_transaction_test( id INT ( 5 ) ) engine= innodb; # 创建数据表
Query OK, 0 ROWS affected ( 0.04 sec)
mysql> SELECT * FROM runoob_transaction_test;
Empty SET ( 0.01 sec)
mysql> BEGIN ; # 开始事务
Query OK, 0 ROWS affected ( 0.00 sec)
mysql> INSERT INTO runoob_transaction_test VALUE ( 5 ) ;
Query OK, 1 ROWS affected ( 0.01 sec)
mysql> INSERT INTO runoob_transaction_test VALUE ( 6 ) ;
Query OK, 1 ROWS affected ( 0.00 sec)
mysql> commit; # 提交事务
Query OK, 0 ROWS affected ( 0.01 sec)
mysql> SELECT * FROM runoob_transaction_test;
+ ------+
| id |
+ ------+
| 5 |
| 6 |
+ ------+
2 ROWS IN SET ( 0.01 sec)
事务回滚
mysql> BEGIN ; # 开始事务
Query OK, 0 ROWS affected ( 0.00 sec)
mysql> INSERT INTO runoob_transaction_test VALUES ( 7 ) ;
Query OK, 1 ROWS affected ( 0.00 sec)
mysql> ROLLBACK ; # 回滚
Query OK, 0 ROWS affected ( 0.00 sec)
mysql> SELECT * FROM runoob_transaction_test; # 因为回滚所以数据没有插入
+ ------+
| id |
+ ------+
| 5 |
| 6 |
+ ------+
2 ROWS IN SET ( 0.01 sec)
一个事务读取到另外一个事务未提交的数据。
mysql> CREATE TABLE `test_table` (
-> `id` INT NOT NULL AUTO_INCREMENT ,
-> `val` INT NOT NULL ,
-> PRIMARY KEY ( `id` )
-> ) ENGINE= InnoDB ;
Query OK, 0 ROWS affected ( 0.15 sec)
mysql> SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
Query OK, 0 ROWS affected ( 0.00 sec)
mysql> INSERT INTO test_table VALUE ( 1 , 1 ) ;
Query OK, 1 ROW affected ( 0.00 sec)
session 1 session 2
begin;
update test_table set val = val+1;
begin;
mysql> select * from test_table;
+----+-----+
| id | val |
+----+-----+
| 1 | 2 |
+----+-----+
1 row in set (0.00 sec)
mysql> SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
Query OK, 0 ROWS affected ( 0.00 sec)
session 1 session 2
mysql> BEGIN;
Query OK, 0 ROWS affected (0.00 sec)
mysql> SELECT * FROM test_table WHERE id = 1;
+----+-----+
| id | val |
+----+-----+
| 1 | 4 |
+----+-----+
1 ROW IN SET (0.00 sec)-
mysql> BEGIN;
Query OK, 0 ROWS affected (0.00 sec)
mysql> UPDATE test_table SET val = val+1;
Query OK, 1 ROW affected (0.00 sec)
ROWS matched: 1 Changed: 1 Warnings: 0
mysql> commit;
Query OK, 0 ROWS affected (0.02 sec)
mysql> SELECT * FROM test_table WHERE id = 1;
+----+-----+
| id | val |
+----+-----+
| 1 | 5 |
+----+-----+
1 ROW IN SET (0.00 sec)
mysql>
幻读:一个事务在前后两次查询同一个范围的时候,后一次查询看到了第一次查询没有看到的行
官网说明:https://dev.mysql.com/doc/refman/5.7/en/innodb-next-key-locking.html
The so-called phantom problem occurs within a transaction when the same query produces different sets of rows at different times.
For example, if a SELECT is executed twice, but returns a row the second time that was not returned the first time, the row is a “phantom” row.
mysql> SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
Query OK, 0 ROWS affected ( 0.00 sec)
session 1 session 2
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from test_table where id = 2;
Empty set (0.00 sec)
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into test_table value (2, 1);
Query OK, 1 row affected (0.00 sec)
mysql> commit;
Query OK, 0 rows affected (0.02 sec)
mysql> select * from test_table where id = 2;
+----+-----+
| id | val |
+----+-----+
| 2 | 1 |
+----+-----+
1 row in set (0.01 sec)
mysql> SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE- READ ;
Query OK, 0 ROWS affected ( 0.00 sec)
session 1 session 2
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from test_table where id = 2;
Empty set (0.00 sec)
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into test_table value (2, 1);
Query OK, 1 row affected (0.00 sec)
mysql> commit;
Query OK, 0 rows affected (0.02 sec)
mysql> insert into test_table value (2, 2);
ERROR 1062 (23000): Duplicate entry '2' for key 'test_table.PRIMARY'
-- MySQL通过MVCC和间隙锁的方式,处理RR隔离级别下的幻读问题
mysql> select * from test_table where id = 2;
Empty set (0.00 sec)
隔离级别 脏读 不可重复读 幻读
READ UNCOMMITTED(读未提交) √ √ √
READ COMMITTED(读已提交) √ √
REPEATABLE READ(可重复读) √
SERIALIZABLE(串行化)
查询当前MySQL的事务隔离级别
mysql> SELECT @@SESSION . tx_isolation; -- 查询会话级的事务隔离级别(MySQL 5.7)
+ ------------------------+
| @@SESSION . tx_isolation |
+ ------------------------+
| REPEATABLE- READ |
+ ------------------------+
1 ROW IN SET ( 0.00 sec)
mysql> SELECT @@SESSION . transaction_isolation; -- 查询会话级的事务隔离级别(MySQL 8)
+ ---------------------------------+
| @@SESSION . transaction_isolation |
+ ---------------------------------+
| REPEATABLE- READ |
+ ---------------------------------+
1 ROW IN SET ( 0.00 sec)
mysql> SELECT @@global. tx_isolation; -- 查询全局的事务隔离级别(MySQL 5.7)
+ -----------------------+
| @@global. tx_isolation |
+ -----------------------+
| REPEATABLE- READ |
+ -----------------------+
1 ROW IN SET ( 0.00 sec)
mysql> SELECT @@global. transaction_isolation; -- 查询全局的事务隔离级别(MySQL 8)
+ --------------------------------+
| @@global. transaction_isolation |
+ --------------------------------+
| REPEATABLE- READ |
+ --------------------------------+
1 ROW IN SET ( 0.00 sec)
mysql> SHOW VARIABLES LIKE 'tx_isolation' ;
+ ---------------+-----------------+
| Variable_name | VALUE |
+ ---------------+-----------------+
| tx_isolation | REPEATABLE- READ |
+ ---------------+-----------------+
1 ROW IN SET ( 0.00 sec)
设置当前MySQL的事务隔离级别
-- 设置会话的隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ ;
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
-- 设置全局的隔离级别
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;
SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ ;
SET GLOBAL TRANSACTION ISOLATION LEVEL SERIALIZABLE;
事务传播行为类型 说明
REQUIRED 如果有事务在运行,当前的方法就在这个事务内运行;否则,就启动一个新的事务,并在自己的事务内运行
REQUIRES_NEW 当前的方法必须启动新事务,并在它自己的事务内运行;如果有事务正在运行,应该将它挂起
MANDATORY 当前的方法必须运行在事务内部,如果没有正在运行的事务,将抛出异常
SUPPORTS 如果有事务在运行,当前的方法就在这个事务内运行;否则它可以不运行在事务中
NOT_SUPPORTED 当前的方法不应该运行在事务中,如果有运行的事务,将它挂起
NEVER 当前的方法不应该运行在事务中,如果有运行的事务,就抛出异常
NESTED 如果有事务在运行,当前的方法就应该在这个事务的嵌套事务内运行。否则,就启动一个新的事务,并在它自己的事务内运行
SELECT * FROM information_schema. innodb_trx ;
innodb_trx表 :
trx_id : 事务ID。
trx_state : 事务状态,有以下几种状态:RUNNING、LOCK WAIT、ROLLING BACK 和 COMMITTING。
trx_started : 事务开始时间。
trx_requested_lock_id : 事务当前正在等待锁的标识,可以和 INNODB_LOCKS 表 JOIN 以得到更多详细信息。
trx_wait_started : 事务开始等待的时间。
trx_weight : 事务的权重。
trx_mysql_thread_id : 事务线程 ID,可以和 PROCESSLIST 表 JOIN。
trx_query : 事务正在执行的 SQL 语句。
trx_operation_state : 事务当前操作状态。
trx_tables_in_use : 当前事务执行的 SQL 中使用的表的个数。
trx_tables_locked : 当前执行 SQL 的行锁数量。
trx_lock_structs : 事务保留的锁数量。
trx_lock_memory_bytes : 事务锁住的内存大小,单位为 BYTES。
trx_rows_locked : 事务锁住的记录数。包含标记为 DELETED,并且已经保存到磁盘但对事务不可见的行。
trx_rows_modified : 事务更改的行数。
trx_concurrency_tickets : 事务并发票数。
trx_isolation_level : 当前事务的隔离级别。
trx_unique_checks : 是否打开唯一性检查的标识。
trx_foreign_key_checks : 是否打开外键检查的标识。
trx_last_foreign_key_error : 最后一次的外键错误信息。
trx_adaptive_hash_latched : 自适应散列索引是否被当前事务锁住的标识。
trx_adaptive_hash_timeout : 是否立刻放弃为自适应散列索引搜索 LATCH 的标识。
innodb_locks表 :
lock_id : 锁 ID。
lock_trx_id : 拥有锁的事务 ID。可以和 INNODB_TRX 表 JOIN 得到事务的详细信息。
lock_mode : 锁的模式。有如下锁类型:行级锁包括:S、X、IS、IX,分别代表:共享锁、排它锁、意向共享锁、意向排它锁。表级锁包括:S_GAP、X_GAP、IS_GAP、IX_GAP 和 AUTO_INC,分别代表共享间隙锁、排它间隙锁、意向共享间隙锁、意向排它间隙锁和自动递增锁。
lock_type : 锁的类型。RECORD 代表行级锁,TABLE 代表表级锁。
lock_table : 被锁定的或者包含锁定记录的表的名称。
lock_index : 当 LOCK_TYPE=’RECORD’ 时,表示索引的名称;否则为 NULL。
lock_space : 当 LOCK_TYPE=’RECORD’ 时,表示锁定行的表空间 ID;否则为 NULL。
lock_page : 当 LOCK_TYPE=’RECORD’ 时,表示锁定行的页号;否则为 NULL。
lock_rec : 当 LOCK_TYPE=’RECORD’ 时,表示一堆页面中锁定行的数量,亦即被锁定的记录号;否则为 NULL。
lock_data : 当 LOCK_TYPE=’RECORD’ 时,表示锁定行的主键;否则为NULL。
innodb_lock_waits表 :
requesting_trx_id : 请求事务的 ID。
requested_lock_id : 事务所等待的锁定的 ID。可以和 INNODB_LOCKS 表 JOIN。
blocking_trx_id : 阻塞事务的 ID。
blocking_lock_id : 某一事务的锁的 ID,该事务阻塞了另一事务的运行。可以和 INNODB_LOCKS 表 JOIN。
-- 查看正在锁的事务
SELECT * FROM information_schema. innodb_locks;
-- 查看等待锁的事务
SELECT * FROM information_schema. innodb_lock_waits;
-- 列举在表缓存中当前被打开的非TEMPORARY表
-- Database: 含有该表的数据库
-- Table: 表名称
-- In_use: 表当前被查询使用的次数。如果该数为零,则表是打开的,但是当前没有被使用
-- Name_locked: 表名称是否被锁定。名称锁定用于取消表或对表进行重命名等操作
SHOW OPEN TABLES WHERE In_use > 0 ;
-- 查询进程
SHOW PROCESSLIST;