【MySQL 进阶笔记】InnoDB 的事务隔离级别以及死锁

推荐阅读

  • 学习笔记 《 深入理解 Java 虚拟机》
  • 学习笔记 《 后端架构设计》
  • 学习笔记 《 Java 基础知识进阶》
  • 学习笔记 《 Nginx 学习笔记》
  • 学习笔记 《 前端开发杂记》
  • 学习笔记 《 设计模式学习笔记》
  • 学习笔记 《 DevOps 最佳实践指南》
  • 学习笔记 《 Netty 入门与实战》
  • 学习笔记 《 高性能MYSQL》
  • 学习笔记 《 JavaEE 常用框架》
  • 学习笔记 《 Java 并发编程学习笔记》
  • 学习笔记 《 分布式系统》
  • 学习笔记 《 数据结构与算法》

锁在InnoDB 存储引擎中锁的重要举足轻重,他保证了在并发情况下临界资源的数据完整性和数据一致性。但有的时候错误的或者不合理的使用锁也会造成一些问题,本文将详细的阐述在锁的使用过程中可能出现的问题。


1、隔离级别(Isolation Level)

首先需要了解的是,在DBMS中定义了四种 事务的隔离级别(Transaction isolation) 分别是: 读未提交(Read Uncommitted)读已提交(Read Committed)可重复读(Repeatable Read)串行化(Serializable)


这四种隔离界别都能解决一定的问题事务问题,如读已提交解决了脏读的问题,但没有解决不可重复读的问题,可重复读解决了脏读以及不可重复读的问题,但没有解决幻读的问题;串行化则解决了所有问题,但是其性能降低。


【MySQL 进阶笔记】InnoDB 的事务隔离级别以及死锁_第1张图片


下面,笔者会根据实例逐一讲解着这些问题。首先创建一张数据表,用于演示

CREATE TABLE test
(
	`id`   BIGINT       NOT NULL PRIMARY KEY COMMENT 'ID',
	`name` VARCHAR(128) NOT NULL COMMENT 'NAME'
) ENGINE = InnoDB;

1.1 脏读

脏读,是最简单的问题,所谓的脏读就是读取到脏数据。即事务A读取到事务B未提交的数据,而后续事务B可能提交事务,也有可能回滚事务,脏读是最严重的问题。


【MySQL 进阶笔记】InnoDB 的事务隔离级别以及死锁_第2张图片


可以看到,在上面的实例中,即使事务B没有提交数据,事务A也仍然能够读取到事务B的数据,这就是脏读。脏读出现的根本原因就是读取了未提交的数据,那么解决脏读的办法就是读已提交

1.2 读已提交

读已提交,顾名思义就是允许读取其他事务已经提交的数据,这样就解决了脏读的问题,他不会读取其他事务未提交的数据。但是其仍然解决不了不可重复的问题,所谓的不可重复读值得就是在同一个事务中一会能读到,一会读不到的问题。


【MySQL 进阶笔记】InnoDB 的事务隔离级别以及死锁_第3张图片


在事务A和B中设置隔离级别为读已提交,根据上面的示例可以看到,在事务A中一会能读到id=1 的数据,一会又读不到了,这就是不可重复问题的问题,下面的可重复度就解决了这种问题。

1.3 可重复读


可重复度的隔离级别解决了读已提交出现的不可重复度的问题,但是其也没有解决幻读的问题,所谓的幻读就是事务A认为数据库中不存在或者存在某些数据,但是在查询或者更新的时候数据其实是不存在。


【MySQL 进阶笔记】InnoDB 的事务隔离级别以及死锁_第4张图片


在事务B插入一条id=1的数据,立即提交,此时事务A由于事先开启了事务,由于事务可重复的隔离级别,事务A就会出现 ID=1 的数据不存的幻觉,仍然尝试新增ID=1的数据,此时就会出现PK冲突。

1.4 串行化

串行化解决上述所有的问题,其是最高级的隔离级别。但其不一定就是非常合适的所有场景的,串行化将所有事务编程串行操作,一个事务的查询或者更新必须等待另外一个事务的提交或者回滚,这以失去了数据库并发性作为代价保证了数据一致性和完整性。所以串行化使用的场景并不多。

2、阻塞

因为 S锁与 X锁以及其与自身之间的不兼容性的存在,导致在事务冲突的时候会可能出现事务进入阻塞状态的情况,这种情况我们在 InnoDB 存储引擎中锁 中已经展示了事务进入 LOCK WAIT 状态的情况,这就是事务阻塞。事务阻塞并发坏事,它是确保数据一致性以及完整性下出现和一种方案。

在InnoDB 存储引擎中,参数 innodb_lock_wait_timeout用来控制事务阻塞后的超时时间,参数 innodb_rollback_on_timeout 用来控制事务超时之后是否回滚,前者是是动态参数可以进行修改,后者是静态参数不能进行修改。


-- 查询事务超时时间
mysql root@localhost:test> show variables like 'innodb_lock_wait_timeout';
+--------------------------+-------+
| Variable_name            | Value |
+--------------------------+-------+
| innodb_lock_wait_timeout | 50    |
+--------------------------+-------+
1 row in set
Time: 0.067s

-- 查询事务超时之后是否 Roolback事务
mysql root@localhost:test> show variables like 'innodb_rollback_on_timeout';
+----------------------------+-------+
| Variable_name              | Value |
+----------------------------+-------+
| innodb_rollback_on_timeout | OFF   |
+----------------------------+-------+
1 row in set
Time: 0.015s

3、死锁

同其他语言一样,死锁是两个事务互相等待对方释放资源,解决MySQL死锁的问题是事务超时,当出现死锁的时候,权重比较低的事务进行回滚,权重较高的事务得以继续执行。检测死锁除了超时机制还有等待图是检查是否存在死锁,这里不做深入讨论,感兴趣的读者可以查阅一下资料。


【MySQL 进阶笔记】InnoDB 的事务隔离级别以及死锁_第5张图片

在上面的第4不是的时候,事务A持有ID=1 的X锁,事务2 持有ID=2 的X锁,然后事务1 申请ID=2 的锁,由于事务2 没有提交,所以进入阻塞状态,然后事务2申请ID=1 的锁,此时就出现了事务1等待事务2提交,事务2等待事务1提交。需要注意的是,InnoDB存储引擎出现异常的时候并不会回滚事务,但死锁是个例外,他会立马回滚一个事务,已解除死锁的情况,所以在应用程序中出现了1213的错误的时候,并不需要手动回滚事务。

4、锁升级

锁升级指的是将锁的粒度降低,比如将1000个行级锁升级为1个页级锁,将页级锁升级为表锁。如果数据库认为锁是一种稀缺资源,那么锁升级就会经常出现,MS SQL 认为锁是一种稀缺资源,在适当的时候会进行锁升级。但是InnoDB是不存在锁升级的问题,这是因为InnoDB 是基于页来管理行级锁的锁,所以行级锁的增加并不会对性能造成严重影响。

你可能感兴趣的:(MySQL,进阶,mysql,lock,隔离级别,isolation)