mysql-面试题
1.事务隔离级别
ACID
原子性:undo log
一致性:最核心本质的要求
隔离性:锁+mvcc
持久性:redo log
读未提交
读已提交:不可重复读,前后2次读取的数据有变化
可重复读:两次快照读,读到的数据一样。会有幻读现象:如果读一个区间,发现多出一条数据。其实mysql没有幻读了,如果幻读要求2次读都是快照读的话。
序列化:所有事务依次执行。
2.MVCC
数据库读分2种,当前读和快照读。mvcc多版本并发控制是指维持一个数据多个版本,使得读写不冲突。主要依赖表的几个隐藏字段(DB_TRX_ID, DB_ROLL,DB_ROW_ID),undolog(记录多版本),readview(快照)。
RR隔离级别,事务第一个快照读生成readview视图,后续所有的快照读都复用这个视图。如果第二个是当前读,会更新操作行的readview视图
RC隔离级别,事务中每一个快照读都生成一个最新视图,可以读到别人提交的内容。
3.mysql的幻读是如何解决
事务1执行快照读age=20的记录发现2条,事务2执行插入age=20的记录1条提交。事务1更新age=20的记录,发现更新3条(当前读)。事务1查询age=20的记录发现3条。这就是mysql的幻读,其实这不是严格意义的幻读。如果要解决的话,快照读变当前读for update加间隙锁。
4.sql join原理
简单嵌套递归(两个表笛卡尔积匹配),索引驱动递归嵌套(被驱动表有索引,快速匹配),批量递归嵌套(一次拿一批数据匹配)
5.说一下索引原理
索引是一颗B+树,树的高度够低,另外支持范围查询(B树不支持范围查询)。分为聚簇索引和非聚簇索引,聚簇索引一般以自增主键(方便预读和插入的时候页不会分裂),非聚簇索引叶子节点存储的主键,需要回到聚簇索引查找具体数据。
索引常见不生效场景:组合索引左匹配原则,like操作,函数转换,or操作等。(思考一下索引结构,就知道是否能够走索引)
6. mysql主从同步原理
主写binlog 通过io thread同步到从,从写到relay log(顺序写),从的sql thread执行relay log(随机写)。由于sql thread是单线程,如果主库qps高,会导致sql thread执行不及时,导致主从延迟。 mysql 5.7增加了mts并行复制技术(为什么sql thread是单线程,因为有些sql是有先后执行限制。如果事务提交的时候,判断一组事务无先后限制,可以组提交事务。这样这组组提交的事务到从库也是可以并行执行的。组提交还可以解决主库bin log日志刷盘慢的问题)
7. 如何优化sql,执行计划有哪些关键参数
type:访问数据的方式,index(索引),range(范围查询),index_merge(组合索引),ref(非唯一索引进行数据查找),key:使用的索引。
8. sql执行过程
连接器-分析器-优化器-执行器,执行器判断sql类型,select和update会从磁盘读数据到内存,普通查询通过mvcc查询,update,insert等会写undolog(回滚),redo log和bin log(redo 和 bin会采取2阶段提交,保证数据最终一致性)
9. 说一下mysql的锁
当前读需要加锁(update,delete,insert,for update),先加临键锁,如果是唯一索引且是等值查询退化成行锁。如果是非唯一索引且等值查询,向右查到第一个大于等值查询的值,然后退化为间隙锁。原则是要锁住间隙和行。
10常见sql优化场景
- 规避回表,覆盖索引
- order by 规避fileSort,尽量用索引覆盖排序字段
- join动作,连接字段需要有索引,规避全量数据笛卡尔积,尽量用inner join,小表去驱动大表。
LEFT JOIN A表为驱动表
INNER JOIN MySQL会自动找出那个数据少的表作用驱动表
RIGHT JOIN B表为驱动表
- group by字段如果无索引会在临时表中计次,然后按照统计字段排序返回,如果不想排序 order by null。
- limit 10000,10,这样的语句可以优化成先查主键id
Select * from table limit 1000000, 10
Select * from table where id in (select pk from table limit 100000, 10)
- 子查询改写成临时表模式:
示例:select id from t1 where num in(select id from t2 where id > 10)
此时外层查询会全表扫描,不使用索引。可以修改为:
select id from t1,(select id from t1 where id > 10)t2 where t1.id = t2.id
- exist和in分场景使用
in是把外表和内表作hash连接,而exists是对外表作loop循环,每次loop循环再对内表进行查询,一直以来认为exists比in效率高的说法是不准确的。
如果查询的两个表大小相当,那么用in和exists差别不大;如果两个表中一个较小一个较大,则子查询表大的用exists,子查询表小的用in;
例如:表A(小表),表B(大表)
select * from A where cc in(select cc from B) -->效率低,用到了A表上cc列的索引;
select * from A where exists(select cc from B where cc=A.cc) -->效率高,用到了B表上cc列的索引。
相反的:
select * from B where cc in(select cc from A) -->效率高,用到了B表上cc列的索引
select * from B where exists(select cc from A where cc=B.cc) -->效率低,用到了A表上cc列的索引。
- 用union all 不用union,union会去重。