「这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战」
首先本文具有的标签性质是:锁、间隙锁、乐观锁、悲观锁、事务、隔离级别、MVCC、索引、B+树、explain关键字、主从复制等。
建议从上往下一点一点看,知识点之间都是相互关联的,顺序看有助于理解。
锁机制
按照锁范围分类
全局锁
一般只有在进行库的逻辑备份的时候才会使用
表级锁
表锁
表级锁分为读锁和写锁。使用关键字lock tables t1 read/write加锁,使用unlock tables解锁(无需指定表)。或者在该线程的链接断开之后自动释放锁
锁定的表的操作其他线程就无法进行了
MDL锁
MDL锁也是一个在表上操作的锁
在进行表字段的增删改查的时候,上读锁,在事务结束的时候释放
在进行表结构变更/索引变更的时候,上写锁,在事务结束的时候释放
只要有写锁的存在,其他的线程的操作都是会阻塞的。也就是说在修改表结构,创建表索引的时候,会影响其他线程的一切操作。
行级锁
行锁是由各自的引擎实现的,例如InnoDB有行锁,MyIsam没有行锁。
在进行数据变更的时候上锁,在事务结束的时候自动释放锁
多个线程对同一行进行修改就会导致需要等待一个线程的锁释放(事务结束)才能进行修改
死锁
行锁很容易造成死锁
例如
- 线程1修改了id=1的数据,上锁
- 线程2修改了id=2的数据,上锁
- 线程1想要修改id=2的数据,但是在锁中,所以等待线程2的释放
- 此时线程2又想要对id=1的数据修改,所以等待线程1的释放
- 此时就造成了死锁(你俩搁着闹呢)
解决方案:
-
等待其中一个线程的链接请求超时时间过了,就会自动断开链接从而自动释放锁
-
发起死锁检测,发现死锁后,主动回滚死锁链条中的某一个事务,让其他事务得以继续执行。将参数innodb_deadlock_detect设置为on,表示开启这个逻辑(默认开启)
问题:就是每当一条数据被锁住的时候,就要检测下别的依赖这条数据的线程是否存在被锁住的情况。对性能有很高的损耗
如果对业务清晰不会出现类似上述的死锁情况,可以将参数改为off从而提升性能
间隙锁
一个很特殊的存在,是为了解决幻读问题(即,我以为我修改了全部的十条数据,其实在我修改期间又新增了第十一条数据)
例如:update t1 set record="蛋炒饭" where num<10;
虽然num<10的只有三条数据(num=2,num=5,num=7),但是在这个期间如果新增了数据就会造成幻读的情况。所以这个时候上锁的其实是
- num=2的记录行锁
- num=5的记录行锁
- num=7的记录行锁
- (-无穷大,2)(2,5)(5,7)(7,10)这四个范围的间隙锁
按照锁本质分类
| 乐观锁 | 悲观锁 | |
|---|---|---|
| 原理 | 认定不会出现数据冲突, 所以只是维护一个版本号, 在业务处理结束的时候验证下版本号是否正确(即该业务处理期间有没有数据变更) | 认定业务处理期间一定会发生数数据变更冲突 在业务处理之前先获取到锁 要求别的线程必须等待锁的释放才能继续该逻辑 |
| 实现方式 | 业务实现 1. 获取该条数据的版本号 2. 业务操作 3. 业务处理完之后判断该条数据的版本号是否变更,如果变更就进行回滚等操作 | Mysql实现,在进行select操作的时候加上for update关键字,在当前事务结束之后自动释放锁。 别的线程在事务结束之前进行该条记录的变更都会进入阻塞状态,直到锁的释放 |