很多人在开发的时候应该很少注意到这些锁的问题,也很少在程序中加锁(除了inventory这种对数量精度要求极高的情况),即使我们不知道了这些锁,我们的程序在正常情况下还是可以很好运行的。因为数据库隐式的为我们加了这些锁,我们只需要在某些特定的场景下手动加锁即可。对于UPDATE、DELETE和INSERT语句,InnoDB会自动为涉及的数据集添加排他锁(X)。而MyISAM会在执行查询语句SELECT前自动对涉及的所有表加读锁,在执行增删改操作前自动对涉及的表加写锁。这个过程不需要我们手动操作。那么在某些情况下,我们如何锁定呢?让我们仔细看看。看上图我们可以看出MySQL锁按照使用方式可以分为乐观锁和悲观锁。按照粒度可以分为表级锁、行级锁、页级锁。表锁从锁的粒度上可以分为两类:表锁:开销小,加锁速度快;没有死锁;强锁,锁冲突概率高,并发度最低。行锁:开销大,加锁慢;可能会出现死锁;锁粒度小,锁冲突概率低,并发度高不同的存储引擎支持不同的锁粒度。InnoDB行锁和表锁都支持,MyISAM只支持表锁!InnoDB在通过索引条件检索数据时只使用行级锁。否则,InnoDB使用表锁。也就是说,InnoDB的行锁是基于索引的!表锁分为两种模式:TableReadLock&&TableWriteLock从下图可以清楚的看出,在表读锁和表写锁的环境下:读写锁阻塞,读写阻塞,写和写作阻塞!读写非阻塞:当前用户正在读取数据,其他用户也在读取数据,不会被锁定读写阻塞:当前用户正在读取数据,其他用户无法修改当前用户读取的数据,并且会被锁定!写写阻塞:当前用户正在修改数据,其他用户无法修改当前用户正在修改的数据,会被锁定!由上可见:读锁和写锁是互斥的,读写操作是串行的。如果一个进程想要获取一个读锁,另一个进程想要获取一个写锁。在mysql中,写锁优先于读锁!写锁和读锁的优先级可以通过参数调整:max_write_lock_count和low-priority-updates注:MyISAM支持并发查询和插入操作,也可以通过系统变量concurrent_insert指定使用哪种模式。MyISAM中的默认值:如果MyISAM表的中间没有删除的行,则MyISAM允许一个进程读取该表,而另一个进程从表的末尾插入记录。但不支持INNODB。InnoDB和MyISAM有两个本质区别:InnoDB支持行锁,InnoDB支持事务。InnoDB实现了以下两种类型的行锁:共享锁(S锁,读锁):允许一个事务读取一行,防止其他事务获取同一数据集上的排它锁。即多个客户端可以同时读取同一个资源,但不允许其他客户端对其进行修改。独占锁(X锁、写锁):允许获取独占锁的事务更新数据,防止其他事务获取同一数据集的读写锁。写锁是排他的,写锁会阻塞其他的写锁和读锁。另外,为了让行锁和表锁共存,实现多粒度的锁机制,InnoDB内部还有两种意向锁(IntentionLocks),都是表锁:IntentionSharedLock(IS):Transaction意在给数据行加行共享锁,事务在给数据行加共享锁之前必须先获取表的IS锁。意向排他锁(IX):事务打算给数据行加排他锁,事务在给数据行加排他锁之前必须先获得表的IX锁。意向锁也是数据库隐式帮我们做的,程序员不用关心!MVCC行级锁MVCC(Multi-VersionConcurrencyControl)多版本并发控制可以简单的认为:MVCC是行级锁的变种(升级版)。在表锁中,我们的读写都被阻塞了。基于提高并发性能的考虑,MVCC一般不会阻塞读写(很多情况下会避免加锁操作)。可以简单理解为:对数据库的任何修改的提交都不会直接覆盖之前的数据,而是会生成一个与旧版本共存的新版本,这样就可以完全不用加锁读取了。事务隔离级别事务隔离级别是通过锁机制实现的。锁的应用最终导致不同事务的隔离级别,但是加锁的细节是隐藏的。事务隔离级别有4种:未提交读:会出现脏读、不可重复读、幻读已提交读:会出现不可重复读、幻读可重复读:会出现幻读(Mysql默认隔离级别,但是repeatablereadwithgaplock不会出现幻读!)Serializable:串行的,避免以上情况。读未提交:现象->脏读:一个事务读取了另一个事务未提交的数据。例子:A给B转钱,A执行了转账语句,但是A还没有提交交易,B读取数据发现你账户里的钱增加了!B告诉A我收到了钱。A回滚交易[rollback],当B再次查看账户里的钱时,发现钱不多了...hascommitted,也就是说一个事务可以看到其他事务所做的修改。例如:A查询数据库获取数据,B修改数据库中的数据,导致A多次查询数据库结果不同【危害:A每次查询的结果都受到B的影响,所以信息A查询无意义】可重复读:避免不可重复读是事务级的快照!每次读取当前交易版本,即使修改,也只会读取当前交易版本的数据。幻读(phantomreading):指在一个事务中读取其他事务插入的数据,导致前后读取不一致。类似于不可重复读,但是幻读(phantomreads)会读取其他事务插入的数据,导致前后读取不一致。幻读的重点是增加或删除(数据项的数量发生变化),而不可重复读的重点是修改。乐观锁和悲观锁,无论是Readcommitted还是Repeatableread隔离级别,都是为了解决读写冲突问题而设计的。现在考虑一个问题:有一个只有id和name字段的数据库表USER,现在有2个请求同时操作表A,过程如下:(模拟更新丢失,虽然不是很适当)操作1查询name="zhangsan"操作2alsoqueryname="zhangsan"操作1将name字段数据修改为lisi并提交操作2将name字段数据修改为wangwu并提交,则操作1的更新丢失,即一个事务的更新覆盖了其他事务的更新结果。解决上面更新丢失的方法有3种:使用Serializable隔离级别,事务串行实现!乐观锁悲观锁悲观锁如果我们使用悲观锁,其实很简单(手动加行锁即可):select*fromxxxxforupdate,在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*fromempwhereempid>100forupdate;以上是范围查询,InnoDB不仅会查询符合条件的empid值为101的记录被锁定,empid大于101的“gap”(这些记录不存在)也会被锁定。InnoDB使用间隙锁有两个目的:防止幻读(上面也提到,Repeatableread在隔离级别下,GAP锁可以避免幻读)满足恢复和复制的需要:MySQL的恢复机制要求在一个事务被执行之前已提交,其他并发事务不能插入任何满足其锁定条件的记录,即不允许幻读死锁发生1.所谓死锁的成因:是指两个或多个进程各自等待其他由于执行期间对资源的竞争。如果没有外力,他们将无法推进。这时候就说系统处于死锁状态或者系统已经发生了死锁。这些始终相互等待的进程称为死锁进程。表级锁不会造成死锁。所以死锁的解决方法主要针对最常用的InnoDB。死锁的关键是两个(或多个)Session加锁的顺序不一致。那么对应的解决死锁问题的关键就是:让不同的session依次加锁2.生成examplecase需求:将投资资金分成几份,随机分配给借款人。起初,业务程序的思路是这样的:投资人投资后,金额随机分成几份,然后从借款人表中随机抽取几份,然后借款人表中的余额为通过selectforupdate一一更新。例如:两个用户同时投资,用户A的金额随机分成2份分配给借款人1,用户2B的金额随机分成2份分配给借款人2和1。由于不同的加锁顺序,死锁当然很快就上来了。这个问题的改进很简单,一次性锁定所有分配的借款人即可。select*fromxxxwhereidin(xx,xx,xx)forupdatemysql中的列表值会自动从小到大排序,锁也是从小到大一个一个加。表锁总结其实我们程序员很少关心它:在MyISAM存储引擎中,它是在执行SQL语句时自动加上的。在InnoDB存储引擎中,如果没有使用索引,会自动加表锁。现在我们大多数人使用MySQL都是为了使用InnoDB。InnoDB支持行锁:共享锁->读锁->S锁排它锁->写锁->X锁。默认情况下,select不加任何行锁,事务可以通过下面的语句对记录集显示共享锁或排它锁。共享锁(S):SELECT*FROMtable_nameWHERE...LOCKINSHAREMODE独占锁(X):SELECT*FROMtable_nameWHERE...FORUPDATEInnoDB也实现了基于行锁的MVCC多版本并发控制。MVCC处于隔离级别Work下的Readcommitted和Repeatableread。MVCC实现了读写不阻塞。更多学习内容,可访问【与各大厂商对比】优质PHP架构师教程目录。只要能读懂,就能保证你的薪水更上一层楼(持续更新中)。以上内容希望对大家有所帮助。很多PHPer总会遇到一些问题和瓶颈,业务代码写多了没有方向感,不知道从哪里开始改进,我整理了一些这方面的资料,包括但不限于:分布式架构,高可扩展性、高性能、高并发、服务器性能调优、TP6、laravel、YII2、Redis、Swoole、Swoft、Kafka、Mysql优化、shell脚本、Docker、微服务、Nginx等知识点免费分享给你如果你需要高级干货。有需要的可以点击链接领取高级PHP月薪30k>>>架构师成长之路【免费获取视频和面试资料】
