美文网首页MySQL数据库知识点Oracle数据库管理之道
两段封锁、三级封锁、多粒度封锁

两段封锁、三级封锁、多粒度封锁

作者: jufengliushao | 来源:发表于2018-11-17 22:00 被阅读22次

引言
现在数据库的实现已经很成熟了,基本已经能够满足我们日常的工作和学习所需,但是有的时候,人还是要“犯贱”一下嘛,理解数据库系统是怎么保证我们的事务操作的一致性(当然也有不一致的情况,大不了全部回滚嘛)

  • 文章有点长,慢慢食用~

废话不多说,本篇文章从以下三个方面,深入理解数据库系统的锁的机制,如果有不正确的地方,还请留言告知

  • 三级封锁
  • 两段封锁
  • 多粒度封锁

锁的由来

数据库(DB),数据库系统(DBMS) 相信大家都比我还清楚,不清楚的可以去找度娘...

  • 举个栗子
    人物:小明 (常出现于初中作文、英语、数学题、物理题、化学题, 这次也有幸在我们的数据库出现)
    任务:开商店
    没错,就是我

情景一
-- 时间:电脑还未普及的年代

  • 需求:记账
  • 解决方案:
    给他一支笔,一个本子(有条件就给专门记账本,没条件给一张纸);
    记录内容:日期,天气,时间,购买人,购买物品,数量,单价,实收;
    (好了,够他玩一阵子了)
  • 缺点:适应现阶段的需求

情景二
-- 时间:电脑普及

  • 需求:电脑记账(对的,你没看错,小明就是这么潮)
  • 解决方案:
    1.创建一个记账.txt,好了,拿去玩吧;
    2.使用数据库,创建字段,进行录入每笔交易记录;
  • 小明现在就一个店铺,一台电脑,所以,他自己去玩吧...

情景三
--- 时间:电脑不值钱,并且每件物品的库存总数变化要在DB中有所展示

  • 需求:连锁商店自动化记账,并且录入一个数据库(假设当前每个店铺只有一台电脑)
  • 解决方案1:
  1. 给每个连锁店的电脑安装数据库,并且每台电脑之间的数据互不相通;
  2. 每台电脑各自记录,并将当前数据记录到各自的DB中;
  3. 固定时间,进行店铺汇总;
  • 解决方案2:
  1. 建立云端的数据库,每个连锁店铺的电脑对云端的数据库进行读写;
  2. 可以实时读写,即李梅买了一桶油,付钱完成,就与远端服务器进行链接并进行记录;也可以先记录到本地,每天固定时间,各个店铺对远端服务器上面的DB进行事务操作;

情景四
-- 时间:小明连锁商店变成了连锁超市

  • 需求:每间店铺内的结算电脑不只一台
  • 解决方案:
    暂时不是本片文章讨论范围,以后会在其他文章内进行给出,敬请谅解



好了,至此,已经可以大致了解了小明同学一路的创业之路,我们要好好向他学习成功的经验,要从他....
(咳咳,走错片场了....┭┮﹏┭┮)

下面我们来逐步分析各个情景:
1.对于情景一、二,我们提供的解决方案基本可以满足小明同学的需求了,至于他有什么界面美观呀,操作便捷呀之类的需求,一概无视,我是程序员,又不是美工;[○・`Д´・ ○]
2.对情景三而言,问题比较严肃。我们需要对每台电脑同步数据到云端的数据库,进行控制,否则会出现写入异常问题,特别是对总数进行增减。




假设现在有两台电脑
1.T1,T2
2.DB中A的数量为100
他们都需要对数据库中的商品A进行数量修改
T1 今天卖出了30件
T2今天进货60件,卖出去70件

操作如表1

Time T1 T2 A
time1 Read(A) --- T1-A: 100 T2-A: 0 DB-A: 100
time2 ---- Read(A) T1-A: 100 T2-A: 100 DB-A: 100
time3 Write(A - 30) Commit(A) --- T1-A: 70 T2-A: 100 DB-A: 70
time3 ---- Write(A + 60) T1-A: 70 T2-A: 160 DB-A: 70
time4 ---- Write(A - 70) T1-A: 70 T2-A: 90 DB-A: 70
time5 ---- Commit(A) T1-A: 70 T2-A: 90 DB-A: 90

显然,在不经控制的时候,T1和T2对数据的操作是存在很大的问题的,那么我们该如何解决呢?
...

串行,先执行T1,当T1执行完毕后,再执行T2,或者两者交换顺序。

这是一个不错的解决方法,但是对于小明这样的,动不动就喜欢开连锁店的人而言,你可以顺序执行10家,100家,但是对于1000家,10000家呢?(不要以为小明不会开这么多店铺,他可是小明呀o(╥﹏╥)o)

我们这个时候要实现串行怎么办呢?要保证一台电脑执行完了,在执行另一台电脑。这就是下面我们将要提到的两段封锁机制

  • 在此之前,我们要明白我们为什么要串行执行任务,是因为串行执行可以保证数据库A的值安全;
  • 但是为什么串行就可以保证数据库A的值是安全的呢?
  • 这里我们将对数据的操作可以分为Read和Write两个操作;
  • 表1中,T1读取了A的值,还没来得及对A进行写的时候,T2也对A进行了读取;
  • 那我们要阻止在T2读取数据A的时候,T1对A进行写;或者在T1对数据A写的时候,不允许T2进行读写;

是不是晕了?没事,吸两口,就能懂,很简单的


吸两口

有了上述的要求,那我们是不是可以创建锁,对A进行加锁,

  1. 当读取的时候添加读锁(共享锁,share,这个名字真的不是我随意取的);
  2. 当写入的时候添加写锁(排它锁,x锁,这个名字也不是我随意取的)

我们要求:

  • 在读取的时候,别的事务不能对其进行写操作,但是可以读操作;
  • 在写取的时候,别的事务不能对其进行读、写操作;
    具体的情况我们可以用表2来展示
S(share-lock) X(x-lock) no-lock
S(share-lock) ✔️ ✔️
X(x-lock) ✔️

好了,我们对造成表1的原因进行的分析,并进行了限制,现在我们再来模拟一下表1中的执行(这里我们假设锁的释放为用完即释放,Eg:S-lock(A) -> Read(A) -> S-Unlock(A))
表3

Time T1 T2 A
time1 S-lock(A) --- T1-A: 0 T2-A: 0 DB-A: 100
time2 Read(A) --- T1-A: 100 T2-A: 0 DB-A: 100
time3 S-unlock(A) --- T1-A: 100 T2-A: 0 DB-A: 100
time4 --- S-lock(A) T1-A: 100 T2-A: 0 DB-A: 100
time5 ---- Read(A) T1-A: 100 T2-A: 100 DB-A: 100
time6 --- S-unlock(A) T1-A: 100 T2-A: 100 DB-A: 100
time7 X-lock(A) --- T1-A: 100 T2-A: 100 DB-A: 100
time8 Write(A - 30) Commit(A) --- T1-A: 70 T2-A: 100 DB-A: 70
time9 X-unlock(A) --- T1-A: 100 T2-A: 100 DB-A: 100
time10 --- X-unlock(A) T1-A: 70 T2-A: 100 DB-A: 70
time11 ---- Write(A + 60) T1-A: 70 T2-A: 160 DB-A: 70
time12 ---- Write(A - 70) T1-A: 70 T2-A: 90 DB-A: 70
time13 ---- Commit(A) T1-A: 70 T2-A: 90 DB-A: 90
time14 --- X-unlock(A) T1-A: 70 T2-A: 90 DB-A: 90

WTF 为啥还不行...

Why?
  • 看样子,仅仅只是对数据项添加对应的锁是不行的,那是锁的问题吗?还是加锁的机制有问题?
  • (装逼时刻)我明确的告诉你,锁是没错的,我们要在加锁的时候,采用一定的策略,这样就能实现我们所要达成的问题

接下来,我们再考虑一下,什么策略可以保证各个计算机在并行对数据库进行操作的时候,可以确保其结果和某一个串行的执行顺序一致?

  • 可能有人会说,为什么是某一种串行,因为就现在小明连锁店的情况而言,可能只需要对DB中的数据进行加减即可;但是不能保证每次的执行的事务都是加减法吧。2333

咱们引入一个概念串行调度

  • 多个事务进行操作时,如果以事务为单位,多个事务依次执行

可串行化

  • 对于一个并发事务集来说,如果一个调度与同一事务集中某一个串行调度等价,则称该调度为可串行化
讲人话
  • 简单的来讲就是,在并行调度的时候,我们要确保并发执行的操作事务,与某一种事务串行的结果一致;
  • 至于课串行化,就是在并行调度的过程中,对原子事务进行交换,且不改变原并行调度执行结果,可以得到一个串行调度的队列

有了以上的知识,明确了我们需要一种机制来保证事务的并发执行与串行调度一致,介绍以下封锁机制


两段封锁

首先搬出各大书中对其的定义(定义嘛,看不懂直接跳过)

  • 扩展阶段:申请并获得各种类型的锁。此阶段只能申请事务中需要的锁,但是不能释放锁
  • 收缩阶段:释放所有申请的锁。此阶段只能释放该事务申请的锁,且不能再申请锁
  • 简单的来讲就是,在一个事务中所有的封锁操作必须出现在第一个释放锁的操作之前

Eg:

  • T Slock(A) Slock(B) Slock(C) 第一阶段 扩展阶段
  • T Unclock(B) Unclock(C) Unclock(A) 第二阶段 收缩阶段
    不正确的例子
  • Slock(A) Slock(B) Unclock(B) Slock(C) Unclock(C) Unclock(A)
thinking...

下面我们通过表4来演示一遍两段封锁

Time T1 T2 A
time1 S-lock(A) --- T1-A: 0 T2-A: 0 DB-A: 100
time2 Read(A) --- T1-A: 100 T2-A: 0 DB-A: 100
time3 X-lock(A) --- T1-A: 100 T2-A: 0 DB-A: 100
time4 --- S-lock(A) T1-A: 100 T2-A: 0 DB-A: 100
time5 --- Wait() T1-A: 100 T2-A: 0 DB-A: 100
time6 S-unlock(A) Wait() T1-A: 100 T2-A: 0 DB-A: 100
time7 Write(A-30) Wait() T1-A: 70 T2-A: 0 DB-A: 100
time8 Commit() Wait() T1-A: 70 T2-A: 0 DB-A: 70
time9 X-lock(A) Wait() T1-A: 70 T2-A: 0 DB-A: 70
time10 --- Read(A) T1-A: 70 T2-A: 70 DB-A: 70
time11 --- X-lock(A) T1-A: 70 T2-A: 70 DB-A: 70
time12 --- Write(A+60) T1-A: 70 T2-A: 130 DB-A: 70
time13 --- Write(A-70) T1-A: 70 T2-A: 60 DB-A: 70
time14 --- Commit() T1-A: 70 T2-A: 60 DB-A: 60
time15 --- S-unlock(A) T1-A: 70 T2-A: 60 DB-A: 60
time16 --- X-unlock(A) T1-A: 70 T2-A: 60 DB-A: 60

终于对了,按照两段封锁协议,T1和T2的并行操作没有问题
但是,However他们只是对一个数据进行修改呀,要是对DB中多个数据进行修改呢?


假设:DB中现有三个商品分别为A:30 B:60 C:90

  1. T1: A: -10 B: + 30 C:-7
  2. T2: C:-10 A: -20 B: + 20
    我们依旧采用两段封锁来做实验,如表5
Time T1 T2
time1 S-lock(A) ---
time2 Read(A) S-lock(C)
time3 X-lock(A) Read(C)
time4 S-lock(B) S-lock(A)
time5 Write(A-10) Wait(A)
time6 X-lock(B) ---
time7 Write(B+30) ---
time8 S-lock(C) ---
time9 Write(C) ---
time10 --- ---

额,死锁了...
由于两段封锁协议的定义,T1在未对C进行加锁和操作前,不能对A,B进行释放;同样的,T2在没有对A,B进行加锁操作前,不能释放C,这就导致了死锁。

由此可知,两段封锁协议,虽然可以从并发调度的角度上面将事务进行串行化,以保证其结果满足一种串行调度,但是仍然无法有效的排除死锁问题。


三级封锁

已经理解了两段加锁,对于现在要讲的三级封锁,理解上就会很容易

  • 一级封锁协议:事务T在对数据D进行写操作之前,必须对D加X锁,保持加锁状态直到事务操作结束;
  • 二级封锁协议:事务T在读取数据D之前必须先对D加S锁,在读完之后即刻释放加在D上的S锁;与一级封锁协议一起构成二级封锁协议
  • 三级封锁协议:事务T在对数据D读之前必须先对D加S锁,直到事务结束才能释放加在D上的S锁;与一级和二级封锁协议构成三级封锁协议
  1. 对于一级加锁协议,二级加锁协议,均是我们在上面对事务操作的读、写进行添加的共享锁(share-lock)和排他锁(x-lock)
  2. 至于三级协议,是对二级协议的扩充,在二级封锁协议上,读操作结束后,立即释放S锁;到三级封锁协议中的,在事务结束后,释放。
  3. 不知道大家有没有一种感觉,三级封锁协议与我们上面所提及的两段封锁协议在原理上是一致的。其实两段封锁就是从三级封锁协议里面抽象出来的原理形成的

多粒度封锁

好啦,现在又是小明,对,还是他.... 他说,他想要对数据库中的商品总量添加其他信息,即进货时间、产品批次、进货商等信息。
没办法,谁叫咱是程序员呢,天生就是和需求搏斗的勇士。那就改数据库字段,将原来两个字段的商品总量,进行扩充,形成四个,五个,100个字段的一条记录。
这样的话,我们再用上述的S锁、X锁进行操作的时候,就需要对当前商品的整条记录进行加锁。

可是,对,还是小明....

怎么还是你
发现,T1对当前记录进行X锁定修改,而同时T2需要将商品按照供应商映射出来,操作响应慢。
(你是拿秒表计时的吗?你是不是傻,你咋这么多需求的呢)


哪来那么多需求
  • 好了没办法,既然你提出来了,我只能去解决。我现在只需要对一条记录里面的一个字段进行修改,有没有可能只对当前字段加锁,对于其他字段,S锁可以随意加。
  • 回答是当然存在啦....
    那就是意向锁
  • 基于数据对象自身的封锁;显示封锁,对需要操作的数据字段直接封锁,目标明确
  • 基于数据对象上级对象的封锁;隐式封锁,对操作的字段的上级结构进行添加,表明其内部某字段正在被处理
  • 共享意向锁(IS) - 当事务T对给定粒度数据对象实施IS锁时,表明T的一项就是读取该粒度数据对象的部分(而不是全部)下级颗粒度数据对象;
  • 排他意向锁(IX) - 当事务T对给定粒度数据对象实施IX锁时,表明T的一项就是更新该粒度数据对象的部分(而不是全部)下级颗粒度数据对象;
  • 共享排他意向锁(SIX) - 当事务T对给定粒度数据对象实施SIX锁时,表明T的一项就是读取该粒度数据对象的全部下级颗粒度数据对象并更新部分(而不是全部)下级粒度数据对象。

表6即为相容矩阵

IS IX S X SIX
IS ✔️ ✔️ ✔️ ✔️
IX ✔️ ✔️
S ✔️ ✔️
X
SIX ✔️

对于DB中的各种锁,我会单独开一篇文章进行详细的介绍,这里提到意向锁是为了引出多粒度封锁协议

DB中的数据是一个广泛的概念

  1. 从逻辑上看,可以是整个数据库的所有关系,也可以是若干个关系表;可以是所有元组或者经过选择的元组;
  2. 从物理角度上来看,他可以是涉及整个存储块,也可以是其中的一个或若干个存储页面等。

我们现在使用的基本都是关系型数据库(Oracle,MySQL等等),可以采用不同的策略来对数据进行加锁,就像上面提及的意向锁
但是有一点需要注意,对粒度越小的元组进行加锁控制,意味着可以提高并发性,同时也意味着需要创建和管理更多的锁,从而带来的系统开销会增多。


好了,这篇文章介绍了两段封锁、三级封锁和多粒度封锁,希望能给大家带来不一样的理解。

~特别鸣谢 蹦擦擦 童鞋帮忙~

转载请标明出处

相关文章

  • 两段封锁、三级封锁、多粒度封锁

    引言现在数据库的实现已经很成熟了,基本已经能够满足我们日常的工作和学习所需,但是有的时候,人还是要“犯贱”一下嘛,...

  • 系统原理-封锁

    封锁的类型以及粒度,两段锁协议,隐式和显示锁定。 一、封锁粒度 MySQL 中提供了两种封锁粒度: 行级锁 表级锁...

  • 封锁

    请问 通往你内心的道路 怎么走 不知道 它一生都在维修

  • 封锁

    要不是掌握了自我赦免的法宝 我们凭什么替上帝编造真理 口吐莲花 还曾派一场雪来封锁消息

  • 封锁

    很奇怪明明很累,步行上班听着上官文露的读书分享会很惬意,今日听张爱玲的《封锁》在上海封锁的公车上两个陌生人吴翠远和...

  • 封锁

    文/夏莲 那徜徉的风里 夹杂着细碎的言语 我仿佛听到阿姑阿婆们 谈论着谁家的姑娘 又在快活地卖弄风骚了 那滑落的小...

  • 封锁

    稀碎的车轮 柔美的青丝 匆匆劫走时光老囚,飞离远去 走入你的生命,像一朵白莲 在孤独中开放,而我未曾后悔 在遇到你...

  • 封锁

    我曾经害怕发生的事情 在它还没发生之前 我是如何地顽强 顽强地抵抗 不让自己掉入无尽头的深渊 在它降临之后 我是如...

  • 《封锁》

    好莱坞式的叙事,奇诡精巧的悬疑设置,饱含戏谑与哲思的故事。孤岛时期的上海,汉奸头目在寓所的爆炸中身亡。 为追捕刺客...

  • 《封锁》

    [原创] 倘若衣食无忧 倘若与世无争 倘若小趣疲乏 倘若失去自由 当麻将油腻 当游戏乏味 当睡发凌乱 当抖音无色 ...

网友评论

    本文标题:两段封锁、三级封锁、多粒度封锁

    本文链接:https://www.haomeiwen.com/subject/tacmfqtx.html