note4cs/技术/MySQL/基础.md
2025-03-19 14:36:09 +08:00

121 lines
11 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## 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 logundo 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 有四个重要的字段:
![img](https://cdn.xiaolincoding.com//picgo/539cfd46e0f12ab0764f20e009705502.png)
- 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 存储引擎的数据库表,它的聚簇索引记录中都包含下面两个隐藏列:
![img](https://cdn.xiaolincoding.com//picgo/401c88a098b64517a4e343414142472a.png)
- trx_id当一个事务对某条聚簇索引记录进行改动时就会**把该事务的事务 id 记录在 trx_id 隐藏列里**
- roll_pointer每次对某条聚簇索引记录进行改动时都会把旧版本的记录写入到 undo 日志中,然后**这个隐藏列是个指针,指向每一个旧版本记录**,于是就可以通过它找到修改前的记录。
在创建 Read View 后,我们可以将记录中的 trx_id 划分这三种情况:
![img](https://cdn.xiaolincoding.com//picgo/format%2Cpng-20250122202229381.png)
一个事务去访问记录的时候,除了自己的更新记录总是可见之外,还有这几种情况:
- 如果记录的 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多版本并发控制**