持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第4天,点击查看活动详情
1、锁的作用
锁是计算机在执行多线程或线程时用于并发访问同一共享资源时的同步机制,MySQL中的锁是在服务器层或者存储引擎层实现的,保证了数据访问的一致性与有效性。
2、锁的分类与比较
按照锁的粒度从小到大:行锁,页锁,表锁
| 开销 | 死锁 | 粒度 | 锁冲突概率 | 并发度 | 加锁速度 | |
|---|---|---|---|---|---|---|
| 表锁 | 小 | 无 | 大 | 高 | 低 | 快 |
| 行所 | 大 | 有 | 小 | 低 | 高 | 慢 |
| 页锁 | 中 | 有 | 中 | 中 | 中 | 中 |
总结:行锁相比于表锁并发度高,发生锁冲突的概率低,所以适用于有大量按索引条件并发更新少量不同数据,同时又有 并发查询的应用,如一些在线事务处理(OLTP)系统;行所开销小,加锁快,不会出现死锁,加锁速度快,所以更适合以查询为主,只有少量按索引条件更新数据的应用,如web应用。MyISAM和Memory支持表锁;InnoDB既支持行锁又支持表锁,默认情况下采用行锁
按照锁的级别:共享锁,排他锁,意向锁
共享锁:所有事务都能对锁定表进行读操作,只是不可写
排他锁:锁定表只能被当前事务操作,而且当前事务只能操作锁定表不能操作非锁定表
意向锁:分为意向共享锁和意向排他锁,主要是为了事务对表进行锁请求的时候通过意向锁判断是否会存在锁冲突
3、两阶段锁
两阶段锁是数据库事务处理时的并发控制方法,以保证可串行化(串行化很重要,尤其是在数据恢复和备份的时候),但是可能会导致死锁/select S锁、select以外的写操作加X锁
扩张阶段:不断上锁,没有锁被释放/commit
收缩阶段:锁被陆续释放,没有新的加锁
4、行锁的三种加锁算法
主要是为了解决幻读
Record Lock、Gap Lock、Next-Key Lock
Record Lock:行锁,该锁是对索引记录加锁而不是行上。Innodb一定存在聚簇索引,因此行锁最终落在聚簇索引上
Gap Lock:间隙所,对索引的间隙枷锁,主要是为了防止其他事务插入数据。只有可重复读和可串行化才有间隙锁
Next-Key Lock:Record Lock+Gap Lock。前开后闭合。
5、加锁情况
针对不同的搜索引擎,不同隔离级别,针对不同的语句的加锁是完全不一样的,本文对此进行了详细的介绍
MyISAM
MyISAM只有表锁没有行锁。
表锁分为两类,表共享读锁和表共享写锁
表共享读锁:阻塞写
| 当前session | 其他session | |
|---|---|---|
| 锁定表 | 可读不可写 | 可读不可写 |
| 非锁定表 | 不可读不可写 | 可读可写 |
表排他写锁:阻塞读写
| 当前session | 其他session | |
|---|---|---|
| 锁定表 | 可读可写 | 不可读不可写 |
| 非锁定表 | 不可读不可写 | 可读可写 |
一般来说MyISAM在执行查询语句的时候自动加读锁,执行更新语句的时候自动加写锁,用户不需要使用命令人为加锁。MyISAM读写锁调度是写优先,这也是MyISAM不适合做写为主表的引擎,因为写锁以后,其他线程不能做任何操作,大量的更新使查询很难得到锁,从而造成永久阻塞
InnoDB
| 条件列非索引 | 条件列聚簇索引 | 条件列非聚簇索引(非唯一索引) | |
|---|---|---|---|
| RR/Serializable | 对全表的聚簇索引加锁,加间隙锁 | 精确索引,只在聚簇索引加锁;非精确索引聚簇索引加锁且加间隙锁 | 聚簇索引,非聚簇索引加锁,且加间隙锁 |
| RC/RU | 根据条件给聚簇索引加行锁 | 根据条件给聚簇索引加行锁 | 根据条件给聚簇索引,非聚簇索引加锁 |
补充:条件列非索引 Serializable 全表的聚簇索引加锁,和间隙锁
条件列非聚簇索引,如果是唯一索引, RR/Serizable枷锁情况和条件列为聚簇索引类似
在RC/RU中,为什么条件列加不加索引,加锁情况是一样的?
其实是不一样的。在RC/RU隔离级别中,MySQL Server做了优化。在条件列没有索引的情况下,尽管通过聚簇索引来扫描全表,进行全表加锁。但是,MySQL Server层会进行过滤并把不符合条件的锁当即释放掉,因此你看起来最终结果是一样的。但是RC/RU+条件列非索引比本例多了一个释放不符合条件的锁的过程!