每日学习Java之一万个为什么?[MySQL面试篇]

分析SQL语句执行流程中遇到的问题

  • 前言
  • [1 MySQL是怎么在一台服务器上启动的](#1 MySQL是怎么在一台服务器上启动的)
  • [2 MySQL主库和从库是同时启动保持Alive的吗?](#2 MySQL主库和从库是同时启动保持Alive的吗?)
  • [3 如果不是主从怎么在启动的时候保证数据一致性](#3 如果不是主从怎么在启动的时候保证数据一致性)
  • [4 ACID原则在MySQL上的体现](#4 ACID原则在MySQL上的体现)
  • [5 数据在MySQL是通过什么DTO实现的](#5 数据在MySQL是通过什么DTO实现的)
  • [6 客户端怎么与MySQL Server建立连接,有哪些建立连接的方式,默认是什么,可以选什么,有哪些应用场景](#6 客户端怎么与MySQL Server建立连接,有哪些建立连接的方式,默认是什么,可以选什么,有哪些应用场景)
  • 7.SQL组件接受的SQL是什么形式?为什么单一一条SQL不做任何处理就被MySQL认为是一个事务?
  • 8.查询后的结果是由哪个组件以什么形式返回?
  • 9.解析器是怎么解析的,解析器会调用API吗?
  • 10.优化器是怎么优化的?会调用API吗?
  • 11.SQL报错机制会在哪些区域出现?以什么形式发给客户端?以什么形式写到哪个日志中?
  • 12.执行器本质上是什么?
  • [13 MySQL内部有哪些线程?怎么分类?](#13 MySQL内部有哪些线程?怎么分类?)
  • [14 MySQL进程包含哪些内容](#14 MySQL进程包含哪些内容)
  • [15 执行器会将sql提交给引擎还是调用引擎,又是怎么调用的](#15 执行器会将sql提交给引擎还是调用引擎,又是怎么调用的)
  • [16 buffer pool区域中的数据页有多少,以什么结构存储,脏页呢?](#16 buffer pool区域中的数据页有多少,以什么结构存储,脏页呢?)
  • [17 buffer pool 中的数据页怎么更新的,淘汰后的数据页由谁清理](#17 buffer pool 中的数据页怎么更新的,淘汰后的数据页由谁清理)
  • [18 后台线程对脏页刷盘是怎么实现的](#18 后台线程对脏页刷盘是怎么实现的)
  • [19 sql语句中加锁在哪个部分实现的](#19 sql语句中加锁在哪个部分实现的)
  • [20 事务在engine中怎么实现](#20 事务在engine中怎么实现)
  • [21 MVCC怎么实现](#21 MVCC怎么实现)
  • [22 redolog分为哪几块内容?有什么作用](#22 redolog分为哪几块内容?有什么作用)
  • [23 binlog呢](#23 binlog呢)
  • [24 undolog 呢](#24 undolog 呢)
  • [25 数据写磁盘怎么实现的,怎么传输的,怎么保证一致性的](#25 数据写磁盘怎么实现的,怎么传输的,怎么保证一致性的)
  • [26 MySQL断开连接可以从线程和客户端区分吗](#26 MySQL断开连接可以从线程和客户端区分吗)
  • [27 MySQL进程结束会执行哪些持久化操作](#27 MySQL进程结束会执行哪些持久化操作)

前言

面试想要过关斩将,你就必须扪心自问,问得越多问的越细,才能披荆斩棘。盔甲若是漏洞百出,不被戳死才怪。

下列问题为作者提出,回答搜集而来。

由于作者并没有系统化从零开始学习MySQL,所以可能某些非常基础的问题并不了解。

欢迎读者留言比较重要的基础,wink👍

1 MySQL是怎么在一台服务器上启动的

  • MySQL通过配置文件:my.cnf my.ini 来指定各种参数,如端口号、数据目录等。
  • 初始化数据库:首次启动的时候,MySQL需要初始化系统表(这里指定的是系统表空间吗)
  • 启动系统,生成日志

2 MySQL主库和从库是同时启动保持Alive的吗?

为了实现数据同步,通过binlog+并行复制维持数据一致性,主库应该首先启动并正常运行,然后从库再启动并连接到主库。

3 如果不是主从怎么在启动的时候保证数据一致性

  • 如果不是主从库,没法保证,可以自己设置一些通信维护。
    如果没有同时启动,其实无所谓。
  • 保证数据一致性,主从之间首先需要通信,分为三类异步、同步、半同步。由于是主库写日志,等待从库响应。所以主库到底等不等从库是一个影响性能的问题。异步-半同步-同步,一致性会越来越高,期望响应时间越来越长。(那么这里的sql写日志都是事务写进去的吗)
  • 一句话概括:主库提交会写binlog,会由一个dump线程监听binlog文件的变更,如果有更新会推送更新时间到从库,从库接收到事件后会拉取数据,有一个IO线程将binlog传过来的数据写到自己的relaylog中,慢慢消化。所以,我们先更新的是事务语句,至于执行则是慢慢来。
  • 5.6库级别并行复制:
  • 5.7 Group Commit 级别 并行复制
  • logical_clock 逻辑时钟复制
  • Write Set复制

4 ACID原则在MySQL上的体现

  • A:MySQL使用BEGIN /COMMIT /ROLLBACK 来管理事务
  • C:通过约束和触发器确保数据库的状态始终符合预期
  • I:支持四种隔离级别,读未提交,读已提交,可重复读,串行化,通过锁定机制和MVCC实现
  • D:一旦事务提交,数据就会永久保存,即使发生系统崩溃也不会丢失。MySQL使用重做日志 和 双写缓冲区来实现持久性

5 数据在MySQL是通过什么DTO实现的

6 客户端怎么与MySQL Server建立连接,有哪些建立连接的方式,默认是什么,可以选什么,有哪些应用场景

  • TCP IP 通过网络地址和端口连接到MySQL服务器,适用于远程连接
  • Unix Socket连接:在本地Linux/Unix系统上,可以通过Unix域套接字连接到MySQL服务器。这种方式比TCP IP更快,因为它避免了网络层的开销
java 复制代码
jdbc:mysql://database_name?socket=/temp/mysql.sck
  • 命名管道:仅适用于win 本地连接

  • 共享内存:另一种win特有的连接方法

  • 默认使用TCP IP

7.SQL组件接受的SQL是什么形式?为什么单一一条SQL不做任何处理就被MySQL认为是一个事务?

MySQL接收到的SQL是标准的SQL语句,通常以文本的形式发给服务层

在MySQL中,默认情况下,每条单独提交的SQL语句都被视为一个独立的事务。这是因为autocommit模式下,每条语句执行完毕后会自动提交事务。如果autocommit被关闭,需要显式的 使用 BEGIN 和 COMMIT 提交事务

8.查询后的结果是由哪个组件以什么形式返回?

执行器也就是sql线程生成结果集,通过网络协议返回给客户端

  • 执行器:执行查询并生成结果集 MYSQL_RES结构
  • 网络层:将结果集打包成适当的格式(如二进制或文本),并通过连接通道返回给客户端。
java 复制代码
typedef struct st_mysql_res
{
my_ulonglong row_count;
unsigned int field_count,current_field;
MYSQL_FIELD *fields;
MYSQL_DATA *data;
MYSQL_ROWS *data_cursor;
MEM_ROOT field_alloc;
MYSQL_ROW row;
MYSQL_ROW current_row;
unsigned long *lengths;
MYSQL *handle;
my_bool eof;
}MYSQL_RES

typedef struct st_mysql_rows 
{
  struct st_mysql_rows *next;	//列表的行
  MYSQL_ROW data;
} MYSQL_ROWS;   //mysql的数据的链表节点。可见mysql的结果集是链表结构
 
typedef struct st_mysql_data 
{
  my_ulonglong rows;
  unsigned int fields;
  MYSQL_ROWS *data;
  MEM_ROOT alloc;
} MYSQL_DATA;   // 数据集的结构
 
typedef struct st_mysql_field 
{
  char *name;					// 列名称 
  char *table;	                //如果列是字段,列表
  char *def;	                //默认值(由mysql_list_fields设置)
  enum enum_field_types type;	//类型的字段。Se mysql_com。h的类型
  unsigned int length;	        //列的宽度
  unsigned int max_length;	    //选择集的最大宽度
  unsigned int flags;	        //Div标记集
  unsigned int decimals;	    //字段中的小数位数
} MYSQL_FIELD;  //列信息的结构
 
 
typedef struct st_used_mem      //结构为once_alloc
{	
  struct st_used_mem *next;   	//下一个块使用
  unsigned int left;	        //记忆留在块
  unsigned int size;	        //块的大小
} USED_MEM;     //内存结构
 
typedef struct st_mem_root 
{
  USED_MEM *free;
  USED_MEM *used;
  USED_MEM *pre_alloc;
  unsigned int	min_malloc;
  unsigned int	block_size;
  void (*error_handler)(void);
} MEM_ROOT;  //内存结构

可以看到MySQL是用C++/C 开发的

9.解析器是怎么解析的,解析器会调用API吗?

  • 词法分析:将SQL语句分解为一个个token

  • 语法分析:tokens转换语法树

  • 语义分析

  • 解析器本身并不调用外部API,但他可能会调用内部函数或者模块来完成某些任务,例如:

  • 元数据访问:访问系统表或缓存中的元数据,以验证表和列的存在性。

  • 权限检查:调用权限管理模块,确保用户有足够的权限执行该操作

10.优化器是怎么优化的?会调用API吗?

  • 查询重写:对查询初步重写,以简化查询结构
  • 统计信息收集:收集表的统计信息(例如行数、索引选择性等),用于生成最优执行计划。
  • 生成候选计划:基于不同的访问路径(如全表扫描、索引扫描),生成多个候选执行计划(生成策略?)
  • 选择最佳计划:评估每个候选计划的成本(评估算法?),选择成本最低的计划作为最终执行计划
  • 优化器内部统计信息模块:获取表的统计信息(包含什么?)
  • 代价模型:计算成本 (理论支持?)
  • 访问路径模块:确定可用的访问路径(索引、表扫描等)

11.SQL报错机制会在哪些区域出现?以什么形式发给客户端?以什么形式写到哪个日志中?

报错机制出现的区域:

  • 解析阶段:SQL语法错误或未知表/列名
  • 优化阶段:无法生成有效的执行计划
  • 执行阶段:运行时错误,如违反约束、超时等
  • 网络传输阶段:连接断开或其他通信错误

发给客户端的形式:

  • 错误代码:一个唯一的数字标识。
  • 错误消息:详细的描述信息,帮助理解错误原因。
java 复制代码
{
	"code":1054,
	"message":"Unknown column 'nonexistent_column'in 'field list'"
}

写入日志的形式:

  • 错误日志:记录所有严重的错误和警告,默认位于/var/log/mysql/error.log 或安装目录下的data文件夹中。
  • 慢查询日志:记录执行时间超过阈值的查询,默认位于/var/log/mysql/slow-query.log
java 复制代码
2025-02-27T11:41:23.123456Z 0 [ERROR] [MY-01054] Unknown column 'nonexistent_column' in 'field list'

12.执行器本质上是什么?

执行器是MySQL查询处理过程中实际执行查询计划的部分。它根据优化器生成的执行计划,调用存储引擎接口来访问和修改数据。

执行器的主要职责:

1.物理操作执行:根据执行计划中的物理操作(如表扫描、索引查找等)执行相应的数据库操作。

2.数据获取与处理:从存储引擎中获取数据,并进行必要的过滤、排序、聚合等操作。

3.结果集生成:将处理后的数据组织成结果集,准备返回给客户端。

执行器的工作流程:

1.初始化执行计划:加载执行计划并准备执行环境。

2.执行物理操作:按顺序执行各个物理操作步骤。

3.返回结果

13 MySQL内部有哪些线程?怎么分类?

1.主线程(Main Thread)

  • 职责:负责启动和管理其他的线程
  • 实例:mysql进程的主线程

2.连接处理线程(Connection Threads)

  • 职责:处理客户端连接请求,每个客户端连接对应一个独立的线程
  • 实例:thd线程对象,用于处理每个客户端连接

3.后台线程(Background Threads)

  • 职责:执行各种后台任务,如日志刷新、数据页刷盘等。
  • 示例:InnoDB缓冲池线程:负责缓冲池管理和脏页刷盘。Binlog Dump线程:监听binlog 发送给从库。Purge线程:负责清理不再需要的undo日志

4.IO线程(I/O Threads)

  • 职责:处理与存储设备的IO操作
  • 示例:Redo Log线程:负责写入重做日志。 Binlog线程:负责写入二进制日志。

5.事务处理线程(Transaction Handing Threads)

  • 职责:管理事务的开始、提交和回滚操作。
  • 示例:事务管理线程,负责协调事务的状态。

14 MySQL进程包含哪些内容

MySQL进程(mysqld)包含多个组件和模块,主要分为以下几个部分:

  • 1.服务器核心 Server Core

    1. 存储引擎 Strorage Engines
  • 3.缓存和缓冲区(Caches and Buffers)

    • Buffer Pool:用于缓存数据和索引数据。
    • Query Cache:缓存查询结果以提高查询效率(读缓存已被移除)
  • 日志系统 (Logging System)

    • Binlog:用于主从复制和数据备份,记录所有更改操作。
    • Undo Log:用于事务回滚,记录事务的反向操作
    • Redo Log:用于崩溃恢复,记录所有未提交的事务
  • 网络层(Network Layer)

    • 处理客户端连接、数据传输和协议解析。
  • 安全管理(Security Management)

    • 执行各种后台任务,如日志刷新、数据页刷盘等。

15 执行器会将sql提交给引擎还是调用引擎,又是怎么调用的

执行器会通过调用存储引擎的接口来执行具体的数据库操作

调用方式:

  • 接口调用:执行器通过调用存储引擎提供的接口(如InnoDB的API)来执行具体的操作。
  • 物理操作执行:根据优化器生成的执行计划,执行器调用相应的存储引擎接口来访问和修改数据。

16 buffer pool区域中的数据页有多少,以什么结构存储,脏页呢?

数据页数量:

  • 大小可配置:Buffer Pool的大小可以通过配置文件中的innodb_buffer_pool_size参数设置,默认值通常为系统内存的70%-80%。
  • 每页大小:默认情况下,每页大小为16KB。

存储结构:

  • LRU链表:Buffer Pool 使用LRU(Least Recently Used)链表来管理数据也。最近使用的页面位于链表前端,较少使用的页面位于链表末端。
  • Flush链表:脏页(已修改但尚未写入磁盘的页面)会被放入Flush链表中,等待刷盘

脏页:

  • 定义:脏页是指已经被修改但还写回到磁盘的数据页。
  • 存储位置:脏页同样存储在Buffer Pool中,并被标记为脏页。
  • 管理:脏页通过Flush链表管理,定期或在特定条件下(如内存不足或打到一定阈值)被刷回磁盘。

17 buffer pool 中的数据页怎么更新的,淘汰后的数据页由谁清理

数据页更新:

  • 读取当前数据页:当需要访问某个数据页时,如果该页不在Buffer Pool中,则从磁盘加载到Buffer Pool。
  • 修改数据页:对数据页进行修改后,将其标记为脏页。
  • 写回磁盘:脏页会在适当的时候(如内存不足或达到一定的阈值)通过后台线程写回到磁盘。

淘汰后的数据页清理:

  • LRU链表:当Buffer Pool 满时,使用LRU算法淘汰最近最少使用的页面

  • 清理过程:

    • 如果被淘汰的页面是干净的(未修改),可以直接丢弃。
    • 如果被淘汰的页面是脏页,则必须先将其写回磁盘(通过Flush链表管理)。

18 后台线程对脏页刷盘是怎么实现的

后台线程通过以下几种机制实现脏页的刷盘:

1.定时刷盘:

  • InnoDB自动刷新:InnoDB会定期检查脏页的比例,如果超过设定的阈值(如innodb_max_dirty_pages_pct),则触发自动刷新操作。

2.后台线程:

  • Page Cleaner线程:负责将脏页写回到磁盘。它会定期检查Flush链表中的脏页,并将这些脏页回到磁盘。

3.事务提交:

  • 同步刷盘:在某些情况下(如事务提交),为了保证数据一致性,需要立即刷盘。此时会调用fsync()函数确保数据已经写入磁盘。

4.内存压力:

  • 当Buffer Pool内存不足时,会优先淘汰脏页,并将其写回磁盘。

19 sql语句中加锁在哪个部分实现的

SQL语句的加锁操作主要在执行器和存储引擎中实现:

1.解析阶段:

  • 解析器解析SQL语句,识别出需要加锁的对象(如表、行等)

2.执行器:

  • 执行器根据解析结果,在执行过程中调用存储引擎的加锁接口,对相关对象加锁。

3.存储引擎:

  • 存储引擎实现具体的加锁机制。例如,InnoDB支持行级锁(共享锁,排他锁)和表级锁。
java 复制代码
SELECT * FROM users WHERE id = 1 FOR UPDATE;

执行器在执行这条查询时,会调用InnoDB的加锁接口,对users表中id=1的行加排它锁

20 事务在engine中怎么实现

事务在存储引擎中的实现主要包括以下几个方面:

1.事务管理器:

  • 负责事务的生命周期,包括开始、提交、回滚。

2.日志系统:

  • Redo Log:记录所有未提交的事务操作,用于崩溃恢复。
  • Undo Log:记录事务的反向操作

3.并发控制:

  • MVCC(多版本并发控制):支持高并发下的读写操作,避免锁冲突
  • 锁机制:提供行级锁和表级锁,确保事务的隔离性。

4.持久化:

  • 双写缓冲区(Doublewrite Buffer):确保数据页在写入磁盘时的一致性。
  • 刷盘操作:通过后台线程定期将脏页协会到磁盘里。

21 MVCC怎么实现

MVCC,通过维护数据不同的版本来支持高并发下的读写操作。其主要实现机制如下:

  • 隐藏列:每个数据行包含两个隐藏列:DB_TRX_ID 事务ID DB_ROLL_PTR 指向undo的指针。
  • 快照读:读操作(如SELECT)不会阻塞写操作,而是读取数据的历史版本。读操作会根据当前事务的Read View来决定读取哪个版本的数据
  • 写操作:写操作会在新版本的数据上进行操作,并记录undo日志以便回滚。
  • Undo日志:记录事务的反向操作,用于事务回滚和旧版本构建

假如有一个事务在时间点T1插入一条记录,另一个事务B在时间点T2更新了该记录。事务C在T3时间点进行读取操作时,会读取到T2时间点之前的数据版本。

22 redolog分为哪几块内容?有什么作用

Redo Log 是 InnoDB 存储引擎用于崩溃恢复的日志系统,其主要分为以下几个部分:

  • 1.Log Blocks:

    • Redo Log被划分为多个固定大小的块(通常是512字节),每个块称为一个 Log Block。
  • 2.Log Sequence Number(LSN):

    • 每条日志记录都有一个唯一的LSN,表示日志记录的顺序号。
  • 3.Checkpoint

    • Checkpoint 是 Redo Log中的一个特殊标记,表示已成功写入磁盘的数据页。它用于加速崩溃恢复的过程。
  • 崩溃恢复:在MySQL崩溃重启时,通过Redo Log恢复未完成的事务,确保数据的一致性。

  • 提高性能:通过批量写入Redo Log,减少频繁的磁盘IO操作。

23 binlog呢

主要用于主从复制和数据备份。

  • 事件类型:

    • Query Event:记录SQL查询语句。
    • Row Event:记录行级别的变更。
    • XID Event:记录事务提交信息。
  • 格式:

    • Statement-Based Logging:记录SQL语句。
    • Row-Based Logging : 记录每一行的变化。
    • Mixed-Based Logging :混合使用Statement和Row格式。

24 undolog 呢

Undo Log 是 InnoDB 存储引擎用于事务回滚和MVCC 的日志系统。其主要分为以下几个部分:

  • 1.Undo Segment:每个事务分配一个Undo Segment,用于记录该事务的所有Undo日志。

  • 2.Undo Record:每条Undo日志记录一次事务的反向操作,用于回滚和旧版本数据的构建。

  • 事务回滚:在事务回滚时,通过Undo Log 将数据恢复到事务开始前的状态。

  • MVCC:提供历史版本的数据,支持高并发下的操作。

25 数据写磁盘怎么实现的,怎么传输的,怎么保证一致性的

数据写磁盘的实现:

  • 1.Buffer Pool:数据首先写入Buffer Pool,而不是直接写入磁盘。
  • 2.Dirty Page:修改后的数据页标记为脏页,等待后续刷盘操作。
  • 3.后台线程:通过后台线程定期将脏页写回磁盘。

数据传输:

  • 异步写入:大多数情况下,数据写入磁盘是异步的,以提高性能。
  • 同步写入:在某些情况下(如事务提交),需要确保数据已经写入磁盘,此时会调用fsync()函数。

保证一致性:

  • Redolog:记录所有未提交的事务操作,用于数据恢复。
  • 双写缓冲区:确保数据页在写入磁盘时的一致性。
  • 两阶段提交:在分布式环境中,使用两阶段提交协议,确保事务的原子性和一致性。

26 MySQL断开连接可以从线程和客户端区分吗

线程角度:

线程终止:MySQL 服务器端会终止对应的连接线程,并释放相关的资源。

日志记录:断开连接的信息会被记录到错误日志中。

客户端角度:

异常处理:客户端应用程序需要捕获连接断开的异常,并进行相应的处理(如重新连接)。

超时机制:客户端可以通过设置超时参数(如 wait_timeout 和 interactive_timeout)来控制连接的有效期。

27 MySQL进程结束会执行哪些持久化操作

MySQL 进程结束时会执行一系列持久化操作,以确保数据的一致性和完整性:

Redo Log 刷盘:

将所有未提交的事务操作写入 Redo Log,确保在崩溃恢复时能够恢复这些事务。

Dirty Page 刷盘:

将 Buffer Pool 中的所有脏页写回到磁盘,确保数据的一致性。

Binlog 刷盘:

将 Binlog 中的所有更改操作写入磁盘,确保主从复制和数据备份的一致性。

Checkpoints:

创建 Checkpoint,标记已成功写入磁盘的数据页,加快崩溃恢复的速度。

关闭存储引擎:

关闭所有存储引擎实例,释放相关资源。

日志记录:

记录进程结束的相关信息到错误日志中,便于后续排查问题。

相关推荐
strongwyy23 分钟前
蓝牙墨水屏上位机学习(2)
学习
九皇叔叔26 分钟前
(3)手摸手-学习 Vue3 之 变量声明【ref 和 reactive】区别
学习
考虑考虑41 分钟前
JDK9中的dropWhile
java·后端·java ee
想躺平的咸鱼干1 小时前
Volatile解决指令重排和单例模式
java·开发语言·单例模式·线程·并发编程
hqxstudying1 小时前
java依赖注入方法
java·spring·log4j·ioc·依赖
·云扬·1 小时前
【Java源码阅读系列37】深度解读Java BufferedReader 源码
java·开发语言
KellenKellenHao2 小时前
MySQL数据库主从复制
数据库·mysql
Bug退退退1232 小时前
RabbitMQ 高级特性之重试机制
java·分布式·spring·rabbitmq
小皮侠2 小时前
nginx的使用
java·运维·服务器·前端·git·nginx·github
一只fish3 小时前
MySQL 8.0 OCP 1Z0-908 题目解析(16)
数据库·mysql