MYSQL经典面试题
1、数据库存储引擎
数据库存储引擎是数据库底层软件组织,数据库管理系统(DBMS)使用数据引擎进行创建、查询、更新和删除数据。不同的存储引擎提供
不同的存储机制、索引技巧、锁定水平等功能,使用不同的存储引擎,还可以 获得特定的功能。现在许多不同的数据库管理系统都支持多
种不同的数据引擎。存储引擎主要有: 1. MyIsam , 2. InnoDB, 3. Memory, 4. Archive, 5. Federated 。
2、InnoDB(B+树)
InnoDB 底层存储结构为B+树, B树的每个节点对应innodb的一个page, page大小是固定的,一般设为 16k。其中非叶子节点只有键值,
叶子节点包含完成数据
适用场景:
1)经常更新的表,适合处理多重并发的更新请求。
2)支持事务。
3)可以从灾难中恢复(通过 bin-log 日志等)。
4)外键约束。只有他支持外键。
5)支持自动增加列属性 auto_increment。
2、TokuDB( Fractal Tree-节点带数据)
TokuDB 底层存储结构为 Fractal Tree,Fractal Tree 的结构与 B+树有些类似, 在 Fractal Tree中,
每一个 child 指针除了需要指向一个 child
节点外,还会带有一个 Message Buffer ,这个Message Buffer 是一个 FIFO 的队列,用来缓存更新操作。
例如,一次插入操作只需要落在某节点的 Message Buffer 就可以马上返回了,并不需要搜索到叶子节点。这些缓存的更新会在查询时或后
台异步合并应用到对应的节点中。
TokuDB 在线添加索引,不影响读写操作, 非常快的写入性能, Fractal-tree 在事务实现上有优
势。 他主要适用于访问频率不高的数据或历史数据归档
3、MyIASM
MyIASM是 MySQL默认的引擎,但是它没有提供对数据库事务的支持,也不支持行级锁和外键,因此当 NSERT(插入)或 UPDATE(更新)数据
时即写操作需要锁定整个表,效率便会低一些。
ISAM 执行读取操作的速度很快,而且不占用大量的内存和存储资源。在设计之初就预想数据组织成有固定长度的记录,按顺序存储的。 ---
ISAM 是一种静态索引结构。
缺点是它不 支持事务处理。
4、Memory
Memory(也叫 HEAP)堆内存:使用存在内存中的内容来创建表。每个 MEMORY 表只实际对应一个磁盘文件。 MEMORY 类型的表访问
非常得快,因为它的数据是放在内存中的,并且默认使用HASH 索引。但是一旦服务关闭,表中的数据就会丢失掉。 Memory 同时支持散
列索引和 B 树索引, B树索引可以使用部分查询和通配查询,也可以使用<,>和>=等操作符方便数据挖掘,散列索引相等的比较快但是对于
范围的比较慢很多
5、数据库引擎有哪些
如何查看mysql提供的所有存储引擎
mysql> show engines;
mysql常用引擎包括:MYISAM、Innodb、Memory、MERGE
1. MYISAM:全表锁,拥有较高的执行速度,不支持事务,不支持外键,并发性能差,占用空间相对较小,对事务完整性没有要求,以
select、insert为主的应用基本上可以使用这引擎
2. Innodb:行级锁,提供了具有提交、回滚和崩溃回复能力的事务安全,支持自动增长列,支持外键约束,并发能力强,占用空间是
MYISAM的2.5倍,处理效率相对会差一些
3. Memory:全表锁,存储在内容中,速度快,但会占用和数据量成正比的内存空间且数据在mysql重启时会丢失,默认使用HASH索引,
检索效率非常高,但不适用于精确查找,主要用于那些内容变化不频繁的代码表
4. MERGE:是一组MYISAM表的组合
6、InnoDB与MyISAM的区别
1. InnoDB支持事务,MyISAM不支持,对于InnoDB每一条SQL语言都默认封装成事务,自动提交,这样会影响速度,所以最好把多条
SQL语言放在begin和commit之间,组成一个事务;
2. InnoDB支持外键,而MyISAM不支持。对一个包含外键的InnoDB表转为MYISAM会失败;
3. InnoDB是聚集索引,数据文件是和索引绑在一起的,必须要有主键,通过主键索引效率很高。但是辅助索引需要两次查询,先查询到
主键,然后再通过主键查询到数据。因此,主键不应该过大,因为主键太大,其他索引也都会很大。而MyISAM是非聚集索引,数据文
件是分离的,索引保存的是数据文件的指针。主键索引和辅助索引是独立的。
4. InnoDB不保存表的具体行数,执行select count(*) from table时需要全表扫描。而MyISAM用一个变量保存了整个表的行数,执行上
述语句时只需要读出该变量即可,速度很快;
5. Innodb不支持全文索引,而MyISAM支持全文索引,查询效率上MyISAM要高
7、索引
索引(Index)是帮助 MySQL 高效获取数据的数据结构。 常见的查询算法,顺序查找,二分查找,二叉排序树查找,哈希散列法,分块查找,平衡多
路搜索树 B 树(B-tree) ,索引是对数据库表中一个或多个列的值进行排序的结构,建立索引有助于快速获取信息。
你也可以这样理解:索引就是加快检索表中数据的方法。数据库的索引类似于书籍的索引。在书籍中,索引允许用户不必翻阅完整个书就能
迅速地找到所需要的信息。在数据库中,索引也允许数据库程序迅速地找到表中的数据,而不必扫描整个数据库
mysql 有4种不同的索引:
主键索引(PRIMARY)
唯一索引(UNIQUE)
普通索引(INDEX)
全文索引(FULLTEXT)
索引并非是越多越好,创建索引也需要耗费资源,一是增加了数据库的存储空间,二是在插入和删除时要花费较多的时间维护索引
索引加快数据库的检索速度
索引降低了插入、删除、修改等维护任务的速度
唯一索引可以确保每一行数据的唯一性
通过使用索引,可以在查询的过程中使用优化隐藏器,提高系统的性能
索引需要占物理和数据空间
8、常见索引原则有
1. 选择唯一性索引,唯一性索引的值是唯一的,可以更快速的通过该索引来确定某条记录。
2. 为经常需要排序、分组和联合操作的字段建立索引。
3. 为常用作为查询条件的字段建立索引。
4. 限制索引的数目:
越多的索引,会使更新表变得很浪费时间。尽量使用数据量少的索引
5. 如果索引的值很长,那么查询的速度会受到影响。尽量使用前缀来索引
6. 如果索引字段的值很长,最好使用值的前缀来索引。
7. 删除不再使用或者很少使用的索引
8. 最左前缀匹配原则,非常重要的原则。
9. 尽量选择区分度高的列作为索引区分度的公式是表示字段不重复的比例
10. 索引列不能参与计算,保持列“干净”:带函数的查询不参与索引。
11. 尽量的扩展索引,不要新建索引
9、数据库的三范式是什么
第一范式:列不可再分
第二范式:行可以唯一区分,主键约束
第三范式:表的非主属性不能依赖与其他表的非主属性 外键约束
且三大范式是一级一级依赖的,第二范式建立在第一范式上,第三范式建立第一第二范式上 。
10、第一范式(1st NF -列都是不可再分)
第一范式的目标是确保每列的原子性:如果每列都是不可再分的最小数据单元(也称为最小的原子单元),则满足第一范式(1NF)
11、第二范式(2nd NF-每个表只描述一件事情)
首先满足第一范式,并且表中非主键列不存在对主键的部分依赖。 第二范式要求每个表只描述一件事情
12、第三范式(3rd NF-不存在对非主键列的传递依赖)
第三范式定义是,满足第二范式,并且表中的列不存在对非主键列的传递依赖。 除了主键订单编号外,顾客姓名依赖于非主键顾客编号。
13、数据库是事务
事务(TRANSACTION)是作为单个逻辑工作单元执行的一系列操作,
这些操作作为一个整体一起向系统提交,要么都执行、要么都不执行 。
事务是一个不可分割的工作逻辑单元事务必须具备以下四个属性,简称 ACID 属性:
原子性(Atomicity)
1. 事务是一个完整的操作。事务的各步操作是不可分的(原子的);要么都执行,要么都不执
行。
一致性(Consistency)
2. 当事务完成时,数据必须处于一致状态。
隔离性(Isolation)
3. 对数据进行修改的所有并发事务是彼此隔离的,
这表明事务必须是独立的,它不应以任何方
式依赖于或影响其他事务。
永久性(Durability)
4. 事务完成后,它对数据库的修改被永久保持,事务日志能够保持事务的永久性
14、SQL优化
1、查询语句中不要使用select *
2、尽量减少子查询,使用关联查询(left join,right join,inner join)替代
3、减少使用IN或者NOT IN ,使用exists,not exists或者关联查询语句替代
4、or 的查询尽量用 union或者union all 代替(在确认没有重复数据或者不用剔除重复数据时,union all会更好)
5、应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。
6、应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如: select id from t where num
is null 可以在num上设置默认值0,确保表中num列没有null值,然后这样查询: select id from t where num=0
15、简单说一说drop、delete与truncate的区别
SQL中的drop、delete、truncate都表示删除,但是三者有一些差别
delete和truncate只删除表的数据不删除表的结构
速度,一般来说: drop> truncate >delete
delete语句是dml,这个操作会放到rollback segement中,事务提交之后才生效;
如果有相应的trigger,执行的时候将被触发. truncate,drop是ddl, 操作立即生效,原数据不放到rollbacksegment中,不能回滚. 操作不触发
trigger
16、什么是视图
视图是一种虚拟的表,具有和物理表相同的功能。可以对视图进行增,改,查,操作,试图通常是有一个表或者多个表的行或列的子集。对
视图的修改不影响基本表。它使得我们获取数据更容易,相比多表查询
17、什么是内联接、左外联接、右外联接?
内联接(Inner Join):匹配2张表中相关联的记录。
左外联接(Left Outer Join):除了匹配2张表中相关联的记录外,还会匹配左表中剩余的记录,右表中未匹配到的字段用NULL表示。
右外联接(Right Outer Join):除了匹配2张表中相关联的记录外,还会匹配右表中剩余的记录,左表中未匹配到的字段用NULL表示。在
判定左表和右表时,要根据表名出现在Outer Join的左右位置关系
18、并发事务带来哪些问题?
在典型的应用程序中,多个事务并发运行,经常会操作相同的数据来完成各自的任务(多个用户对同一
数据进行操作)。并发虽然是必须的,但可能会导致以下的问题。
脏读(Dirty read): 当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务也访问了这
个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是“脏数据”,依据“脏数据”所做的操
作可能是不正确的。
丢失修改(Lost to modify): 指在一个事务读取一个数据时,另外一个事务也访问了该数据,那么在第一个事务中修改了这个数据后,第
二个事务也修改了这个数据。这样第一个事务内的修改结果就被丢失,因此称为丢失修。 例如:事务1读取某表中的数据A=20,事务2也读
取A=20,事务1修改A=A-1,事务2也修改A=A-1,最终结果A=19,事务1的修改被丢失。
不可重复读(Unrepeatableread): 指在一个事务内多次读同一数据。在这个事务还没有结束时,另一个事务也访问该数据。那么,在第
一个事务中的两次读数据之间,由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样。这就发生了在一个事务内两次读到的
数据是不一样的情况,因此称为不可重复读。
幻读(Phantom read): 幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据
时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。
不可重复读和幻读区别:
不可重复读的重点是修改比如多次读取一条记录发现其中某些列的值被修改,幻读的重点在于新增或者删除比如多次读取一条记录发现记录
增多或减少了
19、事务隔离级别有哪些?MySQL的默认隔离级别是?
SQL 标准定义了四个隔离级别:
READ-UNCOMMITTED(读取未提交):
最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
READ-COMMITTED(读取已提交):
允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。
REPEATABLE-READ(可重复读):
对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复
读,但幻读仍有可能发生
SERIALIZABLE(可串行化):
最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干
扰,也就是说,该级别可以防止脏读、不可重复读以及幻读
MySQL InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重读)。我们可以通过
SELECT @@tx_isolation; 命令来查看
这里需要注意的是:与 SQL 标准不同的地方在于 InnoDB 存储引擎在 REPEATABLE-READ(可重读)事务隔离级别下使用的是Next-Key
Lock 锁算法,因此可以避免幻读的产生,这与其他数据库系统(如SQL Server) 是不同的。所以说InnoDB 存储引擎的默认支持的隔离级别是
REPEATABLE-READ(可重读)
已经可以完全保证事务的隔离性要求,即达到了 SQL标准的 SERIALIZABLE(可串行化) 隔离级别。因为隔离
级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是 READCOMMITTED(读取提交内容) ,但是你要知道的是InnoDB 存
储引擎默认使用 REPEAaTABLEREAD(可重读)
并不会有任何性能损失
InnoDB 存储引擎在 分布式事务 的情况下一般会用到 SERIALIZABLE(可串行化) 隔离级别。
20、大表如何优化?
当MySQL单表记录数过大时,数据库的CRUD性能会明显下降,一些常见的优化措施如下:
限定数据的范围
务必禁止不带任何限制数据范围条件的查询语句。比如:我们当用户在查询订单历史的时候,我们可以控制在一个月的范围内;
读/写分离
经典的数据库拆分方案,主库负责写,从库负责读;
垂直分区
根据数据库里面数据表的相关性进行拆分。 例如,用户表中既有用户的登录信息又有用户的基本信息,可以将用户表拆分成两个单独的
表,甚至放到单独的库做分库。
简单来说垂直拆分是指数据表列的拆分,把一张列比较多的表拆分为多张表。 如下图所示,这样来说大家应该就更容易理解了
垂直拆分的优点:
可以使得列数据变小,在查询时减少读取的Block数,减少I/O次数。此外,垂
直分区可以简化表的结构,易于维护。
垂直拆分的缺点:
主键会出现冗余,需要管理冗余列,并会引起Join操作,可以通过在应用层进行
Join来解决。此外,垂直分区会让事务变得更加复杂;
21、水平分区
保持数据表结构不变,通过某种策略存储数据分片。这样每一片数据分散到不同的表或者库中,达到了分布式的目的。 水平拆分可以支撑
非常大的数据量。
水平拆分是指数据表行的拆分,表的行数超过200万行时,就会变慢,这时可以把一张的表的数据拆成多张表来存放。举个例子:我们可以
将用户信息表拆分成多个用户信息表,这样就可以避免单一表数据量过大对性能造成影响。
水平拆分可以支持非常大的数据量。需要注意的一点是:分表仅仅是解决了单一表数据过大的问题,但由于表的数据还是在同一台机器上,
其实对于提升MySQL并发能力没有什么意义,所以 水平拆分最好分库 。
水平拆分能够 支持非常大的数据量存储,应用端改造也少,但 分片事务难以解决 ,跨节点Join性能较差,逻辑复杂。《Java工程师修炼之
道》的作者推荐 尽量不要对数据进行分片,因为拆分会带来逻辑、部署、运维的各种复杂度 ,一般的数据表在优化得当的情况下支撑千万
以下的数据量是没有太大问题的。如果实在要分片,尽量选择客户端分片架构,这样可以减少一次和中间件的网络I/O。
1. 下面补充一下数据库分片的两种常见方案:
客户端代理:
分片逻辑在应用端,封装在jar包中,通过修改或者封装JDBC层来实现。 当当网的Sharding-JDBC 、阿里的TDDL是两种
比较常用的实现。
2. 中间件代理:
在应用和数据中间加了一个代理层。分片逻辑统一维护在中间件服务中。 我们现在谈的 Mycat 、360的Atlas、网易的
DDB等等都是这种架构的实现。
详细内容可以参考: MySQL大表优化方案: https://segmentfault.com/a/1190000006158186
22、分库分表之后,id 主键如何处理
因为要是分成多个表之后,每个表都是从 1 开始累加,这样是不对的,我们需要一个全局唯一的 id 来支持。
生成全局 id 有下面这几种方式:
UUID:不适合作为主键,因为太长了,并且无序不可读,查询效率低。比较适合用于生成唯一的名字的标示比如文件的名字。
数据库自增 id : 两台数据库分别设置不同步长,生成不重复ID的策略来实现高可用。这种方式生成的 id 有序,但是需要独立部署数据库实
例,成本高,还会有性能瓶颈。
利用 redis 生成 id : 性能比较好,灵活方便,不依赖于数据库。但是,引入了新的组件造成系统更加复杂,可用性降低,编码更加复杂,增
加了系统成本。
Twitter的snowflake算法 :Github 地址:https://github.com/twitter-archive/snowflake。
美团的Leaf分布式ID生成系统 :Leaf 是美团开源的分布式ID生成器,能保证全局唯一性、趋势递增、单调递增、信息安全,里面也提到了
几种分布式方案的对比,但也需要依赖关系数据库、Zookeeper等中间件。感觉还不错。美团技术团队的一篇文章:https://tech.meituan.com/2017/04/21/mt-leaf.html
23、存储过程(特定功能的 SQL 语句集)
一组为了完成特定功能的 SQL 语句集,存储在数据库中,经过第一次编译后再次调用不需要再次编译,用户通过指定存储过程的名字并给
出参数(如果该存储过程带有参数)来执行它。存储过程是数据库中的一个重要对象。
24、存储过程优化思路
1. 尽量利用一些 sql 语句来替代一些小循环,例如聚合函数,求平均函数等。
2. 中间结果存放于临时表,加索引。
3. 少使用游标。 sql 是个集合语言,对于集合运算具有较高性能。而 cursors 是过程运算。比如对一个 100 万行的数据进行查询。游标
需要读表 100 万次,而不使用游标则只需要少量几次读取。
4. 事务越短越好。 sqlserver 支持并发操作。如果事务过多过长,或者隔离级别过高,都会造成并发操作的阻塞,死锁。导致查询极慢,
cpu 占用率极地。
5. 使用 try-catch 处理错误异常。
6. 查找语句尽量不要放在循环内
25、触发器(一段能自动执行的程序)
触发器是一段能自动执行的程序,是一种特殊的存储过程,
触发器和普通的存储过程的区别是:触发器是当对某一个表进行操作时触发。
诸如: update、 insert、 delete 这些操作的时候,系统会自动调用执行该表上对应的触发器。 SQL Server 2005 中触发器可以分为两类:
DML 触发器和DDL 触发器,其中 DDL 触发器它们会影响多种数据定义语言语句而激发,这些语句有 create、alter、 drop 语句。
26、数据库并发策略
并发控制一般采用三种方法,分别是乐观锁和悲观锁以及时间戳。
27、MySQL 中有哪几种锁?
1、表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。
2、行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
3、页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般
28、MySQL 中有哪些不同的表格?
共有 5 种类型的表格:
1、MyISAM
2、Heap
3、Merge
4、INNODB
5、ISAM
29、简述在 MySQL 数据库中 MyISAM 和 InnoDB 的区别
MyISAM:
不支持事务,但是每次查询都是原子的;
支持表级锁,即每次操作是对整个表加锁;
存储表的总行数;
一个 MYISAM 表有三个文件:索引文件、表结构文件、数据文件;
采用非聚集索引,索引文件的数据域存储指向数据文件的指针。辅索引与主索引
基本一致,但是辅索引不用保证唯一性。
InnoDb:
支持 ACID 的事务,支持事务的四种隔离级别;
支持行级锁及外键约束:因此可以支持写并发;
不存储总行数:
一个 InnoDb 引擎存储在一个文件空间(共享表空间,表大小不受操作系统控制,一个表可能分布在多个文件里),也有可能为多个(设置
为独立表空,表大小受操作系统文件大小限制,一般为 2G),受操作系统文件大小的限制;
主键索引采用聚集索引(索引的数据域存储数据文件本身),辅索引的数据域存储主键的值;因此从辅索引查找数据,需要先通过辅索引找
到主键值,再访问辅索引;最好使用自增主键,防止插入数据时,为维持 B+树结构,文件的大调整。
30、MySQL 中 InnoDB 支持的四种事务隔离级别名称,以及逐级之间的区别?
SQL 标准定义的四个隔离级别为:
1、read uncommited :读到未提交数据
2、read committed:脏读,不可重复读
3、repeatable read:可重读
4、serializable :串行事物
31、CHAR 和 VARCHAR 的区别?
1、CHAR 和 VARCHAR 类型在存储和检索方面有所不同
2、CHAR 列长度固定为创建表时声明的长度,长度值范围是 1 到 255 当 CHAR值被存储时,它们被用空格填充到特定长度,检索 CHAR 值
时需删除尾随空格。
32、主键和候选键有什么区别?
表格的每一行都由主键唯一标识,一个表只有一个主键。
主键也是候选键。按照惯例,候选键可以被指定为主键,并且可以用于任何外键
引用。
33、myisamchk 是用来做什么的?
它用来压缩 MyISAM 表,这减少了磁盘或内存使用。
34、MyISAM Static 和 MyISAM Dynamic 有什么区别?
在 MyISAM Static 上的所有字段有固定宽度。动态 MyISAM 表将具有像 TEXT,BLOB 等字段,以适应不同长度的数据类型。
MyISAM Static 在受损情况下更容易恢复。
35、如果一个表有一列定义为 TIMESTAMP,将发生什么?
每当行被更改时,时间戳字段将获取当前时间戳。
列设置为 AUTO INCREMENT 时,如果在表中达到最大值,会发生什么情况?
它会停止递增,任何进一步的插入都将产生错误,因为密钥已被使用。
怎样才能找出最后一次插入时分配了哪个自动增量?
LAST_INSERT_ID 将返回由 Auto_increment 分配的最后一个值,并且不需要指定表名称
36、你怎么看到为表格定义的所有索引?
索引是通过以下方式为表格定义的:
SHOW INDEX FROM <tablename>;
37、LIKE 声明中的%和_是什么意思?
%对应于 0 个或更多字符,_只是 LIKE 语句中的一个字符
如何在 Unix 和 MySQL 时间戳之间进行转换?
UNIX_TIMESTAMP 是从 MySQL 时间戳转换为 Unix 时间戳的命令
FROM_UNIXTIME 是从 Unix 时间戳转换为 MySQL 时间戳的命令