【MySQL】深入理解MySQL锁和事务隔离级别

 先看个小案例:

  • 话不多说,上案例,先创建一个表
mysql> CREATE TABLE IF NOT EXISTS `account`( `id` INT UNSIGNED AUTO_INCREMENT, `name` VARCHAR(60), `balance` INT(11) DEFAULT NULL, PRIMARY KEY (id))ENGINE=innodb charset=utf8mb4;
  • 插入几条数据
mysql> insert into account(name,balance) values(‘lilei‘,450);
Query OK, 1 row affected (0.00 sec)

mysql> insert into account(name,balance) values(‘hanmei‘,16000);
Query OK, 1 row affected (0.00 sec)

mysql> insert into account(name,balance) values(‘lucy‘,2400);
Query OK, 1 row affected (0.00 sec)
  • 此时可以看到三条数据:
mysql> select * from account;
+----+--------+---------+
| id | name   | balance |
+----+--------+---------+
|  1 | lilei  |     450 |
|  2 | hanmei |   16000 |
|  3 | lucy   |    2400 |
+----+--------+---------+
3 rows in set (0.00 sec)
  • 我们开启两个命令行界面,然后实践一下

1》我们先在一个命令行(下面称为命令行A)中,执行一段事务操作,但是不提交

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> update account set balance=3000 where id=3;
Query OK, 1 row affected (0.07 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> select * from account;
+----+--------+---------+
| id | name   | balance |
+----+--------+---------+
|  1 | lilei  |     450 |
|  2 | hanmei |   16000 |
|  3 | lucy   |    3000 |
+----+--------+---------+
3 rows in set (0.00 sec)

2》然后在另一个命令行(下面称为命令行B)中,进行一次修改操作:

mysql> update account set balance=3500 where id =3;

3》可以看到在命令行B中,并不会看到修改结构,因为命令行A中的事务会默认加上一个行锁,只要事务不停,行锁就一直保留:

【MySQL】深入理解MySQL锁和事务隔离级别

4》此时我们在命令行A中进行提交,在命令行B中会看到提交返回:

【MySQL】深入理解MySQL锁和事务隔离级别

5》但是命令行A提交了事务后,因为A本想将ID为3的用户【balance】修改为3000,可此时查看结果后发现:

【MySQL】深入理解MySQL锁和事务隔离级别

6》所以此时A对ID为3的用户的修改未达到理想值

  • 此处引申一点,由于MySQL的默认隔离级别是行锁,如何验证它是行锁呢:
  • 我们在A中修改【ID=3】的数据,然后在B中修改【ID=2】的数据,可以明显看到有结果返回,由此可以证明,锁仅仅是加在行上的

【MySQL】深入理解MySQL锁和事务隔离级别

此时我们言归正传:

  • 事务与ACID属性:
原子性(Atomicity)、一致性(Consistent)、隔离性(Isolation)、持久性(Durable)
  • 并发事务处理带来的问题:
1》更新丢失:两个事务同事操作相同数据,后提交的事务会覆盖先提交的事务处理结果,通过乐观锁就可以解决。(即文章开篇介绍的案例)

2》脏读:事务A读取到了事务B已经修改,但尚未提交的数据,如果事务B回滚,A读取的数据无效,不符合一致性。

3》不可重读:事务A读取到了事务B已经提交的修改数据,不符合隔离性。

4》幻读:事务A读取到了事务B提交的新增数据,不符合隔离性。
  • 事务隔离级别:
    • 常看数据库事务隔离级别:
mysql> show variables like ‘tx_isolation‘;
+---------------+-----------------+
| Variable_name | Value           |
+---------------+-----------------+
| tx_isolation  | REPEATABLE-READ |
+---------------+-----------------+
    • 设置事务隔离级别:
mysql> set tx_isolation=‘REPEATABLE-READ‘;(其他三种:READ-UNCOMMITTED,READ-COMMITTED,SERIALIZABLE)

【MySQL】深入理解MySQL锁和事务隔离级别

  • 此时我们分别聊聊剩下的案例,为了方便验证,我们先把隔离级别设置到最低级:
mysql> set tx_isolation =‘READ-UNCOMMITTED‘;

1》脏读,不废话,直接上实例:

【MySQL】深入理解MySQL锁和事务隔离级别

 分析一下,上述指令,我们按顺序执行,可以看出因为事务B(右面)最后进行了回滚操作,但是事务A(左面)两次执行结果并不一样,造成了脏读情况。

2》不可重读,也不废话,线上案例:

【MySQL】深入理解MySQL锁和事务隔离级别

 分析来看,上述指令情况使得原子的一致性隔离性均未实现,因为事务A还没有结束,所以两次所读数据应该一致,才可以保证事务实现一致性和隔离性。

3》幻读,先不墨迹,看案例就完了:

【MySQL】深入理解MySQL锁和事务隔离级别

 事务A一直处在一个事务内,但事务B缺新增了数据并提交了,而事务A两次查询结果不一致,从而该情况叫幻读。

  • 数据库默认隔离级别是【可重复度】,但是并没有解决【幻读】情况,请问为什么不使用【可串行化】呢?

因为【可串行化】的性能非常低,如果一个商城,开了【可串行化】的级别后,下个订单却要一分钟,这种用户体验你会接受么^ - ^

相关推荐