侧边栏壁纸
博主头像
xh博主等级

永远是一个学者

  • 累计撰写 15 篇文章
  • 累计创建 6 个标签
  • 累计收到 8 条评论
标签搜索

目 录CONTENT

文章目录

深入理解MySQL事务隔离级别

xh
xh
2023-07-08 / 0 评论 / 2 点赞 / 1,065 阅读 / 2,113 字
温馨提示:
本文最后更新于 2024-06-16,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

概述

数据库一般都会并发执行多个事务,多个事务可能会并发的对相同的一批数据进行增删改查操作,可能就会导致我们说的脏写脏读不可重复读幻读这些问题。

这些问题的本质就是数据库的多事务并发问题,为了解决多事务并发问题,数据库设计了事务隔离机制锁机制MVCC多版本并发控制隔离机制,用一整套机制来解决多事务并发问题


事务

事务是由一组SQL语句组成的逻辑处理单元,事务具有以下4个属性,通常简称为事务的ACID属性。

属性 描述
原子性(Atomicity) 事务是一个原子操作单元,其对数据的修改,要么全都执行,要么全都不执行
一致性(Consistent) 在事务开始和结束时,数据都必须保持一致状态。这意味着所有相关的数据规则都必须应用于事务的修改,以保持数据的完整性。
隔离性(Isolation) 数据库系统提供一定的隔离机制,保证事务在不受外部并发操作影响的“独立”环境执行,这意味着事务处理过程中的中间状态对外部是不可见的
持久性(Durable) 事务完成之后,它对于数据的修改是永久性的,即使出现系统故障也能够保持。

并发事务处理带来的问题

脏写(更新丢失 Lost Update)

当两个或多个事务选择同一行数据,然后基于最初选定的值修改这一行时,由于每个事务都不知道其它事务的存在,就会发生丢失更新问题,最后的更新覆盖了其它事务所做的更新。


脏读(Dirty Reads)

一个事务正在对一条记录做修改,在这个事务完成并提交前,这条记录的数据就处于不一致的状态;这时,另一个事务也来读取同一条记录,如果不加控制,第二个事务读取了这些“脏”数据,并据此作进一步的处理,就会产生未提交的数据依赖关系。这种现象被形象的叫做 “脏读”。

简单理解:事务A读取到了事务B已经修改但尚未提交的数据,还在这个数据基础上做了操作


不可重复读(Non-Repeatable Reads)

一个事务在读取某些数据后的某个时间,再次读取以前读过的数据,却发现其读出的数据已经发生了改变、或某些记录已经被删除了!这种现象就叫做 “不可重复读”。

简单理解:事务A内部的相同的查询语句,在不同时刻读出的结果不一致,不符合隔离性


幻读(Phantom Reads)

一个事务按照相同的查询条件重新读取以前检索过的数据, 却发现其它事务插入了满足其查询条件的新数据, 这种情况称为 “幻读”。

简单理解:事务A读取到了事务B提交的新增数据,不符合隔离性


事务隔离级别

“脏读”、“不可重复读”和“幻读”,其实都是数据库读一致性问题,必须由数据库提供一定的事务隔离机制来解决。

隔离级别 脏读 不可重复读 幻读
读未提交(ru) 可能 可能 可能
读已提交(rc) 不可能 可能 可能
可重复读(rr) 不可能 不可能 可能
可串行化 不可能 不可能 不可能

数据库的事务隔离越严格,并发副作用越小,但付出的代价也就越大,因为事务隔离实质上就是使事务在一定程度上“串行化”进行,这显然与“并发”是矛盾的。

同时,不同的应用对读一致性和事务隔离程度的要求也是不同的,比如许多应用对"不可重复读"和"幻读"并不敏感,可能更关心数据库并发访问的能力。

查看当前数据的事务隔离级别:show variables like 'transaction_isolation';

设置事务隔离界别:set transaction_isolation = 'REPEATABLE-READ';

mysql默认的事务隔离级别是可重复读,用spring开发程序时,如果不设置隔离级别,默认使用mysql设置的隔离级别,如果spring设置了就用spring设置的隔离级别。


事务隔离级别验证

读未提交

设置隔离级别:set transaction_isolation = 'read-uncommitted';
验证:事务A读取到了事务B已经修改但尚未提交的数据,产生了脏读
结论:无MVCC机制,不用readview,所有语句都不加锁

  1. 客户端A:开启事务表数据

    1

  2. 客户端B:更新account表数据,但未提交事务

    2

  3. 客户端A:再次查询表数据,发现已经能够查到客户端B事务修改但还未提交的数据(如果客户端B的事务因为某种原因回滚,所有的操作都将会被撤销,那客户端A查询到的数据其实就是脏数据

    3


读已提交

设置隔离级别:set transaction_isolation = 'read-committed';
验证:事务A只能读取事务B已经修改并提交的数据,解决了脏读,但产生了不可重复读
结论:有MVCC,每次select读的都是一个新的视图,所以会产生不可重复读

  1. 客户端A:开启事务,查询表数据

    3

  2. 客户端B:更新account表数据,未提交事务,此时客户端A已经不能查询B未提交的数据,解决了脏读

    3

  3. 客户端B:提交事务

    3

  4. 客户端A:再次查询表数据,能够查询到其它事务修改提交的数据,但是在客户端A事务中前后查询的内容不一致,产生了不可重复读的问题,不符合事务的隔离性。

    3


可重复读

设置隔离级别:set transaction_isolation = 'repeatable-read';
验证:事务A内部相同的查询语句,在不同时刻读出的结果一致,不受其它事务提交的数据影响,解决了不可重复读,但未解决幻读
结论:有MVCC,每次select读的都是同一个视图(快照、历史版本),解决了不可重复读,insert、update、delete操作会更新版本号,读的是最新版本,所以会产生幻读

  1. 客户端A:开启事务,查询表数据

    3

  2. 客户端B:更新account表数据,并提交事务

    3

  3. 客户端A:再次查询表数据,发现客户端A前后读的数据一致,不受客户端B修改提交的数据所影响,解决了不可重复读,但没有解决幻读

    3

  4. 客户端B:新增一条记录,客户端A修改新增的数据,再次查询,发现新增的数据能够读出来(验证幻读)

    3


可串行化

设置隔离级别:set transaction_isolation = 'serializable';
验证:解决幻读问题
结论:无MVCC,不用readview,select读的时候会将读出来的数据加锁,其它事务不能操作这些数据,会阻塞等待,

  1. 客户端A:开启事务,查询表数据

    3

  2. 客户端B:修改或新增数据,发现都会被阻塞等待,因为在serializable模式下,客户端A事务里面所查询的数据都被加了锁,所以就避免了幻读这种隔离级别并发性极低,开发中很少会用到。

    3

2

评论区