• 忘掉天地
  • 仿佛也想不起自己
bingliaolongBingliaolong  2026-03-15 23:59 Aet 隐藏边栏 |   抢沙发  1 
文章评分 1 次,平均分 5.0

概述

  1. 数据库(Database
    1. 数据的容器,一个 MySQL 服务可以有多个数据库
  2. 表(Table
    1. 数据库中存储数据的基本单位,由行(记录)和列(字段)组成
  3. 字段(Column
    1. 数据类型的约束,比如 INTVARCHARDATE

数据类型

整数类

  1. TINYINT, INT, BIGINTINT 最常用,BIGINT 用于存 ID 或大数字

字符串类

  1. CHAR(n) 定长,VARCHAR(n) 变长
    1. 短且固定的用 CHAR(如性别),长度不定的用 VARCHAR(如用户名)
  2. TEXT 用于存大段文字

时间类

  1. DATE(日期)、DATETIME(日期+时间)、TIMESTAMP(时间戳,会自动转时区)

浮点类

  1. FLOAT, DOUBLE 有精度问题,涉及金额建议用 DECIMAL(m,d)

数据库和表的管理

常规操作

关键约束

  1. PRIMARY KEY — 主键,唯一且非空,一般配合 AUTO_INCREMENT
  2. NOT NULL — 不允许为空
  3. UNIQUE — 唯一,不能重复
  4. DEFAULT — 默认值
  5. FOREIGN KEY — 外键,关联另一张表
    1. 实际开发中很多团队选择在应用层维护,不在数据库层加外键

  1. 注意
    1. DELETE 之前,先把 DELETE 换成 SELECT 跑一遍,确认要删的数据是对的,再改回 DELETE 执行

  1. 注意
    1. UPDATE 一定要带 WHERE 条件。不带 WHERE 会更新整张表,这是生产环境的经典事故

  1. 基础查询

  1. 排序与分页

  1. 聚合函数

  1. 分组

  1. 多级联查JOIN

  1. 子查询

SQL 执行顺序

  1. SQL 的顺序和数据库实际执行的顺序不一样,理解这个很重要
    1. 这就是为什么 WHERE 里不能用 SELECT 中定义的别名,但 ORDER BY 可以

实用小技巧

  1. 如果字段为 NULL,给一个默认值:

  1. 范围查询

  1. 拼接字符串

  1. 当前时间

MySQL 整体架构

概述

  1. MySQL 采用的是分层架构,从上到下大致分三层
    1. 连接层
    2. 服务层
    3. 存储引擎层

连接层

  1. 概述
    1. 客户端通过 TCP 连接到 MySQL 服务端,连接层负责认证(用户名、密码、权限校验)和连接管理
    2. 每个连接会分配一个线程来处理请求
    3. 频繁创建销毁连接开销大,所以生产中一般会用连接池(如 HikariCPDruid)来复用连接

服务层

  1. MySQL 的"大脑",与存储引擎无关的逻辑都在这一层,包含几个关键组件:
  2. 解析器(Parser):
    1. SQL 文本做词法分析和语法分析,生成一棵解析树(Parse Tree
    2. 如果语法错误,就在这一步报错
  3. 优化器(Optimizer):
    1. 拿到解析树后,优化器决定"怎么执行最快",比如选择用哪个索引、JOIN 的顺序、是否用临时表等
    2. 同一条 SQL 可能有多种执行方案,优化器基于成本模型(Cost-Based Optimization)选出代价最小的那个
  4. 执行器(Executor):
    1. 按照优化器给出的执行计划,调用存储引擎的接口逐步执行,拿到数据后返回给客户端
    2. 执行前会再做一次权限校验

存储引擎层

  1. MySQL 的一大特色是存储引擎可插拔
    1. 不同的表可以用不同的存储引擎
  2. InnoDB(默认,最重要):
    1. 支持事务、行级锁、外键、MVCC、崩溃恢复
  3. MyISAM
    1. 不支持事务和行锁,但查询快、全文索引支持好,老系统中常见
  4. Memory
    1. 数据存在内存中,重启丢失,适合临时表
  5. Archive
    1. 只支持插入和查询,适合归档日志类数据

完整流程

InnoDB 存储引擎原理

概述

  1. InnoDBMySQL 最核心的存储引擎

数据结构

  1. InnoDB 的数据是以页(Page)为基本单位存储在磁盘上的,每页默认大小 16KB
  2. 层级
    1. 一张表的数据存在一个 .ibd 文件中(独立表空间模式下)
    2. 读写数据时,MySQL 不是一行一行读磁盘的,而是一次读一整页到内存中,这是因为磁盘 IO 的最小单位是页

索引结构:B+

  1. InnoDB 的索引用的是 B+ 树,这是理解 MySQL 底层最关键的数据结构
  2. 为什么是 B+ 树而不是其他结构
    1. 二叉树 / 红黑树:层数太高,每一层就是一次磁盘 IO,数据量大时性能差
    2. B 树:每个节点都存数据,导致每个节点能存的 key 数量少,树更高
    3. B+ 树的优势:非叶子节点只存 key 不存数据,所以一个节点可以存更多 key,树更矮更胖,IO 次数少
      叶子节点之间用双向链表串联,范围查询(BETWEENORDER BY)非常高效
  3. 实际数据
    1. 一棵 3 层的 B+ 树大约可以存 两千万行数据
    2. 也就是说,查两千万行的表,最多只需要 3 次磁盘 IO

两种索引

  1. 聚簇索引Clustered Index
    1. 就是主键索引
    2. InnoDB 中表数据本身就是按主键组织的 B+ 树,叶子节点存的是完整的行数据
    3. 所以 InnoDB 的表本质上就是一棵 B+
  2. 二级索引Secondary Index
    1. 非主键索引
    2. 叶子节点存的不是行数据,而是主键值
    3. 通过二级索引查到主键后,还要回到聚簇索引去取完整数据,这个过程叫做回表

Buffer Pool(缓冲池)

  1. 磁盘 IO 是数据库最大的性能瓶颈,所以 InnoDB 在内存中维护了一个 Buffer Pool
  2. 工作原理
    1. 读数据时,先看 Buffer Pool 里有没有对应的页,有就直接从内存读(快几个数量级)
      没有就从磁盘读进来放到 Buffer Pool
    2. 写数据时也是先写 Buffer Pool 中的页(变成"脏页"),然后由后台线程异步刷到磁盘
  3. Buffer Pool 用的是改良的 LRU(Least Recently Used)算法来管理页的淘汰
    1. 为什么要"改良"?
    2. 因为如果执行一个全表扫描,会一次性把大量冷数据加载进来,把真正的热数据挤出去
    3. 所以 InnoDBLRU 链表分成了年轻代和老年代两段
      新读入的页先放老年代
      只有在老年代待够一定时间后再次被访问才会晋升到年轻代
  4. 生产环境中 innodb_buffer_pool_size 一般设为物理内存的 60%~80%

日志系统

  1. InnoDB 的可靠性和性能都靠日志系统支撑

  2. redo log(重做日志)—— 崩溃恢复的保障

    1. 问题:
      数据修改先写在 Buffer Pool 内存中,如果这时候 MySQL 崩溃了,内存中的脏页还没刷到磁盘,数据不就丢了?
    2. 解决:
      每次修改数据时,先把"做了什么修改"写到 redo log 中(顺序写磁盘,极快),再修改 Buffer Pool
      这样即使崩溃,重启后也能从 redo log 中恢复
      这就是 WALWrite-Ahead Logging,先写日志再写数据)机制
    3. redo logInnoDB 引擎层的日志,大小固定,循环写入(写满了就把对应的脏页刷盘腾出空间)
  3. undo log(回滚日志)—— 事务回滚 + MVCC

    1. 每次修改数据前,先把修改前的旧值记到 undo log 中
    2. 作用有两个:
      事务回滚时,用 undo log 把数据恢复原样
      MVCC 中用来构造数据的历史版本,让不同事务能读到不同时刻的快照
  4. binlog(归档日志)—— 主从复制 + 数据恢复

    1. binlogServer 层(不是 InnoDB 层)的日志,记录所有对数据有修改的 SQL
    2. 它的作用是主从复制(从库通过 binlog 重放主库的操作)和数据恢复(配合备份 + binlog 可以恢复到任意时间点)
  5. redo logbinlog 的两阶段提交:

    1. 一次事务提交的流程:
    2. 为什么要两阶段?
      为了保证 redo logbinlog 的一致性
      如果在第 2 步崩溃,重启后发现 redo logpreparebinlog 没写完,就回滚
      如果 binlog 写完了但第 3 步没执行,就提交。这保证了主从数据一致

事务与 MVCC

  1. 事务的四大特性(ACID):
    1. 原子性(Atomicity):靠 undo log 实现,要么全做要么全不做
    2. 一致性(Consistency):是最终目标,靠其他三个特性保证
    3. 隔离性(Isolation):靠锁和 MVCC 实现
    4. 持久性(Durability):靠 redo log 实现
  2. MVCC(多版本并发控制)的核心原理:
    1. 每一行数据有两个隐藏字段:trx_id(最后修改该行的事务 ID)和 roll_pointer(指向 undo log 中旧版本的指针)
      多次修改会形成一个版本链
    2. 当一个事务执行 SELECT 时,InnoDB 会生成一个 Read View(读视图),里面记录了当前活跃的事务 ID 列表
      通过对比 trx_idRead View,决定该行的哪个版本对当前事务"可见"
    3. 这样读操作不用加锁,读写互不阻塞,大大提升了并发性能

锁机制

  1. 行锁:
    1. InnoDB 默认的锁粒度,只锁住涉及的行
  2. 间隙锁(Gap Lock):
    1. 锁住索引记录之间的"间隙",防止其他事务在间隙中插入新行,用于解决幻读
  3. 临键锁(Next-Key Lock):
    1. 行锁 + 间隙锁的组合,是 InnoDB 在可重复读隔离级别下的默认加锁方式
  4. 表锁:
    1. 粒度大,MyISAM 用的就是表锁

其他

一条 UPDATE 语句的完整执行流程

  1. InnoDB 中的执行过程:
    1. 连接层:验证权限
    2. 解析器:解析 SQL,生成解析树
    3. 优化器:选择用主键索引查找
    4. 执行器:调用 InnoDB 接口
    5. InnoDB:在 Buffer Pool 中查找 id=1 的页,没有则从磁盘读入
    6. 将修改前的旧值写入 undo log(用于回滚和 MVCC
    7. Buffer Pool 中修改数据(此时数据页变为脏页)
    8. 将修改操作写入 redo logprepare 状态)
    9. 将修改操作写入 binlog
    10. redo log 标记为 commit
    11. 返回"更新成功"
    12. 后台线程会在合适时机将脏页刷到磁盘

学习

全连接和交叉连接(笛卡尔积)的区别

  1. 条件
    1. 全连接有ON条件
    2. 交叉连接没有条件
  2. 结果行数
    1. 全连接:小于等于A+B
    2. 交叉连接:AxB
  3. NULL处理
    1. 全连接:无匹配补NULL
    2. 交叉连接:不涉及补NULL
  4. 用途
    1. 全连接:保留两表所有记录
    2. 交叉连接:生成所有组合

主键

  1. 举例
    1. 去办事,工作人员要找你的档案。叫"张三"的人太多了,但你的身份证号全国唯一,一找一个准
  2. 特点
    1. 唯一:不能有两行的主键值相同
    2. 非空:不能为 NULL
    3. 一张表只能有一个主键(但主键可以由多列组合)

  1. 作用
    1. 唯一标识每一行,防止重复
    2. 自动建索引,加速查询
      主键背后 MySQL 自动建了一棵 B+ 树索引,所以按主键查数据非常快

外键

  1. 举例
    1. 学校有"学生表"和"班级表"。学生表里存了一个"班级ID",这个 ID 指向班级表的主键
    2. 这个"班级ID"就是外键
  2. 外键
    1. 就是在 A 表里,存了一个指向 B 表主键的字段,用来建立两张表之间的关联

  1. 作用
    1. 外键的核心作用是数据完整性约束——防止出现"孤儿数据"
    2. 插入时校验:你引用的数据必须存在
    3. 删除时校验:被引用的数据不能随便删
  2. 主外键关联使用

主键的底层原理

  1. B+ 树索引
    1. MySQLInnoDB)的表数据本身就是按主键排好序、存在一棵 B+ 树里的(叫做"聚簇索引")
    2. 学号 = 1001:沿树走几步就找到,O(log n) 效率
    3. 插入数据:按主键值插入树的对应位置,自动保持有序

  1. 主键为什么要求唯一且非空
    1. 因为树的节点靠这个值来定位,重复或空值会让树无法正确工作

外键的原理

  1. 外键本质上是 MySQL 在增删改时自动执行的约束检查

  1. 外键还支持级联策略,控制父表被删/改时子表怎么处理:

索引

  1. 没有索引时会发生什么
    1. MySQL 的做法:从第一行翻到最后一行,逐行比对
    2. 这叫全表扫描
  2. 举例
    1. 新华字典。想找"张"字,你不会从第1页翻到最后,而是先查目录(拼音索引),直接翻到对应页
    2. 目录就是索引
  3. 索引的底层结构:B+
    1. InnoDB 引擎的索引,底层是一棵 B+
    2. 假设有8个数,普通查找最多比8次。但如果组织成二叉搜索树,只需3次。数据量越大,优势越明显。

  1. B+ 树长什么样?
    1. 只有叶子节点存真实数据,上面的节点只存"路标"
    2. 所有叶子节点用链表连接,方便范围查询
    3. 树很矮(通常只有3-4层),查任何数据最多走3-4

  1. 为什么是 B+ 树而不是其他结构?
结构 问题
数组 插入/删除要移动大量数据,慢
普通二叉树 极端情况退化成链表,变成O(n)
哈希表 只能等值查询,不支持范围查询(><BETWEEN
B 每个节点都存数据,导致树更高,范围查询要回溯
B+ 矮胖、叶子链表支持范围查询、非常适合磁盘IO
  1. 索引的常见类型
    1. 复合索引的"最左前缀"原则
      复合索引 (姓名, 年龄) 的存储是先按姓名排序,姓名相同再按年龄排序

  1. 索引什么时候会失效?

两种核心索引

  1. 聚簇索引(主键索引)
    1. 数据本身就按主键顺序存在 B+ 树的叶子节点里
      索引即数据,数据即索引
    2. 每张表只有一棵聚簇索引(因为数据只能按一种顺序存)
    3. 按主键查询,一次 B+ 树查找直接拿到完整数据行
  2. 非聚簇索引(二级索引)
    1. 给非主键字段建的索引
    2. 叶子节点存的不是完整数据,而是该字段值 + 对应的主键值

  1. 回表 vs 覆盖索引
    1. 覆盖索引是一种重要的性能优化手段,能减少一次 B+ 树查找

视图

声明:本文为原创文章,版权归所有,欢迎分享本文,转载请保留出处!

bingliaolong
Bingliaolong 关注:0    粉丝:0
Everything will be better.

发表评论

表情 格式 链接 私密 签到
扫一扫二维码分享