MySQl(面试题持续更新中....)

1. 什么是内连接、外连接、交叉连接、笛卡尔积呢?

  • 内连接(inner join):取得两张表中满足存在连接匹配关系的记录。

  • 外连接(outer join):不只取得两张表中满足存在连接匹配关系的记录,还包括某张表(或两张表)中不满足匹配关系的记录。

  • 交叉连接(cross join):显示两张表所有记录一一对应,没有匹配关系进行筛选,它是笛卡尔积在 SQL 中的实现,如果 A 表有 m 行,B 表有 n 行,那么 A 和 B 交叉连接的结果就有 m*n 行。

  • 笛卡尔积:是数学中的一个概念,例如集合 A={a,b},集合 B={1,2,3},那么 A✖️B={,,,,,,}

2. 那 MySQL 的内连接、左连接、右连接有有什么区别?

MySQL 的连接主要分为内连接和外连接,外连接常用的有左连接、右连接

MySQl(面试题持续更新中....)_第1张图片

  • inner join 内连接,在两张表进行连接查询时,只保留两张表中完全匹配的结果集

  • left join 在两张表进行连接查询时,会返回左表所有的行,即使在右表中没有匹配的记录。

  • right join 在两张表进行连接查询时,会返回右表所有的行,即使在左表中没有匹配的记录。

3.说一下数据库的三大范式?

  • 第一范式:数据表中的每一列(每个字段)都不可以再拆分。例如用户表,用户地址还可以拆分成国家、省份、市,这样才是符合第一范式的。

  • 第二范式:在第一范式的基础上,非主键列完全依赖于主键,而不能是依赖于主键的一部分。例如订单表里,存储了商品信息(商品价格、商品类型),那就需要把商品 ID 和订单 ID 作为联合主键,才满足第二范式。

  • 第三范式:在满足第二范式的基础上,表中的非主键只依赖于主键,而不依赖于其他非主键。例如订单表,就不能存储用户信息(姓名、地址)。

三大范式的作用是为了控制数据库的冗余,是对空间的节省,实际上,一般互联网公司的设计都是反范式的,通过冗余一些数据,避免跨表跨库,利用空间换时间,提高性能 .

4.varchar 与 char 的区别?

char

  • char 表示定长字符串,长度是固定的;

  • 如果插入数据的长度小于 char 的固定长度时,则用空格填充;

  • 因为长度固定,所以存取速度要比 varchar 快很多,甚至能快 50%,但正因为其长度固定,所以会占据多余的空间,是空间换时间的做法;

  • 对于 char 来说,最多能存放的字符个数为 255,和编码无关

varchar

  • varchar 表示可变长字符串,长度是可变的;

  • 插入的数据是多长,就按照多长来存储;

  • varchar 在存取方面与 char 相反,它存取慢,因为长度不固定,但正因如此,不占据多余的空间,是时间换空间的做法;

  • 对于 varchar 来说,最多能存放的字符个数为 65532

日常的设计,对于长度相对固定的字符串,可以使用 char,对于长度不确定的,使用 varchar 更合适一些。

.blob 和 text 有什么区别?

  • blob 用于存储二进制数据,而 text 用于存储大字符串。

  • blob 没有字符集,text 有一个字符集,并且根据字符集的校对规则对值进行排序和比较

7.MySQL 中 in 和 exists 的区别?

MySQL 中的 in 语句是把外表和内表作 hash 连接,而 exists 语句是对外表作 loop 循环,每次 loop 循环再对内表进行查询。我们可能认为 exists 比 in 语句的效率要高,这种说法其实是不准确的,要区分情景:

  1. 如果查询的两个表大小相当,那么用 in 和 exists 差别不大。

  2. 如果两个表中一个较小,一个是大表,则子查询表大的用 exists,子查询表小的用 in。

  3. not in 和 not exists:如果查询语句使用了 not in,那么内外表都进行全表扫描,没有用到索引;而 not extsts 的子查询依然能用到表上的索引。所以无论那个表大,用 not exists 都比 not in 要快。

8.MySQL 里记录货币用什么字段类型比较好?

货币在数据库中 MySQL 常用 Decimal 和 Numric 类型表示,这两种类型被 MySQL 实现为同样的类型。他们被用于保存与货币有关的数据。

例如 salary DECIMAL(9,2),9(precision)代表将被用于存储值的总的小数位数,而 2(scale)代表将被用于存储小数点后的位数。存储在 salary 列中的值的范围是从-9999999.99 到 9999999.99。

DECIMAL 和 NUMERIC 值作为字符串存储,而不是作为二进制浮点数,以便保存那些值的小数精度。

之所以不使用 float 或者 double 的原因:因为 float 和 double 是以二进制存储的,所以有一定的误差。

9.MySQL 怎么存储 emoji?

MySQL 可以直接使用字符串存储 emoji。

但是需要注意的,utf8 编码是不行的,MySQL 中的 utf8 是阉割版的 utf8,它最多只用 3 个字节存储字符,所以存储不了表情。那该怎么办?

需要使用 utf8mb4 编码。

 alter table blogs modify content text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci not null;

10.drop、delete 与 truncate 的区别?

三者都表示删除,但是三者有一些差别:

DELETE TRUNCATE DROP
类型 属于 DML 属于 DDL
回滚 可回滚 不可回滚
删除内容 表结构还在,删除表的全部或者一部分数据行 表结构还在,删除表中的所有数据
删除速度 删除速度慢,需要逐行删除 删除速度快

因此,在不再需要一张表的时候,用 drop;在想删除部分数据行时候,用 delete;在保留表而删除所有数据的时候用 truncate。

11.UNION 与 UNION ALL 的区别?

  • 如果使用 UNION,会在表链接后筛选掉重复的记录行

  • 如果使用 UNION ALL,不会合并重复的记录行

  • 从效率上说,UNION ALL 要比 UNION 快很多,如果合并没有刻意要删除重复行,那么就使用 UNION All  

12.说说 MySQL 的基础架构?

MySQL 逻辑架构图主要分三层:

  • 客户端:最上层的服务并不是 MySQL 所独有的,大多数基于网络的客户端/服务器的工具或者服务都有类似的架构。比如连接处理、授权认证、安全等等。
  • Server 层:大多数 MySQL 的核心服务功能都在这一层,包括查询解析、分析、优化、缓存以及所有的内置函数(例如,日期、时间、数学和加密函数),所有跨存储引擎的功能都在这一层实现:存储过程、触发器、视图等。
  • 存储引擎层:第三层包含了存储引擎。存储引擎负责 MySQL 中数据的存储和提取。Server 层通过 API 与存储引擎进行通信。这些接口屏蔽了不同存储引擎之间的差异,使得这些差异对上层的查询过程透明

13.一条 SQL 查询语句在 MySQL 中如何执行的?

  • 先检查该语句是否有权限,如果没有权限,直接返回错误信息,如果有权限会先查询缓存 (MySQL8.0 版本以前)。
  • 如果没有缓存,分析器进行语法分析,提取 sql 语句中 select 等关键元素,然后判断 sql 语句是否有语法错误,比如关键词是否正确等等。
  • 语法解析之后,MySQL 的服务器会对查询的语句进行优化,确定执行的方案。
  • 完成查询优化后,按照生成的执行计划调用数据库引擎接口,返回执行结果。

14.MySQL 有哪些常见存储引擎?

MySQl(面试题持续更新中....)_第2张图片

MySQl(面试题持续更新中....)_第3张图片

MySQL5.5 之前,默认存储引擎是 MylSAM,5.5 之后变成了 InnoDB。

InnoDB 支持的哈希索引是自适应的,InnoDB 会根据表的使用情况自动为表生成哈希索引,不能人为干预是否在一张表中生成哈希索引。

MySQL 5.6 开始 InnoDB 支持全文索引。

那存储引擎应该怎么选择?

大致上可以这么选择:

  • 大多数情况下,使用默认的 InnoDB 就够了。如果要提供提交、回滚和恢复的事务安全(ACID 兼容)能力,并要求实现并发控制,InnoDB 就是比较靠前的选择了。
  • 如果数据表主要用来插入和查询记录,则 MyISAM 引擎提供较高的处理效率。
  • 如果只是临时存放数据,数据量不大,并且不需要较高的数据安全性,可以选择将数据保存在内存的 MEMORY 引擎中,MySQL 中使用该引擎作为临时表,存放查询的中间结果。

使用哪一种引擎可以根据需要灵活选择,因为存储引擎是基于表的,所以一个数据库中多个表可以使用不同的引擎以满足各种性能和实际需求。使用合适的存储引擎将会提高整个数据库的性能

15.InnoDB 和 MylSAM 主要有什么区别?

存储结构:每个 MyISAM 在磁盘上存储成三个文件;InnoDB 所有的表都保存在同一个数据文件中(也可能是多个文件,或者是独立的表空间文件),InnoDB 表的大小只受限于操作系统文件的大小,一般为 2GB。

2. 事务支持:MyISAM 不提供事务支持;InnoDB 提供事务支持事务,具有事务(commit)、回滚(rollback)和崩溃修复能力(crash recovery capabilities)的事务安全特性。

3   最小锁粒度:MyISAM 只支持表级锁,更新时会锁住整张表,导致其它查询和更新都会被阻塞 InnoDB 支持行级锁。

4. 索引类型:MyISAM 的索引为非聚簇索引,数据结构是 B 树;InnoDB 的索引是聚簇索引,数据结构是 B+树。

5.   主键必需:MyISAM 允许没有任何索引和主键的表存在;InnoDB 如果没有设定主键或者非空唯一索引,**就会自动生成一个 6 字节的主键(用户不可见)**,数据是主索引的一部分,附加索引保存的是主索引的值。

6. 表的具体行数:MyISAM 保存了表的总行数,如果 select count(*) from table;会直接取出出该值; InnoDB 没有保存表的总行数,如果使用 select count(*) from table;就会遍历整个表;但是在加了 wehre 条件后,MyISAM 和 InnoDB 处理的方式都一样。

7.   外键支持:MyISAM 不支持外键;InnoDB 支持外键。

16、MySQL 日志文件有哪些?分别介绍下作用? 

MySQl(面试题持续更新中....)_第4张图片

MySQL 日志文件有很多,包括 :

  • 错误日志(error log):错误日志文件对 MySQL 的启动、运行、关闭过程进行了记录,能帮助定位 MySQL 问题。
  • 慢查询日志(slow query log):慢查询日志是用来记录执行时间超过 long_query_time 这个变量定义的时长的查询语句。通过慢查询日志,可以查找出哪些查询语句的执行效率很低,以便进行优化。
  • 一般查询日志(general log):一般查询日志记录了所有对 MySQL 数据库请求的信息,无论请求是否正确执行。
  • 二进制日志(bin log):关于二进制日志,它记录了数据库所有执行的 DDL 和 DML 语句(除了数据查询语句 select、show 等),以事件形式记录并保存在二进制文件中。

还有两个 InnoDB 存储引擎特有的日志文件:

  • 重做日志(redo log):重做日志至关重要,因为它们记录了对于 InnoDB 存储引擎的事务日志。
  • 回滚日志(undo log):回滚日志同样也是 InnoDB 引擎提供的日志,顾名思义,回滚日志的作用就是对数据进行回滚。当事务对数据库进行修改,InnoDB 引擎不仅会记录 redo log,还会生成对应的 undo log 日志;如果事务执行失败或调用了 rollback,导致事务需要回滚,就可以利用 undo log 中的信息将数据回滚到修改之前的样子。

17.binlog 和 redo log 有什么区别?

  • bin log 会记录所有与数据库有关的日志记录,包括 InnoDB、MyISAM 等存储引擎的日志,而 redo log 只记 InnoDB 存储引擎的日志。
  • 记录的内容不同,bin log 记录的是关于一个事务的具体操作内容,即该日志是逻辑日志。而 redo log 记录的是关于每个页(Page)的更改的物理情况。
  • 写入的时间不同,bin log 仅在事务提交前进行提交,也就是只写磁盘一次。而在事务进行的过程中,却不断有 redo ertry 被写入 redo log 中。
  • 写入的方式也不相同,redo log 是循环写入和擦除,bin log 是追加写入,不会覆盖已经写的文件。

18.一条更新语句怎么执行的了解吗?

更新语句的执行是 Server 层和引擎层配合完成,数据除了要写入表中,还要记录相应的日志

MySQl(面试题持续更新中....)_第5张图片

  1. 执行器先找引擎获取 ID=2 这一行。ID 是主键,存储引擎检索数据,找到这一行。如果 ID=2 这一行所在的数据页本来就在内存中,就直接返回给执行器;否则,需要先从磁盘读入内存,然后再返回。
  2. 执行器拿到引擎给的行数据,把这个值加上 1,比如原来是 N,现在就是 N+1,得到新的一行数据,再调用引擎接口写入这行新数据。
  3. 引擎将这行新数据更新到内存中,同时将这个更新操作记录到 redo log 里面,此时 redo log 处于 prepare 状态。然后告知执行器执行完成了,随时可以提交事务。
  4. 执行器生成这个操作的 binlog,并把 binlog 写入磁盘。
  5. 执行器调用引擎的提交事务接口,引擎把刚刚写入的 redo log 改成提交(commit)状态,更新完成。

从上图可以看出,MySQL 在执行更新语句的时候,在服务层进行语句的解析和执行,在引擎层进行数据的提取和存储;同时在服务层对 binlog 进行写入,在 InnoDB 内进行 redo log 的写入。

不仅如此,在对 redo log 写入时有两个阶段的提交,一是 binlog 写入之前prepare状态的写入,二是 binlog 写入之后commit状态的写入。

那为什么要两阶段提交呢?

为什么要两阶段提交呢?直接提交不行吗?

我们可以假设不采用两阶段提交的方式,而是采用“单阶段”进行提交,即要么先写入 redo log,后写入 binlog;要么先写入 binlog,后写入 redo log。这两种方式的提交都会导致原先数据库的状态和被恢复后的数据库的状态不一致。

先写入 redo log,后写入 binlog:

在写完 redo log 之后,数据此时具有crash-safe能力,因此系统崩溃,数据会恢复成事务开始之前的状态。但是,若在 redo log 写完时候,binlog 写入之前,系统发生了宕机。此时 binlog 没有对上面的更新语句进行保存,导致当使用 binlog 进行数据库的备份或者恢复时,就少了上述的更新语句。从而使得id=2这一行的数据没有被更新。

先写入 binlog,后写入 redo log:

写完 binlog 之后,所有的语句都被保存,所以通过 binlog 复制或恢复出来的数据库中 id=2 这一行的数据会被更新为 a=1。但是如果在 redo log 写入之前,系统崩溃,那么 redo log 中记录的这个事务会无效,导致实际数据库中id=2这一行的数据并没有更新

简单说,redo log 和 binlog 都可以用于表示事务的提交状态,而两阶段提交就是让这两个状态保持逻辑上的一致。

19.redo log 怎么刷入磁盘的知道吗?

redo log 的写入不是直接落到磁盘,而是在内存中设置了一片称之为redo log buffer的连续内存空间,也就是redo 日志缓冲区

MySQl(面试题持续更新中....)_第6张图片

什么时候会刷入磁盘?

在如下的一些情况中,log buffer 的数据会刷入磁盘:

  • log buffer 空间不足时

log buffer 的大小是有限的,如果不停的往这个有限大小的 log buffer 里塞入日志,很快它就会被填满。如果当前写入 log buffer 的 redo 日志量已经占满了 log buffer 总容量的大约一半左右,就需要把这些日志刷新到磁盘上。

  • 事务提交时

在事务提交时,为了保证持久性,会把 log buffer 中的日志全部刷到磁盘。注意,这时候,除了本事务的,可能还会刷入其它事务的日志。

  • 后台线程输入

有一个后台线程,大约每秒都会刷新一次log buffer中的redo log到磁盘。

  • 正常关闭服务器时
  • 触发 checkpoint 规则

重做日志缓存、重做日志文件都是以块(block)的方式进行保存的,称之为重做日志块(redo log block),块的大小是固定的 512 字节。我们的 redo log 它是固定大小的,可以看作是一个逻辑上的 log group,由一定数量的log block 组成。

20.慢 SQL 如何定位呢?

慢 SQL 的监控主要通过两个途径:

  • 慢查询日志:开启 MySQL 的慢查询日志,再通过一些工具比如 mysqldumpslow 去分析对应的慢查询日志,当然现在一般的云厂商都提供了可视化的平台。
  • 服务监控:可以在业务的基建中加入对慢 SQL 的监控,常见的方案有字节码插桩、连接池扩展、ORM 框架过程,对服务运行中的慢 SQL 进行监控和告警.

21.有哪些方式优化慢 SQL?

慢 SQL 的优化,主要从两个方面考虑,SQL 语句本身的优化,以及数据库设计的优化

MySQl(面试题持续更新中....)_第7张图片

避免不必要的列

这个是老生常谈,但还是经常会出的情况,SQL 查询的时候,应该只查询需要的列,而不要包含额外的列,像slect * 这种写法应该尽量避免。

#分页优化

在数据量比较大,分页比较深的情况下,需要考虑分页的优化。

例如:

select * from table where type = 2 and level = 9 order by id asc limit 190289,10;

优化方案:

  • 延迟关联

先通过 where 条件提取出主键,在将该表与原数据表关联,通过主键 id 提取数据行,而不是通过原来的二级索引提取数据行

例如:

select a.* from table a, 
 (select id from table where type = 2 and level = 9 order by id asc limit 190289,10 ) b
 where a.id = b.id
  • 书签方式

书签方式就是找到 limit 第一个参数对应的主键值,根据这个主键值再去过滤并 limit

例如:

  select * from table where id >
  (select * from table where type = 2 and level = 9 order by id asc limit 190
#索引优化

合理地设计和使用索引,是优化慢 SQL 的利器。

利用覆盖索引

InnoDB 使用非主键索引查询数据时会回表,但是如果索引的叶节点中已经包含要查询的字段,那它没有必要再回表查询了,这就叫覆盖索引

例如对于如下查询:

select name from test where city='上海'

我们将被查询的字段建立到联合索引中,这样查询结果就可以直接从索引中获取

alter table test add index idx_city_name (city, name);

低版本避免使用 or 查询

在 MySQL 5.0 之前的版本要尽量避免使用 or 查询,可以使用 union 或者子查询来替代,因为早期的 MySQL 版本使用 or 查询可能会导致索引失效,高版本引入了索引合并,解决了这个问题。

避免使用 != 或者 <> 操作符

SQL 中,不等于操作符会导致查询引擎放弃查询索引,引起全表扫描,即使比较的字段上有索引

解决方法:通过把不等于操作符改成 or,可以使用索引,避免全表扫描

例如,把column<>’aaa’,改成column>’aaa’ or column<’aaa’,就可以使用索引了

适当使用前缀索引

适当地使用前缀所云,可以降低索引的空间占用,提高索引的查询效率。

比如,邮箱的后缀都是固定的“@xxx.com”,那么类似这种后面几位为固定值的字段就非常适合定义为前缀索引

alter table test add index index2(email(6));

PS:需要注意的是,前缀索引也存在缺点,MySQL 无法利用前缀索引做 order by 和 group by 操作,也无法作为覆盖索引

避免列上函数运算

要避免在列字段上进行算术运算或其他表达式运算,否则可能会导致存储引擎无法正确使用索引,从而影响了查询的效率

select * from test where id + 1 = 50;
select * from test where month(updateTime) = 7;

正确使用联合索引

使用联合索引的时候,注意最左匹配原则。

#JOIN 优化

优化子查询

尽量使用 Join 语句来替代子查询,因为子查询是嵌套查询,而嵌套查询会新创建一张临时表,而临时表的创建与销毁会占用一定的系统资源以及花费一定的时间,同时对于返回结果集比较大的子查询,其对查询性能的影响更大

小表驱动大表

关联查询的时候要拿小表去驱动大表,因为关联的时候,MySQL 内部会遍历驱动表,再去连接被驱动表。

比如 left join,左表就是驱动表,A 表小于 B 表,建立连接的次数就少,查询速度就被加快了。

 select name from A left join B ;

适当增加冗余字段

增加冗余字段可以减少大量的连表查询,因为多张表的连表查询性能很低,所有可以适当的增加冗余字段,以减少多张表的关联查询,这是以空间换时间的优化策略

避免使用 JOIN 关联太多的表

《阿里巴巴 Java 开发手册》规定不要 join 超过三张表,第一 join 太多降低查询的速度,第二 join 的 buffer 会占用更多的内存。

如果不可避免要 join 多张表,可以考虑使用数据异构的方式异构到 ES 中查询。

#排序优化

利用索引扫描做排序

MySQL 有两种方式生成有序结果:其一是对结果集进行排序的操作,其二是按照索引顺序扫描得出的结果自然是有序的

但是如果索引不能覆盖查询所需列,就不得不每扫描一条记录回表查询一次,这个读操作是随机 IO,通常会比顺序全表扫描还慢

因此,在设计索引时,尽可能使用同一个索引既满足排序又用于查找行

例如:

--建立索引(date,staff_id,customer_id)
select staff_id, customer_id from test where date = '2010-01-01' order by staff_id,customer_id;

只有当索引的列顺序和 ORDER BY 子句的顺序完全一致,并且所有列的排序方向都一样时,才能够使用索引来对结果做排序

#UNION 优化

条件下推

MySQL 处理 union 的策略是先创建临时表,然后将各个查询结果填充到临时表中最后再来做查询,很多优化策略在 union 查询中都会失效,因为它无法利用索引

最好手工将 where、limit 等子句下推到 union 的各个子查询中,以便优化器可以充分利用这些条件进行优化

此外,除非确实需要服务器去重,一定要使用 union all,如果不加 all 关键字,MySQL 会给临时表加上 distinct 选项,这会导致对整个临时表做唯一性检查,代价很高

 

你可能感兴趣的:(mysql,数据库)