一、什么是事务
1.概念:事务是包含了一组有序的数据库操作命令的序列,它是数据库并发操作的最小控制单位。
2.特性:
原子性:事务包含的数据库操作命令要么都执行,要么都不执行。
一致性:当事务完成时,数据库处于稳定而一致的状态。即事务执行后,数据库数据要符合规定,而且所有数据查询的结果是一致的。
隔离性:多个事务同时执行时,他们之间是互不干扰的。
永久性:一旦事务提交成功,它引发的变化也就永久保存了下来,硬件与应用程序发生错误也不能改变。
二、在并发执行事务时会发生什么问题呢?
1、脏读:一个事务读到另一个事务未提交的更新数据。
2、不可重复读:一个事务读到另一个事务已提交的更新数据。
3、幻读:一个事务读到另一个事务已提交的新插入的数据。
三、四种隔离级别
1、Read Uncommitted(读未提交数据):一个事务在执行过程中可以看到其他事务没有提交的新插入的记录,而且能看到其他事务没有提交的对已有记录的更新。
2、Read Commited(读已提交数据):一个事务在执行过程中可以看到其他事务已经提交的新插入的记录,而且能看到其他事务已经提交的对已有记录的更新。
3、Repeatable Read(可重复读):一个事务在执行过程中可以看到其他事务已经提交的新插入的记录,但是不能看到其他其他事务对已有记录的更新。
4、Serializable(串行化):事务执行的时候不允许别的事务并发执行。
四、通过例子理解事务的4种隔离级别
首先,我们使用study数据库,新建study表,如下:
+-------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+----------------+
| id | int(10) | NO | PRI | NULL | auto_increment |
| title | varchar(80) | YES | | | |
+-------+-------------+------+-----+---------+----------------+
接着开启两个客户端。
第1级别:Read Uncommitted(读取未提交内容)
所有事务都可以看到其他未提交事务的执行结果,该级别会引发脏读。
例子:
事务A | 事务B |
mysql> select @@tx_isolation; +—————–+ | @@tx_isolation | +—————–+ | REPEATABLE-READ |#默认隔离级别 +—————–+ 1 row in set | mysql> select @@tx_isolation; +—————–+ | @@tx_isolation | +—————–+ | REPEATABLE-READ | +—————–+ 1 row in set |
mysql> set tx_isolation=’READ-UNCOMMITTED’;#修改隔离级别 Query OK, 0 rows affected | mysql> set tx_isolation=’READ-UNCOMMITTED’; Query OK, 0 rows affected |
mysql> select @@tx_isolation; +——————+ | @@tx_isolation | +——————+ | READ-UNCOMMITTED | +——————+ 1 row in set | mysql> select @@tx_isolation; +——————+ | @@tx_isolation | +——————+ | READ-UNCOMMITTED | +——————+ 1 row in set |
mysql> start transaction;#开启事务A Query OK, 0 rows affected | mysql> start transaction;#开启事务B Query OK, 0 rows affected |
mysql> select * from study; +—-+——-+ | id | title | +—-+——-+ | 1 | aaa | | 2 | bbb | | 3 | ccc | | 4 | ddd | +—-+——-+ 4 rows in set | mysql> select * from study; +—-+——-+ | id | title | +—-+——-+ | 1 | aaa | | 2 | bbb | | 3 | ccc | | 4 | ddd | +—-+——-+ 4 rows in set |
mysql> update study set title=’a1a1a1′ where id=1; Query OK, 1 row affected Rows matched: 1 Changed: 1 Warnings: 0 | |
mysql> select * from study; +—-+——–+ | id | title | +—-+——–+ | 1 | a1a1a1 |#事务B update后,可以看到事务B还没有提交的数据 | 2 | bbb | | 3 | ccc | | 4 | ddd | +—-+——–+ 4 rows in set | mysql> select * from study; +—-+——–+ | id | title | +—-+——–+ | 1 | a1a1a1 |#更新还未提交 | 2 | bbb | | 3 | ccc | | 4 | ddd | +—-+——–+ 4 rows in set |
mysql> rollback;#回滚 Query OK, 0 rows affected mysql> | |
mysql> select * from study; +—-+——-+ | id | title | +—-+——-+ | 1 | aaa |#事务B回滚,看到的也是事务B没有提交的数据 | 2 | bbb | | 3 | ccc | | 4 | ddd | +—-+——-+ 4 rows in set |
第2级别:Read Committed(读取提交内容)
这个级别是大多数数据库系统的默认隔离级别,但MySQL不是;在这个级别,一个事务只能看见已经提交事务所做的改变;这种隔离级别出现的问题是——不可重复读(Nonrepeatable Read)。
例子:
事务A | 事务B |
mysql> set tx_isolation=’READ-COMMITTED’;#修改隔离级别 Query OK, 0 rows affected | mysql> set tx_isolation=’READ-COMMITTED’;#修改隔离级别 Query OK, 0 rows affected |
mysql> select @@tx_isolation; +——————+ | @@tx_isolation | +——————+ | READ-COMMITTED | +——————+ 1 row in set | mysql> select @@tx_isolation; +——————+ | @@tx_isolation | +——————+ | READ-COMMITTED | +——————+ 1 row in set |
mysql> start transaction;#开启事务A Query OK, 0 rows affected | mysql> start transaction; Query OK, 0 rows affected |
mysql> select * from study; +—-+——-+ | id | title | +—-+——-+ | 1 | aaa | | 2 | bbb | | 3 | ccc | | 4 | ddd | +—-+——-+ 4 rows in set | mysql> select * from study; +—-+——-+ | id | title | +—-+——-+ | 1 | aaa | | 2 | bbb | | 3 | ccc | | 4 | ddd | +—-+——-+ 4 rows in set |
mysql> update study set title=’b2b2b2′ where id=2; Query OK, 1 row affected Rows matched: 1 Changed: 1 Warnings: 0 | |
mysql> select * from study; +—-+——-+ | id | title | +—-+——-+ | 1 | aaa | | 2 | bbb |#事务B修改了数据,看不到变化 | 3 | ccc | | 4 | ddd | +—-+——-+ 4 rows in set | mysql> select * from study; +—-+——–+ | id | title | +—-+——–+ | 1 | aaa | | 2 | b2b2b2 | | 3 | ccc | | 4 | ddd | +—-+——–+ 4 rows in set |
mysql> commit; Query OK, 0 rows affected | |
mysql> select * from study; +—-+——–+ | id | title | +—-+——–+ | 1 | aaa | | 2 | b2b2b2 |#事务B提交更新后,可以看到变了 | 3 | ccc | | 4 | ddd | +—-+——–+ 4 rows in set |
第3级别:Repeatable Read(可重读)
这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行;此级别可能出现的问题——幻读(Phantom Read):当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行;InnoDB和Falcon存储引擎通过多版本并发控制(MVCC)机制解决了该问题。
例子:
事务A | 事务B |
mysql> start transaction;#开启事务A Query OK, 0 rows affected | mysql> start transaction; Query OK, 0 rows affected |
mysql> select * from study; +—-+——–+ | id | title | +—-+——–+ | 1 | aaa | | 2 | b2b2b2 | | 3 | ccc | | 4 | ddd | +—-+——–+ 4 rows in set | mysql> select * from study; +—-+——–+ | id | title | +—-+——–+ | 1 | aaa | | 2 | b2b2b2 | | 3 | ccc | | 4 | ddd | +—-+——–+ 4 rows in set |
mysql> update study set title=’c3c3c3′ where id=3;#更新 Query OK, 1 row affected Rows matched: 1 Changed: 1 Warnings: 0 | |
mysql> commit;#提交 Query OK, 0 rows affected | |
mysql> select * from study; +—-+——–+ | id | title | +—-+——–+ | 1 | aaa | | 2 | b2b2b2 | | 3 | ccc |#事务B 更新并提交更新,还看不到变化 | 4 | ddd | +—-+——–+ 4 rows in set | |
mysql> commit;#事务A提交事务 Query OK, 0 rows affected | |
mysql> select * from study; +—-+——–+ | id | title | +—-+——–+ | 1 | aaa | | 2 | b2b2b2 | | 3 | c3c3c3 |#可以看到更新了 | 4 | ddd | +—-+——–+ 4 rows in set |
第4级别:Serializable(可串行化)
这是最高的隔离级别,它在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。
例子:
事务A | 事务B |
mysql> set tx_isolation=’SERIALIZABLE’; Query OK, 0 rows affected | mysql> set tx_isolation=’SERIALIZABLE’; Query OK, 0 rows affected |
mysql> start transaction; Query OK, 0 rows affected | mysql> start transaction; Query OK, 0 rows affected |
mysql> select * from study; +—-+——–+ | id | title | +—-+——–+ | 1 | aaa | | 2 | b2b2b2 | | 3 | c3c3c3 | | 4 | ddd | +—-+——–+ 4 rows in set | mysql> select * from study; +—-+——–+ | id | title | +—-+——–+ | 1 | aaa | | 2 | b2b2b2 | | 3 | c3c3c3 | | 4 | ddd | +—-+——–+ 4 rows in set |
mysql> insert into study (title) values (‘eee’); 1205 – Lock wait timeout exceeded; try restarting transaction mysql> update study set title=’d4d4d4′ where id=4; 1205 – Lock wait timeout exceeded; try restarting transaction | |
mysql> commit;#提交后,事务B可以正常增改 Query OK, 0 rows affected | |
mysql> update study set title=’d4d4d4′ where id=4;#事务A提交后,可以正常操作 Query OK, 1 row affected Rows matched: 1 Changed: 1 Warnings: 0 |