2023-03-01
温故知新
00

目录

简介
什么是事务?
概述和举例
事务特性
事务隔离级别
Read uncommitted:读未提交
Read committed:读已提交
Repeatable read:重复读
Serializable:序列化
怎么解决脏读?
什么时候会出现幻读?
怎么解决幻读(虚读)?
怎么解决不可重复读?
Mysql中的事务操作
修改事务隔离级别
如何查看 MySQL 事务隔离级别

简介

对于不擅长记忆的我,在面试中,被数据库事务特性和隔离级别相关问题虐了好多遍(OS:他奶奶滴)。

迫于无奈,还是写一篇笔记吧,尽量做到以后有面试需求,直接看这篇文章就够了!。

什么是事务?

此部分来自百度百科:数据库事务有时候百度还是有点用的。。。

数据库事务( transaction)是访问并可能操作各种数据项的一个数据库操作序列,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位。事务由事务开始与事务结束之间执行的全部数据库操作组成。

概述和举例

在数据库系统中,事务是工作的离散单位,它可以是修改一个用户的账户余额,也可以是库存项的写操作。在单用户、单数据库环境下执行事务比较简单,但在分布式环境下,维护多个数据库的完整性就比较复杂。大多数联机事务处理系统是在大型计算机上实现的,这是由于它的操作复杂,需要快速的输入/输出和完善的管理。如果一个事务在多个场地进行修改,那就需要管理机制来防止数据重写并提供同步。另外还需要具有返回失效事务的能力,提供安全保障和提供数据恢复能力。

比如,我们去银行转账,操作可以分为下面两个环节:

  1. 从第一个账户划出款项。
  2. 将款项存入第二个账户。

在这个过程中,两个环节是关联的。第一个账户划出款项必须保证正确的存入第二个账户,如果第二个环节没有完成,整个的过程都应该取消,否则就会发生丢失款项的问题。整个交易过程,可以看作是一个事务,成功则全部成功,失败则需要全部撤消,这样可以避免当操作的中间环节出现问题时,产生数据不一致的问题。

数据库事务是一个逻辑上的划分,有的时候并不是很明显,它可以是一个操作步骤也可以是多个操作步骤。我们可以这样理解数据库事务:对数据库所做的一系列修改,在修改过程中,暂时不写入数据库,而是缓存起来,用户在自己的终端可以预览变化,直到全部修改完成,并经过检查确认无误后,一次性提交并写入数据库,在提交之前,必要的话所做的修改都可以取消。提交之后,就不能撤销,提交成功后其他用户才可以通过查询浏览数据的变化。

事务特性

  1. 原子性(Atomicity):事务中的全部操作在数据库中是不可分割的,要么全部完成,要么全部不执行。

  2. 一致性(Consistency):几个并行执行的事务,其执行结果必须与按某一顺序 串行执行的结果相一致。

  3. 隔离性(Isolation):事务的执行不受其他事务的干扰,事务执行的中间结果对其他事务必须是透明的。

  4. 持久性(Durability):对于任意已提交事务,系统必须保证该事务对数据库的改变不被丢失,即使数据库出现故障。

事务的ACID特性是由关系数据库系统(DBMS)来实现的,DBMS采用日志来保证事务的原子性、一致性和持久性。日志记录了事务对数据库所作的更新,如果某个事务在执行过程中发生错误,就可以根据日志撤销事务对数据库已做的更新,使得数据库回滚到执行事务前的初始状态。

对于事务的隔离性,DBMS是采用锁机制来实现的。当多个事务同时更新数据库中相同的数据时,只允许持有锁的事务能更新该数据,其他事务必须等待,直到前一个事务释放了锁,其他事务才有机会更新该数据。

事务隔离级别

隔离级别脏读不可重复读幻读
Read uncommitted
Read committed
Repeatable read
Serializable
  • 表中脏读,不可重复读,幻读为事务的并发操作中可能会出现问题,✔代表存在该问题,✖代表不存在该问题。

此部分内容主要来源于腾讯云开发者社区:数据库事务详解

数据库事务的隔离级别有4种,由低到高分别为Read uncommittedRead committedRepeatable readSerializable,分别翻译为读未提交读已提交重复读序列化

在事务的并发操作中可能会出现脏读,不可重复读,幻读。

Read uncommitted:读未提交

Read uncommitted:读未提交,就是一个事务可以读取另一个未提交事务的数据。

事例:老板要给程序员发工资,程序员的工资是3.6万/月。但是发工资时老板不小心按错了数字,按成3.9万/月,该钱已经打到程序员的户口,但是事务还没有提交,就在这时,程序员去查看自己这个月的工资,发现比往常多了3千元,以为涨工资了非常高兴。但是老板及时发现了不对,马上回滚差点就提交了的事务,将数字改成3.6万再提交。

分析:实际程序员这个月的工资还是3.6万,但是程序员看到的是3.9万。他看到的是老板还没提交事务时的数据。这就是脏读。

Read committed:读已提交

Read committed:读提交,就是一个事务要等另一个事务提交后才能读取数据。

事例:程序员拿着信用卡去享受生活(卡里当然是只有3.6万),当他埋单时(程序员事务开启),收费系统事先检测到他的卡里有3.6万,就在这个时候!!程序员的妻子要把钱全部转出充当家用,并提交。当收费系统准备扣款时,再检测卡里的金额,发现已经没钱了(第二次检测金额当然要等待妻子转出金额事务提交完)。程序员就会很郁闷,明明卡里是有钱的…

分析:这就是读提交,若有事务对数据进行更新(UPDATE)操作时,读操作事务要等待这个更新操作事务提交后才能读取数据,可以解决脏读问题。但在这个事例中,出现了一个事务范围内两个相同的查询却返回了不同数据,这就是不可重复读。

Repeatable read:重复读

Repeatable read:可重复读

在可重复读中,该sql第一次读取到数据后,就将这些数据加锁(悲观锁),其它事务无法修改这些数据,就可以实现可重复读了。但这种方法却无法锁住insert新增的数据,所以当事务A先前读取了数据,或者修改了全部数据,事务B还是可以进行insert数据提交,这时事务A就会发现莫名其妙多了一条之前没有的数据,这就是幻读,不能通过行锁来避免。需要Serializable隔离级别 ,读用读锁,写用写锁,读锁和写锁互斥,这么做可以有效的避免幻读、不可重复读、脏读等问题,但会极大的降低数据库的并发能力。

解决了更新丢失、脏读、不可重复读、但是还会出现幻读。

Serializable:序列化

Serializable 是最高的事务隔离级别,在该级别下,事务串行化顺序执行,可以避免脏读、不可重复读与幻读。但是这种事务隔离级别效率低下,比较耗数据库性能,一般不使用。

在MYSQL数据库中,支持上面四种隔离级别,默认的为Repeatable read(可重复读);而在Oracle数据库中,只支持Serializeble(序列化)级别和Read committed(读已提交)这两种级别,其中默认的为Read committed级别。

怎么解决脏读?

Read committed!读提交能解决脏读问题。

脏读是指在一个事务处理过程里读取了另一个未提交的事务中的数据。

当一个事务正在多次修改某个数据,而在这个事务中这多次的修改都还未提交,这时一个并发的事务来访问该数据,就会造成两个事务得到的数据不一致

什么时候会出现幻读?

事例:程序员某一天去消费,花了2千元,然后他的妻子去查看他今天的消费记录(全表扫描FTS,妻子事务开启),看到确实是花了2千元,就在这个时候,程序员花了1万买了一部电脑,即新增INSERT了一条消费记录,并提交。当妻子打印程序员的消费记录清单时(妻子事务提交),发现花了1.2万元,似乎出现了幻觉,这就是幻读。

怎么解决幻读(虚读)?

Serializable!序列化

幻读是事务非独立执行时发生的一种现象。

幻读和不可重复读都是读取了另一条已经提交的事务(这点就脏读不同),所不同的是不可重复读查询的都是同一个数据项,而幻读针对的是一批数据整体(比如数据的个数)

怎么解决不可重复读?

Repeatable read 重复读

不可重复读是指在对于数据库中的某个数据,一个事务范围内多次查询却返回了不同的数据值,这是由于在查询间隔,被另一个事务修改并提交了。

例如事务T1在读取某一数据,而事务T2立马修改了这个数据并且提交事务给数据库,事务T1再次读取该数据就得到了不同的结果,发送了不可重复读。

不可重复读和脏读的区别是,脏读是某一事务读取了另一个事务未提交的脏数据,而不可重复读则是读取了前一事务提交的数据。

在某些情况下,不可重复读并不是问题,比如我们多次查询某个数据当然以最后查询得到的结果为主。但在另一些情况下就有可能发生问题,例如对于同一个数据A和B依次查询就可能不同,A和B就可能打起来了……

Mysql中的事务操作

此部分内容来自掘金:朝雾轻寒:MySQL | 事务隔离级别详解

修改事务隔离级别

  1. 可以在 MySQL 的配置文件 my.cnfmy.ini 中设置:
transaction-isolation=READ-UNCOMMITTED # 读未提交 或 transaction-isolation=READ-COMMITTED # 读已提交 或 transaction-isolation=REPEATABLE-READ # 可重复读 或 transaction-isolation=SERIALIZABLE # 串行化
  1. 可以在连接 MySQL 服务端命令行用 --transaction-isolation;

  2. 可以使用 SET TRANSACTION 命令改变单个或者所有新连接的事务隔离级别:

SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE} # 如 set session transaction isolation level read committed;

GLOBAL 或 SESSION 关键字表示设置下一个事务的隔离级别;

用 GLOBAL 关键字表示对全局设置事务隔离级别,设置后的事务隔离级别对所有新的数据库连接生效;

用 SESSION 关键字表示对当前的数据库连接设置事务隔离级别,只对当前连接生效;

户端都可以自由改变当前会话的事务隔离级别,可以在事务中间改变,也可以改变下一个事务的隔离级别。

如何查看 MySQL 事务隔离级别

sql
# MySQL 8.0 之前 SELECT @@global.tx_isolation; SELECT @@session.tx_isolation; SELECT @@tx_isolation; # MySQL8.0 SELECT @@global.transaction_isolation; SELECT @@session.transaction_isolation; SELECT @@transaction_isolation;

在 MySQL 8.0 之前的版本中 tx_isolation 是作为 transaction_isolation 的别名被应用的,新版本已经弃用了,需要把 tx_isolation 换成 transaction_isolation,否则会出现 1193 - Unknown system variable 'xx_isolation' 错误。

如果对你有用的话,可以打赏哦
打赏
ali pay
wechat pay

本文作者:DingDangDog

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!