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. 数据库常见知识点总结

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Debezium日常分享系列之:Debezium UI 的状态

Debezium日常分享系列之&#xff1a;Debezium UI 的状态 一、下一阶段工作二、设计新的UI三、目前阶段四、更多内容 虽然Debezium的UI是我们愿景的重要组成部分&#xff0c;但开发与Kafka Connect紧密绑定的UI并不是正确的方向。因此&#xff0c;决定冻结当前Web UI项目的开发。…

【UE 网络】Network Role and Authority、Actors Owner、Actor Role and RemoteRole

目录 0 引言1 Network Role and Authority&#xff08;网络角色和授权&#xff09;1.1 Authority (权威角色 / 权威端)1.2 Simulated Proxy (模拟代理 / 模拟端)1.3 Autonomous Proxy (自主代理 / 主动端)1.4 示例&#xff1a;多人塔防游戏中的 NetRole 2 Actors and their Own…

QT 网络聊天室简易版

视频:qt开发网络聊天w室软件3.4界面开发_哔哩哔哩_bilibili 目录 UI部分 设计稿图 放置控件 界面美化 拖动窗体 设置界面 网络部分 配置对话框 多项目结果和服务器端设计 客户端框架开发 UI部分 设计稿图 放置控件 界面美化 现在我们把窗体自带的标题栏给去了,用我们自…

[论文泛读]zkLLM: Zero Knowledge Proofs for Large Language models

文章目录 介绍实验数据实验数据1实验数据2实验数据3 介绍 这篇文章发在CCS2024&#xff0c;CCS是密码学领域的顶会。作者是来自加拿大的University of Waterloo。文章对大语言模型像GPT和LLM等大语言模型实现了零知识可验证执行&#xff0c;但不涉及零知识可验证训练。个人觉得…

WUP-CH34X ch34x系列芯片USB转串口通信uniapp插件使用说明

插件地址&#xff1a;WUP-CH34X 系列芯片USB转串口通信安卓库 简介 本文档是针对 CH340/CH341/CH342/CH343/CH344/CH347/CH9101/CH9102/CH9103/CH9104/CH9143的 USB 转串口安卓库的开发说明文档。 主要介绍如何使用芯片的 USB 转异步串口功能&#xff08;以下简称 CH34XUART…

循环神经网络三

一.介绍 在普通的神经网络中&#xff0c;信息的传递是单向的&#xff0c;这种限制虽然使得网络变得更容易学习&#xff0c;单在一定程度上也减弱了神经网络模型的能力。特别是在现实生活中&#xff0c;网络的输出不仅和当前时刻的输入相关&#xff0c;也过去一段时间的输出相关…

Typora 伪装 LaTeX 中文样式主题 学习笔记

最近发现一个比较有意思的项目&#xff0c;Typora 伪装 LaTeX 中文样式主题 用来写毕设论文的初稿&#xff0c;格式可以统一控制&#xff0c;比较方便。项目“第五章”源格式是“5 系统测试”靠左&#xff0c;就像5.1一样。搜索了一下获得了一些零散的知识点记下来。 在Typora的…

Linux C 程序 【04】线程分离

1.开发背景 Linux 线程区分于FreeRTOS&#xff0c;线程的属性形态有2中&#xff0c;在 pthread.h 中有注解&#xff0c;如下。 /* Detach state. */ enum {PTHREAD_CREATE_JOINABLE, #define PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_JOINABLEPTHREAD_CREATE_DETACHED #defin…

Jenkins保姆笔记(2)——基于Java8的Jenkins插件安装

前面我们介绍过&#xff1a; Jenkins保姆笔记&#xff08;1&#xff09;——基于Java8的Jenkins安装部署 本篇主要介绍下基于Java8的Jenkins插件安装。为什么要单独讲一个插件安装&#xff1f;因为一些原因&#xff0c;Jenkins自带的插件源下载几乎都会失败&#xff0c;如图…

【vulhub靶场之thinkphp】——(5-rce)

第一步&#xff1a;打开靶场 输入开启命令 cd vulhub/thinkphp/5-rce docker-compose up -d docker-compose ps//查看开启的端口 第二步&#xff1a; 访问网址 第三步&#xff1a;漏洞利用 漏洞根本源于 thinkphp/library/think/Request.php 中method方法可以进行变量覆盖&am…

24/8/6算法笔记 支持向量机

支持向量机&#xff08;Support Vector Machine, SVM&#xff09;是一种监督学习算法&#xff0c;主要用于分类和回归任务。它基于统计学习理论中的结构风险最小化原理&#xff0c;通过找到数据点之间的最优边界来实现模型的泛化能力。 import numpy as np import matplotlib.…

Android 下载安装配置

文章目录 Android Studio 下载安装配置1. 下载JDK2. JDK环境配置&#xff1a;3. 测试JDK是否安装成功&#xff1a;4. 下载Android Studio:5. 配置Android Studio:6. android studio提速方法1&#xff08;不行&#xff09;方法2&#xff1a;(很行&#xff09; Android Studio 下…

STL容器之vector

1.vector的介绍及使用 1.1vector的介绍 1. vector是表示可变大小数组的序列容器。 2. 就像数组一样&#xff0c;vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问&#xff0c;和数组一样高效。但是又不像数组&#xff0c;它的大小是可…

快速基于 ClickHouse + Grafana 搭建可观测性解决方案 - 分布式链路追踪篇(ClickHouse 官方博客)...

引言 在 ClickHouse&#xff0c;我们认为可观测性仅仅是另一个实时分析问题。作为一款高性能的实时分析数据库&#xff0c;ClickHouse 被用于多种场景&#xff0c;包括时间序列数据的实时分析。其应用场景的多样性推动了大量分析函数的发展&#xff0c;这些函数有助于查询大多数…

【Python】PyWebIO 初体验:用 Python 写网页

目录 前言1 使用方法1.1 安装 Pywebio1.2 输出内容1.3 输入内容 2 示例程序2.1 BMI 计算器2.2 Markdown 编辑器2.3 聊天室2.4 五子棋 前言 前两天正在逛 Github&#xff0c;偶然看到一个很有意思的项目&#xff1a;PyWebIo。 这是一个 Python 第三方库&#xff0c;可以只用 P…

Atcoder Beginner Contest 366

传送门 A - Election 2 时间限制&#xff1a;2秒 内存限制&#xff1a;1024MB 分数&#xff1a;100分 问题描述 在 AtCoder 市举行市长选举。候选人是 Takahashi 和 Aoki。 目前有 N 张有效选票投给了这两个候选人&#xff0c;并且计票正在进行中。这里&#xff0…

配置Cuttlefish 虚拟 Android 设备

google 参考资料&#xff1a; https://source.android.com/docs/setup/start?hlzh-cn https://source.android.com/docs/devices/cuttlefish/get-started?hlzh-cn Cuttlefish 开始 验证 KVM 可用性 Cuttlefish 是一种虚拟设备&#xff0c;依赖于宿主机上可用的虚拟化。 …

Laravel 使用Excel导出的文件中,指定列数据格式为日期,方便后期的数据筛选操作

背景 最近&#xff0c;后台运营人员要求导出的 Excel 文件&#xff0c; 要求能够满足对于 [下单日期] 的筛选操作&#xff0c;即满足在年份、月份上的选择 通过了解&#xff0c;发现&#xff1a; 先前导出的文件&#xff0c;默认列数据都是字符串&#xff08;文本&#xff09;格…

bia文件中码偏差对实时PPP解算分析

1. 码偏差对定位影响 码偏差对未知收敛时间有影响&#xff0c;对最终精度影响不大&#xff08;权比1000:1&#xff09;

前端工程化14-git merge 以及 git rebase。

rebase会把当前分支的 commit 放到公共分支的最后面,所以叫变基。就好像从公共分支又重新拉出来这个分支一样。 举例&#xff1a; 如果从 master 拉个feature分支出来,然后提交了几个 commit,这个时候刚好有人把他开发的东西合并到 master 了,这个时候 master 就比你拉分支的…