数据库事务详解

数据库事务

最近为了面试整理了一下数据库事务的相关面试知识点,希望能够帮到大家

事务的四大特性

数据库事务四大特性ACID,分别是原子性(Atomicity),一致性(Consistency), 隔离性(Isolation),持久性(Durability)。

  1. 原子性 :事务是不可分隔的最小操作单元,要么全部成功,要么全部失败(例如转账的时候就要保证事务的原子性,不能转账方账户扣了钱,但是因为异常,接受方没收到钱,这时候就要回滚,把钱退给转账方);
  2. 一致性 :事务完成时,必须使用所有的数据都保持一致状态;(例如转账的时候不能转了1000元但是收到了10000元,一定要保证数据是一致的)
  3. 隔离性 :数据库系统提供的隔离机制,保证事务不受外部并发操作影响的独立环境下运行;
  4. 持久性 :事务一旦提交或回滚,它对数据库中的数据的改变就是永久的;

如何保证事务的原子性

Innodb储存引擎的undo log。
undo log名为回滚日志,是实现原子性的关键,当事务回滚时能够撤销所有已经成功执行的sql语句,他需要记录你要回滚的相应日志信息。
eg:
当一个事务中执行的是一系列delete操作的时候,就需要记录已经执行delete操作后删除的数据的信息,如果在执行这些delete操作过程中出现异常,就需要回滚,在回滚的时候,就会撤销原先已经执行的删除操作,从而保证事务的原子性,即要么事务执行成功,要么执行失败,回复到执行前的状态。同理update和insert是一样的道理。
undo log记录了这些回滚需要的信息,当事务执行失败或调用了rollback,导致事务需要回滚,便可以利用undo log中的信息将数据回滚到修改之前的样子。

如何保证事务的持久性

Innodb的redo log。
正如之前说的,Mysql是先把磁盘上的数据加载到内存中,在内存中对数据进行修改,再刷回磁盘上。如果此时突然宕机,内存中的数据就会丢失。 怎么解决这个问题? 简单啊,事务提交前直接把数据写入磁盘就行啊。 这么做有什么问题?

只修改一个页面里的一个字节,就要将整个页面刷入磁盘,太浪费资源了。毕竟一个页面16kb大小,你只改其中一点点东西,就要将16kb的内容刷入磁盘,听着也不合理。
毕竟一个事务里的SQL可能牵涉到多个数据页的修改,而这些数据页可能不是相邻的,也就是属于随机IO。显然操作随机IO,速度会比较慢。
于是,决定采用redo log解决上面的问题。当做数据修改的时候,不仅在内存中操作,还会在redo log中记录这次操作。当事务提交的时候,会将redo log日志进行刷盘(redo log一部分在内存中,一部分在磁盘上)。当数据库宕机重启的时候,会将redo log中的内容恢复到数据库中,再根据undo log和bin log内容决定回滚数据还是提交数据。
采用redo log的好处? 其实好处就是将redo log进行刷盘比对数据页刷盘效率高,具体表现如下
redo log体积小,毕竟只记录了哪一页修改了啥,因此体积小,刷盘快。
redo log是一直往末尾进行追加,属于顺序IO。效率显然比随机IO来的快。

如何保证事务的隔离性

隔离性的四个等级

  • 未提交读(read uncommitted)

这个等级是最低等级,也可以认为,事务之间完全不隔离,事务A开始一个事务,接着事务B开始,事务B对数据C继续update,这时候,A读取了B未提交(commit)的数据,这种情况叫做脏读(dirty read)。这个时候要是事务B遇到错误必须回滚rollback,那么A读取的数据就完全是错的。可以想象这样完全不隔离的状态下,我们相对于数据库的业务方程序员写的一个sql,提交个db的执行引擎,返回的结果是多么不可确定啊。

  • 提交读(read committed)

提交读就是在未提交读的基础上,保证事务读取的数据,都是别的事务已经提交了的。但是只要在还没达到串行执行的情况下,总会有问题的,事务A select了一条数据,接着事务B update 这条数据,然后commit,这时候A还未提交,A再回来读这条数据,发现数据居然变了,按照我们之前所说,我们的目标是:对于一个事务本身来说,它所感知的数据库,应该只有它自己在操作,那么A会觉得自己并没有更新数据啊,怎么数据突然变了,这种情况叫做 不可重复读(Non-repeatable reads)

  • 可重复读(repeatable read)

可重复读,即是在上一个级别的基础上,保证不会在一个事务内两次select同一条数据会出现变化,即是别的事务对你select的对象进行update操作不会影响,换句话说,只要此时事务没有结束,在该事务中被select出来的结果永远是一样的,不会发生改变。但是,如果是insert操作,在这个隔离级别还是会受到影响。事务A开启事务,并select一段有范围的数据,然后事务B开启事务,在先前A事务select的那段有范围的数据中insert一条数据,然后提交事务,接着事务A再insert一条相同的数据,就会发现报错,提示说表中已经有这条数据(假设数据库表中数据不可重复),但是事务A select出来的结果并没有要插入的这条数据,就像是产生了幻影,这种情况叫幻读(Phantom Read)

  • 序列化读(serializable)

这也就是最高级别,保证事务之间不会有任何踩踏,每个事务都可以认为只有它自己在操作数据库,相当于是每个事务之间是穿行操作,如果在某一刻有两个事务在操作同一段数据,则有会有一个事务被阻塞,只有另一个事务结束,这个事务才会继续执行。
在这里插入图片描述

通过锁的方式来实现隔离性

1.读写锁

读写锁的概念很平常,当在读取数据的时候,应该先加读锁,读取完之后的某个时间再解开读锁,那么加了读锁的数据,应该需要有什么特性呢,应该只能读,不能写,因为加了读锁,说明有事务准备读取这个数据,如果被别的事务重写这个事务,那数据就不准确了。所以一个事务给这个数据加了读锁,别的事务也可以对这个数据加读锁,因为大家都是只读不写。

写锁则具有排他性(exclusive lock),当一个事务准备对一个数据进行写操作的时候,先要对数据加写锁,那么数据就是可变的,这时候,其他事务就无法对这个数据加读锁了,除非这个写锁释放。

两端式提交锁(Two-phase locking)

两段式提交分为两步:

  1. 这个阶段只加锁,或者释放锁(读写锁)

  2. 这个阶段只会释放锁

下面对应于不同隔离级别对加锁方式进一步分析:

  • 未提交读(read uncommitted):这个级别加锁,其实并不需要用两端式加锁,每一个具体操作执行完,锁就可以释放了。

  • 提交读(read committed):这个阶段其实也可以按照每个操作执行前加锁,执行之后释放锁的方式。

  • 可重复读(repeatable read) : 这个级别,就要求读锁必须到事务结束前最后时刻才能释放,这样才能保证读取到数据是不可变的,可重复读的。但是这样会阻塞其他事务对加锁的数据的写操作。

  • 序列化读(serializable):这个级别要求,两段式提交的第一步,要在事务开始的时候,原子性的把需要的锁全部加好(这显然很难估算,除非很大力度的锁),在事务结束前最后时刻,把全部锁一次性释放。这样做的结果就是使很多数据在事务执行期能都被加锁,无法被其他事务所使用。

缺点: 加锁的方式处理事务一个比较大的问题就是会造成死锁(dead lock),原因就是一个事务加锁的数据并不止只有一行。事务A对行C加写锁,事务B对行D加写锁,接着事务希望获取行D的锁,事务B希望获取行C的锁,这样很容就死锁了。

使用多版本并发控制(MVCC)

什么是MVCC

MVCC提供的只是一种思路,具体的实现比较多样化。大体的思路是每一行保存冗余数据,读写的时间戳,也可以称为版本号,在对某一行数据继续update或者delete的时候,并不直接操作,而是复制多一份副本进行操作,这个就是所谓多版本(multiversion)

mysql Innodb对于MVCC的实现

innodb对每一行保存两个系统版本号,一个更新操作的版本号,一个删除操作的版本号,这两个版本号的来源是事务的ID(transaction id),也就是说,当某个事务对这一行数据进行update,或者删除的时候,相应会把它的事务ID写入这行数据的更新操作的版本号,删除操作的版本号中。

事务ID是随时间推移而增长,而且不可重复的。一个事务打开之后:

  • 对于select操作:每次只会select具有比当前事务ID更小的更新操作版本号的数据,而且这些数据要保证删除版本号为空,或者删除版本号大于当前事务ID。

  • 对于update操作:对该行数据复制出一份副本,同时在更新操作版本号写入当前事务ID,同时把当前事务ID写入之前的删除操作的版本号中。

  • 对于insert操作:写入新行,同时在更新操作版本号写入当前事务ID

  • 对于delete操作:在删除操作版本号写入当前事务ID

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值