数据库事务

zxbandzby
1
2025-06-25

一、为什么需要事务

信不信我可以几分钟就教会你数据库事务,

数据库事务无非就是要解决以下几个问题,

啥叫数据库事务,

为啥要有事务,

事物的四大特性都是啥,

怎么实现的,

并发事物会带来什么问题,

怎么解决这些问题,

事务的隔离级别都有啥,

怎么实现这些隔离级别的,

看一个经典的转账场景啊,

A给B转账100块钱,

你就得写两个SQL,

B账户增加100块钱,

A账户减少100块钱,

那给B账户加钱成功了,

但是A账户捡钱的时候发现余额不足失败了,

那B不就平白无故多加了100块钱吗,

在这个转账场景中,

这两个SQL应该是一个整体,

要么都成功,

要么都失败,

一个成功了,

一个失败了,

那数据就有问题了,

所以就需要有数据库事务,

所谓事物就是一系列SQL语句,

构成一个逻辑上的整体,

要么全部成功,

要么全部失败,

二、事务四大特性

那给刚才的例子加上事务。

如果说B账户增加100块钱,

执行成功了,

那A账户减少100块钱,

执行失败了,

那就回滚对B账户的所有操作,

把这两个操作呢就变成了一个不可分割的整体,

要么全升功,

要么全失败,

不会说存在一个没有执行成功的一个中间串,

这就是事物的原则性,

那原则性怎么实现呢,

可以通过undo log回滚日志去实现啊,

比如说N个连续的四个操作失败了,

一个通过undo log给前面的操作全部回滚,

那失误提交之后,

如果数据库崩溃了,

那数据丢失了怎么办,

所以还要保持事物的一个持久性,

只要事务一提交,

无论发生什么事,

数据库的修改都不丢失,

那持久性怎么实现的呢,

通过redo log来实现,

每一次事务提交之后,

就把这个修改写入redo log,

那即便MYSQL数据没有持久化到磁盘中,

也能读取redo log来恢复数据,

所以redo log呢其实保证了事物的持久性,

这也就是为啥说redo log让MYSQL,

有了这个崩溃恢复的一个能力,

那就一定有人在想啊,

那redo log自己本身也要持久化到磁盘的,

他也是要刷盘的,

那就买SL崩了,

那redo log刷盘失败了,

那数据不就丢了,

这个问题呢后面讲到日志啊,

再详细去聊,

在并发执行事务的时候,

如果说事物A把商品库存从零修改为一事,

物B1读发现是一准备扣减库存啦,

但是失误A回滚了,

那又把商品库存回滚到零了,

那事务B在做扣减库存的操作不就会出问题吗,

这是脏读啊,

一个事物读取到的,

另外一个事物没有提交的数据,

所以在并发执行事务的时候,

多个事物之间应该相互隔离,

互不影响,

否则就会出问题,

这就是事物的隔离性,

那隔离性怎么实现呢,

需要用MACC加锁去配置undo log去实现,

那这个就比较复杂了,

后面去详细聊啊,

除了这个原子性持久性隔离性之外,

事物还要保证一致性,

对于刚才的这个转账业务来说,

无论事物是否成功,

转账人和收款人的钱的总数应该是保持不变的,

如果说转账前后两个人一共100块钱,

转账之后两个人总额变300了,

那就那就有问题了,

那一致性怎么实现呢,

其实只要保证了原子性,

持久性,

隔离性,

一致性自然就实现了,

事物的并发执行会带来很多问题


三、并发事务的问题

刚才讲隔离性提到了脏读一个事物,

读取到其他事物未提交的数据,

那么问题又来了,

然后事务B修改值为二,

事务B提交,

然后是为第二次读的时候,

发现值为二了,

那同一个事物中前后两次读取的数据不一致,

这就是不可重复读,

那么问题又又来啦,

查询id大于100的数据有十条,

然后事务B又差了20条数据,

发现A1大于100的有30条了,

这就是换读,

换读和不可重复读挺像的,

但是不可重复读呢强调的是多次读取,

那个数据值不一样

四、隔离级别

换读呢强调的是数据条数的一个增减,

那怎么解决脏读不可重复读,

幻读的这些问题呢,

通过事务的隔离级别,

数据库呢提供了四种隔离级别呀,

读未提交,

读已提交,

可重复读,

串行化读未提交呢是最低的一种隔离级别,

每个事物都能看到其他事务未提交的一个数据,

读未提交呢它不能解决脏读啊,

不可重复读啊,

换读的问题啊,

所以实际根本就不会用独立提交,

那就是说每个事物只能读到其他事物,

已经提交的数据,

这就可以解决脏读的问题,

因为其他事物没提交的,

你读不到,

但是它无法解决不可重复读和幻读的问题,

可重复读呢是MYSQL的一个默认的隔离级别,

可重复读的这种隔离级别之下呢,

当你第一次去读的时候,

会生成一份数据快照,

生成快照之后,

其他事物的修改呢,

对当前事物来说就是不可见的,

因为你每次都读这个快照啊,

就能保证在同一个事物内,

两次读到的数据其实是一样的,

那就解决了不可重复读的问题,

你重复读他也不会读到这个值变了的那些数据,

但是他还是无法解决幻读的问题,

串行化呢是MYSQL最高的一个隔离级别,

串行化的要求事物你不能并发执行,

只能一个接一个的顺序执行,

你都无法并发执行了,

那那些并发问题自然就不存在了,

所以串行化呢是可以解决所有的并发问题,

但是性能很差,

并发量高的情况下,

可能就会导致大量的超时和所竞争的问题,

通常根本就不会用这个隔离级别,

所以呢可以看到啊,

就是事务的隔离级别越高,

就越能保证数据的一致性和完整性,

那执行效率也越低,

事务的隔离级别越低,

那执行的效率也就越高,

但是数据的一致性就越差

五、隔离级别如何实现


那现在有这四种隔离级别能解决问题了
,

那怎么实现这几种隔离级别呢,

首先读未提交应该怎么实现,

不用实现,

你根本就不用管,

因为多个并发事务同时执行,

天然就是读未提交,

所以呢根本就不用管,

根本就不用实现,

但是啊这里说的是读不用管,

那写的时候还是要加锁的,

你不能同时写一个数据,

那串行化应该怎么实现呢,

这就很简单了,

你直接加锁就行了,

只有拿到锁的事物才能执行,

这样事物就串行化执行了,

那读已提交和可重复读应该怎么实现呢,

这就涉及到了MYSQL中一个非常非常复杂,

而且很常考的东西啊,

MVCC因为这个东西太复杂了,

所以这一期呢就长话短说,

MVC的详细讲解放到下一期视频,

MACC呢叫多版本并发控制,

就是维护数据的多个版本,

比如说某个数据第一个版本是一,

然后有事物修改为了二,

然后呢就产生了第二个版本,

又有事物修改成为三,

然后就产生了第三个版本,

那问题来了,

现在有个事物它需要读这个数据,

那他读到的是哪个版本,

版本一版本二还是版本三,

那他读到哪个版本,

取决于read view,

执行select的时候会生成一个快照啊,

叫read view,

read view呢主要是用来判断数据版本,

对当前事物的可见性,

通过read view就能在一堆数据版本中找到哪个版本,

对当前事物是可见的,

你能读哪个版本,

然后呢把这个版本的数据给他返回,

OK这就是MACC的一个基本思想,

那讲完了MACC,

那你来看一看可重复读和读,

已提交这两个隔离级别到底是怎么实现的,

可重复读呢怎么实现呢,

可重复读,

意思就是说每次会读到和第一次相同的数据,

即便后面有新事物修改的数据,

还是会读到和第一次相同的数据,

所以呢这就解决了不可重复读的问题,

那么问题来了,

怎么让每次让他都能读到相同的数据呢,

可重复读呢,

第一次select会生成一个review,

刚才说了,

read view是判断数据版本对当前事物是否可见的,

所以呢可重复读每次都会复用第一次生成的review,

也就是说你生成这个review的时候,

哪些版本的数据对当前事物可见,

就已经固定下来了,

后续每次通过这个同一个review,

去判断数据的可见性,

那你能看到的数据版本都是一样的,

所以每一次读到的都是同一个数据版本,

那数据都是一样的,

无论其他事物再怎么改,

你看不到,

OK这就解决了不可重复读的问题,

所以可重复读就是通过MACC加复,

用第一次的review去实现的,

除此之外呢可重复读呢,

他为了避免换毒问题,

还要加锁啊,

这个呢下期再聊吧,

这个就比较复杂了,

再看一下读已提交是怎么实现的,

毒已提交,

那就是说事物只能读取到其他事物,

已经提交的数据,

只要其他事物提交了,

你就能读到读已提交的本质,

那就是每次select就生成一个新的review,

每次读都形成新的review,

那每次就能读到最新的已经提交的数据版本,

对写简历,

编程学习找工作有任何疑问,

可以进我主页,

点击小店或者充电问答查看

动物装饰