> 文章列表 > 面试必会-MySQL篇

面试必会-MySQL篇

面试必会-MySQL篇

1. Mysql查询语句的书写顺序

Select [distinct ] <字段名称>

from 表1 [ <join类型> join 表2 on <join条件> ]

where <where条件>

group by <字段>

having <having条件>

order by <排序字段>

limit <起始偏移量,行数>

2. Mysql查询语句的执行顺序

(8)Select

(9)distinct 字段名1,字段名2,

(7)[fun(字段名)]

(1)from 表1

(3)<join类型>join 表2

(2)on <join条件>

(4)where <where条件>

(5)group by <字段>

(6)having <having条件>

(10)order by <排序字段>

(11)limit <起始偏移量,行数>

3. Mysql 如何实现多表查询

MYSQL多表查询主要使用连接查询 , 连接查询的方式主要有 :

  • 内连接

  • 隐式内连接 : Select 字段 From 表A , 表B where 连接条件

  • 显式内连接 : Select 字段 From 表A inner join 表B on 连接条件

  • 外连接

  • 左外连接 : Select 字段 From 表A left join 表B on 连接条件

  • 右外连接 : Select 字段 From 表A right join 表B on 连接条件

  • 全外连接:(很少用)

  • 交叉连接 : 笛卡尔效应 (很少用)

4. MYSQL内连接和外连接的区别 ?

  • 内连接:只有两个元素表相匹配的才能在结果集中显示。

  • 外连接:左外连接: 左边为驱动表,驱动表的数据全部显示,匹配表的不匹配的不会显示。

  • 右外连接:右边为驱动表,驱动表的数据全部显示,匹配表的不匹配的不会显示。

  • 全外连接:连接的表中不匹配的数据全部会显示出来。

  • 交叉连接:笛卡尔效应,显示的结果是链接表数的乘积。

5. CHAR和VARCHAR的区别?

  1. char的长度是不可变的,用空格填充到指定长度大小,而varchar的长度是可变的。

  1. char的存取速度比varchar要快得多

  1. char的存储方式是:对英文字符(ASCII)占用1个字节,对一个汉字占用两个字节。

varchar的存储方式是:对每个英文字符占用2个字节,汉字也占用2个字节。

6. 索引的底层数据结构了解过嘛 ?

索引是是在存储引擎中实现的,也就是说不同的存储引擎,会使用不同的索引。

InnoDB引擎默认用的是B+树,

memory存储引擎把HASH当成默认索引,

myisam存储引擎支持B+和全文索引,默认也是B+树。

B+树:

根和子节点,存储的是索引值,叶子节点存储的数据。

一个节点存储的索引值,取决与我们索引值的大小。默认一个节点页,16K 1K = 1024B

一个叶子节点存储的记录,取决于一条记录的大小,比如一条记录是1KB,一个叶能存16条

从存储角度,索引分为聚集索引与非聚集索引

聚集索引(主键):叶子节点存储的值是我们插入的原始数据

非聚集索引:叶子节点存储的是索引与主键值的对应关系。

面试必会-MySQL篇

B+树与B树:

最大区别,B树的每个节点都存储数据。

面试必会-MySQL篇

B+Tree是B-Tree的变种,如图所示:

面试必会-MySQL篇

我们可以看到,两部分:

  • 绿色框框起来的部分,是索引部分,仅仅起到索引数据的作用,不存储数据。

  • 红色框框起来的部分,是数据存储部分,在其叶子节点中要存储具体的数据。形成单向链表结构

拓展-一个三阶(三层高)的B+树,可以存储多少条数据:

非叶子节点都是由key+指针域组成的,一个key占8字节,一个指针占6字节,而一个节点总共容量是16KB,那么可以计算出一个节点可以存储的元素个数:16*1024字节 / (8+6)=1170个元素。

  • 查看mysql索引节点大小:show global status like 'innodb_page_size'; -- 节点大小:16384

当根节点中可以存储1170个元素,那么根据每个元素的地址值又会找到下面的子节点,每个子节点也会存储1170个元素,那么第二层即第二次IO的时候就会找到数据大概是:1170*1170=135W。也就是说B+Tree数据结构中只需要经历两次磁盘IO就可以找到135W条数据。

对于第二层每个元素有指针,那么会找到第三层,第三层由key+数据组成,假设key+数据总大小是1KB,而每个节点一共能存储16KB,所以一个第三层一个节点大概可以存储16个元素(即16条记录)。那么结合第二层每个元素通过指针域找到第三层的节点,第二层一共是135W个元素,那么第三层总元素大小就是:135W*16结果就是2000W+的元素个数。

结合上述分析B+Tree有如下优点:

  • 千万条数据,B+Tree可以控制在小于等于3的高度

  • 所有的数据都存储在叶子节点上,并且底层已经实现了按照索引进行排序,还可以支持范围查询,叶子节点是一个双向链表,支持从小到大或者从大到小查找

关于B+树数据结构,感兴趣的同学可以通过一个网站进行演示:

https://www.cs.usfca.edu/~galles/visualization/BPlusTree.html

面试必会-MySQL篇

插入一组数据: 100 65 169 368 900 556 780 35 215 1200 234 888 158 90 1000 88 120 268 250 。然后观察一些数据插入过程中,节点的变化情况。

面试必会-MySQL篇

7. 索引的类型

1. 按数据结构形式

B+Tree索引(InnoDB引擎默认)、Hash索引(memory引擎默认)、Full-text索引(myisam存储引擎支持)

2. 按物理存储分类

聚集索引(聚簇)、非聚集索引(非聚簇,二级索引)

3. 按字段特性分类

主键索引、唯一索引、普通索引、全文索引

  • 普通索引:MySQL中基本索引类型,没有什么限制,允许在定义索引的列中插入重复值和空值,纯粹为了查询数据更快一点。

  • 唯一索引:索引列中的值必须是唯一的,但是允许为空值

  • 主键索引:是一种特殊的唯一索引,不允许有空值,一个表只能由一个主键索引。

  • 全文索引: 只有在MyISAM引擎、InnoDB(5.6以后)上才能使⽤用,而且只能在CHAR,VARCHAR,TEXT类型字段上使⽤用全⽂文索引。

4. 按字段个数分类

单列索引 : 在MYSQL数据库表的某一列上面创建的索引叫单列索引 , 单列索引又分为

组合索引(联合索引) : 在MYSQL数据库表的多个字段组合上创建的索引 , 称为组合索引也叫联合索引

  • 组合索引的使用,需要遵循左前缀原则

  • 一般情况下,建议使用组合索引代替单列索引(主键索引除外)

create table index idxNameage on 表(name,age,addr)

8. 聚集索引与非聚集索区别 ?

分类

含义

特点

聚集(聚簇)索引(Clustered Index)

将数据存储与索引放到了一块,索引结构的叶子节点保存了行数据

必须有,而且只有一个

二级索引/非聚集(非聚簇)(Secondary Index)

将数据与索引分开存储,索引结构的叶子节点关联的是对应的主键

可以存在多个

聚集索引选取规则:

  • 如果存在主键,主键索引就是聚集索引。

  • 如果不存在主键,将使用第一个唯一(UNIQUE)索引作为聚集索引。

  • 如果表没有主键,或没有合适的唯一索引,则InnoDB会自动生成一个rowid作为隐藏的聚集索引。

8. MYSQL支持的存储引擎有哪些 ?

面试必会-MySQL篇

MYSQL存储引擎有很多, 常用的就二种 : MyISAMInnoDB , 者两种存储引擎的区别 ;

  • MyISAM支持256TB的数据存储 , InnerDB只支持64TB的数据存储

  • MyISAM 不支持事务 , InnerDB支持事务

  • MyISAM 不支持外键 , InnerDB支持外键

9. 如何创建索引

创建索引

create  [ unique ]  index 索引名 on  表名 (字段名,... ) ;

为tb_emp表的name字段建立一个索引

create index idx_emp_name on tb_emp(name);

为tb_emp表的name、username字段建立一个联合索引

create index idx_emp_name_username on tb_emp(name,username);

10. 在一个非主键字段上创建了索引, 想要根据该字段查询到数据, 需要查询几次 ?

需要查询二次

如果使用MyISAM存储引擎 , 会首先根据索引查询到数据行指针, 再根据指针获取数据

如果是InnoDB存储引擎 , 会根据索引查找指定数据关联的主键ID , 再根据主键ID去主键索引中查找数据

11. 知道什么是回表查询嘛 ?

当我们为一张表的name字段建立了索引 , 执行如下查询语句 :

select name,age from user where name='Alice'

那么获取到数据的过程为 :

  1. 根据name='Alice'查找索引树 , 定位到匹配数据的主键值为 id=18

面试必会-MySQL篇
  1. 根据id=18到主索引获取数据记录 (回表查询)

面试必会-MySQL篇

**先定位主键值,再定位行记录就是所谓的回表查询,它的性能较扫一遍索引树低 **

12. 知道什么叫覆盖索引嘛 ?

覆盖索引是指只需要在一棵索引树上就能获取SQL所需的所有列数据 , 因为无需回表查询效率更高

实现覆盖索引的常见方法是:将被查询的字段,建立到联合索引里去。

执行如下查询语句 : select name,age from user where name='Alice'

因为要查询 name 和 age二个字段 , 那么我们可以建立组合索引

create index index_name_age on user(name,age) 

那么索引存储结构如下 :

面试必会-MySQL篇

这种情况下, 执行select name,age from user where name='Alice' , 会先根据name='Alice', 找到记录 , 这条记录的索引上刚好又包含了 age 数据 , 直接把 Alice 77数据返回 , 就不会执行回表查询 , 这就是覆盖索引

13. 知道什么是左前缀原则嘛 ?

在mysql建立联合索引时会遵循左前缀匹配的原则,即最左优先,在检索数据时从联合索引的最左边开始匹配,组合索引的第一个字段必须出现在查询组句中,这个索引才会被用到 ;

如果索引了多列(联合索引),要遵守最左前缀法则。最左前缀法则指的是查询从索引的最左列开始,并且不跳过索引中的列。如果跳跃某一列,索引将会部分失效(后面的字段索引失效)。

例如 : create index index_age_name_sex on tb_user(age,name,sex);

上述SQL语句对 age,name和sex建一个组合索引index_age_name_sex ,实际上这条语句相当于建立了(age) , (age,name) , (age,name,sex)三个索引 .

select * from tb_user where age = 49 ;  -- 使用索引select * from tb_user where age = 49 and name = 'Alice' ;  -- 使用索引select * from tb_user where age = 49 and name = 'Alice' and sex = 'man';  -- 使用索引select * from tb_user where age = 49  and sex = 'man';  -- 使用索引 , 但是只有 age 匹配索引 sex没有走索引select * from tb_user where name = 'Alice' and age = 49 and sex = 'man' ;  -- 使用索引 ,  因为MySQL的查询优化器会自动调整 where 子句的条件顺序以使用适合的索引select * from tb_user where name = 'Alice'  and sex = 'man' ;  -- 不会使用索引

14. 什么情况下索引会失效 ?

MySQL 索引通常是被用于提高 WHERE 条件的数据行匹配时的搜索速度,编写合理化的SQL能够提高SQL的执行效率

  1. 不要在列上使用函数和进行运算

  1. 不要在列上使用函数,这将导致索引失效而进行全表扫描。

  1. 尽量避免使用 != 或 <> 等否定操作符

联合索引中,出现范围查询(>,<),范围查询右侧的列索引失效。

比如如下查询: age+status

 select * from tb_user where profession = '软件工程' and age > 30 and status = '0';

当范围查询使用> 或 < 时,走联合索引了,但是索引的长度只有age的长度,就说明范围查询右边的status字段是没有走索引的。

select * from tb_user where profession = '软件工程' and age >= 30 and status = '0';

当范围查询使用>= 或 <= 时,走联合索引了,索引的长度是age+status的长度,就说明所有的字段都是走索引的。

所以,在业务允许的情况下,尽可能的使用类似于 >= 或 <= 这类的范围查询,而避免使用 > 或 < 。

  1. 尽量避免使用 or 来连接条件

用or分割开的条件, 如果or前的条件中的列有索引,而后面的列中没有索引,那么涉及的索引都不会被用到。

 select * from tb_user where id = 10 or age = 23;select * from tb_user where phone = '17799990017' or age = 23; 

由于age没有索引,所以即使id、phone有索引,索引也会失效。所以需要针对于age也要建立索引。

当or连接的条件,左右两侧字段都有索引时,索引才会生效。

  1. 多个单列索引并不是最佳选择,建立组合索引代替多个单列索引, 可以避免回表查询

  1. 查询中的某个列有范围查询,则其右边所有列都无法使用索引优化查找

  1. 索引不会包含有NULL值的列

  1. 当查询条件左右两侧类型不匹配的时候会发生隐式转换,隐式转换带来的影响就是可能导致索引失效而进行全表扫描。

  1. like 语句的索引失效问题

like 的方式进行查询,在 like “value%” 可以使用索引,但是对于 like “%value%” 这样的方式,执行全表查询

  1. 尽量不要使用in或not in

使用in查询数据,会进行全表扫描,降低效率

  1. 对于连续的数值,能用 between 就不要用 in 了:

#优化前: 
select id from t where num in(8,23,99) 
#优化后
select id from t where num between 1 and 3
  1. 必须使用in 可以使用使用union all 或 union 拆分sql

select id from t where id = 7
union all
select id from t where id = 5
union all
select id from t where id = 27

15. 索引是越多越好嘛? 什么样的字段需要建索引, 什么样的字段不需要 ?

需要创建索引情况

  1. 主键自动建立主键索引

  1. 频繁作为查询条件的字段应该创建索引

  1. 多表关联查询中,关联字段应该创建索引 (on 两边都要创建索引)

  1. 查询中排序的字段,应该创建索引

  1. 频繁查找字段 , 应该创建索引

  1. 查询中统计或者分组字段,应该创建索引

不要创建索引情况

  1. 表记录太少

  1. 经常进⾏行行增删改操作的表

  1. 频繁更新的字段

  1. where条件里使用频率不高的字段

索引设计原则

1). 针对于数据量较大,且查询比较频繁的表建立索引。

2). 针对于常作为查询条件(where)、排序(order by)、分组(group by)操作的字段建立索引。

3). 尽量选择区分度高的列作为索引,尽量建立唯一索引,区分度越高,使用索引的效率越高。

4). 如果是字符串类型的字段,字段的长度较长,可以针对于字段的特点,建立前缀索引。

5). 尽量使用联合索引,减少单列索引,查询时,联合索引很多时候可以覆盖索引,节省存储空间,避免回表,提高查询效率。

6). 要控制索引的数量,索引并不是多多益善,索引越多,维护索引结构的代价也就越大,会影响增删改的效率。

7). 如果索引列不能存储NULL值,请在创建表时使用NOT NULL约束它。当优化器知道每列是否包含NULL值时,它可以更好地确定哪个索引最有效地用于查询。

16.前缀索引、单列索引与联合索引

当字段类型为字符串(varchar,text,longtext等)时,有时候需要索引很长的字符串,这会让索引变得很大,查询时,浪费大量的磁盘IO, 影响查询效率。此时可以只将字符串的一部分前缀,建立索引,这样可以大大节约索引空间,从而提高索引效率。

create index  idx_xxxx on table_name(column(n)) ;

为tb_user表的email字段,建立长度为5的前缀索引。

create index idx_email_5 on tb_user(email(5)); 

前缀长度

可以根据索引的选择性来决定,而选择性是指不重复的索引值(基数)和数据表的记录总数的比值,索引选择性越高则查询效率越高, 唯一索引的选择性是1,这是最好的索引选择性,性能也是最好的。

select  count(distinct email) / count(*)   from  tb_user ;
select  count(distinct substring(email,1,5)) / count(*)  from  tb_user ;

单列索引:即一个索引只包含单个列。

联合索引:即一个索引包含了多个列。

17. mysql的性能优化

  1. 从设计方面 选择合适的存储引擎 , 合适的字段类型 , 遵循范式(反范式设计)

存储引擎 :
不需要事务, 不需要外键读写较多的的使用MyIsam
需要事务, 需要外键的使用InnoDB
合适的字段类型 , 例如 :
定长字符串用char , 不定长用varchar
状态, 性别等有限数量值的用tinyint
遵循范式 :
第一范式1NF,原子性
第二范式2NF,消除部分依赖
第三范式3NF,消除传递依赖

• 第⼀范式:1NF 原⼦性,列或者字段不能再分,要求属性具有原⼦性,不可再分解; 单一属性由基本类型构成,包括整型、实数、字符型、逻辑型、日期型等。

• 第⼆范式:2NF 唯⼀性,⼀张表只说⼀件事,是对记录的惟⼀性约束,要求记录有惟 ⼀标识;

• 第三范式:3NF 直接性,数据不能存在传递关系,即每个属性都跟主键有直接关系, ⽽不是间接关系。

2.从功能方面可以对索引优化,采用缓存缓解数据库压力,分库分表。

3.从架构方面可以采用主从复制,读写分离,负载均衡

18. MYSQL超大分页怎么处理 ?

#效率低
select * from table where limit 1000000,10 

正确的处理方法是 : 先快速定位需要获取的id再关联查询获取数据

# 效率高 使用覆盖索引
select * from table where id in (select id from table limit 100000,10)
# 效率高 定位数据,再使用覆盖索引
select * from table where id in (select id from table where id>100000 order by id limit 10)

通过使用覆盖索引查询返回需要的主键,再根据主键关联原表获得需要的数据

19. 如何定位慢查询 ?

可以在MYSQL配置文件中开启慢查询 , 有两种方式可以开启慢查询

方式一 : 修改my.ini配置文件 , 重启 MySQL 生效

[mysqld]
log_output='FILE,TABLE'
slow_query_log='ON'
long_query_time=0.001

方式二 : 设置全局变量

SET GLOBAL slow_query_log = 'ON';
SET GLOBAL log_output = 'FILE,TABLE';
SET GLOBAL long_query_time = 0.001;

20. 一个SQL语句执行很慢, 如何分析

  1. 可以开启慢查询, 通过慢查询日志或者命令, 获取到执行慢的SQL语句

慢查询日志记录了所有执行时间超过指定参数(long_query_time,单位:秒,默认10秒)的所有SQL语句的日志。

MySQL的慢查询日志默认没有开启,我们可以查看一下系统变量 slow_query_log。

  1. 使用EXLPAIN命令分析SQL语句的执行过程

EXLPAIN命令, 比较重要的字段**(加黑加粗的是重要的)** :

Explain 执行计划中各个字段的含义:

字段

含义

id

select查询的序列号,表示查询中执行select子句或者是操作表的顺序(id相同,执行顺序从上到下;id不同,值越大,越先执行)。

select_type

表示 SELECT 的类型,常见的取值有 SIMPLE(简单表,即不使用表连接或者子查询)、PRIMARY(主查询,即外层的查询)、 UNION(UNION 中的第二个或者后面的查询语句)、SUBQUERY(SELECT/WHERE之后包含了子查询)等

type

表示连接类型,性能由好到差的连接类型为NULL、system、const、eq_ref、ref、range、 index、all 。

possible_key

显示可能应用在这张表上的索引,一个或多个。

key

实际使用的索引,如果为NULL,则没有使用索引。

key_len

表示索引中使用的字节数, 该值为索引字段最大可能长度,并非实际使用长度,在不损失精确性的前提下, 长度越短越好 。

rows

MySQL认为必须要执行查询的行数,在innodb引擎的表中,是一个估计值,可能并不总是准确的。

filtered

表示返回结果的行数占需读取行数的百分比, filtered 的值越大越好。

extra

额外信息

select_type重点解读

面试必会-MySQL篇

type重点解读:查询性能从上到下依次是最好到最差

面试必会-MySQL篇

extra重点解读

面试必会-MySQL篇

宠物资料大全