问题 1:分析一下 select * from t_employee t where t.age=27 and t.name = ‘李四’ 这条语句在 mysql 中如何执行的?
在连接器上进行权限检查。首先在连接器如果没有权限,直接返回错误信息;如果有权限,在 MySQL8.0 版本以前,会先查询缓存,以这条 sql 语句为 key 在内存中查询是否有结果,如果有直接缓存,如果没有,执行下一步;
在分析器上进行词法分析和语法分析。词法分析会提取 sql 语句的关键元素 select,提取需要查询的表名为 t_employee,需要查询所有的列,查询条件 age = 27 和 name = ‘李四’;然后语法分析会判断这个 sql 语句是否有语法错误,比如关键词是否正确等等,如果检查没问题就执行下一步。
优化器进行确定执行方案。优化器会根据自己的优化算法进行选择执行效率最好的一个方案(优化器认为,有时候不一定最好)。那么确认了执行计划后就准备开始执行了。
执行器执行优化器选择的执行方案,执行前会校验该用户有没有权限,如果没有权限,就会返回错误信息,如果有权限,就会去调用引擎的接口,返回接口执行的结果。
点评:
更新语句 (update) 的执行过程和 select 大同小异。差异点主要在第 1 步和第 4 步:第 1 步中更新语句会跳过缓存步骤;第 4 步中更新语句还会记录 redo log 和 binlog。
MySQL 的逻辑架构图如下,整体分为两部分:Server 层和存储引擎。
问题 2:"是否进行过数据库调优?一般如何性能调优?"
数据库调优可以从两个方面考虑:一是应用层架构;二是数据库架构;
应用层架构优化。核心是减少对数据库的调用次数,本质上是从业务应用层来审视流程是否合理,常用的方案有:
(1)引入缓存,虚拟一层中间层,减少对数据库的读写;
(2)将多次单个调用改为批量调用,比如说循环十次主键 select * FROM t where id = 'xx’改为使用 IN 一性次读取 select * FROM t where id IN (‘xx’,‘xx’,…);
(3)使用搜索引擎。
数据库架构优化。核心是优化各种配置,提升数据库的性能,可分为:
(1)优化 SQL 及索引,以达到减少数据访问、返回更少数据、减少交互次数等目标。常用的手段包括:创建并正确地使用索引(比如说减少回表查询)、优化执行计划、数据分页处理、只取需要的字段、慢查询优化、使用搜索引擎等;
(2)优化数据库表结构。常用的的手段包括:使用占用空间最小的数据类型、使用简单的数据类型、尽可能地使用 NOT NULL 定义字段、尽量少使用 text 字段、分库分表等;
(3)优化系统配置。包括操作系统的配置优化和数据库的配置优化。
A、操作系统优化。数据库是基于操作系统(多为 Linux 系统)的,所以对于合理使用操作系统也会影响到数据库的性能。比如将数据库服务器应和业务服务器隔离、或者设置 net.ipv4.tcp_max_syn_backlog = 65535 以增加 tcp 支持的队列数等等;
B、数据库配置文件优化,以 MySQL 配置为例,可以修改 innodb_buffer_pool_size(设置数据和索引缓存的缓冲池)、max_connections 等参数。
(4)优化硬件配置。比如说使用更好的服务器、更快的硬盘、更大的内存等等。
点评:
这类 “优化类” 的面试题有 “两要两不要” 之说,两不要:切忌只说自己熟悉的点;切忌说自己不熟悉的点;两要:一定要有体系化系统化的思考;二最好要有自己的实践经验。
如果有可能的话,可以更细化地和面试官讨论,为什么要调优?调优的衡量标准是什么?不过这个也要看面试官的风格,有的面试官会在问题中故意设置模糊地带,希望候选人自己主动去和面试官沟通明确模糊地带,这也可以考察候选人的软能力;有的面试官则比较机械,不喜欢候选人和自己讨价还价。
另一个需要注意的点是切忌上来就说用 explain 去解析执行计划,而应该把调优视为系统化的方案和工程。而且即使是使用 explain 查询执行计划,也需要清晰地描述其步骤:
(1)查看 slowlog,分析 slowlog,分析出查询慢的语句;
(2)按照一定优先级(比如说调用频率),进行一个一个的排查所有慢语句。
(3)分析 top sql,进行 explain 调试,查看语句执行时间。
(4)调整索引或语句本身。
问题 3:什么是分布式系统中的 CAP 理论?如何解决分布式事务问题?
什么是分布式系统中的 CAP 理论?
分布式系统中只可同时满足以下二点,没法三者兼顾。
(1)一致性(Consistency): 数据一致更新,所有数据变动都是同步的;
(2)可用性(Availability): 好的响应性能,有限的时间内返回结果;
(3)分区容错性(Partition tolerance): 可靠性,在网络故障、某些节点不能通信的时候系统仍能继续工作。
如何解决分布式事务问题?
分布式事务的实现主要有以下四 种方案:
(1)XA 二阶段提交方案(2PC)
顾名思义,2PC 分为两个阶段:第一阶段是表决阶段,所有参与者都将本事务能否成功的信息反馈发给协调者;第二阶段是执行阶段,协调者根据所有参与者的反馈,通知所有参与者在所有分支上提交或者回滚。
缺点很明显:A、性能问题,锁会跨越两个阶段,期间其他节点处于阻塞状态;B、协调者单点故障;C、丢失消息导致的不一致问题。如果发生局部网络问题,一部分事务参与者收到了提交消息,另一部分事务参与者没收到提交消息。
(2)TCC 方案
TCC 其实就是采用的补偿机制,其核心思想是:针对每个操作,都要注册一个与其对应的确认和补偿(撤销)操作。分为三个阶段: Try、Confirm、Cancel:
Try 阶段:这个阶段说的是对各个服务的资源做检测以及对资源进行锁定或者预留;
Confirm 阶段:主要是对业务系统做确认提交,Try 阶段执行成功并开始执行 Confirm 阶段时,默认 Confirm 阶段是不会出错的。即:只要 Try 成功,Confirm 一定成功。
Cancel 阶段:主要是在 try 阶段不全部成功的情况下,进行取消或者补偿,回滚已经执行成功的业务逻辑。
这种方案能保证强一致性,但是对业务侵入太大,而且实现难度较大,特别是回滚时需要按照网络状态、系统故障等不同的失败原因实现不同的回滚策略。为了满足一致性的要求,confirm 和 cancel 接口必须实现幂等。
A、在系统 A 处理任务 A 前,首先向消息中间件发送一条消息;
B、消息中间件收到后将该条消息持久化,但并不投递。此时下游系统 B 仍然不知道该条消息的存在;
C、消息中间件持久化成功后,便向系统 A 返回一个确认应答;
D、系统 A 收到确认应答后,则可以开始处理任务 A;
E、任务 A 处理完成后,向消息中间件发送 Commit 请求。该请求发送完成后,对系统 A 而言,该事务的处理过程就结束了,此时它可以处理别的任务了;
F、但 commit 消息可能会在传输途中丢失,此时消息中间件可以通过事务回查机制来查询消息状态,使得最终一致;
G、消息中间件收到 Commit 指令后,便向系统 B 投递该消息,从而触发任务 B 的执行;
H、当任务 B 执行完成后,系统 B 向消息中间件返回一个确认应答,告诉消息中间件该消息已经成功消费,此时,这个分布式事务完成。如果系统 B 未应答,那么消息中间件的确认服务会去查询系统 B 的处理结果,如果未成功便会重发,当时也有可能存在误发的情况,所以系统 B 需要保证幂等。
这种方案需要消息中间件支持事务消息(如阿里的 RocketMq),而且对业务代码的侵入性很高。
(4)本地消息表
本地消息表与业务数据表处于同一个数据库中,这样就能利用本地事务来保证在对这两个表的操作满足事务特性,并且使用了消息队列来保证最终一致性。 在分布式事务操作的一方完成写业务数据的操作之后向本地消息表发送一个消息,本地事务能保证这个消息一定会被写入本地消息表中;之后将本地消息表中的消息转发到 Kafka 等消息队列中,如果转发成功则将消息从本地消息表中删除,否则继续重新转发。 在分布式事务操作的另一方从消息队列中读取一个消息,并执行消息中的操作。
这种方式本质是将分布式事务拆成了两个本地事务,但同时业务会和消息强耦合,对业务的侵入性也很高。
MySQL 等数据库知识是后端开发人员必须熟练掌握的技术武器,大家在平常的工作中很多时候也仅仅停留在够用就行了,但其实 MySQL 可考察的知识点还是很多的。有志于此的候选者们也可以更系统的学习一下,比如说去看看 MySQL 手册或者选读《高性能 MySQL》之类的专业书籍,去了解更多背后的细节。
问:简单说一说drop、delete与truncate的区别?
delete和truncate只删除表的数据不删除表的结构
速度:drop> truncate >delete
delete语句是dml,这个操作会放到rollback segement中,事务提交之后才生效;如果有相应的trigger,执行的时候将被触发。 truncate,drop是ddl,操作立即生效,原数据不放到rollback segment中,不能回滚。操作不触发trigger。
问:谈谈三大范式,什么时候使用反范式设计
第一范式(1NF):确保每列保持原子性即列不可分,即数据库表中的字段都是单一属性的。这个单一属性由基本类型构成,包括整型、实数、字符型、逻辑型、日期型等。
第二范式(2NF):属性完全依赖于主键,也就是说在一个数据库表中,一个表中只能保存一种数据,不可以把多种数据保存在同一张数据库表中。
第三范式(3NF):在第二范式的基础上,属性和主键不能间接相关(减少数据冗余,这样就可以通过主外键进行表之间连接)。比如我们表比较多,需要关联时,但我们的A表只需要关联B表的一个字段,而且每次都需要关联查询你,这时我们可以采用A表放置一个冗余字段来存B表的那个字段。这个操作其实就是一个反范式的。
问:说几个mysql中你常用的函数
sum、count 、avg、min、max
问:redo log和binlog的区别是什么?
问:说一说mysql主从复制。
在MySQL主从复制架构中,读操作可以在所有的服务器上面进行,而写操作只能在主服务器上面进行。主从复制架构虽然给读操作提供了扩展,可如果写操作也比较多的话(多台从服务器还要从主服务器上面同步数据),单主模型的复制中主服务器势必会成为性能瓶颈。
复制过程:
问:如何开启慢日志查询?
有2种方式,一是修改mysql的配置文件,二是通过set global语句来实现。slow_query_log = ON,打开日志,long_query_time = 2,设置时间,2秒就算是慢查询,然后重启mysql服务即可,进入mysql控制台,输入SET GLOBAL slow_query_log = ‘ON’;SET GLOBAL long_query_time = X;不需要重启服务就可以得到慢查询日志。
问:如何生成UUID,以及UUID的缺点?
UUID uuid = UUID.randomUUID();
System.out.println(uuid);
String dxmbid = uuid.toString().replace("-", "");
System.out.println(dxmbid);
System.out.println(dxmbid.length());
缺点:
占空间太大
索引效率低
UUID是唯一随机的,不适合人读
无法排序
问:mysql中varchar与char的区别以及varchar(50)中的50代表的含义?
(1) varchar与char的区别
变长和固定长度
(2)varchar(50)中50的涵义
字符最大长度50,所代表的字节数与字符集有关,比如是utf8占3个字节,那么varchar(50)字段在表中最大取到150个字节。
varchar(50)和(200)存储hello所占空间一样,但后者在排序时会消耗更多内存,因为order by col采用fixed_length计算col长度(memory引擎也一样)。
(3) int(20)中20的涵义
是指显示字符的长度不影响内部存储,只是影响带 zerofill 定义的 int 时,前面补多少个 0,易于报表展示
(4) varchar(20)和int(20)中的20含义一样吗?
int(M) M表示的不是数据的最大长度,只是数据宽度,并不影响存储多少位长度的数据;varchar(M) M表示的是varchar类型数据在数据库中存储的最大长度,超过则不存;
问:FLOAT和DOUBLE的区别是什么?
浮点数以8位精度存储在FLOAT中,有四个字节。
浮点数以18位精度存储在DOUBLE中,有八个字节。
问:CHAR_LENGTH和LENGTH的区别是什么?
CHAR_LENGTH是字符数,而LENGTH是字节数。Latin字符的这两个数据是相同的,但是对于Unicode和其他编码,它们是不同的。
问:CHAR和VARCHAR的区别是什么?
CHAR和VARCHAR类型在存储和检索方面有所不同
问:列的字符串类型可以是什么??
SET、BLOB、ENUM、CHAR、TEXT、VARCHAR
问: myisamchk是用来做什么的?
它用来压缩MyISAM表,这减少了磁盘或内存使用。
问: 怎样才能找出最后一次插入时分配了哪个自动增量?
LAST_INSERT_ID将返回由Auto_increment分配的最后一个值,并且不需要指定表名称。
问: 你怎么看到为表格定义的所有索引?
索引是通过以下方式为表格定义的:SHOW INDEX FROM;
问: LIKE声明中的%和_是什么意思?
%对应于0个或更多字符,_只是LIKE语句中的一个字符。
问:事务是如何通过日志来实现的,说得越深入越好
基本流程如下:
因为事务在修改页时,要先记 undo,在记 undo之前要记 undo 的 redo, 然后修改数据页,再记数据页修改的 redo。redo(里面包括 undo 的修改) 一定要比数据页先持久化到磁盘。当事务需要回滚时,因为有 undo,可以把数据页回滚到前镜像的状态;崩溃恢复时,如果 redo log 中事务没有对应的 commit 记录,那么需要用 undo把该事务的修改回滚到事务开始之前。如果有 commit 记录,就用 redo 前滚到该事务完成时并提交掉。
问:BLOB和TEXT有什么区别?
BLOB是一个二进制对象,可以容纳可变数量的数据。有四种类型:TINYBLOB、BLOB、MEDIUMBLOB、LONGBLOB,它们只是在所能容纳价值的最大长度上有所不同。
TEXT是一个不区分大小写的BLOB。有四种类型:TINYTEXT、TEXT、MEDIUMTEXT和
LONGTEXT。它们对应于四种BLOB类型,并具有相同的最大长度和存储要求。
BLOB和TEXT类型之间的唯一区别在于对BLOB值进行排序和比较时区分大小写,对TEXT值不区分大小写。
问:MySQL数据库cpu飙升到500%的话他怎么处理?
当 cpu 飙升到 500%时,先用操作系统命令 top 命令观察是不是 mysqld 占用导致的,如果不是,找出占用高的进程,并进行相关处理。如果是 mysqld 造成的, show processlist,看看里面跑的 session 情况,是不是有消耗资源的 sql 在运行。找出消耗高的 sql,看看执行计划是否准确, index 是否缺失,或者实在是数据量太大造成。一般来说,肯定要 kill 掉这些线程(同时观察 cpu 使用率是否下降),等进行相应的调整(比如说加索引、改 sql、改内存参数)之后,再重新跑这些 SQL。也有可能是每个 sql 消耗资源并不多,但是突然之间,有大量的 session 连进来导致 cpu 飙升,这种情况就需要跟应用一起来分析为何连接数会激增,再做出相应的调整,比如说限制连接数等
问:SELECT * 和 SELECT 全部字段两种写法有何优缺点?
问:HAVNG 子句 和 WHERE 的异同点?
问:Explain各个字段的含义
(1)、explain出来的各种item的意义
id:每个被独立执行的操作的标志,表示对象被操作的顺序。一般来说, id 值大,先被执行;如果 id 值相同,则顺序从上到下。
select_type:查询中每个 select 子句的类型。
table:名字,被操作的对象名称,通常的表名(或者别名),但是也有其他格式。
partitions:匹配的分区信息。
type:join 类型。
possible_keys:列出可能会用到的索引。
key:实际用到的索引。
key_len:用到的索引键的平均长度,单位为字节。
ref:表示本行被操作的对象的参照对象,可能是一个常量用 const 表示,也可能是其他表的
key:指向的对象,比如说驱动表的连接列。
rows:估计每次需要扫描的行数,数值越大越不好,说明没有用好索引
filtered:rows*filtered/100 表示该步骤最后得到的行数(估计值)。
extra:重要的补充信息。
(2)、explain 中的索引问题
Explain 结果中,一般来说,要看到尽量用 index(type 为 const、 ref 等, key 列有值),避免使用全表扫描(type 显式为 ALL)。比如说有 where 条件且选择性不错的列,需要建立索引。
被驱动表的连接列,也需要建立索引。被驱动表的连接列也可能会跟 where 条件列一起建立联合索引。当有排序或者 group by 的需求时,也可以考虑建立索引来达到直接排序和汇总的需求