我是
方圆
,愿你我皆能在面试中游刃有余
1. MyISAM和InnoDB锁的区别
MyISAM
是表级锁,不支持行级锁InnoDB
是默认是行级锁,也支持表级锁
InnoDB在
不走索引的时候
,用的是表级锁,行级锁的开销更大一些,所以MyISAM查询效率要高于InnoDB
2. MyISAM和InnoDB的适用场景
MyISAM
适用于频繁的执行查询操作;增删改较少;不需要执行事务;频繁的执行全表count语句(SELECT COUNT(*) FROM table)
MyISAM引擎采用的是
非聚集索引
,页中保存的不是数据,而是对应数据的地址
,并且数据和索引分别保存在不同的文件中(.MYD文件和.MYI文件),查询行数时,不需要遍历全表,而是将行数保存为一个变量,查询起来效率较高。
InnoDB
适用于更多的增删改操作;需要执行事务
InnoDB引擎采用的是
聚集索引
,而且在B+Tree的叶子节点中保存的始数据。所有数据和索引保存在一个文件中(.form),查询具体的行数需要遍历整个表,而没有将其保存为一个变量。
3. 用代码实现乐观锁
乐观锁大家都很熟悉了,我们用SQL简单实现一下
我们创建一个简单的表,其中只有一行数据,初始对应的版本号为1
时间 | 线程1 | 线程2 |
---|---|---|
Time1 | SELECT VERSION FROM positive WHERE id = 1 #发现此时的version为1 |
SELECT VERSION FROM positive WHERE id = 1 #发现此时的version也为1 |
Time2 | UPDATEpositive SET name = “c” ,version = version + 1 WHERE id = 1 AND version = 1 #修改数据,版本数+1 |
|
Time3 | UPDATEpositive SET name = “b” ,version = 1 WHERE id = 1 AND version = 0 #此时就会执行失败,版本不匹配 |
如下是我们最后执行的结果,没有如线程1一样,将name修改为c,这样我们就实现了乐观锁
4. ACID原则
原子性:
事务中的所有命令,要么全部执行成功,要么执行全部回滚。一致性:
数据库中所有的数据都会保证完整性,比如一共有200元,在不同线程的存取过程中,总和不会改变隔离性:
多个线程中的事务不会互相干扰持久性:
数据一旦修改被提交,就会持久化到数据库中
5. 事务隔离级别产生的问题和解决办法
脏读,读到了别人未提交的数据
我们先需要对事务的隔离级别进行修改
#查看隔离级别,默认为REPEATABLE-READ(RR)
SELECT @@tx_isolation
#我们需要将隔离级别修改为READ UNCOMMITTED
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
我们创建表如下
执行操作如下
扫描二维码关注公众号,回复:
11217556 查看本文章
时间 | 线程1 | 线程2 |
---|---|---|
Time1 开启线程 | START TRANSACTION | START TRANSACTION |
Time2 | UPDATE test SET balance = 900 WHERE id = 1 修改1000为900 | |
Time3 | 此时事务还没有提交,线程2便读取到了修改后的值 | SELECT * FROM test WHERE id = 1 #此时值为900,发生脏读 |
Time4 | ROLLBACK 事务回滚 | |
Time5 | COMMIT |
解决办法,修改隔离级别为read committed(RC)
set session transaction isolation level read committed
不可重复读
注:此时的隔离级别为
read committed
,不可重复读
,是事务1读取了一个数据,事务2也读取了同一个数据,并且对该数据进行了修改、提交,此时事务1再读取这个数据,就会发生两次读取数据不一致的情况,这就是不可重复读。
时间 | 线程1 | 线程2 |
---|---|---|
Time1 开启线程 | START TRANSACTION | START TRANSACTION |
Time2 | select * from test where id = 2 #查询出余额输出为500; | |
Time3 | update test set balance = balance + 300 where id = 2;commit;#将值修改为800 | |
Time4 | select * from test where id = 2 #查询出余额输出为800,两次查询结果不同,发生不可重复读 |
解决办法,我们需要将隔离级别修改为repeatbale read(RR)
set session transaction isolation level repeatbale read
幻读
幻读
,是针对于插入和删除数据产生的
,事务1在对数据进行读取的时候,事务2插入了1行数据并提交,此时事务1会读取到新增的数据,发生幻觉,称为幻读。此时的隔离级别为repeatbale read
时间 | 线程1 | 线程2 |
---|---|---|
Time1 开启线程 | START TRANSACTION | START TRANSACTION |
Time2 | select count(*) from test where id <=5; #此时行数为3 | |
Time3 | insert into account(id,name,balance) values(“4”,“d”,“0”) ;commit;#添加一行 | |
Time4 | select count(*) from account where id <=10;#这时行数为4,发生幻读 |
解决办法,我们需要将隔离级别修改为serializable即可解决
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE
表格总结
隔离级别→ | read uncommitted | read committed | repeatliable | serializable |
---|---|---|---|---|
脏读 | √ | × | × | × |
不可重复读 | √ | √ | × | × |
幻读 | √ | √ | √ | × |