目标:MySQL 锁
分析:
详解:
第一部分 按锁粒度区分锁
1、MyISAM 表级锁
表锁下又分为两种模式: 表读锁(Table Read Lock)&& 表写锁(Table Write Lock)
| 读锁 | 写锁 | |
|---|---|---|
| 读锁 | 兼容 | 不兼容 |
| 写锁 | 不兼容 | 不兼容 |
- 写锁是优先于读锁的。
- 写锁和读锁优先级的问题是可以通过参数调节的:max_write_lock_count和low-priority-updates
注意:读写兼容
MyISAM支持查询与插入操作的并发进行,也可以通过系统变量concurrent_insert指定哪种模式。在MyISAM中默认:如果MyISAM表的中间没有被删除的行的话,那MyISAM是允许在一个进程读表的同时,另一个进程从表尾做插入记录的。但是INNODB是不支持的。
2、InnoDB 行级锁
InnoDB实现了以下两种类型的行锁:
- 共享锁(S锁、读锁): 允许事务读一行数据,阻止其他事务获得相同数据集的排他锁。即多个客户可以同时读取同一个资源,但不允许其他客户修改。
- 排他锁(X锁、写锁): ** 允许事务删除或更新一行数据允许获得排他锁的事务更新数据,阻止其他事务取得相同数据集的读锁和写锁。写锁是排他的,写锁会阻塞其他的写锁和读锁。 | | S | X | | --- | --- | --- | | S | 兼容 | 不兼容 | | X | 不兼容 | 不兼容 |
在默认的情况下,select是不加任何行锁的,事务可以通过以下语句显示给记录集加共享锁或排他锁。
-
共享锁(S): SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE -
排他锁(X): SELECT * FROM table_name WHERE ... FOR UPDATE
3、InnoDB 间隙锁
当我们用范围条件检索数据而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合范围条件的已有数据记录的索引项加锁;对于键值在条件范围内但并不存在 的记录,叫做“间隙(GAP)”。 InnoDB也会对这个“间隙”加锁,这种锁机制就是所谓的间隙锁。 例子:假如emp表中只有101条记录,其empid的值分别是1,2,...,100,101
Select * from emp where empid > 100 for update;
上面是一个范围查询,InnoDB不仅会对符合条件的empid值为101的记录加锁,也会对empid大于101(这些记录并不存在)的“间隙”加锁 InnoDB使用间隙锁的目的有2个:
- 为了防止幻读(上面也说了,Repeatable read隔离级别下再通过GAP锁即可避免了幻读)
- 满足恢复和复制的需要:MySQL的恢复机制要求在一个事务未提交前,其他并发事务不能插入满足其锁定条件的任何记录,也就是不允许出现幻读
第二部分 按锁使用方式区分锁
1、乐观锁
乐观锁不是数据库层面上的锁,需要用户手动去加的锁。一般我们在数据库表中添加一个版本字段version来实现,例如操作1和操作2在更新User表的时,执行语句如下:
update A set Name=lisi,version=version+1 where ID=#{id} and version=#{version},
此时即可避免更新丢失。
2、悲观锁
我们使用悲观锁的话其实很简单(手动加行锁就行了):select * from xxxx for update,在select 语句后边加了for update相当于加了排它锁(写锁),加了写锁以后,其他事务就不能对它修改了!需要等待当前事务修改完之后才可以修改.也就是说,如果操作1使用select ... for update,操作2就无法对该条记录修改了,即可避免更新丢失。