2025/3/19
This commit is contained in:
109
技术/MySQL/MySQL相关.md
Normal file
109
技术/MySQL/MySQL相关.md
Normal file
@@ -0,0 +1,109 @@
|
||||
## ACID vs BASE
|
||||
|
||||
**ACID**(原子性、一致性、隔离性、持久性)和 **BASE**(基本可用、软状态、最终一致性)是数据库事务和系统设计的两种核心理念,分别适用于不同场景。
|
||||
|
||||
### **ACID**
|
||||
*适用于传统关系型数据库(如 MySQL、PostgreSQL)*
|
||||
1. **原子性(Atomicity)**:事务要么全部成功,要么全部失败回滚。
|
||||
2. **一致性(Consistency)**:数据始终符合预定义规则(如约束、触发器)。
|
||||
3. **隔离性(Isolation)**:并发事务互不干扰,结果等同于串行执行。
|
||||
4. **持久性(Durability)**:事务提交后数据永久保存,即使系统崩溃也不丢失。
|
||||
|
||||
**典型场景**:银行转账、订单支付、库存扣减等对数据准确性要求极高的场景。
|
||||
|
||||
### **BASE**
|
||||
*适用于分布式 NoSQL 系统(如 Cassandra、MongoDB)*
|
||||
1. **基本可用(Basically Available)**:系统即使部分故障,仍能响应请求(允许降级)。
|
||||
2. **软状态(Soft State)**:数据可能随时间变化,无需实时同步。
|
||||
3. **最终一致性(Eventually Consistent)**:数据更新会延迟同步,但最终全局一致。
|
||||
|
||||
**典型场景**:社交网络动态、电商商品浏览、日志存储等高并发、可容忍短暂不一致的场景。
|
||||
|
||||
|
||||
### **核心差异**
|
||||
| **维度** | **ACID** | **BASE** |
|
||||
|----------------|-----------------------------------|-----------------------------------|
|
||||
| **一致性** | 强一致性(实时) | 最终一致性(延迟) |
|
||||
| **优先目标** | 数据安全与准确性 | 高可用性与扩展性 |
|
||||
| **适用系统** | 单机/集中式数据库 | 分布式系统(如微服务、云原生) |
|
||||
| **性能特点** | 读写延迟较高,吞吐量较低 | 读写延迟低,吞吐量高 |
|
||||
|
||||
**一句话总结**:
|
||||
- **ACID**:牺牲性能换安全,适合“钱不能错”(如银行系统)。
|
||||
- **BASE**:牺牲强一致换高可用,适合“用户能等”(如微博评论)。
|
||||
|
||||
根据业务需求选择:**要么严格保数据,要么灵活保体验**。
|
||||
|
||||
## 索引的分类
|
||||
- 按 **「数据结构」** 分类:B+tree索引、Hash索引、Full-text索引。
|
||||
- 按 **「物理存储」** 分类:聚簇索引(主键索引)、二级索引(辅助索引)。
|
||||
- 按 **「字段特性」** 分类:主键索引、唯一索引、普通索引、前缀索引。
|
||||
- 按 **「字段个数」** 分类:单列索引、联合索引。
|
||||
|
||||
|
||||
## InnoDB默认索引
|
||||
在创建表时,InnoDB 存储引擎会根据不同的场景选择不同的列作为索引:
|
||||
|
||||
- 如果有主键,默认会使用主键作为聚簇索引的索引键(key);
|
||||
- 如果没有主键,就选择第一个不包含 NULL 值的唯一列作为聚簇索引的索引键(key);
|
||||
- 在上面两个都没有的情况下,InnoDB 将自动生成一个**隐式自增 id (不可见的名为row_id的列名为GEN_CLUST_INDEX的聚簇索引,该列是一个6字节的自增数值)** 列作为聚簇索引的索引键(key);
|
||||
其它索引都属于**辅助索引(Secondary Index)**,也被称为**二级索引或非聚簇索引**。创建的主键索引和二级索引默认使用的是 **B+Tree 索引**。
|
||||
|
||||
## MySQL 的存储引擎有哪些?为什么常用InnoDB?
|
||||
MySQL 的存储引擎常用的主要有 3 个:
|
||||
|
||||
- **InnoDB存储引擎**:支持事务处理,支持外键,支持崩溃修复能力和并发控制。如果需要对事务的完整性要求比较高(比如银行),要求实现并发控制(比如售票),那选择InnoDB有很大的优势。如果需要频繁的更新、删除操作的数据库,也可以选择InnoDB,因为支持事务的提交(commit)和回滚(rollback)。
|
||||
- **MyISAM存储引擎**:插入数据快,空间和内存使用比较低。如果表主要是用于插入新记录和读出记录,那么选择MyISAM能实现处理高效率。如果应用的完整性、并发性要求比 较低,也可以使用。如果数据表主要用来插入和查询记录,则MyISAM引擎能提供较高的处理效率
|
||||
- **MEMORY存储引擎**:所有的数据都在内存中,数据的处理速度快,但是安全性不高。如果需要很快的读写速度,对数据的安全性要求较低,可以选择MEMOEY。它对表的大小有要求,不能建立太大的表。所以,这类数据库只使用在相对较小的数据库表。如果只是临时存放数据,数据量不大,并且不需要较高的数据安全性,可以选择将数据保存在内存中的Memory引擎,MySQL中使用该引擎作为临时表,存放查询的中间结果
|
||||
**常用InnoDB的原因是支持事务,且最小锁的粒度是行级锁。**
|
||||
|
||||
## 执行一条 SQL 查询语句,期间发生了什么?
|
||||
|
||||
- 连接器:建立连接,管理连接、校验用户身份;
|
||||
- 查询缓存:查询语句如果命中查询缓存则直接返回,否则继续往下执行。MySQL 8.0 已删除该模块;
|
||||
- 解析 SQL,通过解析器对 SQL 查询语句进行词法分析、语法分析,然后构建语法树,方便后续模块读取表名、字段、语句类型;
|
||||
- 执行 SQL:执行 SQL 共有三个阶段:
|
||||
- 预处理阶段:检查表或字段是否存在;将 select * 中的 * 符号扩展为表上的所有列。
|
||||
- 优化阶段:基于查询成本的考虑, 选择查询成本最小的执行计划;
|
||||
- 执行阶段:根据执行计划执行 SQL 查询语句,从存储引擎读取记录,返回给客户端;
|
||||
|
||||

|
||||
|
||||
## MySQL 的 NULL 值是怎么存放的?
|
||||
|
||||
MySQL 的 Compact 行格式中会用「NULL值列表」来标记值为 NULL 的列,NULL 值并不会存储在行格式中的真实数据部分。
|
||||
|
||||
NULL值列表会占用 1 字节空间,当表中所有字段都定义成 NOT NULL,行格式中就不会有 NULL值列表,这样可节省 1 字节的空间。
|
||||
|
||||
MySQL 怎么知道 varchar(n) 实际占用数据的大小?
|
||||
|
||||
MySQL 的 Compact 行格式中会用「变长字段长度列表」存储变长字段实际占用的数据大小。
|
||||
|
||||
## varchar(n) 中 n 最大取值为多少?
|
||||
|
||||
一行记录最大能存储 65535 字节的数据,但是这个是包含「变长字段字节数列表所占用的字节数」和「NULL值列表所占用的字节数」。所以, 我们在算 varchar(n) 中 n 最大值时,需要减去这两个列表所占用的字节数。
|
||||
|
||||
如果一张表只有一个 varchar(n) 字段,且允许为 NULL,字符集为 ascii。varchar(n) 中 n 最大取值为 65532。
|
||||
|
||||
计算公式:65535 - 变长字段字节数列表所占用的字节数 - NULL值列表所占用的字节数 = 65535 - 2 - 1 = 65532。
|
||||
|
||||
如果有多个字段的话,要保证所有字段的长度 + 变长字段字节数列表所占用的字节数 + NULL值列表所占用的字节数 <= 65535。
|
||||
|
||||
## 行溢出后,MySQL 是怎么处理的?
|
||||
|
||||
如果一个数据页存不了一条记录,InnoDB 存储引擎会自动将溢出的数据存放到「溢出页」中。
|
||||
|
||||
Compact 行格式针对行溢出的处理是这样的:当发生行溢出时,在记录的真实数据处只会保存该列的一部分数据,而把剩余的数据放在「溢出页」中,然后真实数据处用 20 字节存储指向溢出页的地址,从而可以找到剩余数据所在的页。
|
||||
|
||||
Compressed 和 Dynamic 这两种格式采用完全的行溢出方式,记录的真实数据处不会存储该列的一部分数据,只存储 20 个字节的指针来指向溢出页。而实际的数据都存储在溢出页中。
|
||||
|
||||
## B+索引
|
||||
|
||||

|
||||
|
||||
相比于标准的B+树,InnoDB使用的B+树有如下特点:
|
||||
|
||||
- B+ 树的叶子节点之间是用「双向链表」进行连接,既能向右遍历、也能向左遍历
|
||||
- B+ 树点节点内容是数据页,数据页里存放了用户的记录以及各种信息,每个数据页默认大小是 16 KB
|
||||
|
||||
|
121
技术/MySQL/基础.md
Normal file
121
技术/MySQL/基础.md
Normal file
@@ -0,0 +1,121 @@
|
||||
## 1. 关系型数据库和非关系型数据库的区别
|
||||
|
||||
| **对比维度** | **关系型数据库** | **非关系型数据库** |
|
||||
|--------------------|--------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------|
|
||||
| **数据存储方式** | 数据以二维表格形式存储,结构化组织,强调行和列的关系 。 | 存储方式多样,如键值对、文档(JSON)、列族或图结构,适合非结构化或半结构化数据 。 |
|
||||
| **数据模型** | 基于关系模型,强调数据的一致性和完整性 。 | 数据模型灵活,支持分布式架构,适合动态变化的数据需求 。 |
|
||||
| **事务特性** | 遵循ACID原则(原子性、一致性、隔离性、持久性),确保强一致性 。 | 基于CAP理论(一致性、可用性、分区容错性),通常牺牲部分一致性以换取高可用性和扩展性 。 |
|
||||
| **扩展性** | 通常采用垂直扩展(增加硬件性能),扩展性有限 。 | 支持水平扩展(增加节点),适合大规模分布式系统,扩展性更强 。 |
|
||||
| **查询语言** | 使用SQL(结构化查询语言),通用性强且易于理解 。 | 通常使用特定API或查询语言,灵活性更高但学习成本较大 。 |
|
||||
| **适用场景** | 适合需要复杂查询、事务处理和强一致性的场景,如银行系统、ERP等 。 | 适合大数据、高并发、实时性要求高的场景,如社交网络、物联网等 。 |
|
||||
|
||||
## 2. 为什么我们需要索引
|
||||
|
||||
* 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。
|
||||
* 可以大大加快数据的检索速度,这也是创建索引的最主要的原因。
|
||||
* 帮助服务器避免排序和临时表
|
||||
* 将随机IO变为顺序IO。
|
||||
* 可以加速表和表之间的连接,特别是在实现数据的参考完整性方面特别有意义。
|
||||
|
||||
## 3. mysql优化了解吗-说一下从哪些方面可以做到性能优化
|
||||
|
||||
* 为搜索字段创建索引
|
||||
* 避免使用 Select \*,列出需要查询的字段
|
||||
* 垂直分割分表
|
||||
* 选择正确的存储引擎
|
||||
|
||||
## 隔离级别和问题避免
|
||||
|
||||
* 脏读:读到其他事务未提交的数据;
|
||||
* 不可重复读:前后读取的数据不一致;
|
||||
* 幻读:前后读取的记录数量不一致。
|
||||
|
||||
* 读未提交(read uncommitted),指一个事务还没提交时,它做的变更就能被其他事务看到;
|
||||
* 读提交(read committed),指一个事务提交之后,它做的变更才能被其他事务看到;
|
||||
* 可重复读(repeatable read),指一个事务执行过程中看到的数据,一直跟这个事务启动时看到的数据是一致的,MySQL InnoDB 引擎的默认隔离级别;
|
||||
* 串行化(serializable );会对记录加上读写锁,在多个事务对这条记录进行读写操作时,如果发生了读写冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行;
|
||||
|
||||
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|
||||
| --------------------- | --- | ----- | --- |
|
||||
| READ-UNCOMMITTED 未提交读 | √ | √ | √ |
|
||||
| READ-COMMITTED 提交读 | × | √ | √ |
|
||||
| REPEATABLE-READ 重复读 | × | × | √ |
|
||||
| SERIALIZABLE 可串行化读 | × | × | × |
|
||||
|
||||
|
||||
## Mysql有哪些日志,简单概括有什么用
|
||||
MySQL 中有多种日志,每种日志的作用各不相同,以下是它们的简单概括:
|
||||
|
||||
1. **Binlog(二进制日志)**
|
||||
- **作用**:记录所有对数据库的修改操作(DDL 和 DML 语句),但不包括查询语句(如 SELECT、SHOW)。主要用于数据恢复、主从复制和审计 。
|
||||
- **特点**:以二进制格式存储,支持 STATEMENT、ROW 和 MIXED 三种模式记录 。
|
||||
|
||||
2. **Redo Log(重做日志)**
|
||||
- **作用**:保证事务的持久性。记录的是数据页的物理修改,用于在 MySQL 崩溃后恢复未写入磁盘的数据(即“崩溃恢复”)。
|
||||
- **特点**:循环写入,固定大小,保存未刷入磁盘的脏页日志 。
|
||||
|
||||
3. **Undo Log(回滚日志)**
|
||||
- **作用**:保证事务的原子性。记录的是事务执行前的数据状态,用于回滚操作或实现 MVCC(多版本并发控制)。
|
||||
- **特点**:与 Redo Log 配合使用,确保事务的一致性和隔离性。
|
||||
|
||||
|
||||
## 执行一条语句操作日志的完整过程
|
||||
具体更新一条记录 `UPDATE t_user SET name = 'xiaolin' WHERE id = 1;` 的流程如下:
|
||||
|
||||
1. 执行器负责具体执行,会调用存储引擎的接口,通过主键索引树搜索获取 id = 1 这一行记录:
|
||||
* 如果 id=1 这一行所在的数据页本来就在 buffer pool 中,就直接返回给执行器更新;
|
||||
* 如果记录不在 buffer pool,将数据页从磁盘读入到 buffer pool,返回记录给执行器。
|
||||
2. 执行器得到聚簇索引记录后,会看一下更新前的记录和更新后的记录是否一样:
|
||||
* 如果一样的话就不进行后续更新流程;
|
||||
* 如果不一样的话就把更新前的记录和更新后的记录都当作参数传给 InnoDB 层,让 InnoDB 真正的执行更新记录的操作;
|
||||
3. 开启事务, InnoDB 层更新记录前,首先要记录相应的 undo log,因为这是更新操作,需要把被更新的列的旧值记下来,也就是要生成一条 undo log,undo log 会写入 Buffer Pool 中的 Undo 页面,不过在内存修改该 Undo 页面后,需要记录对应的 redo log。
|
||||
4. InnoDB 层开始更新记录,会先更新内存(同时标记为脏页),然后将记录写到 redo log 里面,这个时候更新就算完成了。为了减少磁盘I/O,不会立即将脏页写入磁盘,后续由后台线程选择一个合适的时机将脏页写入到磁盘。这就是 WAL 技术,MySQL 的写操作并不是立刻写到磁盘上,而是先写 redo 日志,然后在合适的时间再将修改的行数据写到磁盘上。
|
||||
5. 至此,一条记录更新完了。
|
||||
6. 在一条更新语句执行完成后,然后开始记录该语句对应的 binlog,此时记录的 binlog 会被保存到 binlog cache,并没有刷新到硬盘上的 binlog 文件,在事务提交时才会统一将该事务运行过程中的所有 binlog 刷新到硬盘。
|
||||
7. 事务提交(为了方便说明,这里不说组提交的过程,只说两阶段提交):
|
||||
8. prepare 阶段:将 redo log 对应的事务状态设置为 prepare,然后将 redo log 刷新到硬盘;
|
||||
9. commit 阶段:将 binlog 刷新到磁盘,接着调用引擎的提交事务接口,将 redo log 状态设置为 commit(将事务设置为 commit 状态后,刷入到磁盘 redo log 文件);
|
||||
10. 至此,一条更新语句执行完成。
|
||||
|
||||
|
||||
## 介绍MVCC的原理
|
||||
|
||||
MVCC允许多个事务同时读取同一行数据,而不会彼此阻塞,每个事务看到的数据版本是该事务开始时的数据版本。这意味着,如果其他事务在此期间修改了数据,正在运行的事务仍然看到的是它开始时的数据状态,从而实现了非阻塞读操作。
|
||||
|
||||
对于「读提交」和「可重复读」隔离级别的事务来说,它们是通过 Read View 来实现的,它们的区别在于创建 Read View 的时机不同,大家可以把 Read View 理解成一个数据快照,就像相机拍照那样,定格某一时刻的风景。
|
||||
|
||||
- 「读提交」隔离级别是在「每个select语句执行前」都会重新生成一个 Read View;
|
||||
- 「可重复读」隔离级别是执行第一条select时,生成一个 Read View,然后整个事务期间都在用这个 Read View。
|
||||
|
||||
Read View 有四个重要的字段:
|
||||
|
||||

|
||||
|
||||
- m_ids :指的是在创建 Read View 时,当前数据库中「活跃事务」的**事务 id 列表**,注意是一个列表,**“活跃事务”指的就是,启动了但还没提交的事务**。
|
||||
- min_trx_id :指的是在创建 Read View 时,当前数据库中「活跃事务」中事务 **id 最小的事务**,也就是 m_ids 的最小值。
|
||||
- max_trx_id :这个并不是 m_ids 的最大值,而是**创建 Read View 时当前数据库中应该给下一个事务的 id 值**,也就是全局事务中最大的事务 id 值 + 1;
|
||||
- creator_trx_id :指的是**创建该 Read View 的事务的事务 id**。
|
||||
|
||||
对于使用 InnoDB 存储引擎的数据库表,它的聚簇索引记录中都包含下面两个隐藏列:
|
||||
|
||||

|
||||
|
||||
- trx_id,当一个事务对某条聚簇索引记录进行改动时,就会**把该事务的事务 id 记录在 trx_id 隐藏列里**;
|
||||
- roll_pointer,每次对某条聚簇索引记录进行改动时,都会把旧版本的记录写入到 undo 日志中,然后**这个隐藏列是个指针,指向每一个旧版本记录**,于是就可以通过它找到修改前的记录。
|
||||
|
||||
在创建 Read View 后,我们可以将记录中的 trx_id 划分这三种情况:
|
||||
|
||||

|
||||
|
||||
一个事务去访问记录的时候,除了自己的更新记录总是可见之外,还有这几种情况:
|
||||
|
||||
- 如果记录的 trx_id 值小于 Read View 中的 min_trx_id 值,表示这个版本的记录是在创建 Read View **前**已经提交的事务生成的,所以该版本的记录对当前事务**可见**。
|
||||
|
||||
- 如果记录的 trx_id 值大于等于 Read View 中的 max_trx_id 值,表示这个版本的记录是在创建 Read View **后**才启动的事务生成的,所以该版本的记录对当前事务**不可见**。
|
||||
|
||||
- 如果记录的 trx_id 值在 Read View 的 min_trx_id 和 max_trx_id 之间,需要判断 trx_id 是否在 m_ids 列表中:
|
||||
|
||||
- 如果记录的 trx_id **在** m_ids 列表中,表示生成该版本记录的活跃事务依然活跃着(还没提交事务),所以该版本的记录对当前事务**不可见**。
|
||||
- 如果记录的 trx_id **不在** m_ids列表中,表示生成该版本记录的活跃事务已经被提交,所以该版本的记录对当前事务**可见**。
|
||||
|
||||
**这种通过「版本链」来控制并发事务访问同一个记录时的行为就叫 MVCC(多版本并发控制)**
|
Reference in New Issue
Block a user