荐藏:MySQL数据库常见面试题总结
点击上方蓝色字体,选择“标星公众号”
优质文章,第一时间送达
1、数据库的常用范式:
第一范式(1NF):指表的列不可再分,数据库中表的每一列都是不可分割的基本数据项,同一列中不能有多个值;
第二范式(2NF):在 1NF 的基础上,还包含两部分的内容:一是表必须有一个主键;二是表中非主键列必须完全依赖于主键,不能只依赖于主键的一部分;
第三范式(3NF):在 2NF 的基础上,消除非主键列对主键的传递依赖,非主键列必须直接依赖于主键。
BC范式(BCNF):在 3NF 的基础上,消除主属性对于码部分的传递依赖
2、SQL语句的执行过程:
先说下缓存中数据存储格式:key(sql语句)- value(数据值),所以如果SQL语句(key)只要存在一点不同之处就会直接进行数据库查询了;
由于表中的数据不是一成不变的,大多数是经常变化的,而当数据库中的数据变化了,那么相应的与此表相关的缓存数据就需要移除掉;
(1)首先MySQL执行器根据 执行计划 调用存储引擎的API查询数据
(2)存储引擎先从缓存池buffer pool中查询数据,如果没有就会去磁盘中查询,如果查询到了就将其放到缓存池中
(3)在数据加载到 Buffer Pool 的同时,会将这条数据的原始记录保存到 undo 日志文件中
(4)innodb 会在 Buffer Pool 中执行更新操作
(5)更新后的数据会记录在 redo log buffer 中
(6)提交事务在提交的同时会做以下三件事
(7)(第一件事)将redo log buffer中的数据刷入到redo log文件中
(8)(第二件事)将本次操作记录写入到 bin log文件中
(9)(第三件事)将bin log文件名字和更新内容在 bin log 中的位置记录到redo log中,同时在 redo log 最后添加 commit 标记
(10)使用一个后台线程,它会在某个时机将我们Buffer Pool中的更新后的数据刷到 MySQL 数据库中,这样就将内存和数据库的数据保持统一了
3、常用的存储引擎?InnoDB与MyISAM的区别?
① MyISAM:保存有表的总行数,如果使用 select count() from table 会直接取出出该值,不需要进行全表扫描。
② InnoDB:没有保存表的总行数,如果使用 select count() from table 需要会遍历整个表,消耗相当大。
① MyISAM会在磁盘上存储成三个文件:.frm文件存储表定义,.MYD文件存储数据,.MYI文件存储索引。
② InnoDB:把数据和索引存放在表空间里面,所有的表都保存在同一个数据文件中,InnoDB表的大小只受限于操作系统文件的大小,一般为2GB。
① MyISAM:可被压缩,存储空间较小。支持三种不同的存储格式:静态表(默认,但是注意数据末尾不能有空格,会被去掉)、动态表、压缩表。
② InnoDB:需要更多的内存和存储,它会在主内存中建立其专用的缓冲池用于高速缓冲数据和索引。
① 如果需要提供回滚、崩溃恢复能力的ACID事务能力,并要求实现行锁级别并发控制,InnoDB是一个好的选择;
② 如果数据表主要用来查询记录,读操作远远多于写操作且不需要数据库事务的支持,则MyISAM引擎能提供较高的处理效率;
4、事务的ACID与实现原理?
(1)原子性:事务是一个不可分割的工作单元,事务里的操作要么都成功,要么都失败,如果事务执行失败,则需要进行回滚。
(2)隔离性:事务的所操作的数据在提交之前,对其他事务的可见程度。
(3)持久性:一旦事务提交,它对数据库中数据的改变就是永久的。
(4)一致性:事务不能破坏数据的完整性和业务的一致性。例如在转账时,不管事务成功还是失败,双方钱的总额不变。
读未提交:允许事务在执行过程中,读取其他事务尚未提交的数据;
读已提交:允许事务在执行过程中读取其他事务已经提交的数据;
可重复读(默认级别):在同一个事务内,任意时刻的查询结果都是一致的;
读序列化:所有事务逐个依次执行,每次读都需要获取表级共享锁,读写会相互阻塞。
更新丢失:两个或多个事务操作相同的数据,然后基于选定的值更新该行时,由于每个事务都不知道其他事务的存在,就会发生丢失更新问题:最后的更新覆盖了其他事务所做的更新。
脏读:指事务A正在访问数据,并且对数据进行了修改(事务未提交),这时,事务B也使用这个数据,后来事务A撤销回滚,并把修改后的数据恢复原值,B读到的数据就与数据库中的数据不一致,即B读到的数据是脏数据。
不可重复读:在一个事务内,多次读取同一个数据,但是由于另一个事务在此期间对这个数据做了修改并提交,导致前后读取到的数据不一致;
幻读:在一个事务中,先后两次进行读取相同的数据(一般是范围查询),但由于另一个事务新增或者删除了数据,导致前后两次结果不一致。
排它锁解决脏读
共享锁解决不可重复读
临键锁解决幻读
0:表示不刷入磁盘;
1:事务每次提交的时候,就把缓冲池中的数据刷新到磁盘中;
2:提交事务的时候,把缓冲池中的数据写入磁盘文件对应的 os cache 缓存里去,而不是直接进入磁盘文件。可能 1 秒后才会把 os cache 里的数据写入到磁盘文件里去。
5、数据库中的锁机制?
按锁的粒度划分:表级锁、行级锁、页级锁;
按锁的类型划分:共享(锁S锁)、排他锁(X锁);
按锁的使用策略划分:乐观锁、悲观锁;
表级锁:最大粒度的锁级别,发生锁冲突的概率最高,并发度最低,但开销小,加锁快,不会出现死锁;
行级锁:最小粒度的所级别,发生锁冲突的概率最小,并发度最高,但开销大,加锁慢,会发生死锁;
页级锁:锁粒度界于表级锁和行级锁之间,对表级锁和行级锁的折中,并发度一般。开销和加锁时间也界于表锁和行锁之间,会出现死锁;
InnoDB存储引擎支持行级锁和表级锁,默认情况下使用行级锁,但只有通过索引进行查询数据,才使用行级锁,否就使用表级锁。
MyISAM和MEMORY存储引擎采用的是表级锁;
BDB存储引擎使用的是页面锁,但也支持表级锁;
共享锁(S锁、读锁):多个事务可以对同一数据行共享一把S锁,但只能进行读不能修改;
排它锁(X锁、写锁):一个事务获取排它锁之后,可以对锁定范围内的数据行执行写操作,在锁定期间,其他事务不能再获取这部分数据行的锁(共享锁、排它锁),只允许获取到排它锁的事务进行更新数据。
间隙锁:当使用范围查询而不是精准查询进行检索数据,并请求共享或排它锁时,InnoDB会给符合范围条件的已有数据记录的索引项加锁;对于键值在条件范围内但并不存在的记录,叫做间隙(GAP)。
记录锁:当使用唯一索引,且记录存在的精准查询时,使用记录锁
X锁解决脏读
S锁解决不可重复读
临键锁解决幻读
6、MySQL索引的实现原理:
减少查询需要检索的行数,加快查询速度,避免进行全表扫描,这也是创建索引的最主要的原因。
如果索引的数据结构是B+树,在使用分组和排序时,可以显著减少查询中分组和排序的时间。
通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。
当对表中的数据进行增加、删除和修改时,索引也要进行更新,维护的耗时随着数据量的增加而增加。
索引需要占用物理空间,如果要建立聚簇索引,那么需要的空间就会更大。
WHERE子句中经常出现的列上面创建索引,加快条件的判断速度。
按范围存取的列或者在group by或order by中使用的列,因为索引已经排序,这样可以利用索引加快排序查询时间。
经常用于连接的列上,这些列主要是一些外键,可以加快连接的速度;
作为主键的列上,强制该列的唯一性和组织表中数据的排列结构;
区分度不高的列。由于这些列的取值很少,例如性别,在查询的结果中,结果集的数据行占了表中数据行的很大比例,即需要在表中搜索的数据行的比例很大。增加索引,并不能明显加快检索速度。
在查询中很少的列不应该创建索引。由于这些列很少使用到,但增加了索引,反而降低了系统的维护速度和增大了空间需求。
当添加索引造成修改成本的提高 远远大于 检索性能的提高时,不应该创建索引。当增加索引时,会提高检索性能,但是会降低修改性能。当减少索引时,会提高修改性能,降低检索性能。
定义为text, image和bit数据类型的列不应该增加索引。这些列的数据量要么相当大,要么取值很少。
普通索引:最基本的索引,没有任何限制
唯一索引:但索引列的值必须唯一,允许有空值,可以有多个NULL值。如果是组合索引,则列值的组合必须唯一。
主键索引:一种特殊的唯一索引,不允许有空值。
全文索引:全文索引仅可用于 MyISAM 表,并只支持从CHAR、VARCHAR或TEXT类型,用于替代效率较低的like 模糊匹配操作,而且可以通过多字段组合的全文索引一次性全模糊匹配多个字段。
组合索引:主要是为了提高mysql效率,创建组合索引时应该将最常用作限制条件的列放在最左边,依次递减。
聚簇索引:表中数据存储的物理顺序与索引值的顺序一致,一个基本表最多只能有一个聚簇索引,更新聚簇索引列上的数据时,往往导致表中记录的物理顺序的变更,代价较大,因此对于经常更新的列不宜建立聚簇索引
非聚簇索引:表中数据的物理顺序与索引值的顺序不一致的索引组织,一个基本表可以有多个聚簇索引。
Hash索引仅能满足等值的查询,不能满足范围查询、排序。因为数据在经过Hash算法后,其大小关系就可能发生变化。
当创建组合索引时,不能只使用组合索引的部分列进行查询。因为hash索引是把多个列数据合并后再计算Hash值,所以对单独列数据计算Hash值是没有意义的。
当发生Hash碰撞时,Hash索引不能避免表数据的扫描。因为仅仅比较Hash值是不够的,需要比较实际的值以判定是否符合要求。
页内节点不存储内容,每次IO可以读取更多的行,大大减少磁盘I/O读取次数
带顺序访问指针的B+Tree:B+Tree所有索引数据都存储在叶子结点上,并且增加了顺序访问指针,每个叶子节点都有指向相邻叶子节点的指针,这样做是为了提高区间查询效率。
(1)B+树有利于对数据库的扫描:B树在提高了磁盘IO性能的同时并没有解决元素遍历的效率低下的问题,而B+树只需要遍历叶子节点就可以解决对全部关键字信息的扫描,所以范围查询、排序等操作,B+树有着更高的性能。
(2)B+树的磁盘IO代价更低:B+树的内部结点的data域并没有存储数据,因此其内部结点相对于B树更小。如果把所有同一内部结点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多。一次性读入内存中的需要查找的关键字也就越多,相对来说I/O读写次数也就降低了。
(3)B+树的查询效率更加稳定:由于B+树的内部结点只是叶子结点中关键字的索引,并不存储数据。所以任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当。
对于 Innodb 的组合索引,如果各个列中的长度超过767字节的,则会对超过767字节的列取前缀索引;对于 Innodb 的单列索引,如果列的长度超过767的,则取前缀索引(取前255字符)
对于 MyISAM 的组合索引,所创建的索引长度和不能超过1000 bytes,否则会报错,创建失败;对于 MyISAM 的单列索引,最大长度也不能超过1000,否则会报警,但是创建成功,最终创建的是前缀索引(取前333个字符)
7、SQL优化和索引优化、表结构优化:
8、数据库参数优化:
9、explain的执行计划:
id值不同:id值越大优先级越高,越先被执行;
id值相同:从上往下依次执行;
id列为null:表示这是一个结果集,不需要使用它来进行查询。
system:表中只有一条数据匹配(等于系统表),可以看成 const 类型的特例
const:通过索引一次就找到了,表示使用主键索引或者唯一索引
eq_ref:主键或者唯一索引中的字段被用于连接使用,只会返回一行匹配的数据
ref:普通索引扫描,可能返回多个符合查询条件的行。
fulltext:全文索引检索,全文索引的优先级很高,若全文索引和普通索引同时存在时,mysql不管代价,优先选择使用全文索引。
ref_or_null:与ref方法类似,只是增加了null值的比较。
index_merge:表示查询使用了两个以上的索引,索引合并的优化方法,最后取交集或者并集,常见and ,or的条件使用了不同的索引。
unique_subquery:用于where中的in形式子查询,子查询返回不重复值唯一值;
index_subquery:用于in形式子查询使用到了辅助索引或者in常数列表,子查询可能返回重复值,可以使用索引将子查询去重。
range:索引范围扫描,常见于使用>,<,between ,in ,like等运算符的查询中。
index:索引全表扫描,把索引树从头到尾扫描一遍;
all:遍历全表以找到匹配的行(Index与ALL虽然都是读全表,但index是从索引中读取,而ALL是从硬盘读取)
NULL:MySQL在优化过程中分解语句,执行时甚至不用访问表或索引
using index:使用覆盖索引
using index condition:查询的列未被索引覆盖,where筛选条件使用了索引
using temporary:用临时表保存中间结果,常用于 group by 和 order by 操作中,通常是因为 group by 的列上没有索引,也有可能是因为同时有group by和order by,但group by和order by的列又不一样,一般看到它说明查询需要优化了
using filesort:MySQL有两种方式对查询结果进行排序,一种是使用索引,另一种是filesort(基于快排实现的外部排序,性能比较差),当数据量很大时,这将是一个CPU密集型的过程,所以可以通过建立合适的索引来优化排序的性能
10、MySQL的主从复制:
(1)Slave SQL thread线程:创建用于读取relay log中继日志并执行日志中包含的更新,位于slave端
(2)Slave I/O thread线程:读取 master 服务器Binlog Dump线程发送的内容并保存到slave服务器的relay log中继日志中,位于slave端:
(3)Binlog dump thread线程(也称为IO线程):将bin-log二进制日志中的内容发送到slave服务器,位于master端
(1)master服务器在执行SQL语句之后,记录在binlog二进制文件中;
(2)slave端的IO线程连接上master端,并请求从指定bin log日志文件的指定pos节点位置(或者从最开始的日志)开始复制之后的日志内容。
(3)master端在接收到来自slave端的IO线程请求后,通知负责复制进程的IO线程,根据slave端IO线程的请求信息,读取指定binlog日志指定pos节点位置之后的日志信息,然后返回给slave端的IO线程。该返回信息中除了binlog日志所包含的信息之外,还包括本次返回的信息在master端的binlog文件名以及在该binlog日志中的pos节点位置。
(4)slave端的IO线程在接收到master端IO返回的信息后,将接收到的binlog日志内容依次写入到slave端的relay log文件的最末端,并将读取到的master端的binlog文件名和pos节点位置记录到master-info文件中(该文件存slave端),以便在下一次同步的候能够告诉master从哪个位置开始进行数据同步;
(5)slave端的SQL线程在检测到relay log文件中新增内容后,就马上解析该relay log文件中的内容,然后还原成在master端真实执行的那些SQL语句,再按顺序依次执行这些SQL语句,从而到达master端和slave端的数据一致性;
(1)读写分离,通过动态增加从服务器来提高数据库的性能,在主服务器上执行写入和更新,在从服务器上执行读功能。
(2)提高数据安全,因为数据已复制到从服务器,从服务器可以终止复制进程,所以,可以在从服务器上备份而不破坏主服务器相应数据。
(3)在主服务器上生成实时数据,而在从服务器上分析这些数据,从而提高主服务器的性能。
① 因为记录的SQL语句,所以占用更少的存储空间。binlog日志包含了描述数据库操作的事件,但这些事件包含的情况只是对数据库进行改变的操作,例如 insert、update、create、delete等操作。相反对于select、desc等类似的操作并不会去记录。
② binlog日志文件记录了所有的改变数据库的语句,所以此文件可以作为数据库的审核依据。
① 不安全,不是所有的改变数据的语句都会被记录。对于非确定性的行为不会被记录。例如:对于 delete 或者 update 语句,如果使用了 limit 但是并没有 order by ,这就属于非确定性的语句,就不会被记录。
② 对于没有索引条件的update,insert……select 语句,必须锁定更多的数据,降低了数据库的性能。
① 所有的改变都会被复制,这是最安全的复制方式;
② 对于 update、insert……select等语句锁定更少的行;
① 不能通过binlog日志文件查看什么语句执行了,也无从知道在从服务器上接收到什么语句,我们只能看到什么数据改变。
② 因为记录的是数据,所以说binlog日志文件占用的存储空间要比Statement-based大。
③ 对于数据量大的操作其花费的时间有更长。
11、读写分离:
(1)增加物理服务器,负荷分摊;
(2)主从只负责各自的写和读,极大程度的缓解X锁和S锁争用;
(3)从库可配置MyISAM引擎,提升查询性能以及节约系统开销;
(4)主从复制另外一大功能是增加冗余,提高可用性,当一台数据库服务器宕机后能通过调整另外一台从库来以最快的速度恢复服务。
(1)基于程序代码内部实现:在代码中根据select 、insert进行路由分类。优点是性能较好,因为程序在代码中实现,不需要增加额外的硬件开支,缺点是需要开发人员来实现,运维人员无从下手。
(2)基于中间代理层实现:代理一般介于应用服务器和数据库服务器之间,代理数据库服务器接收到应用服务器的请求后根据判断后转发到后端数据库,有以下代表性的代理层。
12、分库分表:垂直分表、垂直分库、水平分表、水平分库
降低业务中的耦合,方便对不同的业务进行分级管理
可以提升IO、数据库连接数、解决单机硬件存储资源的瓶颈问题
主键出现冗余,需要管理冗余列
事务的处理变得复杂
仍然存在单表数据量过大的问题
解决了单表数据量过大的问题
避免IO竞争并减少锁表的概率
解决了单库大数据量的瓶颈问题
IO冲突减少,锁的竞争减少,某个数据库出现问题不影响其他数据库,提高了系统的稳定性和可用性
分片事务一致性难以解决
跨节点JOIN性能差,逻辑会变得复杂
数据扩展难度大,不易维护
优点:由数据库管理,简单有效。
缺点:性能代价高,特别是shard越来越多。
优点:性能上有优势;
缺点:需要在应用程序在事务上做灵活控制。如果使用了spring的事务管理,改动起来会面临一定的困难。
优点:本地生成ID,不需要远程调用,全局唯一不重复。
缺点:占用空间大,不适合作为索引。
优点:简单易实现。
缺点:在高并发下存在瓶颈。
优点:不依赖数据库,性能比较好。
缺点:引入新的组件会使得系统复杂度增加
1bit:第一个bit默认为0,因为二进制中第一个bit为1的话为负数,但是ID不能为负数.
41bit:表示的是时间戳,单位是毫秒。
10bit:记录工作机器ID,其中5个bit表示机房ID,5个bit表示机器ID。
12bit:用来记录同一毫秒内产生的不同ID。
13、分区:
(1)Range分区:按照连续的区间范围进行分区
(2)List分区:按照给定的集合中的值进行选择分区。
(3)Hash分区:基于用户定义的表达式的返回值进行分区,该表达式使用将要插入到表中的这些行的列值进行计算。这个函数可以包含MySQL中有效的、产生非负整数值的任何表达式。
(4)Key分区:类似于按照HASH分区,区别在于Key分区只支持计算一列或多列,且key分区的哈希函数是由 MySQL 服务器提供。
将分区分在不同磁盘,可以解决单磁盘容量瓶颈问题,存储更多的数据,也能解决单磁盘的IO瓶颈问题。
减少数据库检索时需要遍历的数据量,在查询时只需要在数据对应的分区进行查询。
避免Innodb的单个索引的互斥访问限制
对于聚合函数,例如sum()和count(),可以在每个分区进行并行处理,最终只需要统计所有分区得到的结果
方便管理,对于失去保存意义的数据,通过删除对应的分区,达到快速删除的作用。比如删除某一时间的历史数据,直接执行truncate,或者直接drop整个分区,这比detele删除效率更高;
在某些场景下,单个分区表的备份很恢复会更有效率。
14、主键一般用自增ID还是UUID?
字段长度较 UUID 会小很多。
数据库自动编号,按顺序存放,利于检索
无需担心主键重复问题
因为是自增,在某些业务场景下,容易被其他人查到业务量。
发生数据迁移时,或者表合并时会非常麻烦
在高并发的场景下,竞争自增锁会降低数据库的吞吐能力
唯一标识,不用考虑重复问题,在数据拆分、合并时也能达到全局的唯一性。
可以在应用层生成,提高数据库的吞吐能力。
无需担心业务量泄露的问题。
因为UUID是随机生成的,所以会发生随机IO,影响插入速度,并且会造成硬盘的使用率较低。
UUID占用空间较大,建立的索引越多,造成的影响越大。
UUID之间比较大小较自增ID慢不少,影响查询速度。
15、视图View:
简化了操作,把经常使用的数据定义为视图
安全性,用户只能查询和修改能看到的数据
逻辑上的独立性,屏蔽了真实表的结构带来的影响
性能差,数据库必须把对视图的查询转化成对基本表的查询,如果这个视图是由一个复杂的多表查询所定义,那么,即使是视图的一个简单查询,数据库也要把它变成一个复杂的结合体,需要花费一定的时间。
16、存储过程Procedure:
(1)标准组件式编程:存储过程创建后,可以在程序中被多次调用,而不必重新编写该存储过程的SQL语句。并且DBA可以随时对存储过程进行修改,对应用程序源代码毫无影响。
(2)更快的执行速度:如果某一操作包含大量的Transaction-SQL代码或分别被多次执行,那么存储过程要比批处理的执行速度快很多。因为存储过程是预编译的,在首次运行一个存储过程时查询,优化器对其进行分析优化,并且给出最终被存储在系统表中的执行计划。而批处理的Transaction-SQL语句在每次运行时都要进行编译和优化,速度相对要慢一些。
(3)增强SQL语言的功能和灵活性:存储过程可以用控制语句编写,有很强的灵活性,可以完成复杂的判杂的断和较复运算。
(4)减少网络流量:针对同一个数据库对象的操作(如查询、修改),如果这一操作所涉及的Transaction-SQL语句被组织进存储过程,那么当在客户计算机上调用该存储过程时,网络中传送的只是该调用语句,从而大大减少网络流量并降低了网络负载。
(5)作为一种安全机制来充分利用:通过对执行某一存储过程的权限进行限制,能够实现对相应的数据的访问权限的限制,避免了非授权用户对数据的访问,保证了数据的安全。
17、触发器Trigger:
18、游标Cursor:
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:
https://blog.csdn.net/a745233700/article/details/114242960