事务隔离级别及相关锁实践

这是我参与新手入门的第1篇文章数据库

事务隔离性

事务的四个特性:原子性,一致性,隔离性,持久性。 其中原子性、一致性、持久性容易理解。今天重点来聊聊隔离性。markdown

隔离性简单来讲,就是每一个事务是相互隔离的,你作你的事,他作他的事,你们相互隔离开来。 可是,你们都是在同一个数据库下操做,那这个「隔离」到底隔离到什么程度呢?是“老死不相往来”,仍是”临时隔离“,又或者是"你中有我,我中有你"?这就是涉及到了新的概念:隔离级别并发

事务隔离级别

事务隔离级别有:工具

  • 读未提交(READ-UNCOMMITTED):最低的隔离级别,容许读取还没有提交的数据变动
  • 读已提交(READ-COMMITTED):容许读取并发事务已经提交的数据
  • 可重复读(REPEATABLE-READ):对同一字段的屡次读取结果都是一致的,除非数据是被自己事务本身所修改
  • 串行化(SERIALIZABLE):最高的隔离级别,彻底服从ACID的隔离级别。全部的事务依次逐个执行,这样事务之间就彻底不可能产生干扰

MySQL默认隔离级别为:可重复读spa

经过实际演示,咱们能够很好理解各隔离级别。 相关准备:设计

MySQL 版本:5.7 (可经过 select version() 命令查询)code

表:orm

CREATE TABLE `student` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
`number` varchar(20) NOT NULL COMMENT '学号',
`name` varchar(20) DEFAULT NULL COMMENT '姓名',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='学生表';
复制代码

初始数据:索引

id	number	name
1	2021070501	张三
复制代码

演示工具:Navicat (经过开启多个查询窗口,实现开启多个事务)事务

演示一:

步骤 查询窗口1(事务1) 查询窗口2(事务2)
step1 执行SQL:
-- 开启事务
BEGIN;
> OK
> 时间: 0s
UPDATE student SET name = '李四' WHERE id = 1;
> Affected rows: 1
> 时间: 0.002s
step2 BEGIN;
SELECT * FROM student WHERE id = 1;
=======结果=======
id number name
1 2021070501 张三
step3 COMMIT;
> OK
> 时间: 0.004s;
step4 SELECT * FROM student WHERE id = 1;
=======结果=======
id number name
1 2021070501 张三
step5 COMMIT;
> OK
> 时间: 0s
step6 BEGIN;
SELECT * FROM student WHERE id = 1;
=======结果=======
id number name
1 2021070501 李四

从上面演示能够分析出:

  1. step2中的结果能够看到name仍是「张三」,说明事务2并无读取事务1未提交的数据,因此能够排除「读未提交」隔离级别
  2. 若是MySQL事务的默认隔离级别为「读已提交」,则step3已提交事务1,step4中的结果应为「李四」,但实际结果仍是「张三」,这从侧面印证了MySQL事务的隔离级别为「可重复读」(两次读取的结果都为张三)

事务相关的锁

事务之间虽然有隔离性,但若是两个事务操做同一数据,那结果会怎么样呢,最终以哪一个事务执行结果为准?没有事务的状况下,咱们很容易理解,哪条SQL语句最后执行,就以哪一个数据为准。但加了事务,状况就变得复杂起来了,SQL执行顺序与事务提交顺序可能不一致。好比:

事务1先执行

UPDATE student SET name = '张三1' WHERE id = 1;
复制代码

事务2再执行

UPDATE student SET name = '张三2' WHERE id = 1;
复制代码

而后先提交事务2,再提交事务1。那最终的结果是「张三1」仍是「张三2」呢?站在SQL执行顺序角度来讲,结果应该为「张三2」;站在事务提交顺序来讲,结果应为「张三1」。

那最终实际结果呢?答案是:这种状况永远不会发生!

咱们能够实际操做试试: 演示二:

步骤 查询窗口1(事务1) 查询窗口2(事务2)
step1 BEGIN
> OK
> 时间: 0s


UPDATE student SET name = '张三1' WHERE id = 1
> Affected rows: 1
> 时间: 0.001s
step2 BEGIN
> OK
> 时间: 0s


UPDATE student SET name = '张三2' WHERE id = 1
======观察结果=======
没反应,查询时间一直走
step3 COMMIT;
> OK
> 时间: 0.004s;
step4 再次打开窗口,会发现有如下结果
> Affected rows: 1
> 时间: 4.843s

从演示二咱们能够看出,step2并不能正常执行,须要等待step3提交。这就是MYSQL的机制。

当事务1先执行UPDATE student SET name = '张三1' WHERE id = 1时便得到数据行(id=1)的锁。事务2想执行UPDATE student SET name = '张三2' WHERE id = 1时,由于该数据行是被锁住的,因此不能执行,须要等待事务1释放锁。事务1提交事务便会把锁释放掉。因此,不一样事务之间修改同一数据不会出现修改顺序和提交顺序不一致的状况。

按照锁粒度,能够把MYSQL锁简单归为:行锁表锁。行锁就是锁定行数据,表锁是锁定整张表数据。上面所说示例,就是行锁。通常来讲,匹配条件是主键或索引列时,数据的update和delete会加行锁。若是增删改查时匹配的条件字段不带有索引,innodb使用的将是表级锁。因而可知,咱们数据库表设计中合理的索引是很是关键的。

有锁的地方,必然就会出现死锁的问题。之后有机会再聊更多的锁及死锁问题

相关文章
相关标签/搜索