数据库面试题总结

什么是视图?

1、视图: 是虚拟的内存表;表:是实际在数据库中创建的表
2、视图 :只能进行select操作,大大加强了安全性,用户只能看到视图提供的数据;
表 :可以用update,insert,delete等sql语句修改表中的数据
3、视图:不会对性能产生影响,但是会产生资源消耗; 并且它是已经编好的SQL语句,把查询的结果呈现了出来;
表:它不是已经编译好的SQL语句;
4、视图:他的删除不会对表产生任何的影响,只会影响视图本身;

数据库主键到底是用自增长(INT)好还是UUID好?

主键的作用是什么?
自增的优缺点,应用场景

使用自增长做主键的优点:
1、很小的数据存储空间
2、性能最好
3、容易记忆
使用自增长做主键的缺点:
1、很难(并不是不能)处理分布式存储的数据表,尤其是需要合并表的情况下
2、安全性低,因为是有规律的,容易被非法获取数据

UUID的优缺点,应用场景

参考:https://blog.csdn.net/qq_23994787/article/details/90289743
https://blog.csdn.net/u010867667/article/details/54863270
UUID是128位整数(16字节)的全局唯一标识符(Universally Unique Identifier)。
UUID是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的。
优点:

  • 好处在分布式的软件系统中(比如:DCE/RPC, COM+,CORBA)就能体现出来,它能保证每个节点所生成的标识都不会重复。能够保证独立性,程序可以在不同的数据库间迁移,效果不受影响。
  • 跨服务器数据合并非常方便
  • 安全性较高
    缺点:
  • 比较占地方,和INT自增类型相比,存储一个UUID要花费更多的空间。
  • 对于InnoDB这种聚集主键类型的引擎来说,数据会按照主键进行排序,由于UUID的无序性,InnoDB会产生巨大的IO压力,此时不适合使用UUID做物理主键,可以把它作为逻辑主键,物理主键依然使用自增ID。
雪花算法的优缺点,应用场景

SnowFlake是一种介于自增长和UUID之间的一种主键(存储空间小、速度快、分布式、时间序列)它有如下优点

1.所有生成的id按时间趋势递增
2.整个分布式系统内不会产生ID碰撞(重复id,因为有datacenterId和workerId来做区分)
3.id生成的效率高

mysql都有哪些索引类型;为什么b+树,红黑树、b树为什么不好;

为什么说B树和B+树的每个节点对应一个磁盘页,IO操作最坏的情况下是树的高度?
简单剖析B树(B-Tree)与B+树

  • B+树索引、哈希索引;
  • hash索引查询单条确实比较快,但是他是无序的,但是却无法进行范围查询。
    如果我们要进行模糊查找的话,显然哈希表这种结构是不支持的,只能遍历这个表。而B+树则可以通过最左前缀原则快速找到对应的数据。
  • 因为红黑树子节点只有两个,造成树较高,树越高,那么查询事件复杂度就越高,并且需要更多的磁盘IO,严重影响性能。B树是一个多路搜索树,也就是每个节点可以有多个子节点,这样是为了降低树的高度,减少磁盘IO次数。

相比于B树,B+树有以下优势:
基础知识:在实际设计中,一个树的结点设为一个页,为什么这么干呢,因为磁盘预读是以页为单位的,所以这样的话一页就代表访问一次磁盘,也就是代表一次I/O操作。

更少的IO次数: B+树的非叶节点只包含键,而不包含真实数据,因此每个节点存储的键key个数比B树多很多(即阶m更大),因此B+树的高度更低,访问时所需要的IO次数更少。此外,由于每个节点存储的记录数更多,所以对访问局部性原理的利用更好,缓存命中率更高。
更适于范围查询: 在B树中进行范围查询时,首先通过二分查找找到要查找的下限,然后对B树进行中序遍历,直到找到查找的上限;而B+树通过二分查找,找到范围下限,然后只需要对链表进行遍历即可。
更稳定的查询效率: B树的查询时间复杂度在1到树高之间(分别对应记录在根节点和叶节点),而B+树的查询复杂度则稳定为树高,因为所有数据都在叶节点。

二叉查找树(BST):解决了排序的基本问题,但是由于无法保证平衡,可能退化为链表;
平衡二叉树(AVL):通过旋转解决了平衡的问题,但是旋转操作效率太低;
红黑树:通过舍弃严格的平衡和引入红黑节点,解决了AVL旋转效率过低的问题,但是在磁盘等场景下,树仍然太高,IO次数太多;
B树:通过将二叉树改为多路平衡查找树,解决了树过高的问题;
B+树:在B树的基础上,将非叶节点改造为不存储数据的纯索引节点,进一步降低了树的高度;此外将叶节点使用指针连接成链表,范围查询更加高效。

mysql的主键,唯一索引区别;

普通索引:最基本的索引,没有任何限制
唯一索引:与"普通索引"类似,不同的就是:索引列的值必须唯一,但允许有空值。
主键索引:它 是一种特殊的唯一索引,不允许有空值。
全文索引:仅可用于 MyISAM 表,针对较大的数据,生成全文索引很耗时好空间。
组合索引:为了更多的提高mysql效率可建立组合索引,遵循”最左前缀“原则。

  • 主键索引:
    主键索引可以做外键
    一个表最多只能创建一个主键索引,但可以创建多个唯一索引。
  • 唯一索引:
    被索引的数据列不允许包含重复的值

创建索引的原则

  • 使用短索引,如果对长字符串列进行索引,应该指定一个前缀长度,这样能够节省大量索引空间
  • 较频繁作为查询条件的字段才去创建索引
  • 更新频繁字段不适合创建索引
  • 若是不能有效区分数据的列不适合做索引列(如性别,男女未知,最多也就三种,区分度实在太低)
  • 定义有外键的数据列一定要建立索引。
  • 非空字段:应该指定列为NOT NULL,除非你想存储NULL。在mysql中,含有空值的列很难进行查询优化,因为它们使得索引、索引的统计信息以及比较运算更加复杂。你应该用0、一个特殊的值或者一个空串代替空值;
  • 索引字段越小越好:字段越小索引占据的空间也就越小。数据库的数据存储以页为单位一页存储的数据越多一次IO操作获取的数据越大效率越高。

怎么建索引

在执行CREATE TABLE语句时可以创建索引,也可以单独用CREATE INDEX或ALTER TABLE来为表增加索引。

  1. 新建表中添加索引
① 普通索引
create table t_dept(
  no int not null primary key,
  name varchar(20) null,
  sex varchar(2) null,
  info varchar(20) null,
  index index_no(no)
)
  1. 在已建表中添加索引
① 普通索引
create index index_name on t_dept(column_name);
  1. 以修改表的方式添加索引
① 普通索引
alter table t_dept add index index_name(column_name);

什么是聚簇索引?

聚簇索引:将数据存储与索引放到了一块,找到索引也就找到了数据
非聚簇索引:将数据存储于索引分开结构,索引结构的叶子节点指向了数据的对应行

联合索引是什么?为什么需要注意联合索引中的顺序?

MySQL使用索引时需要索引有序,假设现在建立了"name,age,school"的联合索引,那么索引的排序为: 先按照name排序,如果name相同,则按照age排序,如果age的值也相等,则按照school进行排序。

什么情况下索引失效

  • NOT条件 当查询条件为非时,索引定位就困难了,执行计划此时可能更倾向于全表扫描,这类的查询条件有:<>、NOT、in、not exists
select * from test where id<>500;
select * from test where id in (1,2,3,4,5);
select * from test where not in (6,7,8,9,0);
select * from test where not exists (select 1 from test_02 where test_02.id=test.id);
  • LIKE通配符 通配符%在前面的话,执行计划会更倾向于选择全表扫描。
sql select * from test where name like||'%';
  • 在索引字段上使用函数 查询条件上尽量不要对索引列使用函数,这样是不会走索引的,因为索引在建立时会和计算后可能不同,无法定位到索引。
select * from test where upper(name)='SUNYANG';
#修改为如下,可正常使用索引
select * from test where name=upper('sunyang');
--INDEX RANGE SCAN
  • 数据类型的转换 当索引字段存在隐式转换时,索引会失效。
  • 谓词运算 不能对索引列进行加减乘除的谓词运算,这会使索引失效。
select * from sunyang where id/2=:type_id;
#修改为如下,可正常使用索引
select * from sunyang where id=:type_id*2;

什么是最左匹配原则?

  • mysql创建复合索引的规则是首先会对复合索引的最左边的,也就是第一个字段的数据进行排序,在第一个字段的排序基础上,然后再对后面第二个字段进行排序。
    所以:第一个name字段是绝对有序的,而第二字段就是无序的了。
  • 通常情况下,直接使用第二个cid字段进行条件判断是用不到索引的,当然,可能会出现上面的使用index类型的索引。
  • mysql索引规则中要求复合索引要想使用第二个索引,必须先使用第一个索引的原因。(而且第一个索引必须是等值匹配)。

mysql覆盖索引

如果一个索引包含(或覆盖)所有需要查询的字段的值,称为‘覆盖索引’。即只需扫描索引而无须回表。
只扫描索引而无需回表的优点:
1.索引条目通常远小于数据行大小,只需要读取索引,则mysql会极大地减少数据访问量。
2.因为索引是按照列值顺序存储的,所以对于IO密集的范围查找会比随机从磁盘读取每一行数据的IO少很多。
3.一些存储引擎如myisam在内存中只缓存索引,数据则依赖于操作系统来缓存,因此要访问数据需要一次系统调用
4.innodb的聚簇索引,覆盖索引对innodb表特别有用。(innodb的二级索引在叶子节点中保存了行的主键值,所以如果二级主键能够覆盖查询,则可以避免对主键索引的二次查询)

覆盖索引必须要存储索引列的值,而哈希索引、空间索引和全文索引不存储索引列的值,所以mysql只能用B-tree索引做覆盖索引。

varchar与char的区别

  • char的特点
    char表示定长字符串,长度是固定的;
    如果插入数据的长度小于char的固定长度时,则用空格填充;
    因为长度固定,所以存取速度要比varchar快很多,甚至能快50%,但正因为其长度固定,所以会占据多余的空间,是空间换时间的做法;
    对于char来说,最多能存放的字符个数为255,和编码无关

  • varchar的特点
    varchar表示可变长字符串,长度是可变的;
    插入的数据是多长,就按照多长来存储;
    varchar在存取方面与char相反,它存取慢,因为长度不固定,但正因如此,不占据多余的空间,是时间换空间的做法;
    对于varchar来说,最多能存放的字符个数为65532

总之,结合性能角度(char更快)和节省磁盘空间角度(varchar更小),具体情况还需具体来设计数据库才是妥当的做法。

SQL语句

MySQL中In与Exists的区别

SELECT * FROM A WHERE A.id IN (SELECT id FROM B);
SELECT * FROM A WHERE EXISTS (SELECT * from B WHERE B.id = A.id);

内表:in,exists后面子查询里面的表,即上面代码中的B表。
外表:in,exists左边的外部查询的表,即上面代码中的A表。
Exists:把外部表的每条记录放到子查询里去查,若能查到数据则输出外部表的这条记录,没有查到则不输出。
In:先执行子查询,把子查询的结果拿到放到内存中来,然后外部表的每条记录去匹配子查询的结果,匹配上了则输出记录,匹配不上则不输出。

Exists

exists对外表用loop逐条查询,每次查询都会查看exists的条件语句,当exists里的条件语句能够返回记录行时(无论记录行是的多少,只要能返回),条件就为真,返回当前loop到的这条记录;反之,如果exists里的条件语句不能返回记录行,则当前loop到的这条记录被丢弃,exists的条件就像一个bool条件,当能返回结果集则为true,不能返回结果集则为false
如下:

select * from user where exists (select 1);

对user表的记录逐条取出,由于子条件中的select 1永远能返回记录行,那么user表的所有记录都将被加入结果集,所以与select * from user;是一样的。

又如下:

select * from user where exists (select * from user where user_id = 0);

可以知道对user表进行loop时,检查条件语句(select * from user where user_id = 0),由于user_id永远不为0,所以条件语句永远返回空集,条件永远为false,那么user表的所有记录都将被丢弃。、

总结:如果A表有n条记录,那么exists查询就是将这n条记录逐条取出,然后判断n遍exists条件。

EXISTS()查询会执行SELECT * FROM A查询,执行A.length次,并不会将EXISTS()查询结果结果进行缓存,因为EXISTS()查询返回一个布尔值true或flase,它只在乎EXISTS()的查询中是否有记录,与具体的结果集无关。

EXISTS()查询是将主查询的结果集放到子查询中做验证,根据验证结果是true或false来决定主查询数据结果是否得以保存。

IN

in查询相当于多个or条件的叠加,这个比较好理解,比如下面的查询:

select * from user where user_id in (1, 2, 3);

等效于

select * from user where user_id = 1 or user_id = 2 or user_id = 3;
SELECT   *  FROM A WHERE id IN (SELECT id FROM B);

以上in()中的查询只执行一次,它查询出B中的所有的id并缓存起来,然后检查A表中查询出的id在缓存中是否存在,如果存在则将A的查询数据加入到结果集中,直到遍历完A表中所有的结果集为止。

参考:https://blog.csdn.net/jinjiniao1/article/details/92666614
参考: https://blog.csdn.net/weixin_39539399/article/details/80851817

子查询

单行运算符:子查询结果只有一个:< > = <= >= !=
多行子查询:子查询结果是单列多行:in , any,all
多列子查询:子查询为多列,一定要在FROM后作为表,且一定要取别名,否则无法访问这张表中的字段。

mysql中int(10),char(10),varchar(10),DECIMAL(N,M)之间的区别

  • int(10)的10表示显示的数据的长度,不是存储数据的大小;
  • chart(10)和varchar(10)的10表示存储数据的大小,即表示存储多少个字符,不足10个就用空格补齐。
  • DECIMAL(N,M)中M值的是小数部分的位数,若插入的值未指定小数部分或者小数部分不足M位则会自动补到M位小数,若插入的值小数部分超过了M为则会发生截断,截取前M位小数。N值得是整数部分加小数部分的总长度,也即插入的数字整数部分不能超过N-M位,否则不能成功插入,会报超出范围的错误。

一条sql怎么优化?

  • explain分析sql语句,查看执行计划,分析索引是否用上,分析扫描行数
  • 避免使用 SELECT * FROM TABLE,用具体的列替换,避免全表扫描。
  • 避免在WHERE子句中对字段进行表达式操作,会导致引擎放弃使用索引而进行全表扫描。
  • 避免在WHERE子句中对字段进行函数操作,会导致引擎放弃使用索引而进行全表扫描。

explain解释

字段解释
ID每个select子句的标识ID
select_typeselect语句的类型
table表名
type当前表内访问的方式(下面详解)
possible_keys可能使用到的索引
key经过优化器评估最终使用的索引
key_length使用到的索引长度
ref引用到上一个表的列
rows最终记录索引要扫描经过的记录数
filtered表示存储引擎返回的数据在server层过滤后,剩下多少满足查询的记录数量的比例,注意是百分比,不是具体记录数。
Extra下面详解
select_type类型有哪些
摘抄自:https://blog.csdn.net/li1325169021/article/details/113732028
序号select_type 类型
--
1SIMPLE
2PRIMARY
3SUBQUERY
4DERIVED
5UNION
6UNION RESULT
7DEPENDENT SUBQUERY
8UNCACHEABLE SUBQUREY
Type:
解释
--
all全表扫描,MySQL遍历全表查询匹配行
index索引全扫描,MySQL遍历整个索引查找匹配行
range索引范围查找,常见于< <= > >= bweteen
ref使用非唯一索引扫描或唯一索引的前缀扫描,返回某个单独值的记录行,ref还经常出现在join操作中
const,system单表中最多的一个匹配行,查询起来很迅速,这个匹配行中的其他值可以被OPTIMIZER在当前查询中当作常量处理
NULL不适用访问表或者索引,直接得到结果
ref_or_null类似于ref区别条件中包含对NULL查询
index_merge索引合并优化
unique_subqueryIN的后面是一个查询主键字段的子查询
index_subquery与unique_subquery类似,区别在IN后面是查询非唯一索引的子查询等

Extra:

解释
Using index表示这个语句使用了覆盖索引,选择了索引a,不需要回表;
Using temporary表示使用了临时表;
Using filesort表示需要排序;
Deleting all rows对于DELETE,一些存储引擎(如MyISAM)支持一种处理方法,可以简单而快速地删除所有的行,如果引擎使用此优化,则会显示此值。
Using index表示这个语句使用了覆盖索引,选择了索引a,不需要回表;
Using where表示MySQL将对提取的条件进行过滤,过滤条件字段无索引;
Using join buffer(Block Nested Loop),Using join buffer(Batched Key Access)Block Nested - Loop Join算法,将外层循环的行/结果集存入join buffer中,内层循环的每一行与整个buffer中的记录做对比,从而减少内层循环的次数。优化器管理参数optimizer_switch中的block_nested_loop参数控制着BNL是否被用于优化器。默认条件下是开启的,若设置为OFF,优化器在选择join方式的时候会选择NLJ(Nested Loop Join)算法。

数据库的三大范式?

第一范式(1NF):强调的是列的原子性,即列不能够再分成其他几列。
第二范式(2NF):所有的非主键列必须完全依赖于主键,不允许有不依赖于主键的列。
第三范式(3NF):所有的非主键列必须直接依赖于主键,不允许有间接依赖于主键的列。

大表怎么优化,分库分表怎么做?存在什么问题?

垂直分表:把一个字段很多的表的按访问频次、是否是大字段的原则拆分为多个表,使业务清晰,提升性能。拆分后,尽量从业务角度避免联查,否则性能方面将得不偿失。

垂直分库:把多个表按业务分别存放在不同的库,不同服务器,访问压力被多服务器负载,提升性能,同时能提高整体架构的业务清晰度。

水平分库:可以把一个表的数据(按数据行)分到多个不同的库,每个库只有这个表的部分数据,这些库可以分布在不同服务器,提升性能。它不仅需要解决跨库带来的所有复杂问题,还要解决数据路由的问题(数据路由问题后边介绍)。

水平分表:可以把一个表的数据(按数据行)分到多个同一个数据库的多张表中,每个表只有这个表的部分数据,这样做能小幅提升性能,它仅仅作为水平分库的一个补充优化。

彻底搞清分库分表(垂直分库,垂直分表,水平分库,水平分表)
分库分表就是为了解决由于数据量过大而导致数据库性能降低的问题,将原来独立的数据库拆分成若干数据库组成 ,将数据大表拆分成若干数据表组成,使得单一数据库、单一数据表的数据量变小,从而达到提升数据库性能的目的。

垂直分表

定义:将一个表按照字段分成多表,每个表存储其中一部分字段。
作用:

  1. 为了避免IO争抢并减少锁表的几率,查看详情的用户与商品信息浏览互不影响
  2. 充分发挥热门数据的操作效率,商品信息的操作的高效率不会被商品描述的低效率所拖累。

通常我们按以下原则进行垂直拆分:

  • 把不常用的字段单独放在一张表;
  • 把text,blob等大字段拆分出来放在附表中;
  • 经常组合查询的列放在一张表中;
垂直分库

定义:指按照业务将表进行分类,分布到不同的数据库上面,每个库可以放在不同的服务器上,它的核心理念是专库专用。
作用:

  1. 解决业务层面的耦合,业务清晰
  2. 能对不同业务的数据进行分级管理、维护、监控、扩展等
  3. 高并发场景下,垂直分库一定程度的提升IO、数据库连接数、降低单机硬件资源的瓶颈

垂直分库通过将表按业务分类,然后分布在不同数据库,并且可以将这些数据库部署在不同服务器上,从而达到多个服务器共同分摊压力的效果,但是依然没有解决单表数据量过大的问题。

水平分库

定义:把同一个表的数据按一定规则拆到不同的数据库中,每个库可以放在不同的服务器上。
作用:

  1. 解决了单库大数据,高并发的性能瓶颈。
  2. 提高了系统的稳定性及可用性。

经过水平切分的优化,往往能解决单库存储量及性能瓶颈。但由于同一个表被分配在不同的数据库,需要额外进行数据操作的路由工作,因此大大提升了系统复杂度。

水平分表

定义:在同一个数据库内,把同一个表的数据按一定规则拆到多个表中。
作用:

  1. 优化单一表数据量过大而产生的性能问题
  2. 避免IO争抢并减少锁表的几率

库内的水平分表,解决了单一表数据量过大的问题,分出来的小表中只包含一部分数据,从而使得单个表的数据量变小,提高检索性能。

什么是刷盘?

刷盘:将缓冲区的数据写入到磁盘的过程。
数据写入计算机的两种方式:
一种是保证数据持久化,服务器或宕机或应用假死不会导致数据丢失,性能低
一种是应用数据写到操作系统buffer,何时持久化由操作系统决定,应用挂掉数据不会丢,服务器宕机未写盘的数据会丢,性能高

缓存击穿和缓存穿透的区别有哪些

缓存击穿是指缓存中没有但数据库中有的数据,缓存没读到数据,请求落到了数据库上,数据库压力瞬间增大,造成过大压力。
解决方法:

  1. 设置热点数据永远不过期。
  2. 加互斥锁 对于请求数据相同,只让一个线程取数据库取值,然后加载到缓存中,其他的请求只需要在缓存中访问就行。
  3. 布隆过滤器。当⽤户请求过 来,先判断⽤户发来的请求的值是否存在于布隆过滤器中。不存在的话,直接返回请求参数错误信息给客户端,存在的话才会访问数据库。

缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,如发起的数据特别大而不存在的数据。
解决方法:

  1. 接口层增加校验,把不符合要求的请求拦截掉;
  2. 在缓存中设置值为空的键值对,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用),防止攻击用户反复用同一个id暴力攻击。

redo log 与 undo log

https://blog.csdn.net/dongcheng_2015/article/details/120013137
redo log(重做日志)是用来持久化的,undo log(回滚日志)是用来维持原子性的。
redo log

  • 两部分组成:一是内存中的重做日志缓冲(redo log buffer),其是易失的;二是重做日志文件(redo log file),其是持久的。
  • 当事务提交(COMMIT)时,必须先将该事务的所有日志写入到重做日志文件进行持久化,事务的COMMIT操作完成才算完成。
  • 每次将重做日志缓冲写入重做日志文件,都需要调用一次fsync操作,将日志落到磁盘上。
  • 允许用户手工设置非持久性的情况发生,以此提高数据库的性能。即当事务提交时,日志不写入重做日志文件,而是等待一个时间周期后再执行fsync操作。但是当数据库发生宕机时,由于部分日志未刷新到磁盘,因此会丢失最后一段时间的事务。

(sync负责把缓存区上的东西排到写队列中(缓冲区->写队列),在由守护进程负责把队列里的东西写到磁盘上,而sync函数在把缓存区上的东西排到写队列后不管写队列中的内容是否写到磁盘上都立即返回。
fsync函数则是对指定文件的操作,而且必须等到写队列中的内容都写到磁盘后才返回)

undo log
undo是逻辑日志,因此只是将数据库逻辑地恢复到原来的样子。所有修改都被逻辑地取消了,但是数据结构和页本身在回滚之后可能大不相同。
当InnoDB存储引擎回滚时,它实际上做的是与先前相反的工作。对于每个INSERT,InnoDB存储引擎会完成一个DELETE;对于每个DELETE,InnoDB存储引擎会执行一个INSERT;对于每个UPDATE,InnoDB存储引擎会执行一个相反的UPDATE,将修改前的行放回去。
undo log会产生redo log,因为需要持久性的保护。

参考了以下博客:
MySQL数据库面试题(2020最新版)
索引失效的情况有哪些?索引何时会失效?
https://blog.csdn.net/qq_18657175/article/details/89465240
https://blog.csdn.net/jh993627471/article/details/79421363
https://blog.csdn.net/u010365819/article/details/102708476
https://blog.csdn.net/qq_28289405/article/details/86491432
https://blog.csdn.net/li1325169021/article/details/113732028

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值