美文网首页
2022-09-19

2022-09-19

作者: codeMover | 来源:发表于2022-09-18 23:41 被阅读0次

3.1 从数据操作的类型划分:读锁、写锁

对于数据库中并发事务的<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">读-读</span>情况并不会引起什么问题。对于<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">写-写</span>、<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">读-写</span>或者<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">写-读</span>这些情况可能会引起一些问题,需要使用<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">MVCC</span>或者<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">加锁</span>的方式来解决它们。在使用<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">加锁</span>的方式解决问题时,由于既要允许<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">读-读</span>情况不受影响,又要<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">写-写</span>、<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">读-写</span>或者<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">写-读</span>情况下的操作<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">相互阻塞</span>,所以MySQL实现了一种由两种类型的锁组成的锁系统来解决。这两种类型的锁通常被称为共享锁(Shared Lock,S Lock)排他锁(Exclusive Lock, X Lock),也叫读锁(read lock)写锁(write lock)

  • <span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">读锁</span>:也称为<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">共享锁</span>、英文用<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">S</span>表示。针对同一份数据,多个事务的读操作可以同时进行而不会互相影响,相互不阻塞的。
  • <span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">写锁</span>:也称为<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">排他锁</span>、英文用<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">X</span>表示。当前写操作没有完成之前,它会阻断其他写锁和读锁。这样就能确保在给定的时间里,只有一个事务能执行写入,并防止其他用户读取正在写入的同一资源。

需要注意的是对于InnoDB引擎来说,读锁和写锁可以加在表上,也可以加在行上。

举例(行级读写锁:)如果一个事务T1已经获得了某个行r的读锁,name此时另外的一个事务T2是可以取获得这个行r的读锁的,因为读取操作并没有改变行r的数据;但是,如果事务T3想获得行r的写锁,则必须等待事务T1、T2释放行r上的写锁才行。

总结:这里的兼容是指对同一张表或记录的锁的兼容性情况。

X 锁 S 锁
X 锁 不兼容 不兼容
S 锁 不兼容 兼容
1. 锁定读

在采用<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">加锁</span>方式解决<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">脏读</span>、<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">不可重复读</span>、<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">幻读</span>这些问题时,读取一条记录时需要获取该记录的<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">S锁</span>,其实是不严谨的,有时候需要在读取记录时就获取记录的<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">X锁</span>,来禁止别的事务读写该记录,为此MySQL提出了两种比较特殊的<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">SELECT</span>语句格式:

  • 对读取的记录加<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">S锁</span>:

    SELECT ... LOCK IN SHARE MODE;
    # 或
    SELECT ... FOR SHARE;#(8.0新增语法)
    

    在普通的SELECT语句后边加<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">LOCK IN SHARE MODE</span>,如果当前事务执行了该语句,那么它会为读取到的记录加<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">S锁</span>,这样允许别的事务继续获取这些记录的<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">S锁</span>(比如说别的事务也使用<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">SELECT ... LOCK IN SHARE MODE</span>语句来读取这些记录),但是不能获取这些记录的<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">X锁</span>(比如使用<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">SELECT ... FOR UPDATE</span>语句来读取这些记录,或者直接修改这些记录)。如果别的事务想要获取这些记录的<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">X锁</span>,那么他们会阻塞,直到当前事务提交之后将这些记录上的<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">S锁</span>释放掉。

  • 对读取的记录加<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">X锁</span>

    SELECT ... FOR UPDATE;
    

    在普通的SELECT语句后边加<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">FOR UPDATE</span>,如果当前事务执行了该语句,那么它会为读取到的记录加<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">X锁</span>,这样既不允许别的事务获取这些记录的<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">S锁</span>(比方说别的事务使用<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">SELECT .. LOCK IN SHARE MODE</span>语句来读取这些记录),也不允许获取这些记录的<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">X锁</span>(比如使用<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">SELECT ... FOR UPDATE</span>语句来读取这些记录,或者直接修改这些记录)。如果别的事务想要获取这些记录的<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">S锁</span>或者<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">X锁</span>,那么它们会阻塞,直到当前事务提交之后将这些记录上的<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">X锁</span>释放掉。

MySQL8.0新特性:

在5.7及之前的版本,SELECT ... FOR UPDATE,如果获取不到锁,会一直等待,直到<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">innodb_lock_wait_timeout</span>超时。在8.0版本中,SELECT ... FOR UPDATE, SELECT ... FOR SHARE添加<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">NOWAIT</span>、<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">SKIP LOCKED</span>语法,跳过锁等待,或者跳过锁定。

  • 通过添加NOWAIT、SKIP LOCKED语法,能够立即返回。如果查询的行已经加锁:

    • 那么NOWAIT会立即报错返回
    • 而SKIP LOCKED也会立即返回,只是返回的结果不包含被锁定的行。
    select * from t1 for update nowait;
    
    select * from t1 for update skip locked;
    
2. 写操作

平常所用到的<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">写操作</span>无非是<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">UPDATE</span>、<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">DELETE</span>、<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">INSERT</span>这三种:

  • <span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">DELETE</span>:

    针一条记录做DELETE操作的过程其实是先在<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">B+</span>树中定位到这条记录的位置,然后获取这条记录的<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">X锁</span>,再执行<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">delete mark</span>操作。我们也可以把这个定位待删除记录在B+树中位置的过程看成是一个获取<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">X锁</span>的锁定读。

  • <span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">UPDATE</span>:在对一条记录做UPDATE操作时分为三种情况:

    • 情况1:未修改该记录的键值,并且被更新的列占用存储空间在修改见后未发生变化。

      则先在<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">B+</span>树中定位到这条记录的位置,然后再获取一下记录的<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">X锁</span>,最后在原记录的位置进行修改操作。我们也可以把这个定位待修改记录在<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">B+</span>树中位置的过程看成是一个获取<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">X锁</span>的<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">锁定读</span>。

    • 情况2:未修改该记录的键值,并且至少有一个被更新的列占用的存储空间在修改前后发生变化。

      则现在<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">B+</span>树中定位到这条记录的位置,然后获取一下记录的<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">X锁</span>,将该记录彻底删除掉(就是把记录彻底移入垃圾链表),最后再插入一条新记录。这个定位待修改记录在<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">B+</span>树中位置的过程看成是一个获取<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">X锁</span>的<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">锁定读</span>,新插入的记录由<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">INSERT</span>操作提供的<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">隐式锁</span>进行保护。

    • 情况3:修改了该记录的键值,则相当于在原记录上做<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">DELETE</span>操作之后再来一次<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">INSERT</span>操作,加锁操作就需要按照<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">DELETE</span>和<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">INSERT</span>的规则进行了。

  • <span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">隐式锁</span>:

    一般情况下,新插入一条记录的操作并不加锁,通过一种称之为<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">隐式锁</span>的结构来保护这条新插入的记录在本事务提交前不被别的事务访问。

3.2 从数据操作的粒度划分:表级锁、页级锁、行锁

为了尽可能提高数据库的并发度,每次锁定的数据范围越小越好,理论上每次只锁定当前操作的数据的方案会得到最大的并发度,但是管理锁是很<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">消耗资源</span>的事情(涉及获取、检查、释放锁等动作)。因此数据库系统需要在<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">并发响应</span>和<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">系统性能</span>两方面进行平衡,这样就产生了“<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">锁粒度(Lock granularity)</span>”的概念。

对一条记录加锁影响的也只是这条记录而已,我们就说这个锁的粒度比较细;其实一个事务也可以在<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">表级别</span>进行加锁,自然就被成为<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">表级锁</span>或者<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">表锁</span>,对一个表加锁影响整个表中的记录,我们就说这个锁的粒度比较粗。锁的粒度主要分为表级锁、页级锁和行锁。

1. 表锁(Table Lock)

该锁会锁定整张表,它是MySQL中最基本的锁策略,并<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">不依赖于存储引擎</span>(不管你是MySQL的什么存储引擎,对于表锁的策略都是一样的),并且表锁是<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">开销最小</span>的策略(因为锁粒度比较大)。由于表级锁一次会将整个表锁定,所以可以很好的<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">避免死锁</span>问题。当然,锁的粒度大所带来最大的负面影响就是出现锁征用的概率也会最高,导致<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">并发率大打折扣</span>。

①表级别的S锁、X锁

在对某个表执行SELECT、INSERT、DELETE、UPDATE语句时,InnoDB存储引擎是不会为这张表添加表级别的<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">S锁</span>或者<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">X锁</span>的。在对某个表执行一些诸如<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">ALTER TABLE</span>、<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">DROP TABLE</span>这类的<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">DDL</span>语句时,其他事务对这个表并发执行诸如SELECT、INSERT、DELETE、UPDATE的语句会发生阻塞。同理,某个事务中对某个表执行SELECT、INSERT、DELETE、UPDATE语句时,在其他会话中对这个表执行<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">DDL</span>语句也会发生阻塞。这个过程其实是通过在<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">server层</span>使用一种称之为<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">元数据锁</span>(英文名:<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">Metadata Locks</span>,简称<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">MDL</span>)结构来实现的。

一般情况下,不会使用InnoDB存储引擎提供的表级别的<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">S锁</span>和<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">X锁</span>。只会在一些特殊情况下,比方说<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">崩溃恢复</span>过程中用到。比如,在系统变量<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">autocommic=0,innodb_table_locks=1</span>时,<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">手动</span>获取InnoDB存储引擎提供的表t的<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">S锁</span>或者<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">X锁</span>可以这么写:

  • <span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">LOCK TABLES t READ</span>:InnoDB存储引擎会对表<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">t</span>加表级别的<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">S锁</span>。
  • <span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">LOCK TABLES t WRITE</span>:InnoDB存储引擎会对表<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">t</span>加表级别的<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">X锁</span>。

不过尽量避免在使用InnoDB存储引擎的表上使用<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">LOCK TABLES</span>这样的手动锁表语句,它们并不会提供什么额外的保护,只是会降低并发能力而已。InnoDB的厉害之处还是实现了更细粒度的<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">行锁</span>,关于InnoDB表级别的<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">S锁</span>和<span style="color:#a27e22;background:#e9e9e9;font-size:16px;font-family:Helvetica;">X锁</span>了解一下即可。

举例:下面我们讲解MyISAM引擎下的表锁。

相关文章

  • 写日记的好处

    2022-09-19 晴热 周一 “猪爸,老师布置的作文,我没有思路,咋办?”周六的时候,宝贝一脸愁容...

  • 杂言·菊花酒

    菊花酒 (杂言) 2022-09-19 道旁遍菊科,时人已不宝。 尘垢常污叶,独花顾盼姿色仍娟好。 蜜香自有蜂蝶酿...

  • 高粱和鲜花

    2022-09-19 21:08浙江 你们说是种高粱好呢,还是种鲜花好呢?事情是这样的:曾经有一个最美乡村,村子中...

  • CSS:美化滚动条

    2022-09-19 周一 父元素添加样式 1.例:y轴滚动, 先给父元素盒子限定高度 然后再设置滚动条样式,le...

  • 设计思维-传统即创造

    2022-09-19 《传统即创造》是《今日的艺术》的姐妹篇。 《今日的艺术》出版获得成功后,冈本太郎想要再写一本...

  • 病多是堵出来的 疏通法在这里 爱吃这些食物的

    一、《病多是堵出来的!最好的疏通法都在这里!很实用,请慢看!》 黄帝内经与健康生活 2022-09-19 19:3...

  • 现代诗·影子里困住了一头狼

    影子里困住了一头狼 (现代诗) 2022-09-19 影子在尘埃里扭曲 仿佛里面困住了一头独狼 圆月与它有什么勾连...

  • 毅心睡着了(四)

    2022-09-19 上周上完厕所后的轩轩回到寝室,嘀嘀咕咕,告诉临床的同学,毅心在玩,可以不睡觉。 今天周一,问...

  • 上海驾驶证期满换证经历

    大学时考得驾照过期半年,是外地驾照,今天(2022-09-19)早上抽空去长宁区交警支队换了新证。所需证件只需要身...

  • 2022-09-19

    我的审美已经不认可自己的筷子腿了,想要更健康强壮些。 不知道这算不算又一层觉醒。

网友评论

      本文标题:2022-09-19

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