当前位置: 首页 > news >正文

MySQL 实战 45 讲(01-05)

本文为笔者学习林晓斌老师《MySQL 实战 45 讲》课程的学习笔记,并进行了一定的知识扩充。

sql 查询语句的执行流程

image.png

大体来说,MySQL 可以分为 Server 层和存储引擎层两部分。

Server 层包括连接器、查询缓存、分析器、优化器和执行器。

连接器负责接收客户端的请求,并对权限进行验证,对连接进行管理。确认有权限后进行数据查询,首先会查询缓存,缓存中存的是 sql 语句与结果集的映射关系,如果缓存命中则直接返回数据。如果缓存未命中,则开始真正执行 sql 语句,这就需要分析器对传过来的 sql 进行语法分析,之后再经由优化器进行优化,生成最终的执行计划。最后由执行器去调用操作引擎,返回结果集。

另外,需要注意的是,由于对于经常需要更新的非静态表,缓存命中率是非常低的。因此,MySQL 8.0 版本直接将查询缓存的整块功能删掉了。

日志系统:sql 更新语句是怎么执行的

查询语句的那一套流程,更新语句也是同样会走一遍。不过在此基础上还涉及到两个日志模块,即 redo log(重做日志)和 binlog(归档日志)。redo log 用于数据库突然崩溃时的恢复(crash-safe 能力),binlog 用于恢复误操作时间节点前的数据,或者数据库节点扩容时保证主从数据库数据一致性。

这两种日志有以下三点不同。

  • redo log 是 InnoDB 引擎特有的;binlog 是 MySQL 的 Server 层实现的,所有引擎都可以使用。
  • redo log 是物理日志,记录的是“在某个数据页上做了什么修改”;binlog 是逻辑日志,记录的是这个语句的原始逻辑,比如“给 ID=2 这一行的 c 字段加 1 ”。
  • redo log 是循环写的,空间固定会用完;binlog 是可以追加写入的。“追加写”是指 binlog 文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。
mysql> update Table set c=c+1 where ID=2;

上面更新语句的执行逻辑:

  1. 执行器先找到引擎,取出 ID = 2 这一行。
  2. 执行器拿到引擎给的行数据,并 + 1,得到新的行数据,随后再调用引擎接口写入这行新数据。
  3. 引擎将这个更新操作记录到 redo log 里,此时 redo log 处于 prepare 状态。然后告知执行器执行完成了,随时可以提交事务。
  4. 执行器生成这个操作的 binlog,并把 binlog 写入磁盘。
  5. 执行器再调用引擎的提交事务接口,引擎把刚给写入的 redo log 改成 commit 状态,完成更新。

下面给出这个 update 语句更加具体的执行流程图,图中浅色框表示是在 InnoDB 内部执行的,深色框表示是在执行器中执行的。

image.png

从上面的描述我们可以发现,redo log 的写入拆成了两个步骤:prepare 和 commit,其实这就是我们常说的”两阶段提交”。它保证了数据的一致性。

误操作后恢复数据,以及搭建一些备库来增加系统的读能力的时候。现在常见的做法是用全量备份加上应用 binlog 来实现。如果不采用“两阶段提交”就可能导致数据恢复后和历史数据不同,或者搭建备库的时候出现主从数据库数据不一致的问题。

事务隔离级别

事务的实现是基于存储引擎的,MySQL 的 Innodb 存储引擎支持四种隔离级别:读未提交、读已提交、可重复读、串行化。为的是解决多个事务产生的问题:脏读、不可重复读、幻读。

image.png|300

不同隔离级别下,事务 A 读到的值不同:

V1V2V3
读未提交222
读已提交122
可重复读112
串行化112

解释:

  • 若隔离级别是“读未提交”, 则 V1 的值就是 2。这时候事务 B 虽然还没有提交,但是结果已经被 A 看到了。因此,V2、V3 也都是 2。
  • 若隔离级别是“读提交”,则 V1 是 1,V2 的值是 2。事务 B 的更新在提交后才能被 A 看到。所以, V3 的值也是 2。
  • 若隔离级别是“可重复读”,则 V1、V2 是 1,V3 是 2。之所以 V2 还是 1,遵循的就是这个要求:事务在执行期间看到的数据前后必须是一致的。且在“可重复读”隔离级别下,只会读到已经提交的数据。
  • 若隔离级别是“串行化”,则在事务 B 执行“将 1 改成 2”的时候,会被锁住。直到事务 A 提交后,事务 B 才可以继续执行。所以从 A 的角度看, V1、V2 值是 1,V3 的值是 2。

数据库索引

MySQL 数据库的索引是在存储引擎中实现的,不同的存储引擎支持的索引类型不同,且即使是同一种索引类型其实现方式也可能不同。

索引的常见实现方式有 3 种,即哈希、有序数组、搜索树。

哈希:适合精确查询、数据插入速度快(因为写入数据时只需要追加)、范围查询慢(因为不是有序的)

有序数组:精确查询和范围查询速度都快,但是写入速度慢。因为在数组中写入一个中间值时,需要把大于它的值都后移。

搜索树:搜索效率很高,但数据库的索引不仅仅存在内存中,还存在磁盘中。如果采用二叉树,树的层级会很深,而层级深意味着与磁盘间的 IO 操作数量更多,而 IO 操作是十分耗时的。为此,大多数数据库用的都是 N 叉树。

InnoDB 索引模型

在 InnoDB 中,表中的数据都是根据主键顺序以索引的形式存放的(即使不指定主键,也生成一个默认的主键),这种存储方式的表称为索引组织表。又因为前面我们提到的,InnoDB 使用了 B+ 树索引模型,所以数据都是存储在 B+ 树中的。

根据叶子节点的内容,索引类型分为主键索引和非主键索引。

  • 主键索引的叶子节点存的是整行数据。在 InnoDB 里,主键索引也被称为聚簇索引(clustered index)。
  • 非主键索引的叶子节点内容是主键的值。在 InnoDB 里,非主键索引也被称为二级索引(secondary index)。

因此,为了找到数据:

  • 如果语句是 select * from T where ID=500,即主键查询方式,则只需要搜索 ID 这棵 B+ 树;
  • 如果语句是 select * from T where k=5,即普通索引查询方式,则需要先搜索 k 索引树,得到 ID 的值为 500,再到 ID 索引树搜索一次。这个过程称为回表。

也就是说,基于非主键索引的查询需要多扫描一棵索引树。因此,我们在应用中应该尽量使用主键查询。

索引维护

B+ 树为了维护索引有序性,在插入新值的时候需要做必要的维护。如果插入的新数据对应的主键 id 比原来的大,那只需要追加就行。但如果比原来的小,就需要挪动后面的数据,再进行插入。这个过程中还可能发生页分裂。此时,对性能就会受到影响。

因此,你可能在一些建表规范里面见到过类似的描述,要求建表语句里一定要有自增主键,这保证了新插入的数据只要追加就行,避免了数据挪动和页分裂带来的性能影响。

除此之外,我们还可以从存储的角度来看看使用递增主键的好处。假设你的表中确实有一个唯一字段,比如字符串类型的身份证号,那应该用身份证号做主键,还是用自增字段做主键呢?

由于每个非主键索引的叶子节点上都是主键的值。如果用身份证号做主键,那么每个二级索引的叶子节点占用约 20 个字节,而如果用整型做主键,则只要 4 个字节,如果是长整型(bigint)则是 8 个字节。

显然,主键长度越小,普通索引的叶子节点就越小,普通索引占用的空间也就越小。

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

有没有什么场景适合用业务字段直接做主键的呢?还是有的。比如,有些业务的场景需求是这样的:

  • 只有一个索引;
  • 该索引必须是唯一索引。

由于没有其他索引,所以也就不用考虑其他索引的叶子节点存储空间大小的问题。

这时候我们就要优先考虑上一段提到的“尽量使用主键查询”原则,直接将这个索引设置为主键,可以避免每次查询需要搜索两棵树。

覆盖索引

select ID from T where k between 3 and 5

对于上面的 sql 语句,如果查询值仅为主键 ID,且 k 加了索引,那我们就称它为覆盖索引。

因为 ID 的值已经在 k 索引树上了,可以直接提供查询结果,不需要回表。也就是说,在这个查询里面,索引 k 已经“覆盖了”我们的查询需求,因此我们称为覆盖索引。

由于覆盖索引可以减少树的搜索次数,显著提升查询性能,所以使用覆盖索引是一个常用的性能优化手段。

最左前缀匹配原则

最左匹配原则的底层原理

我们都知道索引的底层是一颗B+树,那么联合索引当然还是一颗B+树,只不过联合索引的健值数量不是一个,而是多个。构建一颗B+树只能根据一个值来构建,因此数据库依据联合索引最左的字段来构建B+树。

例子:假如创建一个(a,b)的联合索引,那么它的索引树是这样的:

image.png

可以看到a的值是有顺序的,1,1,2,2,3,3,而b的值是没有顺序的1,2,1,4,1,2。所以b = 2这种查询条件没有办法利用索引,因为联合索引首先是按a排序的,b是无序的。

同时我们还可以发现在a值相等的情况下,b值又是按顺序排列的,但是这种顺序是相对的。所以最左匹配原则遇上范围查询就会停止,剩下的字段都无法使用索引。例如a = 1 and b = 2 a,b字段都可以使用索引,因为在a值确定的情况下b是相对有序的,而a>1and b=2,a字段可以匹配上索引,但b值不可以,因为a的值是一个范围,在这个范围中b是无序的。

由此,我们可以推出最左前缀原则:最左优先,以最左边的为起点任何连续的索引都能匹配上。同时遇到范围查询(>、<、between、like)就会停止匹配。

例如:如果建立(a,b)顺序的索引,我们的条件只有b=xxx,是匹配不到(a,b)索引的;但是如果查询条件是a = 1 and b = 2或者b=2 and a=1就可以,因为优化器会自动调整a,b的顺序,并不需要严格按照索引的顺序来;再比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,a、b、c能用到索引,但d是用不到索引的,因为c字段是一个范围查询,它之后的字段会停止匹配。

联合索引建立原则

在建立联合索引的时候,如何安排索引内的字段顺序?

由于最左前缀原则,在创建联合索引时,将过滤能力强的列放在前面。对于需要频繁排序的列也是放在前面(因为索引是有序的,对于查询时需要排序的列,如果能走索引,能提高查询性能)。

select * from staffs where id like 'A%';//前缀都是排好序的,使用的都是联合索引
select * from staffs where id like '%A%';//全表查询
select * from staffs where id like '%A';//全表查询

联合索引场景分析

假设我们创建一个联合索引 (id,name,age):

create table `staffs` (`id` int default null,`name` char(32) default null,`age` int default null,KEY `id_name_age_index` (`id`,`name`,`age`)
)

1.全值匹配查询:

select * from staffs where id = 1 and name = 'jack' and age = 20;
select * from staffs where age = 20 and id = 1 and name = 'jack';
select * from staffs where name = 'jack' and id = 1 and age = 20;

过滤条件同时出现 id,name,age,且为精确查询,则不管三者的顺序如何,都能走整个联合索引。

2.匹配最左列:

符合最左匹配原则的场景:

select * from staffs where id = 1; // 使用联合索引中的 id 索引
select * from staffs where id = 1 and name = 'jack'; // 使用联合索引中的 id,name 索引
select * from staffs where id = 1 and name = 'jack' and age = 20; // 命中整个联合索引

不符合最左匹配原则的场景:

select * from staffs where name = 'jack'; // 对整个索引树进行扫描(与全表扫描不同,但也慢)
select * from staffs where age = 20; // 同上
select * from staffs where name = 'jack' and age = 20; // 同上

3.匹配列前缀:

如果 id 是字符类型:

select * from staffs where id like 'A%';//前缀都是排好序的,使用的都是联合索引
select * from staffs where id like '%A%';//全表查询
select * from staffs where id like '%A';//全表查询

4.匹配范围值:

select * from staffs where id > 1 and id < 3; // 联合索引中的 id 走索引
select * from staffs where id < 4 and age > 20 and age < 50; // id 走索引,age 不走索引
select * from staffs where age > 20 and age < 50; // 不走索引

5.精确匹配第一列并范围匹配其他列

select * from staffs where id = 1 and age < 50;

参考

  1. MySQL 实战45讲
  2. 数据库常见知识点总结

相关文章:

MySQL 实战 45 讲(01-05)

本文为笔者学习林晓斌老师《MySQL 实战 45 讲》课程的学习笔记&#xff0c;并进行了一定的知识扩充。 sql 查询语句的执行流程 大体来说&#xff0c;MySQL 可以分为 Server 层和存储引擎层两部分。 Server 层包括连接器、查询缓存、分析器、优化器和执行器。 连接器负责接收客…...

仓颉编程语言入门 -- Array数组详解

仓颉编程语言入门 – Array数组详解 一. 如何创建Array数组 我们可以使用 Array 类型来构造单一元素类型&#xff0c;有序序列的数据。 1.仓颉使用 Array 来表示 Array 类型。T 表示 Array 的元素类型&#xff0c;T 可以是任意类型 , 类似于泛型的概念 var arr:Array<St…...

C#初级——简单单例模式使用

单例模式 单例模式是一种常用的软件设计模式&#xff0c;它确保一个类只有一个实例&#xff0c;并提供一个全局访问点来获取这个实例&#xff0c;通过单例模式防止私有成员被多次引用&#xff0c;防止数据被随意纂改。本文使用的是线程不安全的懒汉式单例。 创建单例模式 首…...

2024.07.29 校招 实习 内推 面经

地/球&#x1f30d; &#xff1a; neituijunsir 交* 流*裙 &#xff0c;内推/实习/校招汇总表格 1、校招 | 美/团// 快驴、小象、优/选/事/业/部2024年校/园/招聘&#xff08;内推&#xff09; 校招 | 美团快驴、小象、优选事业部2024年校园招聘&#xff08;内推&#xff…...

速盾:爬虫攻击和cc攻击的区别是什么?

爬虫攻击和CC&#xff08;Distributed Denial of Service&#xff09;攻击是网络安全领域两种不同类型的攻击方式。尽管它们都涉及对目标网站或服务器的非法访问&#xff0c;但它们的目的、方法和影响各不相同。在接下来的文章中&#xff0c;我们将详细介绍这两种攻击方式的区别…...

Tomcat与Nginx的区别详解

目录 引言Tomcat概述 Tomcat的历史Tomcat的架构Tomcat的功能Nginx概述 Nginx的历史Nginx的架构Nginx的功能Tomcat与Nginx的区别 架构上的区别...

【大模型从入门到精通5】openAI API高级内容审核-1

这里写目录标题 高级内容审核利用 OpenAI 内容审核 API 的高级内容审核技术整合与实施使用自定义规则增强审核综合示例防止提示注入的策略使用分隔符隔离命令理解分隔符使用分隔符实现命令隔离 高级内容审核 利用 OpenAI 内容审核 API 的高级内容审核技术 OpenAI 内容审核 AP…...

JVM系列 | 对象的消亡3——垃圾收集器的对比与实现细节

垃圾收集器 文章目录 各收集器简单对比收集器启动参数各收集器详细说明JDK 1.3 之前JDK 1.3 | SerialJDK 1.4 | ParNewJDK 1.4 | Parallel ScavengeJDK 5 | CMS 收集器JDK 7 | G1 各收集器简单对比 收集器名称出现时间淘汰时间目标采用技术线程数STW分代备注无名JDK 1.3之前JD…...

C# Unity 面向对象补全计划 七大原则 之 开闭原则(OCP) 难度:☆ 总结:已经写好的就别动它了,多用继承

本文仅作学习笔记与交流&#xff0c;不作任何商业用途&#xff0c;作者能力有限&#xff0c;如有不足还请斧正 本系列作为七大原则和设计模式的进阶知识&#xff0c;看不懂没关系 请看专栏&#xff1a;http://t.csdnimg.cn/mIitr&#xff0c;查漏补缺 1.开闭原则&#xff08;OC…...

微信防封指南请收好

一、新号与老号的添加限制 建议新注册的微信号主动添加好友的数量不宜过多&#xff0c;推荐每日添加不超过5个好友&#xff1b;对于老号&#xff0c;建议每日添加不超过20个好友。保持适度的添加速度&#xff0c;避免被系统判定为异常操作。 二、避免使用营销性词汇 在发送消…...

选择排序算法改进思路和算法实现

选择排序 在未排序的数组中&#xff0c;用第一个数去和后面的数比较&#xff0c;找出最小的数&#xff0c;和第一个数交换。第一个数已为已排序的数。 相当于0~7 从0~7中找到最小的数放在0 从1~7中找到最小的数放在1 从2~7中找到最小的数放在2 ...以此类推 从6~7中找到最…...

【文件解析漏洞复现】

一&#xff0e;IIS解析漏洞复现 1.IIS6.X 方式一&#xff1a;目录解析 搭建IIS环境 在网站下建立文件夹的名字为.asp/.asa 的文件夹&#xff0c;其目录内的任何扩展名的文件都被IIS当作asp文件来解析并执行。 访问成功被解析 方式一&#xff1a;目录解析 在IIS 6处理文件解…...

【STL】 vector的底层实现

1.vector的模拟代码完整实现&#xff08;后面会拆分开一个一个细讲&#xff09; #pragma once #include<assert.h>// 抓重点namespace bit {/*template<class T>class vector{public:typedef T* iterator;private:T* _a;size_t _size;size_t _capacity;};*/templa…...

责任链模式:解耦职责,优化请求处理

在软件设计中&#xff0c;如何有效地处理复杂的请求是一个重要的课题。 责任链模式&#xff08;Chain of Responsibility Pattern&#xff09;提供了一种解耦请求发送者和接收者的方法&#xff0c;使得多个对象都有机会处理请求&#xff0c;从而达到灵活和可扩展的设计。 什么…...

【Scene Transformer】scene transformer论文阅读笔记

文章目录 序言(Abstract)(Introduction)(Related Work)(Methods)(Scene-centric Representation for Agents and Road Graphs)(Encoding Transformer)(Predicting Probabilities for Each Futures)(Joint and Marginal Loss Formulation) (Results)(Discussion)(Questions) sce…...

ESP32在ESP-IDF环境下禁用看门狗

最近使用了一款ESP32的开发板。但在调试时发现出现许多看门狗复位事件&#xff1a; E (8296) task_wdt: Task watchdog got triggered. The following tasks/users did not reset the watchdog in time: E (8296) task_wdt: - IDLE (CPU 0) E (8296) task_wdt: Tasks curre…...

基于 uniapp html5plus API,怎么把图片保存到相册

要将图片保存到相册中&#xff0c;可以使用HTML5 API中的plus.gallery.save方法。以下是一个示例代码&#xff0c;展示如何将图片保存到手机相册&#xff1a; // 图片的URL&#xff0c;可以是本地路径或网络路径 var imageUrl path/to/your/image.jpg;// 调用plus.gallery.sa…...

3.特征工程-特征抽取、特征预处理、特征降维

文章目录 环境配置&#xff08;必看&#xff09;头文件引用1.数据集: sklearn代码运行结果 2.字典特征抽取: DictVectorizer代码运行结果稀疏矩阵 3.文本特征抽取(英文文本): CountVectorizer()代码运行结果 4.中文文本分词(中文文本特征抽取使用)代码运行结果 5.中文文本特征抽…...

RISC-V (五)上下文切换和协作式多任务

任务&#xff08;task&#xff09; 所谓的任务就是寄存器的当前值。 -smp后面的数字指的是hart的个数&#xff0c;qemu模拟器最大可以有8个核&#xff0c;此文围绕一个核来讲。 QEMU qemu-system-riscv32 QFLAG -nographic -smp 1 -machine virt -bios none 协作式多任务 …...

Cornerstone加载本地Dicom文件第二弹 - Blob篇

&#x1f340; 引言 当我们刚接触Cornerstone或拿到一组Dicom文件时&#xff0c;如果没有ImageID和后台接口&#xff0c;可能只是想简单测试Cornerstone能否加载这些Dicom文件。在这种情况下&#xff0c;可以使用本地文件加载的方法。之前我们介绍了通过node启动服务器请求文件…...

7.4.分块查找

一.分块查找的算法思想&#xff1a; 1.实例&#xff1a; 以上述图片的顺序表为例&#xff0c; 该顺序表的数据元素从整体来看是乱序的&#xff0c;但如果把这些数据元素分成一块一块的小区间&#xff0c; 第一个区间[0,1]索引上的数据元素都是小于等于10的&#xff0c; 第二…...

label-studio的使用教程(导入本地路径)

文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...

Appium+python自动化(十六)- ADB命令

简介 Android 调试桥(adb)是多种用途的工具&#xff0c;该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具&#xff0c;其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利&#xff0c;如安装和调试…...

从WWDC看苹果产品发展的规律

WWDC 是苹果公司一年一度面向全球开发者的盛会&#xff0c;其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具&#xff0c;对过去十年 WWDC 主题演讲内容进行了系统化分析&#xff0c;形成了这份…...

FFmpeg 低延迟同屏方案

引言 在实时互动需求激增的当下&#xff0c;无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作&#xff0c;还是游戏直播的画面实时传输&#xff0c;低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架&#xff0c;凭借其灵活的编解码、数据…...

在rocky linux 9.5上在线安装 docker

前面是指南&#xff0c;后面是日志 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io -y docker version sudo systemctl start docker sudo systemctl status docker …...

连锁超市冷库节能解决方案:如何实现超市降本增效

在连锁超市冷库运营中&#xff0c;高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术&#xff0c;实现年省电费15%-60%&#xff0c;且不改动原有装备、安装快捷、…...

系统设计 --- MongoDB亿级数据查询优化策略

系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log&#xff0c;共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题&#xff0c;不能使用ELK只能使用…...

ardupilot 开发环境eclipse 中import 缺少C++

目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...

保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek

文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama&#xff08;有网络的电脑&#xff09;2.2.3 安装Ollama&#xff08;无网络的电脑&#xff09;2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...