暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

MySQL 是如何通过 SQL 读取数据的

剽悍的派森先生 2021-06-24
1014

一、背景

MySQL 5.6、InnoDB

要执行的 SQL:

    SELECT * FROM common.user WHERE id = 10;
    复制

    二、MySQL 如何存储数据

    要想知道数据如何读取,首先应该知道数据是如何存储的。

    根据 my.cnf 中配置,确定数据的存储位置:

      datadir=/var/lib/mysql
      复制

      查看

        ll /var/lib/mysql
        复制
          total 303628
          drwxr-xr-x 14 mysql mysql 4096 Apr 9 18:35 .
          drwxr-xr-x. 41 root root 4096 Mar 30 16:40 ..
          drwx------ 2 mysql mysql 4096 Nov 29 2019 common
          -rw-rw---- 1 mysql mysql 79691776 Apr 9 18:35 ibdata1
          -rw-rw---- 1 mysql mysql 50331648 Apr 9 18:35 ib_logfile0
          -rw-rw---- 1 mysql mysql 50331648 Apr 9 18:26 ib_logfile1
          -rw-rw---- 1 mysql mysql 105743537 Jul 18 2019 master.000001
          -rw-rw---- 1 mysql mysql 18950 Aug 2 2019 master.000002
          -rw-rw---- 1 mysql mysql 32 Jul 18 2019 master.index
          -rw-rw---- 1 mysql mysql 49 Apr 9 18:35 master.info
          drwx------ 2 mysql mysql 4096 May 17 2019 mysql
          -rw-rw---- 1 mysql mysql 3859 Aug 3 2019 mysqld-bin.000001
          -rw-rw---- 1 mysql mysql 557 Aug 3 2019 mysqld-bin.000002
          -rw-rw---- 1 mysql mysql 239 Aug 3 2019 mysqld-bin.000003
          -rw-rw---- 1 mysql mysql 239 Aug 3 2019 mysqld-bin.000004
          -rw-rw---- 1 mysql mysql 239 Aug 3 2019 mysqld-bin.000005
          -rw-rw---- 1 mysql mysql 239 Aug 3 2019 mysqld-bin.000006
          -rw-rw---- 1 mysql mysql 1511 Aug 3 2019 mysqld-bin.000007
          -rw-rw---- 1 mysql mysql 239 Aug 3 2019 mysqld-bin.000008
          -rw-rw---- 1 mysql mysql 11344 Aug 8 2019 mysqld-bin.000009
          -rw-rw---- 1 mysql mysql 872 Aug 8 2019 mysqld-bin.000010
          -rw-rw---- 1 mysql mysql 3142 Aug 8 2019 mysqld-bin.000011
          -rw-rw---- 1 mysql mysql 1839890 Apr 9 18:26 mysqld-bin.000012
          -rw-rw---- 1 mysql mysql 214 Apr 9 18:35 mysqld-bin.000013
          -rw-rw---- 1 mysql mysql 260 Apr 9 18:26 mysqld-bin.index
          -rw-rw---- 1 mysql mysql 205 Aug 3 2019 mysqld-relay-bin.000001
          -rw-rw---- 1 mysql mysql 205 Aug 3 2019 mysqld-relay-bin.000002
          -rw-rw---- 1 mysql mysql 205 Aug 3 2019 mysqld-relay-bin.000003
          -rw-rw---- 1 mysql mysql 205 Aug 3 2019 mysqld-relay-bin.000004
          -rw-rw---- 1 mysql mysql 205 Aug 3 2019 mysqld-relay-bin.000005
          -rw-rw---- 1 mysql mysql 205 Aug 3 2019 mysqld-relay-bin.000006
          -rw-rw---- 1 mysql mysql 205 Aug 3 2019 mysqld-relay-bin.000007
          -rw-rw---- 1 mysql mysql 174 Aug 8 2019 mysqld-relay-bin.000008
          -rw-rw---- 1 mysql mysql 143 Aug 8 2019 mysqld-relay-bin.000009
          -rw-rw---- 1 mysql mysql 143 Aug 8 2019 mysqld-relay-bin.000010
          -rw-rw---- 1 mysql mysql 143 Apr 9 18:26 mysqld-relay-bin.000011
          -rw-rw---- 1 mysql mysql 143 Apr 9 18:35 mysqld-relay-bin.000012
          -rw-rw---- 1 mysql mysql 312 Apr 9 18:26 mysqld-relay-bin.index
          drwx------ 2 mysql mysql 4096 May 17 2019 performance_schema
          -rw-rw---- 1 mysql mysql 39 Apr 9 18:35 relay-log.info
          复制

          主要文件说明:

          • ibdata1:undo log

          • ib_logfile*: redo log

          • mysqld-bin.*: bin log  

          • mysqld-relay-bin.*: 主从复制中继日志

          • master.info: 主库信息文件

          • relay-log.info: relay 应用的信息文件

          查看表空间信息

            ll common/
            复制
              total 716
              -rw-rw---- 1 mysql mysql 61 Nov 18 2019 db.opt
              -rw-rw---- 1 mysql mysql 8969 Nov 29 2019 user_menu.frm
              -rw-rw---- 1 mysql mysql 114688 Jan 3 18:44 user_menu.ibd
              -rw-rw---- 1 mysql mysql 9098 Nov 20 2019 user.frm
              -rw-rw---- 1 mysql mysql 589824 Jan 10 18:45 user.ibd
              复制
              • *.frm: 表结构文件,不区分存储引擎

              • *.idb: 数据文件,InnoDB 特有

              从 InnoDB 存储结构看,所有数据都被逻辑地存放在一个空间中,称之为表空间 (tablespace),如 common。

              InnoDB 管理磁盘的最小单位是页(page),每个页默认大小时是 16KB,也是 InnoDB 中磁盘与内存交互的最小单位。页有很多种,比如数据页、索引页等。

              注意:操作系统管理磁盘的最小单位是磁盘块,是操作系统读写磁盘最小单位,Linux 中页一般是 4KB。

              三、如何执行一条 SQL

              知道了数据是怎么存的,那么 SQL 是如何执行的呢?

              先看一下 MySQL 整体架构图。

              再看一下执行流程的简易图。

              大致流程描述:

              • MySQL 客户端通过协议将 SQL 语句发送给 MySQL 服务器;

              • 首先连接器会根据用户名密码做认证和鉴权;

              • 然后查询缓存,服务器会先检查查询缓存中是否有执行过这条 SQL;

                • 如果命中缓存,则将结果返回;

                • 否则进入下一个环节(查询缓存默认不开启);

              • 接下来解析器负责对 SQL 进行解析,涉及词法解析、语法分析、语义分析,最终会解析为抽象语法树;

              • 紧接着是预处理器处理,预处理器会进一步去检查解析树是否合法,预处理之后会得到一个新的解析树;

              • 执行前还有经过优化器处理,查询优化器的作用就是根据解析树生成不同的执行计划,然后选择一种最优的执行计划;

              • 服务器根据查询优化器给出的执行计划,再调用存储引擎的 API 执行查询;

              • 将结果返回给客户端,如果开启查询缓存,则会备份一份到查询缓存中。

              四、索引介绍

              InnoDB 存储引擎是怎么执行查询的呢?

              InnoDB 存储引擎中的索引分为两大类,主键索引和辅助索引。主键索引的叶子节点存储数据行,辅助索引只会存储主键值。InnoDB 的索引是基于 B+ 树实现的。

              根据 InnoDB 的要求,所有表都必须要有主键。那么问题来了,上文中的 id 字段是不是主键呢?

              1. 如果 id 是主键,那么通过主键索引可以定位到当前行所在的页,但是查找的时候要经过多少索引页呢?每在一个索引页上检索一次意味着一次 IO 的开销,都需要从磁盘读取 16K 数据都内存。至于到底需要几次 IO 这个需要根据 id 的字段类型以及 user 表的数据量共同决定。当在索引页中定位到行 id,然后通过 id 找到行所在数据页,然后把整个数据页读到 buffer pool 中,然后在内存中过滤出 id=10 的数据行,最后把数据返回给 server 端;

              2. 如果 id 不是主键索引,仅仅是辅助索引,那么会在辅助索引树上先定位出对应的主键,然后在通过主键索引查找数据;

              3. 如果 id 没有任何索引。直接读取数据页头节点, 顺序扫描, 返回符合条件记录,直到最终节点结束。

              五、扩展

              关于 MySQL 读操作,MySQL 官网文档中给出了两种读:一致性读锁定读。你知道这两者有什么区别吗?在锁定读的情况下,数据的读取又是什么样子呢?如果基于当前条件无法判断的话,需要添加什么条件才可以呢?知详情如何,且看下回分解。


              文章转载自剽悍的派森先生,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

              评论