MySQL实战45讲 1,2

01 | 基础架构:一条SQL查询语句是如何执行的?

MySQL实战45讲 1,2

Server 层 所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图等。

存储引擎层负责数据的存储和提取。其架构模式是插件式的,在 create table 语句中使用 engine=memory, 来指定使用内存引擎创建表

连接器

如果用户名密码认证通过,连接器会到 权限表里面查出你拥有的权限。之后,这个 连接里面的权限判断逻辑,都将依赖于此时读到的权限。

权限表:

https://www.php.cn/mysql-tutorials-493289.html

大致就是mysql库中的user表和db表

db 表中的权限列和 user 表中的权限列大致相同,只是user 表中的权限是针对所有数据库的,而 db 表中的权限只针对指定的数据库。如果希望用户只对某个数据库有操作权限,可以先将 user 表中对应的权限设置为 N,然后在 db 表中设置对应数据库的操作权限。

连接完成后,如果你没有后续的动作,这个连接就处于 空闲状态,你可以在 show processlist 命令中看到它。Command 列显示为”Sleep”的这一行,就表示现在系统里面有一个空闲连接。客户端如果太长时间没动静,连接器就会自动将它断开。

建立连接的过程通常是比较复杂的,尽量使用长连接。

但是全部使用长连接后,你可能会发现,有些时候 MySQL 占用内存涨得特别快,这是因为 MySQL 在执行过程中 临时使用的内存管理在连接对象里面的。这些资源会 在连接断开的时候才释放。所以如果长连接累积下来,可能导致内存占用太大,被系统强行杀掉(OOM),从现象看就是 MySQL 异常重启了

两种解决方案:

重连 or 重置连接

  1. 定期断开长连接。使用一段时间,或者程序里面判断执行过一个占用内存的大查询后, 断开连接,之后要查询再重连
  2. 如果你用的是 MySQL 5.7 或更新版本,可以在每次执行一个比较大的操作后,通过执行 mysql_reset_connection 来 重新初始化连接资源。这个过程不需要重连和重新做权限验证,但是会将连接恢复到刚刚创建完时的状态。

查询缓存

不要使用查询缓存

查询缓存的失效非常频繁,只要有对一个表的更新,这个表上所有的查询缓存都会被清空。

MySQL 8.0 版本直接将查询缓存的整块功能删掉

分析器

MySQL 需要知道你 要做什么,因此需要对 SQL 语句做解析

优化器

优化器是在表里面有多个索引的时候,决定使用哪个 索引

或者在一个语句有 多表关联(join)的时候,决定各个表的连接顺序

优化器阶段完成后,这个语句的 执行方案就确定下来,通过优化器知道了该 怎么做

执行器

开始执行的时候,要先判断一下你对这个表 T 有没有执行查询的权限,如果没有,就会返回没有权限的错误

Q:为什么对权限的检查不在优化器之前做?

A:有些时候,SQL语句要操作的表不只是SQL字面上那些。比如如果有个 触发器,得在执行器阶段(过程中)才能确定。优化器阶段前是无能为力的

如果有权限,就打开表继续执行。

打开表的时候, 执行器就会根据表的引擎定义,去使用这个引擎提供的接口

select * from T where ID=10;

比如我们这个例子中的表 T 中,ID 字段没有索引,那么执行器的执行流程是这样的:

  1. 调用 InnoDB 引擎接口取这个表的第一行,判断 ID 值是不是 10,如果不是则跳过, 如果是则将这行存在结果集中
  2. 调用引擎接口取” 下一行“,重复相同的判断逻辑,直到取到这个表的 最后一行
  3. 执行器将上述遍历过程中所有满足条件的行组成的记录集作为结果集返回给客户端。

对于有索引的表,执行的逻辑也差不多。

第一次调用的是”取满足条件的第一行”这个接口,之后 循环取”满足条件的下一行”这个接口,这些接口都是引擎中已经定义好的。

MySQL 的慢查询日志是 MySQL 提供的一种日志记录,它用来记录在 MySQL 中响应时间超过阀值的语句,具体指运行时间超过 long_query_time 值的SQL,则会被记录到慢查询日志中。

查看慢查询日志是否开启,默认情况下关闭
mysql> SHOW VARIABLES LIKE '%slow_query_log%';
+---------------------+--------------------------------------+
| Variable_name       | Value                                |
+---------------------+--------------------------------------+
| slow_query_log      | OFF                                  |
| slow_query_log_file | /var/lib/mysql/iZ251fpy8x9Z-slow.log |
+---------------------+--------------------------------------+
2 rows in set (0.00 sec)

https://blog.csdn.net/chengqiuming/article/details/120402562

Q:如果表 T 中没有字段 k,而你执行了这个语句 select * from T where k=1, 那肯定是会报”不存在这个列”的错误: “Unknown column ‘k’ in ‘where clause'”。这个错误是在哪个阶段报出来的呢?

A:分析器。Oracle会在分析阶段判断语句是否正确,表是否存在,列是否存在等,MySQL确实在设计上受Oracle影响颇深。

02 | 日志系统:一条SQL更新语句是如何执行的?

与查询流程不一样的是,更新流程还涉及两个重要的日志模块:redo log(重做日志)和 binlog(归档日志)

MySQL实战45讲 1,2

https://blog.csdn.net/weixin_51261234/article/details/124908426
https://blog.csdn.net/qq_38686500/article/details/124584693

REDO LOG

1.2.1 好处

先在粉板上记下这次的账,等打烊以后再把账本翻出来核算。

在 MySQL 里也有这个问题,如果每一次的更新操作都需要 写进磁盘,然后磁盘也 要找到对应的那条记录,然后 再更新整个过程 IO 成本、查找成本都很高。为了解决这个问题,MySQL 的设计者就用了类似酒店掌柜粉板的思路来提升更新效率。

  • redo日志 降低了刷盘频率
  • redo日志 *占用的空间非常小

存储 表空间ID页号偏移量以及需要 更新的值,所需的存储空间是很小的,刷盘快。

1.2.2 WAL

WAL 技术,WAL 的全称是 Write-Ahead Logging,它的关键点就是先写日志,再在系统比较空闲的时候写磁盘。 只有日志写入成功,才算是事务提交成功

当发生宕机且数据未刷新到磁盘的时候,可以通过redo log来恢复,保证ACID中的D,这就是redo log的作用。

1.2.3 特点

  • redo日志是 顺序写入磁盘的

在执行事务的过程中,每执行一条语句,就可能产生 若干条redo日志,这些日志是 按照产生的顺序写入磁盘的,也就是说 使用顺序IO,效率比随机IO快

  • *事务执行过程中,redo log不断记录

redo log跟bin log的区别

MySQL实战45讲 1,2

redo log是存储引擎层产生的,而bin log是数据库层产生的。假设一个事务,对表做 十万行的记录插入,在这个过程中, 一直不断的往redo log 顺序记录,记录的是页面的变化,而bin log不会记录, 直到这个事务提交,才会一次写入到bin log文件中

redo log是物理日志,记录的是在 具体某个数据页上做了什么修改,做了什么改动;

binlog是逻辑日志,有三种模式,statement 格式的话是记sql语句, row格式会记录行的内容,记两条,更新前和更新后都有,mixed根据sql语句特点,由系统决定某个修改使用row还是statement格式进行存储。

MySQL实战45讲 1,2

若sql语句可能引起主备不一致,那么使用row格式,否则使用statement格式。

1.2.4 组成

Redo log可以简单分为以下两个部分:

  • 重做日志的缓冲 (redo log buffer) ,保存在内存中,是易失的。
  • 重做日志文件 (redo log file) ,保存在硬盘中,是持久的。

InnoDB 的 redo log 是固定大小的,比如可以配置为一组 4 个文件,每个文件的大小是 1GB,那么这块”粉板”总共就可以记录 4GB 的操作。从头开始写,写到末尾就又回到开头循环写,如下面这个图所示。

MySQL实战45讲 1,2

write pos 是 当前记录的位置一边写一边后移,写到第 3 号文件末尾后就回到 0 号文件开头。checkpoint 是 当前要擦除的位置,也是 往后推移并且循环的,擦除记录前要把记录 更新到数据文件

write pos 和 checkpoint 之间的是”粉板”上还空着的部分,可以用来记录新的操作。 如果 write pos 追上 checkpoint,表示”粉板”满了, 这时候不能再执行新的更新,得停下来先擦掉一些记录,把 checkpoint 推进一下

1.2.5 整体流程

以一个更新事务为例,redo log 流转过程,如下图所示:

MySQL实战45讲 1,2

第1步:先将原始数据从磁盘中读入内存中来,修改数据的内存拷贝

第2步:生成一条重做日志并写入redo log buffer,记录的是数据被修改后的值

第3步:当事务commit时,将redo log buffer中的内容刷新到 redo log file,对 redo log file采用追加 写的方式

第4步:定期将内存中修改的数据刷新到磁盘中

1.2.6 刷盘策略

redo log的写入并不是直接写入磁盘的,InnoDB引擎会在写redo log的时候先写redo log buffer,之后以 一定的频率 刷入到真正的redo log file 中。这里的一定频率怎么看待呢?这就是我们要说的刷盘策略。

注意,redo log buffer刷盘到redo log file的过程 并不是真正的刷到磁盘中去,只是刷入到 文件系统缓存(page cache)中去,真正的写入会交给系统自己来决定(比如page cache足够大了)。那么对于InnoDB来说就存在一个问题,如果交给系统来同步,同样如果系统宕机,那么数据也丢失了(虽然整个系统宕机的概率还是比较小的)。

针对这种情况,InnoDB给出 innodb_flush_log_at_trx_commit 参数,该参数控制 commit提交事务时,如何将 redo log buffer 中的日志刷新到 redo log file 中。它 支持三种策略

  • 设置为0 :表示每次事务提交时 不进行刷盘操作。(系统默认master thread 每隔1s进行一次重做日志的同步)实例crash最多丢失一秒钟内的事务
  • 设置为1 :表示每次事务提交时都将进行同步,刷盘操作( 默认值 ) 可以保证ACID的D,数据绝对不会丢失,但是效率最差的
  • 设置为2 :表示每次事务提交时都只把 redo log buffer 内容写入 page cache,不进行同步。由 os自己决定什么时候同步到磁盘文件 如果仅仅只是MySQL挂了不会有任何数据的丢失。但是 操作系统宕机可能会有一秒数据的丢失,这种情况下无法满足ACID中的D

InnoDB存储引擎有 一个后台线程,每隔一秒,就会把redo log buffer中的内容写到文件系统缓存(page cache),然后调用刷盘操作。 也就是说,一个没有提交事务的redo log记录,也可能刷盘。因为在事务执行过程中redo log记录是会写入redo log buffer中,这些redo log记录会被后台线程刷盘

除了后台线程每秒1次轮询操作,还有一种情况,当redo log buffer占用的空间即将达到innodb_log_buffer_size(这个参数默认是16M)的一半的时候,后台线程会主动刷盘。

MySQL实战45讲 1,2

BINLOG

归档日志

执行器和 InnoDB 引擎在执行这个简单的 update 语句时的内部流程

  1. 执行器先找引擎取 ID=2 这一行。ID 是主键,引擎直接用树搜索找到这一行。如果 ID=2 这一行所在的数据页本来就在内存中,就直接返回给执行器;否则,需要先从磁盘读入内存,然后再返回。
  2. 执行器拿到引擎给的行数据,把这个值加上 1,比如原来是 N,现在就是 N+1,得到新的一行数据,再调用引擎接口写入这行新数据。
  3. 引擎将这行新数据更新到内存中,同时将这个更新操作记录到 redo log 里面,此时 redo log 处于 prepare 状态。然后告知执行器执行完成了,随时可以提交事务。
  4. 执行器生成这个操作的 binlog,并把 binlog 写入磁盘。
  5. 执行器调用引擎的提交事务接口,引擎把刚刚写入的 redo log 改成提交(commit)状态,更新完成。

图中浅色框表示是在 InnoDB 内部执行的,深色框表示是在执行器中执行的。

MySQL实战45讲 1,2

将 redo log 的写入拆成了两个步骤:prepare 和 commit,这就是”两阶段提交”。

bin log在之间写

两阶段提交

为什么日志需要”两阶段提交”

反证法:

  1. 先写 redo log 后写 binlog。在写完redo log后,写binlog的时候发生 crash。数据库恢复回来后,数据没丢失,因为能根据redo log 恢复回来,但是 这个操作却少了一个 binlog。而在进行数据库备份的时候使用的是binlog, 所以备份的数据里面就丢失了这次更改,以后在使用这个备份恢复的时候,自然恢复回来的数据就不对
  2. 先写 binlog 后写 redo log。如果在 binlog 写完之后 crash,由于 redo log 还没写, 崩溃恢复以后这个事务无效。但是 binlog 里面已经记录了日志,所以, 在之后用 binlog 来恢复的时候就多了一个事务出来,恢复出来的这一行 c 的值就是 1,与原库的值不同。

redolog和binlog具有关联行,在恢复数据时,redolog用于恢复主机故障时的未更新的物理数据,binlog用于备份操作。每个阶段的log操作都是记录在磁盘的,在恢复数据时,redolog 状态为commit则说明binlog也成功,直接恢复数据;如果redolog是prepare, 则需要查询对应的binlog事务是否成功,决定是回滚还是执行。

简单说,redo log 和 binlog 都可以用于表示事务的提交状态,而两阶段提交就是让这两个状态保持逻辑上的一致。

Q:定期全量备份的周期”取决于系统重要性,有的是一天一备,有的是一周一备”。那么在什么场景下,一天一备会比一周一备更有优势呢?或者说,它影响了这个数据库系统的哪个指标?

A:一天一备binlog比较小,恢复时间比较短 一般做法是从库做延迟复制,binlog 一周全量备份

Original: https://www.cnblogs.com/ydssx7/p/16495681.html
Author: ydssx
Title: MySQL实战45讲 1,2

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/621042/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Redis-持久化

    因为Redis是内存操作,意味着掉电就GG, 所以为了保证异常重启等问题后能尽快恢复服务,还是需要一定的持久化机制来保证。Redis提供了两种持久化机制: AOF Append O…

    数据库 2023年6月11日
    094
  • MySQL8.0解决“MySQL 服务无法启动。 服务没有报告任何错误。”

    TL;NRs 初始化服务时最好使用 mysqld –initialized –console命令; MySQL8.0的配置变量与MySQL5.7不同, [mysqld]下面设置…

    数据库 2023年5月24日
    0126
  • mysql常用操作汇总

    工作中经常用会遇到这种情况,可以访问mysql所在的服务器,但是服务器端口不对外暴露(通常因为安全原因)。这时,操作数据库只能通过命令行和 mysql client窗口来实现。我对…

    数据库 2023年6月14日
    077
  • 安装node、npm、vue cli脚手架

    1、node https://www.runoob.com/nodejs/nodejs-install-setup.html 2、npm 安装好node就默认安装好npm 不需要单…

    数据库 2023年6月9日
    074
  • 不要让“Clean Code”更难维护,请使用“Rule of Three”

    当人们试图将”代码整洁之道(Clean Code)”的原则应用于现有的代码库时,我经常会问这个问题。 我认为这是合情合理的。 当我们开始重构遗留代码时,通常…

    数据库 2023年6月14日
    093
  • 关于接口设计的思考–我们真的需要这么多入参吗

    最近,我改造一个旧接口时发现,这个接口有 30 多个入参,而事实上并不需要那么多,而且,这个接口还存在比较大的安全隐患。所以,关于如何设计接口入参,我想谈谈自己的一些想法。 当然,…

    数据库 2023年6月6日
    064
  • 优雅的代码从现在开始

    个人见解: 写代码前 构思明白, 想明白,想全 写着写着都是在写相同的代码,改动麻烦 看到不好的就立马让他优雅 学习别人是如何优雅的 便于维护,避免重复代码,便于开发 提取公共函数…

    数据库 2023年6月11日
    078
  • 代码优化记录

    代码优化记录 神龟虽寿,犹有竟时。 神龟虽寿 犹有竟时 Original: https://www.cnblogs.com/taojietaoge/p/15853508.htmlA…

    数据库 2023年6月14日
    087
  • 时序数据库InfluxDB的基本语法

    一 了解InfluxDB的必要性 Time series data is a series of data points each associated with a specif…

    数据库 2023年6月16日
    088
  • 使用 yum 在 CentOS7 上安装 MySQL8

    时间:2022-07-13安装版本:MySQL-community-8.0.29 0. 删除MariaDB 在CentOS 7中默认有安装MariaDB,这个是MySQL的分支,通…

    数据库 2023年5月24日
    0125
  • Redis SCAN命令

    获取指定前缀的key 需求描述: Redis中有大量以xxx开头的key,在不使用keys命令的情况下,如何快速获取这些前缀的key 解决方案: redis自带的scan命令可以解…

    数据库 2023年6月14日
    076
  • mysql @rownum := @rownum+1 方式获取行号

    MySQL: mysql中没有获取行号的函数,因此需要通过一些自定义语句来进行获取。通常做法是,通过定义用户变量@rownum来保存表中的数据。通过赋值语句@rownum:=@ro…

    数据库 2023年6月16日
    075
  • MySQL数据库CRUD

    INSERT语句 INSERT INTO 表名 (column1,column2,column3,…)VALUES (value1,value2,value3,&#82…

    数据库 2023年6月16日
    074
  • PHP array_count_values()

    array_count_values array_count_values() 函数用于统计数组中所有值出现的次数。 本函数返回一个数组,其元素的键名是原数组的值,键值是该值在原数…

    数据库 2023年6月14日
    066
  • 在线安装Docker

    安装 yum-utils 包yum install -y yum-utils 设置存储库# 官方地址(比较慢) yum-config-manager \ –add-repo \ …

    数据库 2023年6月14日
    076
  • win10彻底永久关闭自动更新的方法【已验证有效】

    [知识整理/来源网络] 原文链接:win10彻底永久关闭自动更新的方法【已验证有效】_电脑知识-电脑配置网 (dnpz.net) win10的自动更新可谓是非常顽固,很多用户在网上…

    数据库 2023年6月9日
    099
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球