当前位置: 首页 > 科技观察

MySQL-InnoDB乐观锁悲观锁共享锁、排他锁、行锁、表锁、死锁概念理解

时间:2023-03-12 08:29:34 科技观察

MySQL/InnoDB加锁一直是面试中经常被问到的话题。比如数据库有高并发请求,如何保证数据的完整性?如何排查和解决死锁问题?我在工作中也经常使用乐观锁、排他锁等。所以今天就把这些概念研究一下,想了很多遍,记录下来。注:MySQL是一个支持插件式存储引擎的数据库系统。本文以下所有介绍均基于InnoDB存储引擎,其他引擎的性能会有较大差异。StorageEngineViewMySQL为开发者提供了查询存储引擎的功能。我这里使用的是MySQL5.6.4,你可以使用:SHOWENGINES乐观锁是通过数据版本(Version)记录机制来实现的,这是最常用的一类乐观锁方法来实现。什么是数据版本?即为数据添加版本标识,通常是在数据库表中添加一个数字“版本”字段。读取数据时,一起读取version字段的值,数据每更新一次,version值就加1。当我们提交更新时,判断数据库表中相应记录的当前版本信息与第一次提取的版本值进行比较,如果数据库表当前版本号等于版本值第一次提取,然后更新,否则认为是过期数据。例1.设计数据库表中的三个字段,分别是id,value,versionselectid,value,versionfromTABLEwhereid=#{id}2.每次更新表中的value字段,为了防止冲突,需要操作updateTABLEsetvalue=2,version=version+1whereid=#{id}andversion=#{version};悲观锁对应乐观锁。悲观锁是指在操作数据的时候,认为这个操作会造成数据冲突,所以每次操作都必须获取锁来操作同一个数据。这和java中的synchronized很相似,所以悲观锁需要花费更多的时间。另外,与乐观锁相对应的是,悲观锁是由数据库自己实现的。当我们需要使用的时候,直接调用数据库的相关语句即可。说到这里,另外两个与悲观锁相关的锁概念就出来了,它们是共享锁和排它锁。共享锁和排它锁是悲观锁的不同实现,都属于悲观锁的范畴。使用,独占锁例如,要使用悲观锁,我们必须关闭mysql数据库的autocommit属性,因为MySQL默认使用autocommit模式,即当你执行更新操作时,MySQL会立即提交结果。我们可以使用命令将MySQL设置为非自动提交模式:setautocommit=0;#设置autocommit后,我们就可以进行正常的业务了。详情如下:#1。开始事务begin;/beginwork;/starttransaction;(三选一)#2。查询表信息selectstatusfromTABLEwhereid=1forupdate;#3。插入一条数据insertintoTABLE(id,value)values(2,2);#4.修改数据为updateTABLEsetvalue=2whereid=1;#5。提交事务commit;/commitwork;共享锁共享锁也称为读锁,是由读操作创建的锁。其他用户可以并发读取数据,但在所有共享锁被释放之前,任何事务都不能修改数据(获得数据的排他锁)。如果事务T给数据A加共享锁,其他事务只能给A加共享锁,不能加排它锁。获取共享锁的事务只能读取数据,不能修改数据打开第一个查询窗口begin;/beginwork;/starttransaction;(三选一)SELECT*fromTABLEwhereid=1lockinsharemode;然后在另一个查询窗口,更新id为1的数据updateTABLEsetname="www.souyunku.com"whereid=1;此时操作界面进入卡死状态,超时后会提示错误信息。如果在超时前执行了commit,这条更新语句就会成功。[SQL]updatetest_onesetname="www.souyunku.com"whereid=1;[Err]1205-Lockwaittimeoutexceeded;tryrestartingtransaction添加共享锁后,同样提示报错信息updatetest_onesetname="www.souyunku.com"whereid=1lockinsharemode;[SQL]updatetest_onesetname="www.souyunku.com"whereid=1lockinsharemode;[Err]1064-YouhaveanerrorinyourSQLsyntax;checkthemanualthatcorresponsttoyourMySQLserverversionfortherightsyntaxtousenear'lockinsharemode'atline1查询语句后添加LOCKINSHAREMODE,Mysql会在查询结果中的每一行添加共享锁,当没有其他线程对查询结果集中的任何一行使用排它锁,它才能成功申请到共享锁,否则会被阻塞。其他线程也可以使用共享锁读取表,这些线程读取相同版本的数据。添加共享锁后,排他锁会自动添加到update、insert、delete语句中。独占锁独占锁独占锁(也叫写锁)也叫写锁。独占锁是悲观锁的一种实现,上面也有介绍。如果事务1给数据对象A加了X锁,则事务1可以读取A或修??改A,其他事务不能给A加任何锁,直到事务1释放对A的锁。这样就保证了其他事务不能再读取和修改A直到事务1释放了A上的锁。排他锁会阻塞所有的排他锁和共享锁。为什么需要加读锁:防止数据在读的时候被其他线程写入,使用方法:加在需要执行的语句后面。对于更新就足够了。行锁分为共享锁和排它锁。从字面意思看就是给某行加锁,也就是给一条记录加锁。注意:行级锁是基于索引的。如果SQL语句不使用索引,则不会使用行级锁,而使用表级锁。共享锁:名词解释:共享锁也叫读锁。所有事务只能对它们进行读写。添加共享锁后,其他事务只能在事务结束前添加共享锁。其他不能再添加任何类型的锁。SELECT*fromTABLEwhereid="1"lockinsharemode;结果集中的数据会被共享锁排它锁:名词解释:如果某物给一行加了排它锁,只有本事务可以读写它,事务结束之前,其他事务不能给它加任何锁,其他进程可以读取它,但不能写入它,必须等待它被释放。selectstatusfromTABLEwhereid=1forupdate;可以参考前面演示的共享锁,排他锁语句相当于索引,因为id字段是表的主键。在进行加锁的??时候,id索引为1的记录会被加锁,那么这个锁就是行锁。如何给表锁加表锁。Innodb的行锁是在索引的情况下。没有索引的表会锁定整个表。Innodb中的行锁和表锁前面已经提到了。在Innodb引擎中,同时支持行锁和表锁。支持表锁,那么什么时候整表加锁,什么时候或者只加锁一行呢?只有通过索引条件检索数据时,InnoDB使用行级锁,否则,InnoDB使用表锁!在实际应用中,要特别注意InnoDB行锁的这个特性,否则可能会造成大量的锁冲突,从而影响并发性能。行级锁基于索引。SQL语句如果不使用索引,就不会使用行级锁,而是使用表级锁。行级锁的缺点是:由于需要申请大量的锁资源,速度慢,内存消耗大。死锁(Deadlock)所谓死锁:是指两个或多个进程在执行过程中由于争夺资源而相互等待的现象。如果没有外力,他们将无法前进。.这时候就说系统处于死锁状态或者系统已经发生了死锁,而这些一直在互相等待的进程就叫做死锁进程。由于资源占用是互斥的,当一个进程申请资源时,如果没有外界的帮助,相关进程将永远无法分配到所需的资源而无法继续运行。这就产生了一种特殊的死锁现象。解除死锁状态有两种方法:第一种方法:1.查询表是否被锁showOPENTABLESwhereIn_use>0;2.查询进程(如果有SUPER权限,可以看到所有线程。否则只能看到自己的线程)showprocesslist3.kill进程id(即上面命令的id列)killid第二种:1:查看当前事务SELECT*FROMINFORMATION_SCHEMA.INNODB_TRX;2:查看当前锁定的TransactionSELECT*FROMINFORMATION_SCHEMA.INNODB_LOCKS;3:查看当前锁等待事务SELECT*FROMINFORMATION_SCHEMA.INNODB_LOCK_WAITS;killtheprocesskillthreadID如果系统资源充足,能够满足进程的资源请求,死锁的可能性就很低,否则会因为争夺有限的资源而陷入死锁。其次,进程运行的进度顺序和速度不同,也有可能出现死锁。死锁的四个必要条件:(1)互斥:一种资源一次只能被一个进程使用。(2)请求和持有条件:当一个进程因请求资源而阻塞时,它会持有获得的资源。(3)非剥夺条件:进程获得的资源在用完之前不能强行剥夺。(4)循环等待条件:几个进程之间形成头尾循环等待资源关系。虽然不能完全避免死锁,但是可以尽量减少死锁的数量。最大限度地减少死锁会增加事务吞吐量并减少系统开销,因为只有少量事务会回滚,这会撤消事务执行的所有工作。由于死锁回滚,由应用程序重新提交。以下方法有助于最大限度地减少死锁:(1)以相同的顺序访问对象。(2)避免交易中的用户交互。(3)保持交易短小、批量化。(4)使用低隔离级别。(5)使用绑定连接。参考:https://blog.csdn.net/puhaiyang/article/details/72284702https://www.jb51.net/article/78088.htm原文链接:https://www.souyunku.com/2018/07/30/mysql如有侵犯您的权益,请告知并删除