【面经专栏】自己的整理的数据库面经

1、关系型数据库和非关系型数据库

1.mysql和redis的数据库类型

mysql是关系型数据库,主要用于存放持久化数据,将数据存储在硬盘中,读取速度较慢。

redis是NOSQL,即非关系型数据库,也是缓存数据库,即将数据存储在缓存中,缓存的读取速度快,能够大大的提高运行效率,但是保存时间有限

2.mysql的运行机制

mysql作为持久化存储的关系型数据库,相对薄弱的地方在于每次请求访问数据库时,都存在着I/O操作,如果反复频繁的访问数据库。第一:会在反复链接数据库上花费大量时间,从而导致运行效率过慢;第二:反复的访问数据库也会导致数据库的负载过高,那么此时缓存的概念就衍生了出来。

3.缓存

缓存就是数据交换的缓冲区(cache),当浏览器执行请求时,首先会对在缓存中进行查找,如果存在,就获取;否则就访问数据库。

缓存的好处就是读取速度快

4.redis数据库

redis数据库就是一款缓存数据库,用于存储使用频繁的数据,这样减少访问数据库的次数,提高运行效率。

https://www.cnblogs.com/Zhao159461/p/11131832.html

2、InnoDB怎样实现高并发控制
  • 普通锁:(本质是串行执行)

    一个事务开始操作以后,不允许其他事务读写

  • 读写锁:(读读并发)

    加读锁以后,只允许其他事物读。加写锁以后,阻塞其他读写的事务

  • MVCC(读写并发)

    1. 写任务发生时,将数据复制一份,设置为新版本
    2. 写任务修改新版本,直至提交
    3. 并发读任务可以继续读取旧版本的数据,不至于阻塞
3、MySQL顺序写与随机写

对于redo log,binlog这种日志进行的磁盘顺序读写

  • 在写redo log日志的时候,其实是不停的在一个日志文件末尾追加日志的,这就是磁盘顺序写。
    磁盘顺序写的性能其实是很高的,某种程度上来说,几乎可以跟内存随机读写的性能差不多,尤其是在数据库里其实也用了os cache机制,就是redo log顺序写磁盘之前,先进入os cache,就是操作系统管理的内存缓存里。

对于表空间磁盘文件里的数据页进行的磁盘随机读写

  • 磁盘随机读的性能是比较差的,所以不可能每次更新数据都进行磁盘随机读,必须是读取一个数据页之后放到buffer pool的缓存里去,下次要更新的时候直接更新到buffer pool里的缓存页。
4、四种隔离级别怎样解决脏读、不可重复读、幻读
  1. 读未提交:允许脏读,但不允许更新丢失。如果一个事务已经开始写数据,则另外一个事务则不允许同时进行写操作,但允许其他事务读此行数据。
  2. 读提交:允许不可重复读取,但不允许脏读。这可以通过“共享读锁”和“排他写锁”实现。读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。
  3. 可重复读:禁止不可重复读取和脏读取,但是有时可能出现幻读数据。这可以通过“共享读锁”和“排他写锁”实现。读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。(MySQL默认,也是InnoDB默认)
  4. 序列化:它要求事务序列化执行,事务只能一个接着一个地执行,不能并发执行。仅仅通过“行级锁”是无法实现事务序列化的,必须通过其他机制保证新插入的数据不会被刚执行查询操作的事务访问到
5、索引树都要加载到内存吗

不是, 索引或者数据从从内存被剔除出去,肯定是遵循一定的算法的,以保障热点数据在内存中。不在内存中,那么就去硬盘去取好了,操作系统的虚拟内存机制简化了这种行为

6、索引是由 MySQL 哪一部分实现的

在MySQL中索引是在存储引擎层实现的,而不是在服务器层实现的。不同存储引擎实现索引的方式也各有不同。

7、主键索引和非主键索引的区别

非主键索引的叶子节点存放的是主键的值,而主键索引的叶子节点存放的是整行数据,其中非主键索引也被称为二级索引,而主键索引也被称为聚簇索引

8、DDL DML
  • DML(data manipulation language)数据操纵语言

    就是我们最经常用到的 SELECT、UPDATE、INSERT、DELETE。 主要用来对数据库的表数据进行一些操作。

    比如:

SELECT 列名称 FROM 表名称

UPDATE 表名称 SET 列名称 = 新值 WHERE 列名称 = 某值

INSERT INTO table_name (1,2,...) VALUES (1,2,....)

DELETE FROM 表名称 WHERE 列名称 =

当执行DML命令如果没有提交,将不会被其他会话看到。

除非在DML命令之后执行了DDL命令命令,或用户退出会话,或终止实例,此时系统会自动发出commit命令,使未提交的DML命令提交。

  • DDL(data definition language)数据定义语言

    数据定义语言,用于定义和管理 SQL 数据库中的所有对象的语言

create table 创建表   
alter table 修改表  
drop table 删除表  
truncate table 删除表中所有行 
create index 创建索引  
drop index 删除索引

当执行DDL语句时,在每一条语句前后,oracle都将提交当前的事务。
如果用户使用insert命令将记录插入到数据库后,执行了一条DDL语句(如create table),此时来自insert命令的数据将被提交到数据库。
当DDL语句执行完成时,DDL语句会被自动提交,不能回滚。

9、数据库的存储过程

存储过程就是为以后的使用而保存的一条或多条MySQL语句的集合

优点:

  • 通过存储过程可以使相关的动作在一起发生,从而可以维护数据库的完整性
  • 在运行存储过程前,数据库已对其进行了语法和句法分析,并给出了优化执行方案。这种已经编译好的过程可极大地改善SQL语句的性能
  • 通过存储过程可以使没有权限的用户在控制之下间接地存取数据库,从而保证数据的安全

分类:

  1. 系统存储过程:由系统定义,主要存放在MASTER数据库中,名称以"sp_"开头。在其他数据库还是可以调用系统存储过程。有一些系统存储过程会在创建新的数据库的时候被自动创建在当前数据库中
  2. 扩展存储过程:是用户可以使用外部程序语言编写的存储过程,而且扩展存储过程的名称通常以xp_开头
  3. 用户自定义的存储过程:由用户创建并完成某一特定功能的存储过程,事实上一般所说的存储过程就是指本地存储过程
10、数据库的查询、更新过程

查询:连接器(权限校验)、查询缓存(MySQL 8.0之后没有)、分析器(词法分析、语法分析)、优化器、执行器(权限校验、调用引擎接口)

更新(增删改):分析器(词法分析、语法分析)、权限校验、执行器、引擎层redo log预提交、写bin log、redo log提交

11、为什么InnoDB的redo log要两次提交

redo log是 InnoDB 的机制,不是MySQL的机制,所以只能被InnoDB 独享的。bin log是MySQL的机制,所以说可以被所有存储引擎共享。

并不是是说只用bin log就不行,只是InnoDB就是通过redo log来支持事务的持久化的。同时用bin log和redo log的话,必须要引入redo log的预提交,如果不用,就会出现以下情况:

  • 先写 redo log 后写 bin log

    假设在 redo log 写完,bin log 还没有写完的时候,MySQL 进程异常重启。redo log 写完之后,系统即使崩溃,仍然能够把数据恢复回来,
    所以恢复后这一行 c 的值是 1。但是由于 bin log 没写完就 crash 了,这时候 bin log 里面就没有记录这个语句。因此,之后备份日志的时候,存起来的 bin log 里面就没有这条语句。如果需要用这个 bin log 来恢复临时库的话,由于这个语句的 bin log 丢失,这个临时库就会少了这一次更新,恢复出来的这一行 c 的值就是 0,与原库的值不同。

  • 先写 bin log 后写 redo log

如果在 bin log 写完之后 crash,由于 redo log 还没写,崩溃恢复以后这个事务无效,所以这一行 c 的值是 0。但是 bin log 里面已经记录了 “把 c 从 0 改成 1” 这个日志。所以,在之后用 bin log 来恢复的时候就多了一个事务出来,恢复出来的这一行 c 的值就是 1,与原库的值不同。

主从一致性也会受到影响,因为slave根据master的bin log来保证数据的一致性。如果先写 bin log,在写入 bin log 那一刻,就有可能同步到备节点并执行,后续崩溃恢复删除这个 bin log 后,虽然保证了本节点的两个日志一致,但是这个被删除的 bin log 已经被备节点执行了

12、崩溃恢复

正常的事务提交过程:redo log预提交(prepare)-- 写bin log – redo log提交(commit)

binlog有记录,redolog状态commit:正常完成的事务,不需要恢复
binlog有记录,redolog状态prepare:在binlog写完提交事务之前的crash,恢复操作:提交事务
binlog无记录,redolog状态prepare:在binlog写完之前的crash,恢复操作:回滚事务
binlog无记录,redolog无记录:在redolog写之前crash,恢复操作:回滚事务

13、MySQL主从容灾、主从复制

https://blog.csdn.net/weixin_29261711/article/details/113911911

14、MySQL日志(redo log、undo log、bin log、err log、 slow query log、 general log、relay log)
  1. 重做日志(redo log)

    作用:确保事务的持久性。防止在发生故障的时间点,尚有脏页未写入磁盘,重启 mysql 服务的时候,会根据 redo log进行重做,从而达到事务的持久性这一特性。

    内容:物理格式的日志,记录的是物理数据页面的修改信息,其 redo log 是顺序写入 redo log file 的物理文件中去的。

    产生时间:事务开始后就产生了 redo log。在事务执行的过程中,便开始写入 redo log 文件中。

    释放:当对应事务的脏页写入到磁盘之后,redo log 的使命也就完成了。

  2. 回滚日志(undo log)

    作用:保存了事务发生之前的数据的一个版本,可以用于回滚,同时可以提供多版本并发控制下的读(MVCC)。

    内容:逻辑格式的日志,在执行 undo 的时候,仅仅是将数据从逻辑上恢复至事务之前的状态,而不是从物理页面上操作实现。

    产生时间:事务开始之前,将当前版本生成 undo log,undo 也会产生 redo 来保证 undo 的可靠性。

    释放:当事务提交后,undo log 并不能立马被删除,而是放入待清理的链表。

  3. 二进制日志(bin log)

    作用:用于主从复制,从库利用主库上的 binlog 进行relay,实现主从同步。还可以用于数据库基于时间点的还原。

    内容:逻辑格式的日志,可以简单认为就是执行过的事务中的 sql 语句。但又不完全是 sql 语句那么简单,而是包括了执行 sql 语句反向的信息,比如 delete 对应着其反向的 insert 等。

    产生时间:事务提交的时候,一次性将事务中的 sql 语句按照一定格式记录到 binlog。在开启了 binlog 的情况下,对于大事务的提交,可能会变得比较慢一些,这是因为 binlog 会在事务提交的时候一次性写入造成的。

  4. 补充

    二进制日志的作用之一是还原数据库的,与 redo log 很类似。

    作用不同:redo log 是保证事务持久性的,是事务层面的,binlog 作为还原的功能,是数据库层面的,虽然都有还原的意思,但是其保护的数据的层次是不一样的。

    内容不同:redo log 是物理日志,是数据页面修改之后的物理记录,binlog 是逻辑日志,可以简单理解为就是 sql 语句

    恢复数据时的效率,基于物理日志的 redo log 恢复数据的效率要高于逻辑日志的 binlog。

    MySql 时通过两阶段提交来保证事务一致性的,也就是 redo log 和 binlog 的一致性,理论上是先写 redo log,再写 binlog,两个日志都写入成功(刷入磁盘),事务才算真正的完成。

15、悲观锁与乐观锁的实现机制
  • 悲观锁更新的方式认为:在更新数据的时候大概率会有其他线程去争夺共享资源,所以悲观锁的做法是:第一个获取资源的线程会将资源锁定起来,其他没争夺到资源的线程只能进入阻塞队列,等第一个获取资源的线程释放锁之后,这些线程才能有机会重新争夺资源
  • 乐观锁更新方式认为:在更新数据的时候其他线程争抢这个共享变量的概率非常小,所以更新数据的时候不会对共享数据加锁。但是在正式更新数据之前会检查数据是否被其他线程改变过,如果未被其他线程改变过就将共享变量更新成最新值,如果发现共享变量已经被其他线程更新过了,就重试,直到成功为止CAS机制就是乐观锁的典型实现
16、回表

聚簇索引叶子节点保存行记录,辅助索引是一种非聚簇索引,叶子结点保存主键。使用辅助索引查询行记录需要先找到主键,用这个主键再去聚簇索引中再查一遍,这个过程就叫回表查询

17、千万级表怎样插入一列
  1. 实现业务中对于数据的读写分离,创建一个已经按需求修改好结构的新表。修改业务逻辑,将读操作指向旧表,将写操作指向新表。如果读旧表没有,再读新表,并将旧的数据写入到新表,当然这一步写入操作我们可以不用,我们可以在后台做一个定时任务将旧数据同步到新表。
  2. 保留字段。最初创建表的时候加一些冗余字段,各个类型都加一些,备用。
  3. 增加扩展表。不在原有的表的基础上修改了,以增加扩展表(另外一张新表)的方式,将新字段的数据写入到扩展表中,修改业务逻辑,这些字段从新表中读取。
18、Redis的底层实现(5种数据结构)
  • SDS(简单动态字符串)
    • 主要成员有:
      • buf:char数组,保存字符
      • len:记录SDS的长度
      • free:记录buf数组中未使用的长度
    • 以len字段实现O(1)复杂度获得字符串长度
    • 以free字段和自动扩容实现杜绝缓冲溢出
    • 能够实现二进制安全,以len而不是’\0’作为字符串结束的标志
    • 兼容部分C字符串函数:strcat
  • list(列表)
    • 主要成员有:
      • pre:前置节点指针
      • next:后置节点指针
      • val:节点的值
    • 双向无环链表:支持反向遍历
    • 带表头指针和表尾指针
    • 有链表长度计数器,获取链表节点数量的时间复杂度为O(1)
  • hash(哈希)
    • 主要成员:
      • 键、值、指向下个哈希节点的指针
    • rehash(重新哈希):当哈希表中的节点过多或者过少的时候,需要对哈希表进行扩展或收缩
      • 步骤:新建一个哈希表,将旧哈希表中的键值对重新hash到新的哈希表中;全部键值对转移后,释放旧哈希表
    • 渐进式rehash:针对就哈希表中的键值对太多,为了避免rehash对服务器性能造成影响,服务器不是一次将旧哈希表中的键值对全部rehash到新表,而是分多次、渐进式地rehash
      • 在渐进式rehash期间,新添加的键值对一律放到新表中,查询键值对会先查旧表,再查新表
  • set(无序集合)
    • 不允许出现重复
  • sorted_set(有序集合):跳表+哈希实现
    • 增加了一个权重参数score。使得集合中的元素能够按score进行排序
22、Redis跳表

跳跃表基于有序单链表,在链表的基础上,每个结点不只包含一个指针,还可能包含多个指向后继结点的指针,这样就可以跳过一些不必要的结点,从而加快查找、删除等操作。

查找的时间复杂度:平均O(logN)、最坏O(N)

img

传统的单链表是一个线性结构,向有序的链表中插入、查找一个结点需要O(n)的时间。如果使用上图的跳跃表,就可以减少查找所需的时间。

跳跃表的插入和删除操作都基于查找操作,理解了查找操作,也就理解了跳跃表的本质。查找就是给定一个key,查找这个key是否出现在跳跃表中。

跳表的使用场景:

Redis使用跳跃表作为有序集合键的底层实现之一,若一个有序集合包含的元素数量比较多,或者有序集合中的成员是比较长的字符串时,Redis就会使用跳跃表来作为有序集合键的底层实现。

23、Redis sorted set(有序集合)的实现

有序集合使用两种数据结构来实现,从而可以使插入和删除操作达到O(log(N))的时间复杂度。这两种数据结构是哈希表跳跃表。向哈希表添加元素,用于将成员对象映射到分数;同时将该元素添加到跳跃表,以分数进行排序。

18. MySQL的默认隔离级别

MySQL的默认隔离级别就是Repeatable read(可重复读)

Serializable(序列化)是最高的事务隔离级别,同时代价也花费最高,性能很低,一般很少使用,在该级别下,事务顺序执行,不仅可以避免脏读、不可重复读,还避免了幻读。

19、聚簇索引的优缺点

优点:

  1. 将相关数据保存在一起,读取数据时使用更少的磁盘IO
  2. 数据访问更快。聚簇索引将索引进和数据保存在同一个B+树上,从聚簇索引中获取数据比非聚簇索引要快
  3. 使用索引覆盖能够避免去数据库中再查询

缺点:

  1. 数据增删改的代价比非聚簇索引高
  2. 使用二级索引查询的时候,都要进行回表再去查聚簇索引
20、RDB(快照)持久化

RDB文件是一个压缩后的二进制文件,通过该文件可以还原生成RDB文件时的数据库状态

生成RDB文件的方式:

  1. SAVE:阻塞Redis服务器进程,直到RDB文件创建完毕为止,在服务器阻塞过程中,服务器不能处理任何请求
  2. BGSAVE:fork()一个子进程,由子进程创建RDB文件,父进程能够继续处理请求

RDB文件能够实现重启服务器的时候使用,还能够实现主从复制(将RDB文件传输至slave)

21、什么时候会触发间隙锁

间隙锁是一个在索引记录之间的间隙上的锁。这是mysql数据库中的一种锁,它会出现在如下场景中,我们向表中新增一条数据age=20,这条数据在本来是没有的,在insert还没有提交的时候去

select * from tableA a where a.age>15 and a.age<25

这个时候就会触发间隙锁,我们必须等待insert提交后才能执行select语句。

在MySQL的InnoDB引擎中,如果操作的是一个区间的数据,会锁住这个区间所有的记录,即使这个记录不存在,这个时候另一个会话去插入这个区间的数据,就必须等待上一个结束。

间隙锁的作用:保证某个间隙内的数据在锁定情况下不会发生任何变化。比如mysql默认隔离级别下的可重复读(RR)。

ps: 可重复读(RR):在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。

24、数据库连接有几种

内联接(Inner Join):匹配2张表中字段相等的记录。
左联接(Left Outer Join):返回左表,并且返回左表与右表字段相等的记录
右联接(Right Outer Join):返回右表,并且返回右表与左表字段相等的记录

25、组合索引的生效与失效

https://www.cnblogs.com/hongmoshui/p/10429842.html

联合索引又叫复合索引。两个或更多个列上的索引被称作复合索引

对于复合索引:Mysql从左到右的使用索引中的字段,一个查询可以只使用索引中的一部份,但只能是最左侧部分。例如索引是key index (a,b,c)。 可以支持***a | a,b| a,b,c*** 3种组合进行查找,但不支持 b,c进行查找

这是因为:多列索引是先按照第一列进行排序,然后在第一列排好序的基础上再对第二列排序,如果没有第一列的话,直接访问第二列,那第二列肯定是无序的,直接访问后面的列就用不到索引了

例如组合索引(a,b,c),组合索引的生效原则是 :从前往后依次使用生效,如果中间某个索引没有使用,那么断点前面的索引部分起作用,断点后面的索引没有起作用

where a=3 and b=4 and c=5 … 这种三个索引顺序使用中间没有断点,全部发挥作用;

where a=3 and c=5… 这种情况下b就是断点,a发挥了效果,c没有效果

where b=3 and c=4… 这种情况下a就是断点,在a后面的索引都没有发挥作用,这种写法联合索引没有发挥任何效果;

where b=4 and a=3 and c=5 … 这个跟第一个一样,全部发挥作用,abc只要用上了就行,跟写的顺序无关

组合索引使用判断:

(0) select * from mytable where a=3 and b=5 and c=4;

abc三个索引都在where条件里面用到了,而且都发挥了作用

(1) select * from mytable where c=4 and b=6 and a=3;

这条语句列出来只想说明 mysql没有那么笨,where里面的条件顺序在查询之前会被mysql自动优化,效果跟上一句一样

(2) select * from mytable where a=3 and c=7;

a用到索引,b没有用,所以c是没有用到索引效果的

(3) select * from mytable where a=3 and b>7 and c=3;(范围值就算是断点)

a用到了,b也用到了,c没有用到,这个地方b是范围值,也算断点,只不过自身用到了索引

(4) select * from mytable where b=3 and c=4;

因为a索引没有使用,所以这里 bc都没有用上索引效果

(5) select * from mytable where a>4 and b=7 and c=9;

a用到了 b没有使用,c没有使用

(6) select * from mytable where a=3 order by b;

a用到了索引,b在结果排序中也用到了索引的效果,前面说了,a下面任意一段的b是排好序的

(7) select * from mytable where a=3 order by c;

a用到了索引,但是这个地方c没有发挥排序效果,因为中间断点了,使用 explain 可以看到 filesort

(8) select * from mytable where b=3 order by a;

b没有用到索引,排序中a也没有发挥索引效果
26、RDB和AOF的区别,谁恢复的更快

https://www.cnblogs.com/shizhengwen/p/9283973.html

27、InnoDB 缓冲池(内存池)

https://blog.csdn.net/a1158375969/article/details/105898424

  • InnoDB存储引擎是基于磁盘存储的, 并将其记录按照页的方式进行管理. 而磁盘IO与内存IO的速度相差可能接近1000倍, 所以使用缓冲池(Buffer Pool)可以有效提高响应能力. 当数据库需要进行数据页读取的时候, 先将页数据存储在缓冲池中, 下一次再读取的时候, 先检查缓冲池中是否存在数据, 如果存在则称为命中(hit), 否则会读取磁盘上的页.
  • 为了提高大容量读取的效率, 缓冲池被分为多个页面, 每个页面可以存储多个行. 为了提高缓冲池的管理效率, 缓冲池的页面被实现为链表, 使用LRU算法的变体来将部分数据老化掉
28、double write

当发生数据库宕机时,可能 InnoDB存储引擎正在写入某个页到表中,而这个页只写了一部分,比如16KB的页,只写了前4KB,之后就发生了宕机,这种情况被称为部分写失效(partial page write)。

在 InnoDB存储引擎未使用 doublewrite技术前,曾经出现过因为部分写失效而导致数据丢失的情况。

有经验的DBA也许会想,如果发生写失效,可以通过重做日志进行恢复。这是个办法。但是必须清楚地认识到,重做日志中记录的是对页的物理操作。如果这个页本身已经发生了损坏,再对其进行重做是没有意义的。这就是说,在应用重做日志前,用户需要一个页的副本,当写入失效发生时,先通过页的副本来还原该页,再进行重做,这就是 doublewrite。

Double Write的思路很简单:

A. 在覆盖磁盘上的数据前,先将Page的内容写入到磁盘上的其他地方(InnoDB存储引擎中的doublewrite buffer,这里的buffer不是内存空间,是持久存储上的空间).

B. 然后再将Page的内容覆盖到磁盘上原来的数据。

如果在A步骤时系统故障,原来的数据没有被覆盖,还是完整的。
如果在B步骤时系统故障,原来的数据不完整了,但是新数据已经被完整的写入了doublewrite buffer. 因此系统恢复时就可以用doublewrite buffer中的新Page来覆盖这个不完整的page。

29、MySQL优化方法

高频访问优化:分表分库、增加缓存、增加索引

并发优化:主从读写分离、集群与分布式减轻负载

你可能感兴趣的:(面经专栏,数据库,面试,sql)