一、Mysql基础架构示意图

Server层:

Server 层包括连接器、查询缓存、分析器、优化器、执行器等,涵盖 MySQL 的大多数核心服务功能,以及所有的内置函数(如日期、时间、数学和加密函数等),所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图等。

存储引擎层

而存储引擎层负责数据的存储和提取。其架构模式是插件式的,支持 InnoDB、MyISAM、Memory 等多个存储引擎。现在最常用的存储引擎是 InnoDB,它从 MySQL 5.5.5 版本开始成为了默认存储引擎。

二、Mysql日志系统

1、redo日志(重做日志)

  • IndoDB引擎的日志系统,MySQL 里的 WAL 技术,WAL 的全称是 Write-Ahead Logging,它的关键点就是先写日志,再写磁盘
  • 当有一条记录需要更新的时候,InnoDB 引擎就会先把记录写到 redo log(粉板)里面,并更新内存,这个时候更新就算完成了。同时,InnoDB 引擎会在适当的时候,将这个操作记录更新到磁盘里面,而这个更新往往是在系统比较空闲的时候做
  • 有了 redo log,InnoDB 就可以保证即使数据库发生异常重启,之前提交的记录都不会丢失,这个能力称为 crash-safe。
  • redo log 是循环写的,空间固定会用完
  • redo log 是物理日志,记录的是“在某个数据页上做了什么修改”

    2、binlog日志(归档日志)

  • Server 层也自己的日志,称为 binlog,binlog 日志只能用于归档
  • binlog 是可以追加写入的
  • binlog 是逻辑日志,记录的是这个语句的原始逻辑,比如“给 ID=2 这一行的c字段加 1 ”

3、Mysql写日志的两阶段提交

  1. 执行器先找引擎取 ID=2 这一行。ID 是主键,引擎直接用树搜索找到这一行。如果 ID=2 这一行所在的数据页本来就在内存中,就直接返回给执行器;否则,需要先从磁盘读入内存,然后再返回。
  2. 执行器拿到引擎给的行数据,把这个值加上 1,比如原来是 N,现在就是 N+1,得到新的一行数据,再调用引擎接口写入这行新数据。
  3. 引擎将这行新数据更新到内存中,同时将这个更新操作记录到 redo log 里面,此时 redo log 处于 prepare 状态。然后告知执行器执行完成了,随时可以提交事务。
  4. 执行器生成这个操作的 binlog,并把 binlog 写入磁盘。
  5. 执行器调用引擎的提交事务接口,引擎把刚刚写入的 redo log 改成提交(commit)状态,更新完成。

redo log 和 binlog 都可以用于表示事务的提交状态,而两阶段提交就是让这两个状态保持逻辑上的一致。

4、协助理解两阶段提交的数据一致性:

1 prepare阶段 2 写binlog 3 commit
当在2之前崩溃时
重启恢复:后发现没有commit,回滚。备份恢复:没有binlog 。
一致
当在3之前崩溃
重启恢复:虽没有commit,但满足prepare和binlog完整,所以重启后会自动commit。备份:有binlog。 一致

5、update 语句执行流程

三、事务隔离

事务就是要保证一组数据库操作,要么全部成功,要么全部失败。在 MySQL 中,事务支持是在引擎层实现的。MySQL 是一个支持多引擎的系统,但并不是所有的引擎都支持事务。比如 MySQL 原生的 MyISAM 引擎就不支持事务,这也是 MyISAM 被 InnoDB 取代的重要原因之一。
事务的四个特性:
ACID(Atomicity、Consistency、Isolation、Durability,原子性、一致性、隔离性、持久性)
当数据库上有多个事务同时执行的时候,就可能出现脏读(dirty read)、不可重复读(non-repeatable read)、幻读(phantom read)的问题,为了解决这些问题,就有了“隔离级别”的概念。
SQL 标准的事务隔离级别包括:

  1. 读未提交(read uncommitted)
    读未提交是指,一个事务还没提交时,它做的变更就能被别的事务看到。
  2. 读提交(read committed)
    读提交是指,一个事务提交之后,它做的变更才会被其他事务看到
  3. 可重复读(repeatable read)
    一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。当然在可重复读隔离级别下,未提交变更对其他事务也是不可见的
  4. 串行化(serializable )
    顾名思义是对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。

协助理解

  1. 读未提交:别人改数据的事务尚未提交,我在我的事务中也能读到。
  2. 读已提交:别人改数据的事务已经提交,我在我的事务中才能读到。
  3. 可重复读:别人改数据的事务已经提交,我在我的事务中也不去读。
  4. 串行:我的事务尚未提交,别人就别想改数据。

这4种隔离级别,并行性能依次降低,安全性依次提高。

四、索引

1、索引的常见模型

  • 哈希表
    哈希表是一种以键 - 值(key-value)存储数据的结构,我们只要输入待查找的键即 key,就可以找到其对应的值即 Value。哈希的思路很简单,把值放在数组里,用一个哈希函数把 key 换算成一个确定的位置,然后把 value 放在数组的这个位置。
    哈希表这种结构适用于只有等值查询的场景,比如 Memcached 及其他一些 NoSQL 引擎。
  • 有序数组
    有序数组在等值查询和范围查询场景中的性能就都非常优秀
    但是,在需要更新数据的时候就麻烦了,你往中间插入一个记录就必须得挪动后面所有的记录,成本太高,所以,有序数组索引只适用于静态存储引擎,比如你要保存的是 2017 年某个城市的所有人口信息,这类不会再修改的数据。
  • 搜索树
    N 叉树由于在读写上的性能优点,以及适配磁盘的访问模式,已经被广泛应用在数据库引擎中了
    在 InnoDB 中,表都是根据主键顺序以索引的形式存放的,这种存储方式的表称为索引组织表。又因为前面我们提到的,InnoDB 使用了 B+ 树索引模型,所以数据都是存储在 B+ 树中的。
    每一个索引在 InnoDB 里面对应一棵 B+ 树。

主键索引的叶子节点存的是整行数据。在 InnoDB 里,主键索引也被称为聚簇索引(clustered index)。
非主键索引的叶子节点内容是主键的值。在 InnoDB 里,非主键索引也被称为二级索引(secondary index)。
基于非主键索引的查询需要多扫描一棵索引树。因此,我们在应用中应该尽量使用主键查询

2、索引维护

  1. 自增主键的插入数据模式,正符合了我们前面提到的递增插入的场景。每次插入一条新记录,都是追加操作,都不涉及到挪动其他记录,也不会触发叶子节点的分裂。
  2. 有业务逻辑的字段做主键,则往往不容易保证有序插入,这样写数据成本相对较高
  3. 主键长度越小,普通索引的叶子节点就越小,普通索引占用的空间也就越小。

从性能和存储空间方面考量,自增主键往往是更合理的选择。

3、覆盖索引

二级索引查询结果仅仅是主键 此时不需要回表查主键索引 称为覆盖索引
由于覆盖索引可以减少树的搜索次数,显著提升查询性能,所以使用覆盖索引是一个常用的性能优化手段。

4、最左前缀原则

B+ 树这种索引结构,可以利用索引的“最左前缀”,来定位记录
在建立联合索引的时候,如何安排索引内的字段顺序。

  1. 第一原则是,如果通过调整顺序,可以少维护一个索引,那么这个顺序往往就是需要优先考虑采用的
  2. 第二原则要考虑的就是空间了
作者:于浩  创建时间:2022-12-24 20:45
最后编辑:于浩  更新时间:2025-06-05 12:21