第七章 并发控制

之前我们讨论了单一的TX的恢复。一份数据多个备份怎么保证CONSISTENCY,对多个变量一系列操作放在一个TX会如何?

那么有多个并行的TX会如何呢?
你写的东西被别人看见,但是别人用了你的写,你却回滚了。这就会有问题。
如果不对CONCURRENT TX管理的话,系统会出现各种问题, 和DATA RACE 在parallel program 很相似。

一个问题,你能多提取500块


10803273-5af462f08a9df5bd.png
image.png

你可以让APPLICATION 来保护,比如去拿锁。 但是这会增加APPLICATION DEVELOPER 的负担,造成重复开发,你的APP 和别人的APP会造成相同的数据块的,这样只拿自己内存里的锁是没法保证的没有DATA RACE的。

如果给STORAGE来做,那么APP就很简单,上面什么都不用太考虑。

10803273-5468c6cbb400a9e2.png
image.png

今天我们来讲ISOLATION,因为其他三个上一章已经搞定了。

10803273-68ce35062ea3c154.png
image.png

SERIALIZABILITY
2个TX并行执行,执行完的结果,等同于这2个TX串行的执行的结果。

10803273-e2179b2920887f46.png
image.png

上面2个TX 并行执行 的效果 和串行是等价的,所以他们并行执行没有问题。

10803273-404e6d8b31aa6533.png
image.png

下面这种调度的方式就会有问题

10803273-8687ab7386d460b7.png
image.png

下面有2种SOLUTION,一种就是在TX这完全加锁,这个是可以的,但是太慢。
另外一种是用一种细粒度的锁。在OBJECT LEVEL。但是依然不是SERILIZABLE,看下图。

10803273-11a76cba6afeb078.png
image.png

这个是读未提交的问题。

那么我们是不是可以让写锁的时间变长一直到COMMIT,才放锁来解决这个问题。这个时候读锁还是立即释放的锁。这样会出现另外一种不SERIALIZABLE情况。


10803273-d70c34bee4f41593.png
image.png

这个问题是不可重复读,就是在一个TX里面,读一个OBJ读2次结果不一样。
所以对READ来说,SHORT DURATION 也不行,也要变成LONG duration。
这个比第一种GLOBAL 好的,是细粒度的锁的实现。
还可以用读写锁来优化。

二阶段锁定,第一个阶段是GROWING,(集合会慢慢变大);第二个阶段,SHRINKING(发生在COMMIT的时候,只会收缩)


10803273-6845d81a89737386.png
image.png

10803273-2ba5c91dba31e7d2.png
image.png

上面这种情况是不可能实现的,因为红色的A 的READ,读锁拿不到。

拿不到锁的时候,可以等,可以ABORT。
如果是等的话,就会出现DEADLOCK。避免DEADLOCK,可以使用按照一定的顺序去拿锁。
如果是看到1个变量拿一把锁,是没法实现的。除非在一开始你就知道要锁哪些变量。
我们可以去检查DEADLOCK,发现DEADLOCK,把其中一个ABORT。
1.通过分析拿锁过程是否有环。
2.我检查TIMEOUT,很长时间拿不到就ABORT自己。(你的TX比较久,就会没法执行完就ABORT自己;轮流拿锁轮流ABORT自己,就会有活锁的情况)

因为TWO PHASE LOCK 锁在OBJECT上,但会有在一个LIST 加2个新的ITEM,这2个OBEJCT 锁是不冲突的。概括的说,在查2次集合的时候,2次的ITEMS 数目不一样。一个会比另一个多。

问题就在于把LOCK放在OBJECT上,在上述情况中,应该把这个锁加在搜索的集合上。


10803273-11983fcca653044e.png
image.png

用谓词锁,或者间隙锁(B树上锁一个子树(RANGE))
在实际中,默认不采用SERIALZABLE,因为性能会不好。一个分析的SQL,就会是一个很长的READ ONLY的TX(比如分析1个小时)那么在这个TX,其他的TX会被挡住,所以造成别的功能就挂了。

10803273-911fa158c19cdc59.png
image.png

所以我们需要在一个SERIALZABLE 上的一个优化。突破的方式在一个TRADE OFF的变化。
比如要更好的性能,要牺牲一些CONSISTENCY上的保证。
这就提出了一个MVCC的CONTROL


10803273-7cdda1f22c615072.png
image.png

把所有写操作BUFFER起来(因为不知道最后是COMMIT,还是ABORT,还不希望读到未提交的写),读的时候要选合适的版本。在提交时,系统会验证是不是可以让读VISIBILE。(乐观锁,如果发生了CONFILICT,就产生新的版本)

10803273-fa10d52d32aec54d.png
image.png
10803273-270ea06f85660040.png
image.png

如果在COMMIT的时候,看写X的时间,如果BUFFER SET里有个新的TX提交的X写,就会ABORT。

下面再看一个例子。


10803273-6d3c15d10ae924c7.png
image.png
10803273-df05d3bcb8d01569.png
image.png

上面的方案 等价的SERIAL 的 顺序如下图


10803273-802efa6c475912cc.png
image.png

但也是有个反例的。


10803273-1d23ba81553c4980.png
image.png

但是幻读的问题解决了。

如果隐含的条件不在TX里可以表达,那么无论是TWO PHASE LOCK 还是MVCC都不能实现。

上述都是在一台机器上的并发事务。

如果在2台机器上要保持事务,多台机器要达成一致,应该怎么做呢?
1。要所有人都同意
2。如何处理有的人挂了

2 phase commit - > paxos

猜你喜欢

转载自blog.csdn.net/weixin_34082177/article/details/86892182