MySQL基础
MySQL基本架构
下图是 MySQL 的一个简要架构图,从下图你可以很清晰的看到客户端的一条 SQL 语句在 MySQL 内部是如何执行的。

从上图可以看出, MySQL 主要由下面几部分构成:
- 连接器: 身份认证和权限相关(登录 MySQL 的时候)。
- 查询缓存: 执行查询语句的时候,会先查询缓存(MySQL 8.0 版本后移除,因为这个功能不太实用)。
- 分析器: 没有命中缓存的话,SQL 语句就会经过分析器,分析器说白了就是要先看你的 SQL 语句要干嘛,再检查你的 SQL 语句语法是否正确。
- 优化器: 按照 MySQL 认为最优的方案去执行。
- 执行器: 执行语句,然后从存储引擎返回数据。 执行语句之前会先判断是否有权限,如果没有权限的话,就会报错。
- 插件式存储引擎:主要负责数据的存储和读取,采用的是插件式架构,支持 InnoDB、MyISAM、Memory 等多种存储引擎。
MySQL支持哪些存储引擎?

MyISAM 和 InnoDB 有什么区别?
MySQL 5.5 之前,MyISAM 引擎是 MySQL 的默认存储引擎,可谓是风光一时。
虽然,MyISAM 的性能还行,各种特性也还不错(比如全文索引、压缩、空间函数等)。但是,MyISAM 不支持事务和行级锁,而且最大的缺陷就是崩溃后无法安全恢复。
MySQL 5.5 版本之后,InnoDB 是 MySQL 的默认存储引擎。
1.是否支持行级锁
MyISAM 只有表级锁(table-level locking),而 InnoDB 支持行级锁(row-level locking)和表级锁,默认为行级锁。
也就说,MyISAM 一锁就是锁住了整张表,这在并发写的情况下是多么滴憨憨啊!这也是为什么 InnoDB 在并发写的时候,性能更牛皮了!
2.是否支持事务
MyISAM 不提供事务支持。
InnoDB 提供事务支持,实现了 SQL 标准定义了四个隔离级别,具有提交(commit)和回滚(rollback)事务的能力。并且,InnoDB 默认使用的 REPEATABLE-READ(可重读)隔离级别是可以解决幻读问题发生的(基于 MVCC 和 Next-Key Lock)
关于 MySQL 事务的详细介绍,可以看看我写的这篇文章:MySQL 事务隔离级别详解。
3.是否支持外键
MyISAM 不支持,而 InnoDB 支持。
外键对于维护数据一致性非常有帮助,但是对性能有一定的损耗。因此,通常情况下,我们是不建议在实际生产项目中使用外键的,在业务代码中进行约束即可。
4.索引结构
InnoDB 和 MyISAM 都支持 B+ 树索引,但是它们数据的存储结构实现方式不同。不同之处在于:
- InnoDB 存储引擎:B+ 树索引的叶子节点保存数据本身;
- MyISAM 存储引擎:B+ 树索引的叶子节点保存数据的物理地址;
如何选择
- 如果需要事务支持、数据一致性和完整性以及较好的并发性,应选择InnoDB。
- 如果对性能要求较高,不需要事务支持,且不需要外键约束等高级特性,可以考虑使用MyISAM。
- 对于只读或很少更新的表,MyISAM可能会更适合,因为它在某些情况下可以提供更快的读取性能。
- 对于需要较高的数据一致性、可靠的崩溃恢复和支持高并发的应用程序,通常建议使用InnoDB。
什么是关系型数据库?
顾名思义,关系型数据库(RDB,Relational Database)就是一种建立在关系模型的基础上的数据库。关系模型表明了数据库中所存储的数据之间的联系(一对一、一对多、多对多)。
关系型数据库中,我们的数据都被存放在了各种表中(比如用户表),表中的每一行就存放着一条数据(比如一个用户的信息)。
大部分关系型数据库都使用 SQL 来操作数据库中的数据。并且,大部分关系型数据库都支持事务的四大特性(ACID)。
有哪些常见的关系型数据库呢?
MySQL、PostgreSQL、Oracle、SQL Server、SQLite(微信本地的聊天记录的存储就是用的 SQLite)
什么是数据库, 数据库管理系统, 数据库系统, 数据库管理员?
- 数据库 : 数据库(DataBase 简称 DB)就是信息的集合或者说数据库是由数据库管理系统管理的数据的集合。
- 数据库管理系统 : 数据库管理系统(Database Management System 简称 DBMS)是一种操纵和管理数据库的大型软件,通常用于建立、使用和维护数据库。
- 数据库系统 : 数据库系统(Data Base System,简称 DBS)通常由软件、数据库和数据管理员(DBA)组成。
- 数据库管理员 : 数据库管理员(Database Administrator, 简称 DBA)负责全面管理和控制数据库系统。
什么是元组, 码, 候选码, 主码, 外码, 主属性, 非主属性?
- 元组:元组(tuple)是关系数据库中的基本概念,关系是一张表,表中的每行(即数据库中的每条记录)就是一个元组,每列就是一个属性。 在二维表里,元组也称为行。
- 码:码就是能唯一标识实体的属性,对应表中的列。
- 候选码:若关系中的某一属性或属性组的值能唯一的标识一个元组,而其任何、子集都不能再标识,则称该属性组为候选码。例如:在学生实体中,“学号”是能唯一的区分学生实体的,同时又假设“姓名”、“班级”的属性组合足以区分学生实体,那么{学号}和{姓名,班级}都是候选码。
- 主码 : 主码也叫主键。主码是从候选码中选出来的。 一个实体集中只能有一个主码,但可以有多个候选码。
- 外码 : 外码也叫外键。如果一个关系中的一个属性是另外一个关系中的主码则这个属性为外码。
- 主属性:候选码中出现过的属性称为主属性。比如关系 工人(工号,身份证号,姓名,性别,部门). 显然工号和身份证号都能够唯一标示这个关系,所以都是候选码。工号、身份证号这两个属性就是主属性。如果主码是一个属性组,那么属性组中的属性都是主属性。
- 非主属性: 不包含在任何一个候选码中的属性称为非主属性。比如在关系——学生(学号,姓名,年龄,性别,班级)中,主码是“学号”,那么其他的“姓名”、“年龄”、“性别”、“班级”就都可以称为非主属性。
主键和外键有什么区别?
- **主键(主码)**:主键用于唯一标识一个元组,不能有重复,不允许为空。一个表只能有一个主键。
- **外键(外码)**:外键用来和其他表建立联系用,外键是另一表的主键,外键是可以有重复的,可以是空值。一个表可以有多个外键。
为什么不推荐使用外键与级联?
对于外键和级联,阿里巴巴开发手册这样说到:
【强制】不得使用外键与级联,一切外键概念必须在应用层解决。
说明: 以学生和成绩的关系为例,学生表中的 student_id 是主键,那么成绩表中的 student_id 则为外键。如果更新学生表中的 student_id,同时触发成绩表中的 student_id 更新,即为级联更新。外键与级联更新适用于单机低并发,不适合分布式、高并发集群;级联更新是强阻塞,存在数据库更新风暴的风险;外键影响数据库的插入速度
为什么不要用外键呢?大部分人可能会这样回答:
- 增加了复杂性: a. 每次做 DELETE 或者 UPDATE 都必须考虑外键约束,会导致开发的时候很痛苦, 测试数据极为不方便; b. 外键的主从关系是定的,假如那天需求有变化,数据库中的这个字段根本不需要和其他表有关联的话就会增加很多麻烦。
- 增加了额外工作:数据库需要增加维护外键的工作,比如当我们做一些涉及外键字段的增,删,更新操作之后,需要触发相关操作去检查,保证数据的的一致性和正确性,这样会不得不消耗资源;(个人觉得这个不是不用外键的原因,因为即使你不使用外键,你在应用层面也还是要保证的。所以,我觉得这个影响可以忽略不计。)
- 对分库分表不友好:因为分库分表下外键是无法生效的。
我个人觉得上面这种回答不是特别的全面,只是说了外键存在的一个常见的问题。实际上,我们知道外键也是有很多好处的,比如:
- 保证了数据库数据的一致性和完整性;
- 级联操作方便,减轻了程序代码量;
- ……
所以说,不要一股脑的就抛弃了外键这个概念,既然它存在就有它存在的道理,如果系统不涉及分库分表,并发量不是很高的情况还是可以考虑使用外键的。
什么是 ER 图?
我们做一个项目的时候一定要试着画 ER 图来捋清数据库设计,这个也是面试官问你项目的时候经常会被问到的。
ER 图 全称是 Entity Relationship Diagram(实体联系图),提供了表示实体类型、属性和联系的方法。
ER 图由下面 3 个要素组成:
- 实体:通常是现实世界的业务对象,当然使用一些逻辑对象也可以。比如对于一个校园管理系统,会涉及学生、教师、课程、班级等等实体。在 ER 图中,实体使用矩形框表示。
- 属性:即某个实体拥有的属性,属性用来描述组成实体的要素,对于产品设计来说可以理解为字段。在 ER 图中,属性使用椭圆形表示。
- 联系:即实体与实体之间的关系,在 ER 图中用菱形表示,这个关系不仅有业务关联关系,还能通过数字表示实体之间的数量对照关系。例如,一个班级会有多个学生就是一种实体间的联系。
下图是一个学生选课的 ER 图,每个学生可以选若干门课程,同一门课程也可以被若干人选择,所以它们之间的关系是多对多(M: N)。另外,还有其他两种实体之间的关系是:1 对 1(1:1)、1 对多(1: N)。

数据库范式了解吗?
1NF
1NF的定义:
- 如果一个关系模式R的所有属性都是不可分的基本数据项,则R∈1NF
- 第一范式是对关系模式的最起码的要求。不满足第一范式的数据库模式不能称为关系数据库
- 但是满足第一范式的关系模式并不一定是一个好的关系模式
以下是一个满足1NF,但不是好的关系模式的例子:
关系模式 S-L-C(Sno, Sdept, Sloc, Cno, Grade) -(学号,学院,住址,课程号,成绩)
Sloc为学生住处,假设每个系的学生住在同一个地方


S-L-C不是一个好的关系模式,一个关系模式,R不属于2NF,就会产生以下几个问题:
(1)插入异常。假若要插入一个学生Sno=S7, Sdept =PHY, Sloc =BLD2, 但该生还未选课,即这个学生无Cno,这样的元组就插不进S-L-C中。因为插入元组时必须给定码值,而这时码值的一部分为空,因而学生的固有信息无法插入。
(2)删除异常。假定某个学生只选一门课,如S4就选了一门课C3,现在C3这门课他也不选了,那么C3这个数据项就要删除。而C3是主属性,删除了C3,整个元组就必须一起删除,使得S4的其他信息也被删除了,从而造成删除异常,即不应删除的信息也删除了。
(3)更新异常。某个学生从数学系(MA)转到计算机科学系(CS),这本来只需修改此学生元组中的Sdept分量即可,但因为关系模式S-L-C中还含有系的住处Sloc属性,学生转系将同时改变住处,因而还必须修改元组中的Sloc分量。
(4)数据冗余。如果这个学生选修了k门课,Sdept、 Sloc重复存储了k次,不仅存储冗余度大,而且必须无遗漏地修改k个元组中全部Sdept、Sloc 信息,造成修改的复杂化。
为什么会有这些问题呢?
原因:Sdept、 Sloc部分函数依赖于码。解决方法(也就是2NF的处理方法)S-L-C分解为两个关系模式,以消除这些部分函数依赖
SC(Sno, Cno, Grade)
S-L(Sno, Sdept, Sloc)

2NF
2NF 在 1NF 的基础之上,消除了非主属性对于码的部分函数依赖。

- 采用投影分解法将一个1NF的关系分解为多个2NF的关系,可以在一定程度上减轻原1NF关系中存在的插入异常、删除异常、数据冗余度大、修改复杂等问题。
- 存在问题:将一个1NF关系分解为多个2NF的关系,并不能完全消除关系模式中的各种异常情况和数据冗余,这往往是传递依赖引起的,所以又引入了3NF。
3NF
3NF 在 2NF 的基础之上,消除了非主属性对于码的传递函数依赖 。符合 3NF 要求的数据库设计,基本上解决了数据冗余过大,插入异常,修改异常,删除异常的问题。

采用投影分解法:把S-L分解为两个关系模式,以消除传递函数依赖:
S-D(Sno, Sdept)D-L(Sdept,Sloc)
S-D的码为Sno, D-L的码为Sdept。
分解后的关系模式S-D与D-L中不再存在传递依赖
采用投影分解法将一个2NF的关系分解为多个3NF的关系,可以在一定程度上解决原2NF关系中存在的插入异常、删除异常、数据冗余度大、修改复杂等问题。
将一个2NF关系分解为多个3NF的关系后,仍然不能完全消除关系模式中的各种异常情况和数据冗余。
BCNF
- BCNF ( Boyce Codd Normal Form)是由Boyce与Codd提出的,比上述的3NF又进了一步,通常认为BCNF是修正的第三范式,有时也称为扩充的第三范式。
- BCNF的作用是消除主属性对码的部分依赖和传递依赖

例如
关系模式STJ(S, T, J)中,S表示学生,T表示教师,J表示课程。
每一教师只教一门课,每门课有若干教师,某一学生选定某门课, 就对应一个固定的教师。
由语义可得到如下的函数依赖。
(S,J)→T,(S,T)→J, T→J
函数依赖关系可以用如图表示

这里(S,J)、 (S,T)都是候选码。
STJ是3NF,所有的属性都是主属性,因为没有任何非主属性对码传递依赖或部分依赖,
但STJ不是BCNF关系,因为T是决定因素,而T不包含码。
什么是反范式化?
反范式化(Denormalization)是指将数据库设计中的关联表中的数据冗余或冗余数据添加到表中,以提高查询性能或简化数据模型。通常情况下,范式化设计会将数据分解为多个表,并使用外键关联这些表,以确保数据的一致性和完整性。但在某些情况下,为了提高查询性能或简化查询操作,可以通过反范式化来优化数据库设计。
反范式化的常见方式包括:
- 合并表:将多个关联表中的数据合并到一个表中,减少表之间的关联,从而提高查询性能。这样做可以避免频繁地进行表连接操作。
- 添加冗余数据:将某些常用的数据复制到需要频繁查询的表中,避免了每次查询都要进行表连接操作,提高了查询的效率。但同时也增加了数据冗余,需要注意保持数据的一致性。
- 使用计算字段:在表中添加一些计算字段,避免了在查询时进行复杂的计算操作,提高了查询的效率。
drop、delete 与 truncate 区别?
用法不同
drop
(丢弃数据):drop table 表名
,直接将表都删除掉,在删除表的时候使用。truncate
(清空数据) :truncate table 表名
,只删除表中的数据,再插入数据的时候自增长 id 又从 1 开始,在清空表中数据的时候使用。delete
(删除数据) :delete from 表名 where 列名=值
,删除某一行的数据,如果不加where
子句和truncate table 表名
作用类似。
truncate
和不带 where
子句的 delete
、以及 drop
都会删除表内的数据,但是 truncate
和 delete
只删除数据不删除表的结构(定义),执行 drop
语句,此表的结构也会删除,也就是执行drop
之后对应的表不复存在。
属于不同的数据库语言
truncate
和 drop
属于 DDL(数据定义语言)语句,操作立即生效,原数据不放到 rollback segment 中,不能回滚,操作不触发 trigger。而 delete
语句是 DML (数据库操作语言)语句,这个操作会放到 rollback segment 中,事务提交之后才生效。
DML 语句和 DDL 语句区别:
- DML 是数据库操作语言(Data Manipulation Language)的缩写,是指对数据库中表记录的操作,主要包括表记录的插入、更新、删除和查询,是开发人员日常使用最频繁的操作。
- DDL (Data Definition Language)是数据定义语言的缩写,简单来说,就是对数据库内部的对象进行创建、删除、修改的操作语言。它和 DML 语言的最大区别是 DML 只是对表内部数据的操作,而不涉及到表的定义、结构的修改,更不会涉及到其他对象。DDL 语句更多的被数据库管理员(DBA)所使用,一般的开发人员很少使用。
另外,由于select
不会对表进行破坏,所以有的地方也会把select
单独区分开叫做数据库查询语言 DQL(Data Query Language)。
执行速度不同
一般来说:drop
> truncate
> delete
delete
命令执行的时候会产生数据库的binlog
日志,而日志记录是需要消耗时间的,但是也有个好处方便数据回滚恢复。truncate
命令执行的时候不会产生数据库日志,因此比delete
要快。除此之外,还会把表的自增值重置和索引恢复到初始大小等。drop
命令会把表占用的空间全部释放掉。
你应该更多地关注在使用场景上,而不是执行效率。