“我平时的工作就是 CRUD (增删改查)呀!我怎么提升自己的技术?“、”平时开发我都是用开源的 MyBatis、Hibernate,连原生的 sql 我都没写过几行”、“SQL 调优除了加索引,我也没有什么特别的手段去优化了”。

相信这样的问题你可能也会遇到,但是,这种低水平重复工作做多了后,不免要担心一点:我们要如何才能打破这种僵局,进一步提升自己的技术呢?

从本节起,我们先从与 CRUD 密不可分的数据库开始讲起。MySQL 相信你并不陌生,它已成为多年来最流行的开源数据库。所以,我们将针对 MySQL 展开数据库领域的进阶提升,不再只满足于写简单的 SQL。

本节我们就来 MySQL 的基础架构。

逻辑架构图——“千呼万唤始出来”

首先,让我们来一起看一起 MySQL 的逻辑架构图,如下:

从图中可以看出,MySQL 可以分为「Server 层」「存储引擎层」

为了更好地理解其中的细节,我们来看一下更加具体的逻辑架构。

注:该图片来自于网络,后面文章中如果已有不错的图片,我就不再重复“造轮子”。图侵删。

结合图片,我们简单解释一下各部分的作用:

  • 最上层:客户端。大多数基于网络的客户端/服务端服务都有类似的架构。

    如连接处理、授权认证、安全等。

  • 第二层:核心服务功能。对应图中「连接器分析器、优化器、执行器、查询缓存」

    包含内置函数(如日期、时间、数学函数等)、跨存储引擎功能如存储过程、触发器、视图等。

  • 第三层:存储引擎。负责数据的存储和提取。其架构是插件式的,支持 InnoDB、MyISAM、Memory等多个存储引擎。

    具体实现:服务器通过 API 与存储引擎通信,不同存储引擎不会互相通信,只是简单响应服务器请求。

    从 MySQL 5.5.5 开始,InnoDB 成为了默认的存储引擎。

这里我想补充一点,MySQL 最重要的特性就是「存储引擎架构」

它的优点非常突出,可以将查询处理及其他系统任务和数据的存储/提取相分离,即「处理和存储分离设计」。

  • 启发一: plugin 这种可插拔式的设计给予了 MySQL 存储引擎非常大的灵活性。
  • 启发二:存储与计算分离也是当下热门的趋势,如 Apache Pulsar

一条 SQL 的“奇幻漂流”

接下来,我们通过一条 SQL,跟随着它的“奇幻漂流”,来逐一认识一下看看架构图中的不同部分。

为了有个直观的感觉,我们就假设要执行的 SQL 为: select * from T where id=1;

连接器

第一步,我们要先连接到这个数据库上。

连接器的主要作用:负责和客户端建立连接、获取权限、维持和管理连接

常见的命令一般为:

mysql -h$ip -P$port -u$user -p

输完命令之后,你就需要在交互对话里面输入密码。

这里我想提醒一下:如果用户名密码认证通过,连接器会去权限表中查询用户的所有权限。之后,这个连接的权限都依赖于此时读到的权限。也就是说,即使中途用户权限变更,也不会影响已经存在连接的权限。(只有新建的连接才使用新权限)

show processlist

连接完成后,如果没有后续操作,这个连接就处于空闲状态。可以用 show processlist 命令中看到它。下图就是执行结果,其中 Command 列显示为“Sleep”的这一行,就表示当前系统里有一个空闲连接。

如果客户端太长时间没有动静,连接器就会自动断开连接。这个时间是由参数wait_timeout控制的,默认值是8小时

如果你之前应用中用到了 MySQL,可能就会遇到过在日志中有时空闲太长,应用与 MySQL 的连接断开的情况,这就是根源所在。

连接被断开之后,客户端再次发送请求,就会收到一个错误提醒: Lost connection to MySQL server during query。如果你要继续请求,就需要重连。

「长连接」和「短连接」

  • 长连接:连接成功后,如果客户端持续请求,一直使用同一个连接。

  • 短连接:每次执行完很少的几次查询就断开连接,下次查询再新建一个。

因为建立连接的过程比较复杂,建议你尽量使用长连接

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

怎么解决这个问题呢?你可以考虑以下两种方案:

  • 定期断开长连接

    使用一段时间,或者程序里面判断执行过一个占用内存的大查询后,断开连接,之后要查询再重连。

  • 如果你用的是MySQL 5.7或更新版本,可以在每次执行一个比较大的操作后,执行 mysql_reset_connection 重新初始化连接资源

    这个过程不需要重连和重新做权限验证,但是会将连接恢复到刚创建完时的状态。

查询缓存

上一步我们完成连接建立后,执行逻辑就来到了第二步:查询缓存。

与我们在应用端使用缓存(如 HashMap/redis 等)类似,MySQL 拿到一个查询请求后,会先去查询缓存看看,之前是不是执行过这条语句。之前执行过的语句及其结果会以 key-value 对的形式,直接缓存在内存中。

key 是查询的语句,value 是查询的结果。

  • 如果命中,就直接返回 value 结果。
  • 如果未命中,继续后面的流程,执行完后,将未命中的结果放入缓存中。

你看,这个过程和我们平时在应用开发中实现的思路是不是非常类似?我们在学习 MySQL 同时也能学习到缓存的实现方式。

尽管缓存可以提高查询效率,但是,大多数情况下建议不要使用查询缓存

因为查询缓存往往弊 >> 利。简言之,失效频繁,对更新较多的情况下,缓存命中率非常低。

查询缓存的失效非常频繁,只要对一个表更新,则该表上所有的查询缓存都会被清空。因此很可能你费劲地把结果缓存起来,还没来得及使用呢,就被一个更新全清空了。

对于更新压力大的数据库来说,查询缓存命中率非常低。除非你的业务就是有张静态表,很长时间才会更新一次。比如系统配置表,那才适合使用查询缓存。

适用的场景:静态表或者变更频率非常低的表。

这里扩展一下,对于你确定要使用查询缓存的语句,可以用SQL_CACHE显式指定,像下面这个语句一样:

mysql> select SQL_CACHE * from T where id=10;

️ MySQL 8.0 直接将查询缓存的整块功能删掉了,没有这个功能。

分析器

接下来,就要对 SQL 进行解析了。

首先,先做「词法分析」

SQL语句是由多个字符串和空格组成的,MySQL需要识别出里面的字符串分别是什么,代表什么。

例如,MySQL 从你输入的"select"这个关键字识别出来,这是一个查询语句。它也要把字符串“T”识别成“表名T”,把字符串“id”识别成“列id”。

然后做「语法分析」

语法分析会根据语法规则,判断 SQL 语句是否满足 MySQL 语法。

如果你的语句不对,就会收到“You have an error in your SQL syntax”的错误提醒,例如这个语句select少打了开头的字母“s”。

一般语法错误会提示第一个出现错误的位置,所以你要关注紧接“use near”的内容。

优化器

经过了分析器,接下来便是优化器的处理。

优化器在表里有多个索引的时候,决定使用哪个索引;或者在一个语句多表关联(join)的时候,决定各个表的连接顺序

举个例子,

比如你执行下面这样的语句,这个语句是执行两个表的join:

mysql> select * from t1 join t2 using(ID) where t1.c=10 and t2.d=20;
  • 既可以先从表t1里面取出c=10的记录的ID值,再根据ID值关联到表t2,再判断t2里面d的值是否等于20。
  • 也可以先从表t2里面取出d=20的记录的ID值,再根据ID值关联到t1,再判断t1里面c的值是否等于10。

两种执行方法的逻辑结果一样的,但执行的效率会有不同,而优化器的作用就是决定选择使用哪一个方案。

类似的,还有大表驱动小表等。你写的 sql 语句的顺序不一定是MySQL 真正执行的顺序,原因就在这里。

执行器

前面我们通过分析器知道要做什么,通过优化器知道怎么做,然后就进入了执行器,开始执行 SQL。

1.开始执行前,先做权限验证

先判断一下你对这个表T有没有执行权限,如果没有,就会返回没有权限的错误(在实现上,如果命中查询缓存,会在查询缓存放回结果的时候,做权限验证。查询也会在优化器之前调用 precheck 验证权限)。

2.如果有权限,就打开表继续执行。执行器会根据表的引擎定义,选择用这个引擎的接口

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

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

3.至此,语句执行完成。

如果表有索引,执行逻辑差不多。第一次调用的是“取满足条件的第一行”这个接口,之后循环取“满足条件的下一行”这个接口,这些接口都是引擎中已经定义好的。

这里补充一下:我们在数据库的慢查询日志中会看到一个 rows_examined 的字段,表示某个语句执行过程中扫描了多少行。这个值就是在执行器每次调用引擎获取数据行时累加的。

到这里,我们的 SQL "奇幻漂流"也画上一个句号。

MySQL 架构|给你一个“上帝视角”的更多相关文章

  1. 步步深入:MySQL架构总览->查询执行流程->SQL解析顺序

    前言: 一直是想知道一条SQL语句是怎么被执行的,它执行的顺序是怎样的,然后查看总结各方资料,就有了下面这一篇博文了. 本文将从MySQL总体架构--->查询执行流程--->语句执行顺序来 ...

  2. ch2 MySQL 架构组成

    第 2 章 MySQL 架构组成 前言 麻雀虽小,五脏俱全.MySQL    虽然以简单著称,但其内部结构并不简单.本章从 MySQL 物理组成.逻辑组成,以及相关工具几个角度来介绍    MySQL ...

  3. mysql笔记01 MySQL架构与历史、Schema与数据类型优化

    MySQL架构与历史 1. MySQL架构推荐参考:http://www.cnblogs.com/baochuan/archive/2012/03/15/2397536.html 2. MySQL会解 ...

  4. 理解MySQL——架构与概念

    写在前面:最早接触的MySQL是在三年前,那时候MySQL还是4.x版本,很多功能都不支持,比如,存储过程,视图,触发器,更别说分布式事务等复杂特性了.但从5.0(2005年10月)开始,MySQL渐 ...

  5. MySQL 架构

    原文:MySQL 架构 MySQL架构和结构分析 官方架构图: MySQL DB 各模块架构图如下: MySQL安装方式 MySQL初始化 简介:什么是事务: 事务: ACID :  事务确保了银行不 ...

  6. MySQL架构优化:定时计划任务与表分区

    转自: MySQL架构优化实战系列3:定时计划任务与表分区 - 今日头条(TouTiao.com)http://toutiao.com/a6304736482361049345/?tt_from=mo ...

  7. MySQL架构由小变大的演变过程

    假设一个网站(discuz)从最开始访问量很小做到日pv千万,我们来推测一下它的mysql服务器架构演变过程. 第一阶段网站访问量日pv量级在1w以下.单台机器跑web和db,不需要做架构层调优(比如 ...

  8. 第 2 章 MySQL 架构组成

    麻雀虽小,五脏俱全.MySQL 虽然以简单著称,但其内部结构并不简单.本章从MySQL物理组成.逻辑组成,以及相关工具几个角度来介绍 MySQL 的整体架构组成,希望能够让读者对 MySQL 有一个更 ...

  9. MySQL架构篇(一)

    MySQL复制解决了什么问题? 1.实现在不同服务器上的数据分布 2.利用二进制日志增量进行 3.不需要太多的带宽 4.但是使用基于行的复制在进行大批量的更改时会对带宽带来一定的压力,特别是跨IDC环 ...

  10. mysql 架构篇系列 3 复制运行状态监控与选项参数说明

    一. 概述 在上一篇中,搭建了一主一从的复制架构,这篇通过一些诊断方法来了解复制的运行状态和一些选项参数说明.上次mysql主从服务关机,今天在打开mysql服务,出现了错误信息. 1.首先 启动主从 ...

随机推荐

  1. CSS&HTML标签

    内容索引: 1. HTML标签:表单标签 2. CSS HTML标签:表单标签 * 表单: * 概念:用于采集用户输入的数据的.用于和服务器进行交互. * form:用于定义表单的.可以定义一个范围, ...

  2. 深度理解Python迭代器

    迭代器 迭代是什么 迭代指的是一个重复的过程,每次重复都必须基于上一次的结果而继续,单纯的重复并不是迭代,如Python中的for循环就是一个非常好的迭代例子. for item in range(1 ...

  3. opencv——图像直方图与反向投影

    引言 在图像处理中,对于直方图这个概念,肯定不会陌生.但是其原理真的可以信手拈来吗? 本文篇幅有点长,在此列个目录,大家可以跳着看: 分析图像直方图的概念,以及opencv函数calcHist()对于 ...

  4. 神奇的不可见空格<200b>导致代码异常

    故事是这样发生的,在做一个JSON对象转化的时候,出现了转化异常:刚开始还是以为是格式错误,后来一步步排除,才发现是不可见空格<200b>导致的解析异常 出现 使用Typora编写文字时, ...

  5. 使用U盘软碟通安装原版Windows10

    https://zhuanlan.zhihu.com/p/171534675 使用U盘软碟通安装原版Windows10 一.准备:8G U盘一个,电脑装好UltraISO这个软件下载地址 二.Wind ...

  6. 删除rpc01

    vim /opt/ltp/runtest/stress.part3 修改/opt/ltp/runtest/stress.part3 删除rpc01: 142 systemctl status gdm ...

  7. Ansible_编写循环和条件任务

    一.利用循环迭代任务 1️⃣:Ansible支持使用loop关键字对一组项目迭代任务,可以配置循环以利用列表中的各个项目.列表中各个文件的内容.生成的数字序列或更为复杂的结构来重复任务 1.简单循环 ...

  8. Centos7环境初始化

    最近在做公司的一个环境搭建的任务的时候,要用到三台Centos7服务器,在上面要预装java1.8,docker,zookeeper并且要在docker中跑一个mysql,还要部署其他的软件.由于不是 ...

  9. 把采集到的数据发送到一个Google Docs或者Google Form上 这个网站提供了参考和例子

    把采集到的数据发送到一个Google Docs或者Google Form上这个网站提供了参考和例子 http://www.instructables.com/id/Post-to-Google-Doc ...

  10. JavaEE 前后端分离以及优缺点

    前端概念 前端是一切直接与用户交互的页面或软件(用户看得见.摸得着)的统称,比如各种网站网页.andorid 手机各种 App.苹果手机各种 app.微信小程序.网络游戏客户端等.所以,普通人使用计算 ...