Mysql面试全解(一)

85 阅读4分钟

「这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战

首先本文具有的标签性质是:锁、间隙锁、乐观锁、悲观锁、事务、隔离级别、MVCC、索引、B+树、explain关键字、主从复制等。

建议从上往下一点一点看,知识点之间都是相互关联的,顺序看有助于理解。

锁机制

按照锁范围分类

全局锁

一般只有在进行库的逻辑备份的时候才会使用

表级锁

表锁

表级锁分为读锁和写锁。使用关键字lock tables t1 read/write加锁,使用unlock tables解锁(无需指定表)。或者在该线程的链接断开之后自动释放锁

锁定的表的操作其他线程就无法进行了

MDL锁

MDL锁也是一个在表上操作的锁

在进行表字段的增删改查的时候,上读锁,在事务结束的时候释放

在进行表结构变更/索引变更的时候,上写锁,在事务结束的时候释放

只要有写锁的存在,其他的线程的操作都是会阻塞的。也就是说在修改表结构,创建表索引的时候,会影响其他线程的一切操作。

行级锁

行锁是由各自的引擎实现的,例如InnoDB有行锁,MyIsam没有行锁。

在进行数据变更的时候上锁,在事务结束的时候自动释放锁

多个线程对同一行进行修改就会导致需要等待一个线程的锁释放(事务结束)才能进行修改

死锁

行锁很容易造成死锁

例如

  1. 线程1修改了id=1的数据,上锁
  2. 线程2修改了id=2的数据,上锁
  3. 线程1想要修改id=2的数据,但是在锁中,所以等待线程2的释放
  4. 此时线程2又想要对id=1的数据修改,所以等待线程1的释放
  5. 此时就造成了死锁(你俩搁着闹呢)

解决方案:

  1. 等待其中一个线程的链接请求超时时间过了,就会自动断开链接从而自动释放锁

  2. 发起死锁检测,发现死锁后,主动回滚死锁链条中的某一个事务,让其他事务得以继续执行。将参数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关键字,在当前事务结束之后自动释放锁。 别的线程在事务结束之前进行该条记录的变更都会进入阻塞状态,直到锁的释放