沈洵:分布式事务原理与实践之单机事务

17 篇文章 1 订阅
15 篇文章 1 订阅

tips:容易理解的模型性能往往性能不好,性能好的模型往往不容易理解


什么事事务?

事务看起来很简单,就三个命令begin transaction,commit,rollback,但是如果没有对这个的原理了解的话,就不会取舍。



事务的简介

事务的核心是锁与并发,同步控制。

优势:方便理解

劣势:性能较低


计算机就像是一个标准的打字机,cpu单位时间内只能做一件事,要么读,要么算,要么写,每个cpu只在单位时间做一件事。

version=version+1;一共为三步,先取出数据,加1,再写回去。而事务就是在读写算外再额外提供了一个同步块。

数据库的事务本质来说跟应用做的synchronize 和lock等同步是一样的,没有本质的差异,多个人操作同一块数据的时候,得保证只有一个线程进入事务。



事务单元

一段业务代码的逻辑,希望它就像我写出来的那样去运行,运行这段逻辑的过程中的时候,外边的人不参与这段逻辑运行,我有几个操作看起来是原子操作,不论有多少步,对于应用来说都是一步,通过一个bebgin transaction  comit rollcbck 之间的操作都应该加同步访问。




多个事务单元的情况,都有史密斯,其他事务都得排队,这是一种事务单元一种非常重要的访问控制的方式:锁的方式

一组事务单元

Bob给Joe100块

Joe给Simith100块

Simith给Bob100块


事务单元之间有很多不同的关系,事务和事务之间有哪些关系,如果需要有一种方式来表述,那就是happen before的四种可能性。

事务单元之间的happen-before关系四种可能性,也就是事务冲突的可能性分别为

读写

写读

读读

写写

所有事务冲突的可能性可以抽象为这四种关系

在这之上还有个问题:

如何能够以最快的速度完成这些事务单元之间的关系?又能保证上面四种操作的逻辑顺序?有没有可能尽可能的让事务单元不出现冲突?

假设一个事务耗时1秒,60个事务串行要60秒,但如果并行的化只要1秒。

事务之间没有关联是最快的。

阿木大二定律,如果事务之间没有相关性,所有的事务都可以并行,系统的性能理论上是最优的。

要保证系统之间的这四种happen before的关系在逻辑上是有序的又想办法保证性能又要保证看起来一样,也就是提高系统的易用性的同时,又尽可能不损失系统的性能,尤其是不损失系统的吞吐。


除了转账这样的操作,有一些操作也是一个事务单元这也是一个事务单元

商品要建立一个基于GMT_modified的索引,alter table add index ...,原来的数据读一遍,在新的表写一遍,在读使用一张表的时候,要让原来的表不变,这样才能数据一致

从数据库中读取一行记录

向数据库中写入一行记录,同时更新这行记录的所有索引。

删除整张表

所以说数据库有很多的事务单元。


如何实现事务单元?

Two Phase lock,两段提交协议。

所有的数据库操作CRUD都是先读再写的操作,比如insert和delete,update都是先查再写的操作,每次查出数据时候数据库会再上面加锁。

对于insert读取数据,判断数据又没有,没有写入,又的话返回错误,如果并没有读,只有写那就没有事务操作。

比如最高级别的排它锁,都不会被其他人读到,无论有多少次更新,中间有多少个中间状态其他人都看不到。

当做完中间状态提交后,锁就解开了,所有系统都恢复了,就都可以看到。对于转账,要么开始我有钱你没钱,要么结束是你有钱我没钱。而中间我减钱和你加钱的过程中被锁隐藏掉了,这就是数据库中做事务最标准的方法,而X锁U锁读写锁很多锁都是对标准的排他锁的优化。



具体的事务实现思路


事务的实现方式-排队法

一大堆数据单线程处理是事务最简单最重要的解决方案

单线程来处理事务,例如nosql的REDIS。Redis的核心思路,所有数据都在内存里,单线程处理所有的put、get是效率最高的。

因为多线程是CPU模拟多线程的情况,多线程有上下文切换,在内存的情况下,而没有上下文切换效率最高,这个方案是最好的方案。


第二种简单的方案:排它锁

第三种方案:读写锁

对于读读场景的优化,一个只读的事务,比如只做查询。readwriteLock在读多写少的情况下可以搞定。

第三种方式:MVCC

MVCC是后来orcale提出来的

读是并行,写是串行

这是最优的解法,本质是copy on write。之前所有的版本都在里面,读的时候申请一个版本号,如果版本号比上面的小就可以获得,读读不阻塞的前提下,读写和读读也不阻塞。


什么时候事务需要多线程?

这在于下层使用的存储

内存操作的时候,IOPS非常高的系统,内存的申请和销毁非常快,内存可以动态申请大小,内存还可以随时动态开辟销毁一段空间。

磁盘IOPS很低,吞吐量高。SAS是几百M 传统的磁盘100~150M,意味着必须将大量的读和写攒成一个batch一起提交性能最高。

一个事务读算写在内存里很快完成,内存IOPS是磁盘的1000到1万倍,如果用内存的方式会来操作磁盘会系统性能上不去。所以要将大量的请求攒到一起提交。

方式:

异步,我的请求和线程不绑定,线程不block,处理你完了可以处理别人,也是一种多线程的场景,将大量不同人的请求提交到一个buffer里,再由buffer统一再读或者写。磁盘网络慢速设备,多线程或者=异步场景特别多,面对磁盘网络要面对使用多线程来处理这些事情。


事务基本的调优原则

不影响业务应用的情况下,尽可能减少锁的覆盖范围

Myisam 表锁->InnoDb 行锁

原位锁 -> MVCC多版本,也是某种意义上的减少锁的范围

增加锁上可并行的线程数

读锁写锁分离,允许并行读取数据。

选择正确锁类型

悲观锁 适合并发争抢中比较严重的场景

乐观锁 适合并发争抢中不严重的场景

乐观锁悲观锁定义:

悲关锁:系统一个请求进来后,请求的线程切换出去,正在处理数据的线程处理完后,notify被等待的线程,然后让线程回到这里重新竞争这把锁

乐观锁:进来后发现有人再做操作,不切换出去,不切换上下文,循环,对方把锁打开了,你进来,你持有这把锁,其他的人再做自旋

很多时候业务也会允许要悲观锁和乐观锁。

最重要的差异

悲观锁,请求读的时候发现有锁,请求去等待,锁处理完了,然后notify所有的等待,等待通知。
乐观锁,发现对方有锁,自旋,拿到后让其他请求自旋,等一段时间问你一次。


为什么悲观锁 适合并发争抢中比较严重的场景而乐观锁 适合并发争抢中不严重的场景?

因为每次切换出去,需要CPU开销,要清除内存页和寄存器,然后才能在外面等。


小结:

事务的ACID

原子性

一致性

隔离性(MVCC/SNAPSHO IOSLATION)

持久性(持久性保证策略)


单机事务的典型异常应对策略

事务的调优原则

。。。未完

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值