[db:标题]

摘要:共享锁、排他锁、意向锁、记录锁、间隙锁、临键锁(Next Key Lock)、插入意向锁、AUTO-INC、悲观锁、乐观锁
MySQL的InnoDB的锁机制 MySQL的InnoDB引擎下,在锁的级别上一般分为两种:共享锁(S锁)、排他锁(X锁) 共享锁 共享锁又称为读锁,是读取操作时创建的锁。其他用户可以并发读取数据,但是一旦某行被加上共享锁,其他事务仍可继续对该行加共享锁;但任何事务若想对该行加排他锁(即执行 UPDATE/DELETE 等),则必须等待所有共享锁释放。 例如:事务T1对数据A的加上共享锁,其他事务只能对数据A加共享锁,不能加排他锁。而共享锁的事务只能读取数据,不能修改数据。 共享锁的加锁方式如下: -- MySQL8.0之前的推荐写法 SELECT ID,NAME FROM TABLE1 WHERE ID >1 AND ID <10 LOCK IN SHARE MODE; -- MySQL8.0以及之后的推荐写法 SELECT ID,NAME FROM TABLE1 WHERE ID >1 AND ID <10 FOR SHARE; 在查询语句后面增加LOCK IN SHARE MODE,会对查询范围中的每行都加共享锁,这样的数据行还可以被其他事务成功申请共享锁,但是不能被申请排他锁。 排他锁 排他锁又称写锁,若事务T1对数据A加上排他锁之后,其他事务则不能再对数据A加任何类型的锁。而且排他锁的事务既可以读数据又可以写数据。 排他锁的加锁方式: SELECT ID,NAME FROM TABLE1 WHERE ID >1 AND ID <10 FOR UPDATE; 在查询语句后面增加FOR UPDATE,会对查询命中的每条记录都加排他锁,当没有其他线程对查询结果集中的任何一行使用排他锁时,可以成功申请排他锁,否则会被阻塞。 共享锁和排他锁的总结 当一行数据获取了排他锁,那么其他事务就不能再对这一行数据添加共享锁或者排他锁。 当一行数据获取了共享锁,那么其他事务依然可以对这一行数据添加共享锁,但不能添加排他锁。 使用场景 共享锁 读-读并发高且不允许“脏读”:例如报表统计时,希望别的事务可以并发读,但禁止任何事务修改这些行。 父子表一致性校验:先对父表主键 FOR SHARE,再读子表,防止父记录在此期间被删。 排他锁 读-改-写(Read-Modify-Write): SELECT balance FROM account WHERE id = 1 FOR UPDATE; UPDATE account SET balance = balance - 100 WHERE id = 1; 防止并发扣款出现负余额。 悲观锁实现“秒杀库存”:先 FOR UPDATE 检查库存 > 0,再 UPDATE 减库存。 -- 加排他锁并读取当前库存 SELECT stock FROM merchandise WHERE id = 123 FOR UPDATE; -- 在应用层判断返回的 stock 值,若 stock > 0,则继续;否则回滚并返回“已售罄” -- 真正扣库存(MySQL 8 支持原子写法,也可拆两步) UPDATE merchandise SET stock = stock - 1 WHERE id = 123 AND stock > 0; 意向锁 除了S锁(共享锁)和X锁(排他锁)之外,InnoDB还有两种锁,就是IS锁和IX锁,S和X前面的I是Intention的意思,即意向锁,IS就是意向共享锁,IX就是意向排他锁。 在MySQL的InnoDB引擎中,根据锁的不同范围也是有区分的,例如:表级锁、间隙锁、行级锁等。当多个事务同时访问同一个数据时,多个事务同时申请获取锁,那么就有可能导致互相阻塞甚至产生死锁。 例如: 事务T1对表Table1中的一行加上了行级锁,此时这行记录就不能被其他事务写了。 事务T2申请对Table1增加了表级锁,若申请成功了,那么就可以修改表中的任意一行记录。 这就跟事务T1发生了冲突。 那么,想要解决这个问题,就需要让事务T2在对Table1增加表级锁的时候,先判断一下是不是有事务增加过行级锁。但是,事务T2总不能逐条判断是否有加锁吧? 因此,为了解决这个问题,MySQL引入了意向锁机制,意向锁是数据库管理系统中用于实现锁协议的一种锁机制,主要是用来处理不同粒度锁(如行锁和表锁)之间的并发性问题(而同粒度的锁之间一般是通过互斥来解决并发的)。 意向锁不做锁定资源的操作,主要是通知的作用,防止在已经加锁的数据上设置不兼容的锁。 意向锁不是直接由用户请求的,而是MySQL管理的。 当一个事务想获取一个行级锁或表级锁时,MySQL会自动获取相应的表的意向锁。
阅读全文