02.08 Day 20 - 提炼 Day 1-5

大家好,我是 Snow Hide,作为《MySQL 实战》这个专栏的学员之一,这是我打卡的第 20 天,也是我第 75 次进行这种操作。

今天我温习了该专栏里叫《基础架构:一条SQL查询语句是如何执行的?》、《日志系统:一条 SQL 更新语句是如何执行的?》、《事务隔离:为什么你改了我还看不见?》、《深入浅出索引(上)》、《深入浅出索引(下)》、《全局锁和表锁 :给表加个字段怎么有这么多阻碍?》、《行锁功过:怎么减少行锁对性能的影响?》的文章。

关键词总结:Server 层(连接器、查询缓存、分析器、优化器、执行器、内置函数(日期、时间、数学、加密函数)、跨存储引擎功能(存储过程、触发器、视图))、存储引擎层(存储、提取、InnoDB)、重要的日志模块:redo log(所属层、WAL 技术、Redo Log、Write Pos、Checkpoint、Crash-Safe)、重要的日志模块:binlog(所属层、两种日志的三个区别点)、两阶段提交(恢复到指定的某一秒、先写 redo log 后写 binlog、先写 binlog 后写 redo log)、隔离性与隔离级别、(隔离级别(读未提交、读提交、可重复读、串行化)、隔离级别的事务效果(读未提交、读提交、可重复读、串行化))、事务隔离的实现、事务的启动方式(显式启动、隐式启动)、索引常见模型(哈希表、有序数组、搜索树)、InnoDB 索引模型(主键索引/聚簇索引、非主键索引/二级索引、主键索引与普通索引的查询区别)、索引维护(占用空间)、覆盖索引(提升查询性能)、最左前缀原则(建立联合以及单字段索引)、索引下推、锁分类、全局锁(加锁方法、应用场景、风险、建议使用 FTWRL 方式的两个原因)、表级锁(表级锁种类(表锁、元数据锁、MDL 读写锁))、支持行锁的引擎、两阶段锁(行锁的释放时间)、死锁和死锁检测(解决死锁的两种策略、解决热点行更新导致的性能问题)。

所学总结:

MySQL 基础架构

Server 层

连接器

负责与客户端建立连接、获取权限、维持以及管理连接。

查询缓存

不经常更改的表才适合使用查询缓存,例如系统配置表。

分析器

MySQL 需要识别出里面的字符串分别是什么,代表什么。

优化器

优化器是当表有多个索引时,决定使用哪个索引;或在多表关联语句时,决定表的连接顺序。

执行器

开始执行的时候,片段用户对操作表是否有对应操作类型的权限,没有则返回权限错误消息。
 

MySQL 日志系统

重要的日志模块:redo log / 重做日志

所属层

redo log 处在 InnoDB 引擎层。独有的 crash-safe 能力。

WAL 技术

WAL 的全称是 Write-Ahead Logging。它的原理是先写日志,再写磁盘。

Redo Log

当有记录需要更新时, InnoDB 引擎会先记录到 redo log (暂存)中,并更新内存,在系统空闲的时候再将记录更新至磁盘。

Write Pos

当前记录的未知,写完后移至下一个位置。循环写。

Checkpoint

write pos 与 checkpoint 之间空着的部分是用来记录新操作的。

Crash-Safe

redo log 可以确保数据库发生异常,重启后之前提交的记录依然存在,这个被称为 crash-safe(奔溃安全)。

重要的日志模块:binlog / 归档日志

所属层

binlog 处在 Server 层。没有 crash-safe 能力。

两种日志的三个区别点

  • redo log 为 InnoDB 引擎独有;binlog 由 MySQL 的 Server 层所实现,所有引擎均可使用;
  • redo log 属于物理日志,记录语句所做的改动;binlog 则是逻辑日志,记录的是语句的原始逻辑;
  • redo log 是循环写,空间可以被用完;binlog 是追加写,可以在到达指定数据量后切换至新的文件,不会覆盖原有日志。

两阶段提交

由于 redo log 与 binlog 是两个独立的逻辑,不用两阶段提交的话,只能是先写 redo log 再写 binlog,或者将操作反转。

恢复到指定的某一秒

  • 找到最近的一次全量备份,将其恢复至临时库;
  • 从备份的时间点开始,将备份的 binlog 依次取出并重放至误删表之前的那一时刻。

先写 redo log 后写 binlog

假设 redo log 写完,但 binlog 还未写完时,MySQL 进程发生异常。由于 redo log 已写完,即使系统奔溃,我们仍然能将数据恢复。但是 binlog 就不会存在该语句逻辑的记录。

先写 binlog 后写 redo log

倘若 binlog 写完,但 redo log 还未写完时,在奔溃重启后,该事务不成立。此时,binlog 里的语句逻辑记录是新的,而 redo log 里的物理数据则是旧的。
 

事务隔离级别与实现

隔离性与隔离级别

用以解决脏读(dirty read)、不可重复读(non-repeatable read)、幻读(phantom read)等问题。

隔离级别

SQL 标准的事务隔离级别有:读未提交(read uncommitted)、读提交(read committed)、可重复读(repeatable read)以及串行化(serializable)。

读未提交

事务提交之前其所做的变更可被其他事务看见。

读提交

事务提交之后其所做的变更才可被其他事务看见。

可重复读

事务执行过程看到的数据总是和事务在启动时看到的数据一致。事务提交之前其所做的变更对其他事务不可见。

串行化

对于同一条记录,写时加写锁,读时加读锁。出现读写锁冲突时需要等事务执行完成才能继续。

隔离级别的事务效果

读未提交

虽然事务 B 还未提交,但事务 A 能看到其结果。

读提交

事务 B 的更新在提交后才能被事务 A 所看到。

可重复读

事务在执行期间看到的数据在提交前后必须是一致的。

串行化

事务 B 无法操作事务 A 正在操作的数据,等事务 A 提交并释放锁后,事务 B 才能继续。

事务隔离的实现

每条记录在更新的时候都会同时记录一条回滚操作。通过回滚操作,记录上的最新值可以得到前一个状态的值。

事务的启动方式

显式启动

通过 begin 或 start transaction 的语句开始,以 commit 结尾或异常时以 rollback 结束。

隐式启动

将 set autocommit 设置为 0 时会讲线程自动提交关闭,当执行 select 语句时就触发了事务,该事务会一直存在直至遇到 commit 或 rollback 语句或断开与数据库的连接。
这是通常所说的长连接。建议将 set autocommit 设置为 1,并在 CUD 时显式启动事务。频繁使用事务的地方可以使用 commit work and chain 语法,其作用时将当前事务提交并立即开启下一个事务,从而省去再次调用 begin 语句所带来的开销。
 

MySQL 的索引

索引常见模型

索引可以提高查询效率。

哈希表

键值数据结构,根据 key 找 value。通过哈希函数将 key 换算成一个下标,然后将 value 放在数组中该下标所处的位置。
哈希表只适用于等值查询的场景,例如 Memcached 或其他 NoSQL 存储。

有序数组

在等值或范围查询中都能有比价好的性能表现。
有序数组只适合静态存储,例如一些不会做改动的数据。

搜索树

二叉树的搜索效率最高,但实际上大多数数据库存储却不适用。因为索引不止存在于内存,还要同时写到磁盘。
为了尽量减少 IO 操作,要让查询尽量少访问或不访问数据块,所以我们就不使用二叉树。

InnoDB 索引模型

使用了 B+ 树索引模型,所以数据存储在 B+ 树中。
每个索引对应一棵 B+ 树。

主键索引/聚簇索引 / Clustered Index

叶子结点所存的是一整行数据。

非主键索引/二级索引 / Secondary Index

叶子结点所存的是逐渐的值。

主键索引与普通索引的查询区别

  • 主键索引:只需要搜索主键字段所对应的 B+ 树;
  • 普通索引:需要先搜索普通字段的索引树,得到其主键值,再借助主键值到主键字段所对应的索引树中再查询一次。这个过程叫回表。
  • 非主键索引的查询需要多扫描一棵索引树,因此,应尽量使用主键查询。

索引维护

占用空间

主键长度越小,普通索引的叶子结点就越小,普通索引占用的空间也就越小。
以性能以及空间利用率为出发点的话,自增主键是比较合理的选择。

覆盖索引

提升查询性能

覆盖索引可以减少树的搜索次数,能很明显的提高查询性能,所以使用覆盖索引是常见的优化方式。

最左前缀原则

可以利用 B+ 树索引结构的 “最左前缀” 来定位记录。

建立联合以及单字段索引

第一原则是当通过调整顺序后,如果能少维护一个索引,那该顺序则很有可能被优先采用。

索引下推

在索引遍历过程中对其所包含的字段先做判断,过滤不匹配的记录以减少回表次数。
 

MySQL 的全局锁、表锁、行锁

锁分类

全局锁、表级锁、行锁。

全局锁

加锁方法

FTWRL(Flush Tables With Read Lock),让整库处于只读状态,此时其他线程所运行的一些语句会被阻塞:数据更新(增删改)、数据定义(建表、修改表结构)、更新类事务提交。

应用场景

全库逻辑备份。

风险

  • 主库加锁期间不能执行更新,业务暂停运作;
  • 从库加锁期间不能执行主库同步过来的 binlog,导致主从延迟。

建议使用 FTWRL 方式的两个原因

  • readonly 在一些系统中会被用作其他逻辑,例如判断是主库还是备库。因此修改 global 变量的影响比较大;
  • 执行 FTWRL 后客户端如果发生异常断开,库全局锁会自动释放,并回到正常状态。如果设置为 readonly 期间客户端发生异常,库会一直保持 readonly 状态,不会回到正常状态。

表级锁

表级锁种类

表锁、元数据锁(MDL,Meta Data Lock)

表锁

lock tables … read/write。与 FTWRL 类似,可以借助 unlock tables 来释放锁或在客户端断开时自动释放锁。lock tables 除了限制其他线程的读写外,还会限制本线程接下来的操作对象。

元数据锁

无须显式声明,访问表时自动上锁,并保证读写正确性。

MDL 读写锁
  • 读锁不互斥,可以多线程对表进行增删改查;
  • 读写锁的写锁互斥,保证表结构变更的安全性。多线程按顺序操作表结构。

支持行锁的引擎

InnoDB 支持行锁。

两阶段锁

行锁的释放时间

等到事务结束时才释放。

死锁和死锁检测

解决死锁的两种策略

  • 等待超时。设置超时参数:innodb_lock_wait_timeout;
  • 发起死锁检测,回滚死锁链条的一个事务,让其他事务可以继续。设置参数 innodb_deadlock_detech 为 on。

解决热点行更新导致的性能问题

  • 在确保业务不会出现死锁的情况下临时关掉死锁检测,关掉死锁检测可能会出现大量超时;
  • 控制并发度。同行更新在进入引擎之前排队,以减少 InnoDB 内部的死锁检测工作;
  • 通过将一行改成逻辑上的多行来减少冲突,以减少锁等待个数。

末了

重新总结了一下文中提到的内容:MySQL 逻辑架构、SQL 语句完整执行流程、redo log 物理日志、binlog 逻辑日志、redo log 保证 crash-safe 特性需要配置的参数、日志系统的两阶段提交、跨系统维持数据逻辑一致性方案、事务隔离级别的现象和实现、长事务存在的风险、通过正确方式避免长事务、数据库索引概念、覆盖索引、前缀索引、索引下推、减少资源访问和消耗、全局锁、表级锁、全局锁应用场景、表锁应用场景、MDL 元数据锁、行锁、两阶段锁协议、死锁以及死锁检测、控制访问相同资源的并发事务量。

发布了132 篇原创文章 · 获赞 6 · 访问量 7940

猜你喜欢

转载自blog.csdn.net/stevenchen1989/article/details/104218659