MySQL 实战 45 讲(01-05)

news2024/9/22 9:31:29

本文为笔者学习林晓斌老师《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. 数据库常见知识点总结

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1982603.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【第14章】Spring Cloud之Gateway路由断言(IP黑名单)

文章目录 前言一、内置路由断言1. 案例&#xff08;Weight&#xff09;2. 更多断言 二、自定义路由断言1. 黑名单断言2. 全局异常处理3. 应用配置4. 单元测试 总结 前言 Spring Cloud Gateway可以让我们根据请求内容精确匹配到对应路由服务,官方已经内置了很多路由断言,我们也…

天润融通助力车企做好战败线索分析,实现商机转化最大化

激烈的行业竞争&#xff0c;让车企越来越重视战败客户分析。 对于每一个汽车品牌来说&#xff0c;大约会有80%甚至更多的留资顾客未在本店购车&#xff0c;最终成为广义上的战败客户。因此&#xff0c;挖掘战败背后的原因对车企意义重大。 作为大宗商品&#xff0c;汽车的交易…

基于Python的Bilibili视频信息分析与可视化

文章目录 有需要本项目的代码或文档以及全部资源&#xff0c;或者部署调试可以私信博主项目介绍研究背景研究现状研究目的及意义数据采集及预处理数据清洗数据分析与可视化总结每文一语 有需要本项目的代码或文档以及全部资源&#xff0c;或者部署调试可以私信博主 项目介绍 …

浅谈用二分和三分法解决问题(c++)

目录 问题引入[NOIP2001 提高组] 一元三次方程求解题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 提示思路分析AC代码 思考关于二分和三分例题讲解进击的奶牛题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 思路AC代码 平均数题目描述输入格式输出格式样例 …

【Material-UI】Icon Button 组件详解

文章目录 一、基础用法1. 禁用状态 二、大小&#xff08;Sizes&#xff09;1. 小尺寸&#xff08;Small&#xff09;2. 大尺寸&#xff08;Large&#xff09; 三、颜色&#xff08;Colors&#xff09;1. 主题颜色2. 自定义颜色 四、高级用法和最佳实践1. 无障碍性&#xff08;A…

【香橙派系列教程】(七)香橙派下的Python3安装

【七】香橙派下的Python3安装 为接下来的Linux图像识别智能垃圾桶做准备。 图像处理使用京东SDK只支持pyhton和Java接口&#xff0c;目的是引入C语言的Python调用&#xff0c;感受大厂做的算法bug 此接口是人工智能接口&#xff0c;京东识别模型是通过训练后的模型&#xff0c;…

打靶记录7——Hacker_Kid-v1.0.1

靶机下载地址 https://download.vulnhub.com/hackerkid/Hacker_Kid-v1.0.1.ova难度 OSCP 风格的中级难度靶机&#xff08;只需要获取root权限即可&#xff0c;CTF 风格的靶机就还需要获取flag&#xff09; 涉及的攻击方法&#xff1a; 主机发现端口扫描Web信息收集DNS区域传…

数组——对数组进行更加全面的理解

1.数组的概念 数组是一组相同类型元素的集合。数组可分为一维数组和多维数组&#xff0c;多维数组常见的是二维数组。 2.一维数组的创建和初始化 2.1 数组的创建 一维数组的创建的基本语法是&#xff1a; type arr_name[常量值] 例如&#xff0c;我们现在想要存储某个班级…

【C语言】qsort函数的介绍和使用

0. 引言 我们日常生活中经常能碰到需要给一组数据排序的情况&#xff0c;如将班上同学的身高&#xff0c;年龄从大到小排序&#xff0c;平时网上购物时对商品价格从低到高排序等等场景&#xff0c;那么电脑是根据什么程序完成这些排序的&#xff1f;接下来就来给大家介绍一下C语…

上升探索WebKit的奥秘:打造高效、兼容的现代网页应用

嘿&#xff0c;朋友们&#xff01;想象一下&#xff0c;你正在浏览一个超级炫酷的网站&#xff0c;页面加载飞快&#xff0c;布局完美适应你的设备&#xff0c;动画流畅得就像你在看一场好莱坞大片。这一切的背后&#xff0c;有一个神秘的英雄——WebKit。今天&#xff0c;我们…

MySQL数据库——数据库的基本操作

目录 三、数据库的基本操作 1.数据库中库的操作 ①创建数据库 ②字符集和校验规则 ③操纵数据库 ④备份与恢复 2.数据库中表的操作 ①创建表 ②查看表 1> 查看表位于的数据库 2>查看所有表 3>查看表中的数据 4>查看创建表的时候的详细信息 ③修改表 …

如何使用react在画布上实现redo-undo?

To implement undo/redo functionality with React you don’t need to use Konva‘s serialization and deserealization methods. You just need to save a history of all the state changes within your app. There are many ways to do this. It may be simpler do to th…

IoTDB 入门教程 企业篇③——数据同步和增量备份

文章目录 一、前文二、系统架构三、准备两台服务器四、新建任务五、数据同步测试六、遇到的问题 一、前文 IoTDB入门教程——导读 数据库备份与迁移是数据库运维中的核心任务&#xff0c;其重要性不言而喻。确保备份过程既简单快捷又稳定可靠&#xff0c;对于保障数据安全与业务…

会声会影下载免费吗?会声会影2023中文旗舰版下载及配置最低要求

**会声会影2024&#xff1a;引领视频创作新时代的创新之旅** 在数字时代的浪潮中&#xff0c;视频创作已成为连接世界、表达创意的重要方式。随着技术的不断进步&#xff0c;一款名为“会声会影2024”的视频编辑软件横空出世&#xff0c;它不仅继承了前代产品的优秀传统&#…

【STM32】EXTI与NVIC标准库使用框架

本篇博客重点在于标准库函数的理解与使用&#xff0c;搭建一个框架便于快速开发 目录 EXTI简介 EXTI配置 使能AFIO的时钟 配置GPIO端口为外部中断 外部中断初始化 NVIC介绍与配置 NVIC中断优先级分组 NVIC初始化 NVIC框架 EXTI配置图 中断函数 中断函数配置 获取中…

GPU as Code:趋动OrionX产品的创新之路

在当今快速发展的云计算和DevOps领域&#xff0c;IaC (Infrastructure as Code) 已经成为提升IT基础设施管理效率的关键实践。趋动科技的OrionX产品&#xff0c;通过软件定义GPU硬件&#xff0c;为开发者和运维团队提供了一种全新的AI算力资源管理方式。本文将深入探讨OrionX如…

习题2.32

这个题目 粗看起来好像是很简单,但是我在实现的时候,真的是废了好大的力气,主要原因有三点,第一,对clojure语言的属性程度不够高,第二,课本语言与clojure语言的差异点,我自以为理解的很透彻,导致了出现很奇异的现象,我找不到解释,一点思路都没有。第三,也就是最关键…

Vue 3+Vite+Eectron从入门到实战系列之(二)一Elementplus及VueRouter的配置

为了后续开发方便,在没有 UI 设计师配合的情况下,让我们的界面更加美观,我们使用 elementplus 组件库,并配置路由。 删除不需要的默认文件夹及文件,src 配置如下 实现效果 安装 elementplus,vue-router npm install element-plus --save npm install vue-router --save在…

25考研数据结构复习·8.1插入排序·8.2交换排序

目录 排序的基本概念 插入排序 直接插入排序/折半插入排序 希尔排序 交换排序 冒泡排序 算法原理 性能 &#x1f469;‍&#x1f4bb; 快速排序 排序的基本概念 排序&#xff1a;将各元素按关键字递增/或递减顺序重新排列评价指标 稳定性&#xff1a;关键字相同的元素…

【Langchain大语言模型开发教程】基于Langchain的私人助手

终于学习完了Langchain框架的核心内容&#xff0c;最后基于langchain技术实现一个个人知识库助手的小项目&#xff0c;将这些内容串联起来&#xff0c;在实际中进行应用。 工具清单&#xff1a; 1、langchain框架 2、chroma向量数据库 3、embedding模型&#xff08;bge-larg…