数据库八股文自述

1.MySQL索引有哪些数据结构?
答:B+tree,以及Hash。Hash索引比较适合单值的查找,但不适合范围查询,Hash索引的单值查找时间复杂度为O(1).B+tree查找的时间复杂度为O(logn),但是B+适合范围查询以及扫表。Innodb和MyISAM是不支持hash索引的。
2.MySQL索引结构为什么不用B,而用B+?
答:B树是每个节点都会存放数据,而B+的数据只会存放在叶子节点的链表上,且该链表是个双向链表,因此B+相对于B来说,最大的优势就是适合扫表。具体原因为,因为B每个节点都存放数据,因此当范围查找的时候由于每个节点都可能存在符合我们要求的数据,所以需要从根节点向下遍历子树,这样会带来大量随机的IO,这也是B最大的问题,而B+则可以通过双向链表,只会产生固定的IO。且链表的遍历本来就是树简单的嘛。
3.MySQL为什么不用二叉树(红黑)呢?为啥不用跳表?为啥不用链表?
答:MySQL不用二叉树的原因是,根据计算机著名的局部性原理,当一个数据被用到时,其周围的数据很大概率会被马上用到,所以就有磁盘的预读,但是二叉树不能利用预读,因为树在磁盘中是以堆的形式存在,所以在二叉树中,看似是父子节点,但是实际存储位置可能会隔着非常远,因此会造成大量的IO,IO是非常耗时的,所以MySQL不用二叉树作为索引。
4.B+每个节点的大小为多少?
答:一般一个节点设置为磁盘一个页的大小,可以充分利用磁盘预读原理,不浪费
5.回表
答:谈到回表,首先要谈一下非主键索引,非主键索引的B+树的叶子节点中不止存着索引的值,也存着索引值对应的主键,这也是不建议主键设置太长的原因,然后会通过主键回到主键索引树中查找,这就是回表。
6.覆盖索引
答:为了避免回表,我们可以建立覆盖索引。覆盖索引就是比如我们要通过非主键索引查找主键时,当找到对应的索引时不用回表在查询主键,因为此时非主键索引树中也存放着主键的值,可以直接获取。一次类推,当我们需要通过某个值查询另一个值得时候,我们可以将这两个值的列建一个联合索引,可以直接通过一个找到另一个而不用回表。
7.最左匹配原则
答:就是当我们建立联合索引(a,b,c,d)时,最左匹配原则要求时当我们匹配到a时,才能继续匹配b,就是先命中索引a,才能继续命中后续(注意:where a=1 and b=2和where b=1 and a=1都可以命中,优化器会帮我们调整a,b的顺序与索引一致),且会被范围查找中断,如where a=1 and b=1 and c<1 and d=1,此时索引在命中c之后不会继续往后匹配。这个原因就是联合索引的树中,只有第一个元素是全局有序的,而其余都是局部有序,所谓的局部有序就是当确定a之后,对应的b是有序的,确定b之后对应的c是有序的,而当c是范围的时候,d就无法确定顺序,故无法继续匹配。
8.MySQL的基本架构
答:MySQL的基本架构由连接器、查询缓存器(MySQL8.0版本后移除了这个功能)、分析器、优化器、执行器、以及存储引擎。可以这样分连接器,缓存,分析器,优化器,执行器属于server层,还有存储引擎。其中连接器负责连接客户端,分析器进行语法分析,优化器进行sql“优化”,执行器调用引擎接口,存储引擎负责数据存储与读取。
9.杂谈待总结
答:我认为MySQL最重要的就是它的存储引擎架构,这种架构设计将数据的查询处理、系统其他任务、数据的存储相分离。这种存储和处理相分离的设计可以让程序员根据不同的业务场景进行不同的选择,如我们可以选择不同的存储引擎。
10,ACID
答:原子性,一致性,隔离性,持久性
11.并发的事务带来的问题
答:脏读,丢失修改,不可重复读,幻读
12.事务的隔离级别
答:读未提交,读已提交,可重复读,串行化。读未提交无法解决脏读问题,读已提交可以解决脏读问题,可重复读可以解决不可重复读问题,但是无法解决幻读,串行化可以解决幻读问题,因为串行化会对所有读取的行加锁。但是innode的可重复读级别使用的是next-key lock,可以解决幻读问题。
13.innodb与MyISAM的区别
答:MyISAM是表锁,InnoDB支持行锁,innode支持事务且支持崩溃后的安全恢复,innode支持mvcc(MVCC只工作在读已提交和可重复读两个级别)
14.MVCC
答:MVCC又叫多版本并发控制,MVCC只工作在读已提交和可重复读两个隔离级别。MVCC的实现可以使得数据库的大部分操作不用加锁。MVCC的实现机理是在每行后面加两个标志。分别代表着创建版本号以及过期版本号。就拿可重复读级别下MVCC举例。select操作只会查询早于当前事务版本号的行,或过期版本号大于当前版本号的行。insert操作会向该行赋值当前版本号。delete操作会向改行的过期赋值当前版本号。update操作则会插入一条新的行,并赋值当前版本号,并向旧的行赋值过期版本号。且MVCC分为快照读和当前读,快照读就是普通的读,不加锁;当前读就读的最新的数据,select * from where lock in share mode;(共享锁) select * from where for update;(排他锁);其余update,instert,delete都默认加锁,且加的是排他锁。共享锁更新的时候会产生死锁,只有一个共享锁的事务提交以后另一个才可以。读不加锁,读写不冲突。
15.innodb默认隔离级别
答:可重复读(RR),锁是next-key lock(非唯一索引时会加next-lock锁,从而防止幻读),当索引具有唯一性时,会变为record-lock,RC下只会加record-lock(排他锁)
16.redolog,undolog,binlog
答:redolog是为了数据库崩溃后的恢复,undolog则记录事务前的状态,用于事务的回滚,binlog则是为了分布式数据库的时候主从复制,以及整个数据库的基于时间点的恢复。redolog是物理备份,记录了什么时间,哪张表,哪张页,偏移量是多少,进行了什么更新,每次更新操作都是先记录redolog,之后根据redolog进行刷盘(刷入磁盘)。uddolog则是逻辑日志,记录了事务之前的数据。binlog也是逻辑日志。
17.数据库备份
答:数据库备份分为热备份和冷备份。从实现难度上来说热备份更加难,因为热备份在备份过程数据是在不断发生变化的。备份的日志又分为物理日志和逻辑日志。物理日志我理解就是另一个备用数据库,而逻辑日志则是记录了所有的执行的sql。这里类似于redis的持久化方式rdb与aof。物理日志的优点就是快,但是体积庞大,占用空间多。逻辑日志则是体积小巧,但是恢复慢,则会受mysql版本的影响,且可能会在执行过程中出现bug热备份。可以使用全局锁,将整个数据库变为只读状态,然后进行备份Flush tables with read lock,用unlock tables解锁。
在这里插入图片描述
1.数据库是什么
答:数据库是计算机上组织、存储、管理数据的仓库。关系型数据库与非关系型数据库。关系型数据库数据结构一致,易于维护,且有结构化的SQL语句,可以执行复杂的查询操作,支持事务。但由于有固定的表结构,所以不灵活。非关系型数据库,没有固定的表结构,所以比较灵活,由于是基于keyvalue结构的,所以无法进行复杂的查询
2.为什么需要数据库
答:计算机上数据总是要存储的,断电即失是不行的,且无规则无结构散乱的存储在磁盘上也不方便程序操作,因此需要数据库。
3.SQL是什么
答:SQL是一种结构化的语言,结构化的意思是有自己固定的语法规范,我们可以通过SQL语句操作数据库。
4.事务是什么,ACID
答:在数据库中,我认为事务就是一名词,它代表着数据库中那些要么一起成功要么一起失败的操作,不可能存在在一个操作中,部分成功了,部分没有成功。ACID是事务四个特性,即原子性,一致性,隔离性,持久性。原子性代表要么一起成功,要么一起失败。一致性则是指事务操作的前后总量时不变的,不可能凭空产生也不可能凭空消失,好比两个人转载,转账前后前的总数是一致的。隔离性则是指,每个事务互相隔离,不受影响,当然这个受数据库隔离级别的影响。最后的持久性则是指最终是要落到磁盘上保存的。
5.锁是什么,MySQL有哪些锁
答:我们平时理解锁就是“把什么东西锁住了,一旦东西被锁上,其他人就无法使用,除非正在使用的人用钥匙把锁打开了,其他人才可以使用”。MySQL中的锁也是这样,但也有细微差别。MySQL中的锁分为乐观锁与悲观锁。乐观锁就是很乐观,任何时候都认为别人不会改变它的数据,不会上锁。而悲观锁则是很悲观,任何时候都认为别人会动他的数据,所以任何时候都会上锁。乐观锁与悲观锁其实就是两概念,具体还得看实现方式。MySQL中的共享锁就是一种乐观锁的实现方式,它可以允许别人来读取数据,还有一种加版本号。排他锁则是一种悲观锁的实现方式,只要上了排他锁,其他人无法操作该数据,无论是读还是写。因此共享锁也叫读锁,排他锁也叫写锁。这里可以顺便讲一下MVCC以及加锁方法(undolog 以及read view(里面有当前未提交事务的最大最小版本号,以及未提交版本号集合,若无法匹配则去undolog寻找))。lock in share mode(共享锁),for update(排他锁)。
6.什么是存储引擎,MySQL中引擎有哪几种,分别有什么特性
答:数据库的数据最终都是要持久化到磁盘上去的,而存储引擎则定义了数据存储到磁盘上的形式,如何为数据建立索引,如何更新和查询数据。MySQL有多种存储引擎,我知道的有三种,分别是innodb(Innobase Oy公司),myisam,memory。其中myisam比较适合读密集型业务,因为myisam的索引是非聚簇索引,因此比较小,所以所以是加载进内存的,因此读比较快,innodb查询索引则是需要进行I/O,但是myisam不支持事务,且是表锁,不利于并发。innodb是支持事务的,且默认是行锁,因为还支持mvcc,因此并发很高,还支持自适应哈希索引(AHI),将某些访问频率很高的数据会在内存建立哈希索引。memory则是基于内存的,且支持hash索引,因此crud操作很快,缺点就是数据不会持久化(持久化的只是表结构)。
7.索引是什么,MySQL的索引有哪些
答:索引就是为了更快的查找大数据,如数组为啥么查询快呀,因为有索引。Innodb的索引是存在b+树上的,可以使得查询更快。b+树是树的一种,b+树的每个节点是可以有多个子节点的,这样的话可以利用计算机的局部性原理减少IO次数,从而提高读取速度,因为磁盘IO是比较耗时的(ms级别,内存10ns级别,高速缓存2ns级别,寄存器1ns级别),MySQL的索引主要有主键索引,唯一索引,非唯一索引(普通索引)以及联合索引,还有一个特殊名词覆盖索引,他其实是联合索引的一种,覆盖索引是为了避免回表。在建立唯一索引和普通索引的时候,我们要考虑是否该列真的需要保证唯一,如果没有必要就不要建立唯一索引。唯一索引与普通索引在查询数据的时候没有太大差别,因为都是在内存里读取页,可能非唯一索引会多读取几行,但是在内存里这个时间可以忽略。但是在更新的时候,若更新页在内存,则没有太大差别,但是更新页不在磁盘,则非唯一索引则将更新写入chang buffer然后返回,但是唯一索引则需要进行IO操作,将数据页读入内存,因为需要判断是否唯一,因为有IO操作,所以比较费时。
8.常见的SQL优化方法有哪些
答:首先我们要排除缓存的影响,(5.6版本哈,8.0版本已经去除了缓存),因为当我们写了一个很糟的SQL,但是由于缓存的因素,也会表现出来SQL的响应速度很快,所以我们排除缓存的影响,看一下sql的真实时间。排除缓存的SQL语句为 select SQL_NO_CACHE …。其次我们要使用explain语句分析一下SQL,语法就是 explain +SQL。我们需要关心其中的一些字段,如select_type,该字段表示我们查询的类型,是否有复杂的子查询,其中SIMPLE是代表简单的查询,还有type字段,代表我们的查询是什么类型,是扫表all还是只遍历了索引树index,或者使用唯一索引找到一条数据eq_ref,还有Key字段,表示了我们实际使用的索引,若为null则代表我们的SQL没有走索引,rows字段,估算找到所需信息需要读取的行数。还有一个字段extra,表示是否使用与索引顺序不一致的排序,有using index就代表不错,使用了索引的排序顺序,而using filesort就代表使用了额外的排序顺序,效率不高。针对我们explain分析的结果,我们就要思考是否需要建立索引,或者如果有索引,就思考索引为何失效,看看所有没有对索引使用了函数或进行了计算,是否需要强制走索引(select …from … force index(key))等等。。。其次建立索引的时候我们要思考是要建立普通索引还是唯一索引,是否需要利用覆盖索引来避免回表,还有就是当索引过长的时候应该建立前缀索引,还有就是防止隐形的类型转换,如我们的索引是字符型1,但是我们sql写为了int,此时索引会失效,还有就是不同的字符集也会导致索引失效,所以需要统一字符集(utf8mb4),还有就是order by的时候尽量使用索引的顺序。
select SQL_NO_CACHE count(*) from users where email = ‘hello’;SQL优化是为了数据库的高性能。一个高性能的数据需要最优的表结构,最优的索引以及最优的查询语句。
9.MySQL是如何处理一个SQL请求的
答:首先我们得连接到数据库才能与数据库打交道,所以我们第一步是登录,登录和身份验证是在MySQL的连接器完成的,然后我们就可以向数据库发SQL了,首先数据库会先查询缓存(MySQL8.0之后移除了该功能,因为这个功能不太实用,因为若更新了数据则会清空缓存),如果命中了缓存,则校验一下用户是否有查询该数据的权限,若有则直接返回,若没有命中缓存,则会进入分析器,提取关键字,如查询的表名,字段名等,其次分析语法是否正确。然后会进入优化器,优化器会按照它认为的最优方案去执行,之后就会进入执行器,执行器调用存储引擎的接口,然后返回接口执行的结果。大体就是这样一个流程,然后细分的的话可以分为查询语句与更新语句。查询语句的话按照之前的步骤,然后这里细说一下引擎内部的操作,进入存储引擎之后,先会在buffer poll判断是否有该数据页,若没有则进行io,将对应数据页读到内存然后进行查询,有的话则直接返回。更新语句的话,进入存储引擎之后,普通值或者普通索引先判断是否对应数据页在内存,若在内存则直接进行修改,若不在会记录在change buffer中,然后记录redolog 并进入prepered状态,再记录binlog,然后将redolog状态变为commit状态。然后选择时机进行刷盘。
在这里插入图片描述
10.自增Id用完了怎么办
答:我们不会等到它被用完就会进行分库分表,分布式id有以下几种,首先是自增id,但是每张表设置不同的初始值,相同的步长,但是当数据库再次需要扩容的时候就比较麻烦,所以步长需要大一点,预留一些初始值给扩容使用,但是最终还会面临没有ID可分。UUID(16字节,128位),mac地址+时间+随机数,但是比较长,且无序(无序id会造成节点分裂,导致节点数量增多,但是每个节点的数量减少),不适合做主键。雪花算法,8字节,64位,1位标识,41位时间戳,5位机房标识,5位机器标识,12位序列号,但是比较依赖时间,当系统时间出现回拨的时候可能会产生重复。还有美团的leaf和百度的uidgenter,具体不是很了解,只知道美团的leaf依赖数据库和zk实现的,而uidgenertor是基于雪花的,但是支持自定义时间戳。
11.Redis是什么、Redis有哪些数据结构、Redis常见的使用场景(自己使用的版本是redis5.0.4)
答:redis是一种基于内存的非关系型数据库,是基于key value存储的,有string,list(双向链表),hash(哈希表),set(哈希表),zeset(哈希表+跳表,跳表里存的是score,从而实现排序)。Redis常见的使用场景有被作为缓存来使用,提高应用性能以及并发能力。用来作为分布式锁,以及分布式会话管理,还有就是一些实时的排行榜,共同好友共同关注都可以使用redis的zset来实现。

Spring 关闭数据库中自动提交:在方法执行前关闭自动提交,方法执行完毕后再开启自动提交

12.存储高性能
答:无论我们的数据库,我们的表设计的多么优秀,总是会存在一个绕不开的问题,就是单台数据库是有瓶颈的,如一般的数据库只能达到三千左右的QPS,且当一张表数量过于庞大的时候,尽管有索引,性能还是会下降,且数据库备份恢复会耗费大量时间,且数据丢失风险很高。一般实现存储高性能的方式有两种,分别为读写分离以及分库分表。读写分离采用了主从集群,一台主机多台从机,从机定时向主机同步数据。主机可以读写,从机只用来读。由于数据同步有一定延迟,所以会导致一些问题。如一个用户刚刚注册就立刻登录,此刻用户的数据还未同步到从机,因此会返回该用户未注册的异常。解决方法就是关键业务都在主机操作(如注册登录),非关键业务在从机读(用户的自我介绍啊,签名啊等等)。读写分离缓解了数据库读的压力,但是没有缓解写的压力。还有一种方法就是分库分表。既可以缓解读的压力,也可以缓解存储的压力。分库一般按照业务分。分库也会造成一些问题比如我们有些简单的连表操作就会变得复杂,还有就是事务问题,还需要我们程序介入实现多个库之间事务性。分表,则是分为水平拆分一垂直拆分。垂直拆分则是将将一些不经常用的且比较大的(比如描述)字段分出去,待需要时候在读,水平拆分则是将一张表分为多张表,一般一张表的数据达到五千万左右就需要水平分表了,但是不一定,这个要按表的复杂程度,有些复杂的表达到一千万就需要分表了,有些简单的表达到一亿也不需要分表。水平分表需要查询路由,可以按照ID来分,如1-999999分发到表1,100000-99999分发到表2,但是该方法有个缺点就是分布不均匀,有些表是满的,有些表则是只有几行,还有hash法,就是将一列或几列的值进行hash然后分配,该问题就是表的初始数量不好确定,其次当扩充表的时候,都需要重新计算分配,还有一个配置路由法,就是采用一张表记录路由信息,但是这个也存在问题,就会导致路由表很大。

  • 7
    点赞
  • 98
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值