mysql 锁

select ... lock in share mode //共享锁 select ... for update //排他锁

行锁的3种算法:

record lock:单个行记录上的锁

gap lock:锁定范围,不锁定记录本身,只在RR里使用

next-key lock:锁定范围,并锁定记录本身,假如有两个数据3,7,则对区间(-无穷,3],(3,7],(7,正无穷)进行上锁

Previous-Key Lock:和next-key相反,区间左闭右开

innodb对于行的查询使用next-key lock,当查询的索引含有唯一属性时,将next-key lock降级为record key

乐观锁,适合读多写少的情景

MVCC:update table set name = ‘NewValue‘, version = version + 1 where id = #{id} and version = #{version};

CAS:ABA问题,自旋开销大

悲观锁适合频繁写入的情景

快照读:简单的select操作,属于快照读,不加锁。 select * from table where ?;

当前读:特殊的读操作,插入/更新/删除操作,属于当前读,需要加锁。

select * from table where ? lock in share mode;

select * from table where ? for update;

insert into table values (…);

update table set ? where ?;

delete from table where ?;

所有以上的语句,都属于当前读,读取记录的最新版本。并且,读取之后,还需要保证其他并发事务不能修改当前记录,对读取记录加锁。其中,除了第一条语句,对读取记录加S锁 (共享锁)外,其他的操作,都加的是X锁 (排它锁)。

死锁:指两个或多个事务在同一资源上相互占用,并请求锁定对方占用的资源,从而导致恶性循环的现象

解决死锁:

超时,回滚某一事务,另一事务就能得到执行,缺点是回滚的代价可能很高

wait-for graph(等待图),深度优先算法判断是否存在回路

  • 1)以固定的顺序访问表和行。比如对两个job批量更新的情形,简单方法是对id列表先排序,后执行,这样就避免了交叉等待锁的情形;将两个事务的sql顺序调整为一致,也能避免死锁。
  • 2)大事务拆小。大事务更倾向于死锁,如果业务允许,将大事务拆小。
  • 3)在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁概率。
  • 4)降低隔离级别。如果业务允许,将隔离级别调低也是较好的选择,比如将隔离级别从RR调整为RC,可以避免掉很多因为gap锁造成的死锁。
  • 5)为表添加合理的索引。可以看到如果不走索引将会为表的每一行记录添加上锁,死锁的概率大大增大。

隔离级别与锁的关系 待验证

Read Uncommitted级别下,读取数据不需要加共享锁,这样就不会跟被修改的数据上的排他锁冲突

Read Committed级别下,读操作需要加共享锁,但是在语句执行完以后释放共享锁;

Repeatable Read级别下,读操作需要加共享锁,但是在事务提交之前并不释放共享锁,也就是必须等待事务执行完毕以后才释放共享锁。

SERIALIZABLE 是限制性最强的隔离级别,因为该级别锁定整个范围的键,并一直持有锁,直到事务完成。

 mysql  锁

可通过 show engine innodb status 以及 information_schema 库下的innodb_trx、innodb_locks、innodb_lock_waits 观察 lock 的信息。

只有通过索引进行检索时,才会使用行锁,否则使用表级锁

相关推荐