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

MySQL是否加锁,看一看就可以了

时间:2023-03-13 22:15:47 科技观察

这篇文章为大家介绍了MySQL锁的详解,包括数据库锁、表锁、表读锁、表写锁、行锁、MVCC、事务的基础知识隔离级别、悲观锁、乐观锁、间隙锁GAP、死锁等,需要的朋友可以参考。数据库锁知识很多人在开发的时候应该很少注意到这些锁问题,也很少在程序中加锁(库存除外,对数量精度要求极高),即使我们没有这些锁知识,我们的程序依然可以运行正常情况下就好了。因为这些锁数据库是隐式为我们添加的,所以只有在某些特定场景下才需要手动加锁。对于UPDATE、DELETE和INSERT语句,InnoDB会自动为涉及的数据集添加排他锁(X)。在执行SELECT查询语句之前,MyISAM会自动对涉及到的所有表加读锁。在执行增删改查操作前,会自动给涉及的表加写锁。此过程不需要用户干预。表锁首先,从锁的粒度上,我们可以分为两类:表锁:开销小,加锁速度快;没有死锁;locking强,锁冲突概率高,并发度最低行锁:开销大,加锁慢;会出现死锁;锁粒度小,锁冲突概率低,并发度高不同存储引擎支持的锁粒度不同==:支持InnoDB行锁和表锁,MyISAM只支持表锁!InnoDB只有在通过索引条件==检索数据时才使用行级锁,否则,InnoDB使用表锁,也就是说,InnoDB行锁是基于索引的!表锁分为两种模式:表读锁(TableReadLock)&&表写锁(TableWriteLock)从下图可以清楚的看到,在表读锁和表写锁的环境下下:读和read不阻塞,read和write阻塞,write和write阻塞!读读不阻塞:当前用户正在读数据,其他用户也在读数据,不会被锁定。读写阻塞:当前用户正在读取数据,其他用户无法修改当前用户读取的数据将被锁定!写写阻塞:当前用户正在修改数据,其他用户无法修改当前用户正在修改的数据,会被锁定!从上面我们看到:读锁和写锁是互斥的,读写操作是串行的。如果一个进程想要获取一个读锁,另一个进程想要获取一个写锁。在mysql中,写锁优先于读锁!可以通过参数调整写锁和读锁的优先级:max_write_lock_count和low-priority-updates注:行锁InnoDB和MyISAM有两个本质区别:InnoDB支持行锁,InnoDB支持事务。InnoDB实现了以下两种类型的行锁:共享锁(S锁,读锁):允许一个事务读取一行,并防止其他事务获取同一数据集上的独占锁。即多个客户端可以同时读取同一个资源,但不允许其他客户端对其进行修改。独占锁(X锁、写锁):允许获取独占锁的事务更新数据,防止其他事务获取同一数据集的读写锁。写锁是排他的,写锁会阻塞其他的写锁和读锁。另外,为了让行锁和表锁共存,实现多粒度的锁机制,InnoDB内部还有两种意向锁(IntentionLocks),都是表锁:IntentionSharedLock(IS):Transaction意在给数据行加行共享锁,事务在给数据行加共享锁之前必须先获取表的IS锁。意向排他锁(IX):事务打算给数据行加排他锁,事务在给数据行加排他锁之前必须先获得表的IX锁。意向锁也是数据库隐式帮我们做的,程序员不需要关心!MVCCMVCC(Multi-VersionConcurrencyControl)多版本并发控制可以简单的认为:MVCC是行级锁的变种(升级版)。在表锁中,我们的读写都被阻塞了。基于提高并发性能的考虑,MVCC一般不会阻塞读写(很多情况下会避免加锁操作)。可以简单理解为:对数据库的任何修改的提交都不会直接覆盖之前的数据,而是会生成一个与旧版本共存的新版本,这样就可以完全不用加锁读取了。事务隔离级别事务隔离级别是通过锁机制实现的。锁的应用最终会导致不同事务的隔离级别,只是隐藏了加锁的细节。事务隔离级别有四种:Readuncommitted:会出现脏读、不可重复读、幻读Readcommitted:会出现不可重复读、幻读Repeatableread:会出现幻读(Mysql默认隔离级别,但repeatablereadwithgaplock不会出现幻读!)Serializable:串行,避免以上情况。Readuncommitted:现象--->脏读:一个事务读取了另一个事务未提交的数据,例子:A给B转账,A执行转账语句,但是A还没有提交事务,B读取数据发现他账户里的钱增加了!B告诉A我收到了钱。A回滚事务[rollback],当B再次查看账户中的钱时,发现钱不多了...Readcommitted:现象--->不可重复读:一个事务读取另一个事务已经提交了数据,也就是说一个事务可以看到其他事务所做的修改。例如:A查询数据库获取数据,B修改数据库中的数据,导致A多次查询数据库结果不同【危害:A每次查询结果都被B影响,所以查询的信息byA无意义]可重复读:避免不可重复读是事务级的快照!每次读取的都是当前交易版本,即使修改了,也只会读取当前交易版本的数据。至于幻读(phantomreading):是指在一个事务中读取其他事务插入的数据,导致前后读取不一致。类似于不可重复读,但是幻读(phantomreads)会读取其他事务插入的数据,导致前后读取不一致。幻读的重点是增加或删除(数据项的数量发生变化),而不可重复读的重点是修改、幻读和不可重复的区别吗?乐观锁和悲观锁,无论是Readcommitted还是Repeatableread隔离级别,都是为了解决读写冲突的问题。现在考虑一个问题:有一个数据库表USER,只有id,name字段,现在有2个请求同时操作表A,过程如下:(模拟更新丢失,虽然不是非常合适)1.操作1查询name="zhangsan"2.操作2也查询name="zhangsan"3。操作1,将name字段数据改为lisi提交4.操作2,将name字段数据改为wangwu并提交,则操作1的更新丢失,即一笔交易的更新覆盖了另一笔交易的更新结果transactions,解决上面的updateloss有以下三种方式:使用Serializable隔离级别,事务串行执行!在select语句后加forupdate相当于加了排它锁(写锁)。加写锁后,其他事务无法修改!需要等待当前事务被修改后才能修改。即如果在操作1中使用select...forupdate,则在操作2中不能修改记录,以免丢失更新。乐观锁乐观锁不是数据库级别的锁,需要用户手动添加。一般我们在数据库表中添加一个version字段version来实现。例如操作1和操作2更新User表时,执行语句如下:updateAsetName=lisi,version=version+1whereID=#{id}andversion=#{version},atthis时间,可以避免更新丢失。GaplockGAP当我们取范围条件而不是相等条件的数据,并请求共享锁或排它锁时,InnoDB会锁定现有数据记录中满足范围条件的索引项;对于条件范围内的键值但不存在的记录称为“间隙(GAP)”。InnoDB也会锁住这个“缺口”。这种锁定机制就是所谓的间隙锁。例:如果emp表只有101条记录,则empid值为1,2,...,100,101select*frommempwhereempid>100forupdate;上面是范围查询,InnoDB不仅会匹配符合条件的empid值为101的记录被锁定,empid大于101的“gap”(这些记录不存在)也会被锁定。InnoDB使用间隙锁有两个目的:防止幻读(上面也提到了Repeatablereadisolationlevel然后通过GAP锁可以避免幻读)满足恢复和复制的需要:MySQL的恢复机制要求在事务提交之前,其他并发事务不能插入任何满足其加锁条件的记录,即不允许出现幻读死锁,并发问题中死锁是必不可少的,MySQL中也存在死锁问题。锁总结其实我们的程序员很少关心它:在MyISAM存储引擎中,它是在执行SQL语句时自动加上的。在InnoDB存储引擎中,如果没有使用索引,会自动加表锁。现在我们大多数使用MySQL都是使用InnoDB,而InnoDB支持行锁:共享锁--读锁--S锁排他锁--写锁--X锁默认情况下select不加任何行锁~transactions可以通过以下语句向记录集添加共享锁或排他锁来显示。共享锁(S):SELECT*FROMtable_nameWHERE...LOCKINSHAREMODE独占锁(X):SELECT*FROMtable_nameWHERE...FORUPDATEInnoDB也实现了基于行锁的MVCC多版本并发控制。MVCC处于隔离级别Work下的Readcommitted和Repeatableread。MVCC实现无阻塞读写