何为事务? 一言蔽之,事务是逻辑上的一组操作,要么都执行,要么都不执行。
经典的转账问题
mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> update account set money = money-1000 where name = 'sora'; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> update account set money = money+1000 where name = 'shiro'; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> commit; Query OK, 0 rows affected (0.01 sec) mysql> select * from account; +----+-------+-------+ | id | name | money | +----+-------+-------+ | 1 | sora | 4000 | | 2 | shiro | 6000 | +----+-------+-------+ 2 rows in set (0.00 sec)
关系型数据库(例如:MySQL
、SQL Server
、Oracle
等)事务都有 ACID 特性:
Atomicity
) : 事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;Consistency
): 执行事务前后,数据保持一致,例如转账业务中,无论事务是否成功,转账者和收款人的总额应该是不变的;Isolation
): 并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的;Durabilily
): 一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。在典型的应用程序中,多个事务并发运行,经常会操作相同的数据来完成各自的任务(多个用户对同一数据进行操作)。并发虽然是必须的,但可能会导致以下的问题。
不可重复读和幻读有什么区别呢?
幻读其实可以看作是不可重复读的一种特殊情况,单独把区分幻读的原因主要是解决幻读和不可重复读的方案不一样。
问题演示
1.脏读
//先将隔离级别设置为read uncommitted mysql> set session transaction isolation level read uncommitted; Query OK, 0 rows affected (0.00 sec) mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> update account set money = money+1000 where name='sora'; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0
//这就是脏读,还没有提交事务,这个数据是错误的 mysql> select * from account where name = 'sora'; +----+------+-------+ | id | name | money | +----+------+-------+ | 1 | sora | 4000 | +----+------+-------+ 1 row in set (0.00 sec)
2.不可重复读
mysql> set session transaction isolation level read committed; mysql> start transaction; Query OK, 0 rows affected (0.00 sec) //第一次的数据 mysql> select * from account where name = 'sora'; +----+------+-------+ | id | name | money | +----+------+-------+ | 1 | sora | 5000 | +----+------+-------+ 1 row in set (0.00 sec) //另一个事务提交以后 mysql> select * from account where name ='sora'; +----+------+-------+ | id | name | money | +----+------+-------+ | 1 | sora | 6000 | +----+------+-------+ 1 row in set (0.00 sec)
mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> update account set money = money+1000 where name = 'sora'; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> commit; Query OK, 0 rows affected (0.00 sec)
3.幻读
幻读好像有点问题,提交了好像就能查询到。
mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> insert into account values(3,'kaze',5000); Query OK, 1 row affected (0.00 sec) mysql> commit; Query OK, 0 rows affected (0.01 sec)
mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> select * from account where id = 3; Empty set (0.00 sec) mysql> select * from account where id = 3; Empty set (0.00 sec) mysql> insert into account values(3,'kaze',5000); ERROR 1062 (23000): Duplicate entry '3' for key 'PRIMARY' mysql> select * from account where id = 3; +----+------+-------+ | id | name | money | +----+------+-------+ | 3 | kaze | 5000 | +----+------+-------+ 1 row in set (0.00 sec)
MySQL InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重读)。我们可以通过SELECT @@tx_isolation;
命令来查看,MySQL 8.0 该命令改为SELECT @@transaction_isolation;
mysql> SELECT @@tx_isolation; +-----------------+ | @@tx_isolation | +-----------------+ | REPEATABLE-READ | +-----------------+
SQL 标准定义了四个隔离级别:
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
READ-UNCOMMITTED | √ | √ | √ |
READ-COMMITTED | × | √ | √ |
REPEATABLE-READ | × | × | √ |
SERIALIZABLE | × | × | × |