热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

MySQL高阶面试题

目录1.能说下myisam和innodb的区别吗?2.说下mysql的索引有哪些吧,聚簇和非聚簇索引又是什么?3.那你知道什么是覆盖

目录

1. 能说下myisam 和 innodb的区别吗?

2. 说下mysql的索引有哪些吧,聚簇和非聚簇索引又是什么?

3. 那你知道什么是覆盖索引和回表吗?

4. 锁的类型有哪些呢

5. 你能说下事务的基本特性和隔离级别吗?ACID

6. 那ACID靠什么保证的呢?

7. 那你说说什么是幻读,什么是MVCC(多版本并发控制:数据在某个时间节点的快照)?

8. 那你知道什么是间隙锁吗?

9. 你们数据量级多大?分库分表怎么做的?

10. 那分表后的ID怎么保证唯一性的呢?

11. 分表后非sharding_key的查询怎么处理呢?

12. 说说mysql主从同步怎么做的吧?

13. 那主从的延迟怎么解决呢?

14.我帅吗?



 


1. 能说下myisam 和 innodb的区别吗?



myisam引擎是5.1版本之前的默认引擎,支持全文检索、压缩、空间函数等,但是不支持事务和行级锁,所以一般用于有大量查询少量插入的场景来使用,而且myisam不支持外键,并且索引和数据是分开存储的。

innodb是基于聚簇索引建立的,和myisam相反它支持事务、外键,并且通过MVCC来支持高并发,索引和数据存储在一起。

 


2. 说下mysql的索引有哪些吧,聚簇和非聚簇索引又是什么?



索引按照数据结构来说主要包含B+树Hash索引

假设我们有张表,结构如下:

create table user(id int(11) not null,age int(11) not null,primary key(id),key(age)
);

B+树是左小右大的顺序存储结构,节点只包含id索引列,而叶子节点包含索引列和数据,这种数据和索引在一起存储的索引方式叫做聚簇索引,一张表只能有一个聚簇索引。假设没有定义主键,InnoDB会选择一个唯一的非空索引代替,如果没有的话则会隐式定义一个主键作为聚簇索引。

这是主键聚簇索引存储的结构,那么非聚簇索引的结构是什么样子呢?非聚簇索引(二级索引)保存的是主键id值,这一点和myisam保存的是数据地址是不同的。

最终,我们一张图看看InnoDB和Myisam聚簇和非聚簇索引的区别

 

 


3. 那你知道什么是覆盖索引和回表吗?



覆盖索引指的是在一次查询中,如果一个索引包含或者说覆盖所有需要查询的字段的值,我们就称之为覆盖索引,而不再需要回表查询

而要确定一个查询是否是覆盖索引,我们只需要explain sql语句看Extra的结果是否是“Using index”即可。

以上面的user表来举例,我们再增加一个name字段,然后做一些查询试试。

explain select * from user where age=1; //查询的name无法从索引数据获取
explain select id,age from user where age=1; //可以直接从索引获取

 


4. 锁的类型有哪些呢



mysql锁分为共享锁排他锁,也叫做读锁和写锁。


  1. 读锁是共享的,可以通过lock in share mode实现,这时候只能读不能写。
  2. 写锁是排他的,它会阻塞其他的写锁和读锁。从颗粒度来区分,可以分为表锁行锁两种。
  3. 表锁会锁定整张表并且阻塞其他用户对该表的所有读写操作,比如alter修改表结构的时候会锁表。
  4. 行锁又可以分为乐观锁悲观锁,悲观锁可以通过for update实现,乐观锁则通过版本号实现。

 


5. 你能说下事务的基本特性和隔离级别吗?ACID



事务基本特性ACID分别是:


  1. 原子性指的是一个事务中的操作要么全部成功,要么全部失败。
  2. 一致性指的是数据库总是从一个一致性的状态转换到另外一个一致性的状态。比如A转账给B100块钱,假设中间sql执行过程中系统崩溃A也不会损失100块,因为事务没有提交,修改也就不会保存到数据库。
  3. 隔离性指的是一个事务的修改在最终提交前,对其他事务是不可见的。
  4. 持久性指的是一旦事务提交,所做的修改就会永久保存到数据库中。

而隔离性有4个隔离级别,分别是:


  • read uncommit 读未提交,可能会读到其他事务未提交的数据,也叫做脏读。

用户本来应该读取到id=1的用户age应该是10,结果读取到了其他事务还没有提交的事务,结果读取结果age=20,这就是脏读。


  • read commit 读已提交,两次读取结果不一致,叫做不可重复读。

不可重复读解决了脏读的问题,他只会读取已经提交的事务。

用户开启事务读取id=1用户,查询到age=10,再次读取发现结果=20,在同一个事务里同一个查询读取到不同的结果叫做不可重复读。


  • repeatable read 可重复复读,这是mysql的默认级别,就是每次读取结果都一样,但是有可能产生幻读。
  • serializable 串行,一般是不会使用的,他会给每一行读取的数据加锁,会导致大量超时和锁竞争的问题。

 


6. 那ACID靠什么保证的呢?




  1. A 原子性由undo log日志保证,它记录了需要回滚的日志信息,事务回滚时撤销已经执行成功的sql
  2. C 一致性一般由代码层面来保证
  3. I 隔离性由MVCC来保证
  4. D 持久性由内存+redo log来保证,mysql修改数据同时在内存和redo log记录这次操作,事务提交的时候通过redo log刷盘,宕机的时候可以从redo log恢复

 


7. 那你说说什么是幻读,什么是MVCC(多版本并发控制:数据在某个时间节点的快照)?



要说幻读,首先要了解MVCC,MVCC叫做多版本并发控制,实际上就是保存了数据在某个时间节点的快照。

我们每行数实际上隐藏了两列,创建时间版本号,过期(删除)时间版本号,每开始一个新的事务,版本号都会自动递增。

还是拿上面的user表举例子,假设我们插入两条数据,他们实际上应该长这样。


idnamecreate_versiondelete_version
1张三1 
2李四2 

这时候假设小明去执行查询,此时current_version=3

select * from user where id<&#61;3;

同时&#xff0c;小红在这时候开启事务去修改id&#61;1的记录&#xff0c;current_version&#61;4

update user set name&#61;&#39;张三三&#39; where id&#61;1;

执行成功后的结果是这样的


idnamecreate_versiondelete_version
1张三1 
2李四2 
1张三三4 

如果这时候还有小黑在删除id&#61;2的数据&#xff0c;current_version&#61;5&#xff0c;执行后结果是这样的。


idnamecreate_versiondelete_version
1张三1 
2李四25
1张三三4 

由于MVCC的原理是查找创建版本小于或等于当前事务版本&#xff0c;删除版本为空或者大于当前事务版本&#xff0c;小明的真实的查询应该是这样

select * from user where id<&#61;3 and create_version<&#61;3 and (delete_version>3 or delete_version is null);

所以小明最后查询到的id&#61;1的名字还是&#39;张三&#39;&#xff0c;并且id&#61;2的记录也能查询到。这样做是为了保证事务读取的数据是在事务开始前就已经存在的&#xff0c;要么是事务自己插入或者修改的

明白MVCC原理&#xff0c;我们来说什么是幻读就简单多了。举一个常见的场景&#xff0c;用户注册时&#xff0c;我们先查询用户名是否存在&#xff0c;不存在就插入&#xff0c;假定用户名是唯一索引。


  • 小明开启事务current_version&#61;6查询名字为&#39;王五&#39;的记录&#xff0c;发现不存在。

  • 小红开启事务current_version&#61;7插入一条数据&#xff0c;结果是这样&#xff1a;


idNamecreate_versiondelete_version
1张三1 
2李四2 
3王五7 

  • 小明执行插入名字&#39;王五&#39;的记录&#xff0c;发现唯一索引冲突&#xff0c;无法插入&#xff0c;这就是幻读。

 

 


8. 那你知道什么是间隙锁吗&#xff1f;



间隙锁是可重复读级别下才会有的锁&#xff0c;结合MVCC和间隙锁可以解决幻读的问题。我们还是以user举例&#xff0c;假设现在user表有几条记录


idAge
110
220
330

当我们执行&#xff1a;

begin;
select * from user where age&#61;20 for update;begin;
insert into user(age) values(10); #成功
insert into user(age) values(11); #失败
insert into user(age) values(20); #失败
insert into user(age) values(21); #失败
insert into user(age) values(30); #失败

只有10可以插入成功&#xff0c;那么因为表的间隙mysql自动帮我们生成了区间(左开右闭)

(negative infinity&#xff0c;10],(10,20],(20,30],(30,positive infinity)

由于20存在记录&#xff0c;所以(10,20]&#xff0c;(20,30]区间都被锁定了无法插入、删除。

如果查询21呢&#xff1f;就会根据21定位到(20,30)的区间(都是开区间)。

需要注意的是唯一索引是不会有间隙索引的。

 


9. 你们数据量级多大&#xff1f;分库分表怎么做的&#xff1f;



首先分库分表分为垂直和水平两个方式&#xff0c;一般来说我们拆分的顺序是先垂直后水平。

垂直分库

基于现在微服务拆分来说&#xff0c;都是已经做到了垂直分库了

垂直分表

如果表字段比较多&#xff0c;将不常用的、数据较大的等等做拆分

水平分表

首先根据业务场景来决定使用什么字段作为分表字段(sharding_key)&#xff0c;比如我们现在日订单1000万&#xff0c;我们大部分的场景来源于C端&#xff0c;我们可以用user_id作为sharding_key&#xff0c;数据查询支持到最近3个月的订单&#xff0c;超过3个月的做归档处理&#xff0c;那么3个月的数据量就是9亿&#xff0c;可以分1024张表&#xff0c;那么每张表的数据大概就在100万左右。

比如用户id为100&#xff0c;那我们都经过hash(100)&#xff0c;然后对1024取模&#xff0c;就可以落到对应的表上了。

 


10. 那分表后的ID怎么保证唯一性的呢&#xff1f;



因为我们主键默认都是自增的&#xff0c;那么分表之后的主键在不同表就肯定会有冲突了。有几个办法考虑&#xff1a;


  1. 设定步长&#xff0c;比如1-1024张表我们分别设定1-1024的基础步长&#xff0c;这样主键落到不同的表就不会冲突了。
  2. 分布式ID&#xff0c;自己实现一套分布式ID生成算法或者使用开源的比如雪花算法这种
  3. 分表后不使用主键作为查询依据&#xff0c;而是每张表单独新增一个字段作为唯一主键使用&#xff0c;比如订单表订单号是唯一的&#xff0c;不管最终落在哪张表都基于订单号作为查询依据&#xff0c;更新也一样。

 


11. 分表后非sharding_key的查询怎么处理呢&#xff1f;




  1. 可以做一个mapping表&#xff0c;比如这时候商家要查询订单列表怎么办呢&#xff1f;不带user_id查询的话你总不能扫全表吧&#xff1f;所以我们可以做一个映射关系表&#xff0c;保存商家和用户的关系&#xff0c;查询的时候先通过商家查询到用户列表&#xff0c;再通过user_id去查询。
  2. 打宽表&#xff0c;一般而言&#xff0c;商户端对数据实时性要求并不是很高&#xff0c;比如查询订单列表&#xff0c;可以把订单表同步到离线&#xff08;实时&#xff09;数仓&#xff0c;再基于数仓去做成一张宽表&#xff0c;再基于其他如es提供查询服务。
  3. 数据量不是很大的话&#xff0c;比如后台的一些查询之类的&#xff0c;也可以通过多线程扫表&#xff0c;然后再聚合结果的方式来做。或者异步的形式也是可以的。

List>> taskList &#61; Lists.newArrayList();
for (int shardingIndex &#61; 0; shardingIndex <1024; shardingIndex&#43;&#43;) {taskList.add(() -> (userMapper.getProcessingAccountList(shardingIndex)));
}
List list &#61; null;
try {list &#61; taskExecutor.executeTask(taskList);
} catch (Exception e) {//do something
}public class TaskExecutor {public List executeTask(Collection> tasks) throws Exception {List result &#61; Lists.newArrayList();List> futures &#61; ExecutorUtil.invokeAll(tasks);for (Future future : futures) {result.add(future.get());}return result;}
}

 


12. 说说mysql主从同步怎么做的吧&#xff1f;



首先先了解mysql主从同步的原理


  1. master提交完事务后&#xff0c;写入binlog
  2. slave连接到master&#xff0c;获取binlog
  3. master创建dump线程&#xff0c;推送binglog到slave
  4. slave启动一个IO线程读取同步过来的master的binlog&#xff0c;记录到relay log中继日志中
  5. slave再开启一个sql线程读取relay log事件并在slave执行&#xff0c;完成同步
  6. slave记录自己的binglog

由于mysql默认的复制方式是异步的&#xff0c;主库把日志发送给从库后不关心从库是否已经处理&#xff0c;这样会产生一个问题就是假设主库挂了&#xff0c;从库处理失败了&#xff0c;这时候从库升为主库后&#xff0c;日志就丢失了。由此产生两个概念。

全同步复制

主库写入binlog后强制同步日志到从库&#xff0c;所有的从库都执行完成后才返回给客户端&#xff0c;但是很显然这个方式的话性能会受到严重影响。

半同步复制

和全同步不同的是&#xff0c;半同步复制的逻辑是这样&#xff0c;从库写入日志成功后返回ACK确认给主库&#xff0c;主库收到至少一个从库的确认就认为写操作完成。

 


13. 那主从的延迟怎么解决呢&#xff1f;



这个问题貌似真的是个无解的问题&#xff0c;只能是说自己来判断了&#xff0c;需要走主库的强制走主库查询

 


14.我帅吗&#xff1f;



帅&#xff01;

 

转自&#xff1a;《听说你想进大厂&#xff1f;当心这13个MySQL送命题&#xff01;》

 


推荐阅读
  • 本指南从零开始介绍Scala编程语言的基础知识,重点讲解了Scala解释器REPL(读取-求值-打印-循环)的使用方法。REPL是Scala开发中的重要工具,能够帮助初学者快速理解和实践Scala的基本语法和特性。通过详细的示例和练习,读者将能够熟练掌握Scala的基础概念和编程技巧。 ... [详细]
  • 本文深入探讨了NoSQL数据库的四大主要类型:键值对存储、文档存储、列式存储和图数据库。NoSQL(Not Only SQL)是指一系列非关系型数据库系统,它们不依赖于固定模式的数据存储方式,能够灵活处理大规模、高并发的数据需求。键值对存储适用于简单的数据结构;文档存储支持复杂的数据对象;列式存储优化了大数据量的读写性能;而图数据库则擅长处理复杂的关系网络。每种类型的NoSQL数据库都有其独特的优势和应用场景,本文将详细分析它们的特点及应用实例。 ... [详细]
  • 本文介绍如何使用 Python 的 DOM 和 SAX 方法解析 XML 文件,并通过示例展示了如何动态创建数据库表和处理大量数据的实时插入。 ... [详细]
  • 在PHP中实现腾讯云接口签名,以完成人脸核身功能的对接与签名配置时,需要注意将文档中的POST请求改为GET请求。具体步骤包括:使用你的`secretKey`生成签名字符串`$srcStr`,格式为`GET faceid.tencentcloudapi.com?`,确保参数正确拼接,避免因请求方法错误导致的签名问题。此外,还需关注API的其他参数要求,确保请求的完整性和安全性。 ... [详细]
  • 在安装并配置了Elasticsearch后,我在尝试通过GET /_nodes请求获取节点信息时遇到了问题,收到了错误消息。为了确保请求的正确性和安全性,我需要进一步排查配置和网络设置,以确保Elasticsearch集群能够正常响应。此外,还需要检查安全设置,如防火墙规则和认证机制,以防止未经授权的访问。 ... [详细]
  • Ihavetwomethodsofgeneratingmdistinctrandomnumbersintherange[0..n-1]我有两种方法在范围[0.n-1]中生 ... [详细]
  • 在多线程并发环境中,普通变量的操作往往是线程不安全的。本文通过一个简单的例子,展示了如何使用 AtomicInteger 类及其核心的 CAS 无锁算法来保证线程安全。 ... [详细]
  • 在 Ubuntu 中遇到 Samba 服务器故障时,尝试卸载并重新安装 Samba 发现配置文件未重新生成。本文介绍了解决该问题的方法。 ... [详细]
  • php更新数据库字段的函数是,php更新数据库字段的函数是 ... [详细]
  • 开机自启动的几种方式
    0x01快速自启动目录快速启动目录自启动方式源于Windows中的一个目录,这个目录一般叫启动或者Startup。位于该目录下的PE文件会在开机后进行自启动 ... [详细]
  • 如何将TS文件转换为M3U8直播流:HLS与M3U8格式详解
    在视频传输领域,MP4虽然常见,但在直播场景中直接使用MP4格式存在诸多问题。例如,MP4文件的头部信息(如ftyp、moov)较大,导致初始加载时间较长,影响用户体验。相比之下,HLS(HTTP Live Streaming)协议及其M3U8格式更具优势。HLS通过将视频切分成多个小片段,并生成一个M3U8播放列表文件,实现低延迟和高稳定性。本文详细介绍了如何将TS文件转换为M3U8直播流,包括技术原理和具体操作步骤,帮助读者更好地理解和应用这一技术。 ... [详细]
  • 为了在Hadoop 2.7.2中实现对Snappy压缩和解压功能的原生支持,本文详细介绍了如何重新编译Hadoop源代码,并优化其Native编译过程。通过这一优化,可以显著提升数据处理的效率和性能。此外,还探讨了编译过程中可能遇到的问题及其解决方案,为用户提供了一套完整的操作指南。 ... [详细]
  • 本文详细介绍了在 Oracle 数据库中使用 MyBatis 实现增删改查操作的方法。针对查询操作,文章解释了如何通过创建字段映射来处理数据库字段风格与 Java 对象之间的差异,确保查询结果能够正确映射到持久层对象。此外,还探讨了插入、更新和删除操作的具体实现及其最佳实践,帮助开发者高效地管理和操作 Oracle 数据库中的数据。 ... [详细]
  • 在Ubuntu上安装MySQL时解决缺少libaio.so.1错误及libaio在MySQL中的重要性分析
    在Ubuntu系统上安装MySQL时,遇到了缺少libaio.so.1的错误。本文详细介绍了如何解决这一问题,并深入探讨了libaio库在MySQL性能优化中的重要作用。对于初学者而言,理解这些依赖关系和配置步骤是成功安装和运行MySQL的关键。通过本文的指导,读者可以顺利解决相关问题,并更好地掌握MySQL在Linux环境下的部署与管理。 ... [详细]
  • Java中不同类型的常量池(字符串常量池、Class常量池和运行时常量池)的对比与关联分析
    在研究Java虚拟机的过程中,笔者发现存在多种类型的常量池,包括字符串常量池、Class常量池和运行时常量池。通过查阅CSDN、博客园等相关资料,对这些常量池的特性、用途及其相互关系进行了详细探讨。本文将深入分析这三种常量池的差异与联系,帮助读者更好地理解Java虚拟机的内部机制。 ... [详细]
author-avatar
密斯特_张_
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有