Mysql数据库锁之---表级锁、行级锁、悲观锁、乐观锁、间隙锁GAP、死锁等简单介绍

目录

一、数据库默认加的锁 

二、表级锁与行级锁

    表级锁

    行级锁

三、乐观锁与悲观锁

    乐观锁

    悲观锁

四、间隙锁GAP

五、死锁

总结


    在Mysql中有:排它锁,共享锁,表锁,页锁,间隙锁,意向排它锁,意向共享锁,行锁,读锁,写锁,乐观锁,悲观锁,死锁...等关于锁的名词我们是耳听目染,但这些锁名词是什么?怎么用?

  本文,以Mysql数据库为例,在MyISAMInnoDB引擎下,梳理 表级锁与行级锁乐观锁与悲观锁间隙锁GAP、死锁

之后如果有补充也会在该本章内继续添加。

一、数据库默认加的锁 

    在并发情况下,为了尽可能的保证数据的正确性,故有了锁这个概念。

    很多开发者都对锁概念有了解,但在平时的开发中,感觉并没有使用到"锁",但是程序依然可以正确运行。

    这是因为Mysql的数据库隐式自动对一些语句加锁了。

    InnDB:对于UPDATE、DELETE、INSERT语句,自动给涉及数据集加排他锁(X)

    MyISAM:执行SELECT语句前,会自动给涉及的所有表加读锁。对于UPDATE、DELETE、INSERT等,会自动给涉及的表加写锁

二、表级锁与行级锁

   

锁粒度,我们将锁分为了:表级锁、行级锁

    表级锁

开销小,加锁快;不会出现死锁;锁定力度大,发生锁冲突概率高,并发度最低

    行级锁

开销大,加锁慢;会出现死锁;锁定粒度小,发生锁冲突的概率低,并发度高

    InnDB

    (1) 行锁和表锁都支持,但是!只有通过索引条件检索数据才使用行级锁,否则,InnoDB将使用表锁。

    (2) 不支持查询和插入操  作的并发进行。

    MyISAM

    (1) 只支持表锁。

    (2) 支持查询和插入操作的并发进行。可以通过系统变量concurrent_insert来指定哪种模式,在MyISAM中它默认是:如果MyISAM表中没有空洞(即表的中间没有被删除的行),MyISAM允许在一个进程读表的同时,另一个进程从表尾插入记录。

表级锁 分为两种模式:

  • 表读锁(Table Read Lock)
  • 表写锁(Table Write Lock)

Javaç¼ç¨ââæ°æ®åºä¸¤å¤§ç¥å¨ï¼ç´¢å¼åé

    读读不阻塞:当前用户在读数据,其他的用户也在读数据,不会加锁

    读写阻塞:当前用户在读数据,其他的用户不能修改当前用户读的数据,会加锁!

    写写阻塞:当前用户在修改数据,其他的用户不能修改当前用户正在修改的数据,会加锁!

也就是说:读锁和写锁是互斥的,读写操作是串行

参考资料

  • dev.mysql.com/doc/refman/…--官方手册
  • ourmysql.com/archives/56…---几个参数说明

三、乐观锁与悲观锁

乐观锁

概念

    乐观锁是一种思想!其核心思想是:这个模式没有从数据库加锁!

    其"锁"概念的实现是:通过一些方式来判断数据库数据是否变动,如果未变动,则修改数据;如果发现数据变动,则不修改数据。

例:

    我们为某表添加一个版本字段version。修改一次数据,version则 +1。如下图

Javaç¼ç¨ââæ°æ®åºä¸¤å¤§ç¥å¨ï¼ç´¢å¼åé

    (1) 张三和李四同时查出上面的数据。

    (2) 张三先做出修改:

  • update A set Name=lisi,version=version+1 where ID=#{1} and version=#{1}

    注:判断之前查询到的version与现在的数据的version进行比较,同时会更新version字段+1

   (3) 此时,数据为:

Javaç¼ç¨ââæ°æ®åºä¸¤å¤§ç¥å¨ï¼ç´¢å¼åé

    (4) 李四后又修改:

  • update A set Name=lisi,version=version+1 where ID=#{1} and version=#{1}

    但此时失败了!因为当前数据库中的版本跟查询出来的版本不一致

    

这样便实现了"锁"机制的实现。这种便是"乐观锁"思想。它是一种思想!

悲观锁

使用

悲观锁的话其实很简单(手动加行锁就行了)

  • select * from xxxx for update

在select 语句后边加了 for update相当于加了排它锁(写锁),加了写锁以后,其他的事务就不能对它修改了!需要等待当前事务修改完之后才可以修改.

也就是说,如果张三使用select ... for update,李四就无法对该条记录修改了~

参考资料

  • zhuanlan.zhihu.com/p/31537871---什么是悲观锁和乐观锁
  • www.zhihu.com/question/27…---乐观锁和 MVCC 的区别?

四、间隙锁GAP

概念

    当我们用范围条件检索数据,并请求共享或排他锁时,InnoDB会给符合范围条件的已有数据记录的索引项加锁

对于键值在条件范围内但并不存在的记录,叫做“间隙(GAP)”。InnoDB也会对这个“间隙”加锁

注意!间隙锁只能在Repeatable read隔离级别下使用~

例:

    假如emp表中只有101条记录

  • Select * from emp where empid > 100 for update;

nnoDB不仅会对符合条件的empid值为101的记录加锁,也会对empid大于101(这些记录并不存在)的“间隙”加锁

目的

  • 为了防止幻读:在一个事务未提交前,其他并发事务不能插入满足其锁定条件的任何记录

五、死锁

概念

    是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。

发生条件

    1)互斥条件:指进程对所分配到的资源具有排它性。即在一段时间内该资源只由一个进程占用。如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用毕释放。

    2)请求和保持条件:指进程已经保持(持续获得)至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞(只能等其他进程用完再用),但又对自己已获得的其它资源保持不放。

    3)不剥夺条件:指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放。(感觉和 互斥条件 差不多呢?)

    4)环路等待条件:即{P0,P1,P2,P3}进程中,P0等待P1占用的资源;P1等待P2占用的资源,P3正在等待P0占用的资源。然后就这么等下去了....前三个为具条件,第四个为发生诱因!

解决方案

  • 1)以固定的顺序访问表和行。将两个事务的sql顺序调整为一致,来避免死锁。
  • 2)大事务拆小。大事务更倾向于死锁,如果业务允许,将大事务拆小。
  • 3)一次锁定。在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁概率。
  • 4)降低隔离级别。如果业务允许,将隔离级别调低也是较好的选择,比如将隔离级别从RR调整为RC,可以避免掉很多因为gap锁造成的死锁。
  • 5)为表添加合理的索引。可以看到如果不走索引将会为表的每一行记录添加上锁,死锁的概率大大增大。

参考资料

总结

关于表锁

  • 在MyISAM存储引擎中,当执行SQL语句的时候是自动加的。
  • 在InnoDB存储引擎中,如果没有使用索引,表锁也是自动加的。

所以 其实我们程序员是很少关心它

关于行锁

InnoDB支持行锁:

  • 共享锁--读锁--S锁
  • 排它锁--写锁--X锁

在默认的情况下,select是不加任何行锁的~事务可以通过以下语句显示给记录集加共享锁或排他锁。

  • 共享锁(S):SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE
  • 排他锁(X):SELECT * FROM table_name WHERE ... FOR UPDATE

关于乐观锁与悲观锁

  • 乐观锁其实是一种 思想!它不加锁!一般是添加version字段来验证数据是否异常。如果发现数据异常,则不更新(回滚)
  • 悲观锁是 添加行锁!认为数据库会发生并发冲突,直接上来就把数据锁住,其他事务不能修改,直至提交了当前事务

关于间隙锁GAP

    在InnoDB下,Repeatable read隔离级别配合GAP间隙锁来避免幻读

参考资料

  • zhuanlan.zhihu.com/p/29150809--Mysql锁总结
  • blog.csdn.net/mysteryhaoh…--MySQL学习之——锁(行锁、表锁、页锁、乐观锁、悲观锁等)
  • segmentfault.com/a/119000001…--MySQL InnoDB引擎锁的总结

本文参考:https://www.toutiao.com/i6583941335516971524/

猜你喜欢

转载自blog.csdn.net/With_Her/article/details/81535813