数据库两大必备神器:索引和锁底层原理是什么( 四 )


  • select * from xxxx for update
在select 语句后边加了 for update相当于加了排它锁(写锁),加了写锁以后,其他的事务就不能对它修改了!需要等待当前事务修改完之后才可以修改.
  • 也就是说,如果张三使用select ... for update,李四就无法对该条记录修改了~
3.2、乐观锁
乐观锁不是数据库层面上的锁,是需要自己手动去加的锁 。一般我们添加一个版本字段来实现:
具体过程是这样的:
张三select * from table --->会查询出记录出来,同时会有一个version字段
数据库两大必备神器:索引和锁底层原理是什么

文章插图
 
 
李四select * from table --->会查询出记录出来,同时会有一个version字段
数据库两大必备神器:索引和锁底层原理是什么

文章插图
 
 
李四对这条记录做修改:update A set Name=lisi,version=version+1 where ID=#{id} and version=#{version},判断之前查询到的version与现在的数据的version进行比较,同时会更新version字段
此时数据库记录如下:
数据库两大必备神器:索引和锁底层原理是什么

文章插图
 
 
张三也对这条记录修改:update A set Name=lisi,version=version+1 where ID=#{id} and version=#{version},但失败了!因为当前数据库中的版本跟查询出来的版本不一致!
数据库两大必备神器:索引和锁底层原理是什么

文章插图
 
 
4、间隙锁GAP
当我们用范围条件检索数据而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合范围条件的已有数据记录的索引项加锁;对于键值在条件范围内但并不存在的记录,叫做“间隙(GAP)” 。InnoDB也会对这个“间隙”加锁,这种锁机制就是所谓的间隙锁 。
值得注意的是:间隙锁只会在Repeatable read隔离级别下使用~
例子:假如emp表中只有101条记录,其empid的值分别是1,2,...,100,101
Select * from emp where empid > 100 for update;
上面是一个范围查询,InnoDB不仅会对符合条件的empid值为101的记录加锁,也会对empid大于101(这些记录并不存在)的“间隙”加锁 。
InnoDB使用间隙锁的目的有两个:
  • 为了防止幻读(上面也说了,Repeatable read隔离级别下再通过GAP锁即可避免了幻读)
  • 满足恢复和复制的需要MySQL的恢复机制要求:在一个事务未提交前,其他并发事务不能插入满足其锁定条件的任何记录,也就是不允许出现幻读
5、死锁
并发的问题就少不了死锁,在MySQL中同样会存在死锁的问题 。
但一般来说MySQL通过回滚帮我们解决了不少死锁的问题了,但死锁是无法完全避免的,可以通过以下的经验参考,来尽可能少遇到死锁:
  • 1)以固定的顺序访问表和行 。比如对两个job批量更新的情形,简单方法是对id列表先排序,后执行,这样就避免了交叉等待锁的情形;将两个事务的sql顺序调整为一致,也能避免死锁 。
  • 2)大事务拆小 。大事务更倾向于死锁,如果业务允许,将大事务拆小 。
  • 3)在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁概率 。
  • 4)降低隔离级别 。如果业务允许,将隔离级别调低也是较好的选择,比如将隔离级别从RR调整为RC,可以避免掉很多因为gap锁造成的死锁 。
  • 5)为表添加合理的索引 。可以看到如果不走索引将会为表的每一行记录添加上锁,死锁的概率大大增大 。
6、锁总结
上面说了一大堆关于MySQL数据库锁的东西,现在来简单总结一下 。
表锁其实我们程序员是很少关心它的:
  • 在MyISAM存储引擎中,当执行SQL语句的时候是自动加的 。
  • 在InnoDB存储引擎中,如果没有使用索引,表锁也是自动加的 。
现在我们大多数使用MySQL都是使用InnoDB,InnoDB支持行锁:
  • 共享锁--读锁--S锁
  • 排它锁--写锁--X锁
在默认的情况下,select是不加任何行锁的~事务可以通过以下语句显示给记录集加共享锁或排他锁 。
  • 共享锁(S):SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE 。
  • 排他锁(X):SELECT * FROM table_name WHERE ... FOR UPDATE 。
InnoDB基于行锁还实现了MVCC多版本并发控制,MVCC在隔离级别下的Read committed和Repeatable read下工作 。MVCC能够实现读写不阻塞!
InnoDB实现的Repeatable read隔离级别配合GAP间隙锁已经避免了幻读!


推荐阅读