谈谈事务

本文介绍了事务的基本概念,包括原子性、一致性、隔离性和持久性。深入探讨了事务的不同隔离级别,如可串行化、可重复读和读已提交等,并讨论了多版本并发控制(MVCC)的工作原理。此外,还分析了事务处理中常见的问题及其解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

事务简介

事务的核心是并发
事务的ACID
- 原子性
一个事务是一个不可分割的最小工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚,对于一个事务来说,不可能只执行其中的一部分操作。
通过记录回滚段,即通过undo日志回滚到之前版本(记录下事务中对数据有修改的所有中间状态,并且为了保证事务状态按照顺序恢复,还需要利用并发控制来保证事务和事务之间不存在冲突,回滚也隐含了对事务锁的要求)
原子性的语义只保证记录了一个回滚段,能够回滚到之前的版本
- 一致性
保证事务单元之间具有happen before关系
数据库总是从一个一致性的状态转换到另外一个一致性的状态,而中间状态不可见,即一致性约束可见性。
但强一致性,系统的并发度上不来,所以出现隔离性
- 隔离性
以性能为理由,对一致性的破坏
核心目的:尽可能提高并行度
本质:就是一个锁的范围逐渐扩展,性能逐渐降低,但能提供更强级别的一致性保证的这么一个特征
SQL92标准定义的隔离性:序列化、可重复读、读已提交、读未提交
原来的标准和新的实现出现了差:隔离性的扩展-快照隔离级别(SNAPSHOT ISOLAION)-MVCC(多版本并发控制,代价是实现更为复杂,同时,因为会记录当时同时进行的所有事务处理中的临时版本,会占用更多的磁盘和内存空间)-针对读多写少场景优化,当写大于读时会增加系统成本
并行度能达到或超过读未提交,而隔离级别可以达到可串行化级别
快照隔离级别能保证读到一致性的同时实现写读并行
核心思路:Copy on write + 无锁编程
SQL92标准本来有问题,新的实现没有办法放入原来的隔离级别中,现在一般是将快照读映射到读未提交和读已提交

事物的定义滞后于事物的发展,不是真正意义上对的,即标准不一定是对的
就像当年牛顿说物理上的法则就是这些,因为他是用数学公式表示的,所以他一定是对的,结果爱因斯坦猛抽他一个大嘴巴,说对不起,您现在做的事情是错的,我们有一种新的东西叫相对论
这个过程其实就是对世界认识的一种新的发现的过程

  • 持久性
    一旦事务提交,则其所做的修改就会永久保存到数据库中。此时即使系统崩溃,修改的数据也不会丢失。持久性也分很多不同的级别,而且不可能做到100%的持久性保证的策略(如果数据库本身就能做到真正的持久性,那么备份又怎么能增加持久性呢)
    RAID的持久性:
    磁盘的物理损坏
    每一次commit都要fsync到磁盘->系统性能的下降
    内存中存留是易丢失
    要保证持久性,IOPS就会很低,要想有高的IOPS就要放弃持久性,没有银弹
    1.提交请求到内存后返回,内存打包一起存到磁盘,这样对磁盘是友好的,因为磁盘本身是块存储,写一块进去是效率最高的。那么问题就是如何才能攒够一批然后存储?一些不负责任的存储写到内存就告诉你是成功的,这样可以刷出很高的TPS,代价是内存中的数据一断电就会丢失,需要进行配置
    2.将内存的数据打包到磁盘。
    一个commit不返回,比如等到5个commit才让你提交,group commit,提高了延迟

    也就是说要模拟现实中的各种操作,只需要保证原子性、一致性和持久性就能够完成一次事务操作了,保证了一个事务单元提交逻辑上的正确性,而隔离性通过尽可能的提高并行度,即尽可能降低受到锁影响的事务进程的个数来提升性能。
    一致性描述的是理想的事务过程应该怎样,而理想的事务因为锁的范围太大以致于性能难以接受,所以就使用隔离性的多个隔离级别来破坏一致性应该给出的保证,以换取更小的锁范围,从而获取更高的性能。
    事务有着不同的隔离级别,对应的性能也不一样,性能好意味着锁的颗粒度尽可能小,哪里要加锁,哪里不加锁,增加了模型的复杂度

    容易理解的模型性能都不好,性能好的模型都不容易理解

一组事务单元:
事务单元之间的Happen-before关系:读写、写读、读读、写写
问题:如何能以最快的速度完成事务,同时又能保证上面四种操作的逻辑顺序?
- 可串行化(Serializable)
最高的隔离级别,强制事务串行执行,保证数据的强一致性,但没有并发,性能最低
使用锁分离和读写锁的方式进行优化
锁分离,如果两个事务单元数据没有冲突,那么它们就不会冲突,可以并行
读写锁,将读锁和写锁分离,可以让读读完全并行
这样就又出现两种隔离级别:1,可重复读 2,读已提交
当两个读锁,后面的写锁不能进行升级替换掉前面的读锁时,后面的写就只能在外面等待,因此第一个读后如果锁没有释放,再次读仍然可以读到这个数据。做到读读并行,即可重复读隔离级别。可重读读存在幻读问题,指的是当某个事务在读取某个范围内的记录时,另外一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围内的记录时,会产生幻行。
那么如何才能做到读写并行呢?即当一个事务单元获得读锁后,另外一个事务单元要请求写锁,那么它会先获得该读锁,然后进行锁升级变成写锁,即可写数据,做到读写并行。此时,当在一个事务单元内,第一次读一个数据,再一次读的时候可能就是另外一个数据了,这就是读已提交隔离级别,系统的并行度进一步提升
现在目前主流的数据库实现方法-MVCC(多版本并发控制)

读未提交:
存在脏读问题
只加写锁,读不加锁,可能读到写未提交的数据
读读并行、读写并行、写读并行
最后系统变成了所有的写是串行的,所有的读都可以并行的进行,一般不用这个隔离级别

事务处理的常见问题

  • 多个事务,谁先谁后
  • 如何故障恢复
  • 碰到死锁了怎么办

  • 多个事务,谁先谁后
    一个读请求应该读哪一个写之后的数据?主要针对MVCC,MVCC每次写都会赋予一个新的版本号
    如果一个读发生在一个写之后,那么读的版本号必须要大于写的版本号,那么这个是如何实现的呢?
    逻辑时间戳:
    SCN(Oracle)
    Trx_id(Innodb)
    实现:内存里维持了一个自增号,每次写的时候,都加1。即通过自增号来保证事务的先后顺序
    这其实就是时间戳的一种。时间戳有两种:逻辑时间戳和物理时间戳
    逻辑时间戳只是保证先后顺序,并不是真正意义上的时间的描述;物理时间戳,就是时钟。

  • 故障恢复
    1.业务属性不匹配-事务回滚
    需要记录下当前事务执行过程中所有的反向操作,来进行回滚
    2.系统崩溃
    在事务执行过程中,系统crash,数据库进行数据恢复,恢复过程中,系统是不能被外部其他 应用访问的。数据库重启以后,都会有一个状态叫recovery,对外是不暴露监听的
  • 死锁
    死锁产生的原因:
    两个线程,不同方向加锁,相同资源
    死锁解决方案:
    尽可能不死锁
    碰撞检测-记录所有事物单元维持的锁,终止一边,效率最高
    等锁超时,超长事务,需要超长时间

事务的调优原则

在不影响业务应用的前提下
减少锁的覆盖范围
MyISAM表锁 -> InnoDB行锁 、MVCC多版本
增加锁上可并行的线程数->读锁写锁分离,运行并行读取数据
选择正确的锁类型
悲观锁 使线程到blocking状态 通知信息OK的状态切换回等待状态
换入换出 - 寄存器,CPU cache,缓存
适合并发争抢比较严重的场景
乐观锁 适合并发争抢不太严重的场景
读锁写锁和乐观锁悲观锁是两个不同层次的概念
悲观并发控制
一个锁定系统,可以阻止用户以影响其他用户的方式修改数据。如果用户执行的操作导致应用了某个锁,只有这个锁的所有者释放该锁,其他用户才能执行与该锁冲突的操作。这种方法之所以称为悲观并发控制,是因为它主要用于数据争用激烈的环境中,以及发生并发冲突时用锁保护数据的成本低于回滚事务的成本的环境中。
乐观并发控制
在乐观并发控制中,用户读取数据时不锁定数据。当一个用户更新数据时,系统将进行检查,查看该用户读取数据后其他用户是否又更改了该数据。如果其他用户更新了数据,将产生一个错误。一般情况下,收到错误信息的用户将回滚事务并重新开始。这种方法之所以称为乐观并发控制,是由于它主要在以下环境中使用:数据争用不大且偶尔回滚事务的成本低于读取数据时锁定数据的成本。

将请求打包统一发送到块存储
1.直接写入内存
优点:IOPS高
缺点:可能丢数据
2.Group commit
优点:保证系统的持久性和吞吐量
缺点:请求延迟提升

分布式系统中的事务

分布式系统中包括:传统的跨机ACID事务和多机的数据同步(解决高可用)
用分布式事务来解决高可用问题的,一般都会发现,整个数据库的TPS下降一半以上等严重的性能问题。一切的问题就在“锁”上,原来在单机,加锁操作延迟在纳秒级别,而如果这个加锁操作走了网络,那最少也是毫秒级别。
所以只能放弃锁,最直接的代价就是用户会看到脏数据,比如某些已经在主映射完成的更新,还没来得及在备份映射内写入,这时候用户去读备份映射就会读到脏数据。这种可能会读到脏数据可能的一致性叫做“最终一致性”。
那最终一致性和读未提交的差别在哪呢?差别不在锁,而在原子性。ACID事务是能回滚的,而最终一致性往往不会回滚,而是努力送达,直到成功。

参考资源:
1.沈询,事务与分布式事务原理与实践 http://www.imooc.com/learn/272
2.《高性能MySQL》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值