• select count(*)的底层究竟在干啥?


    在这里插入图片描述

    一、InnoDB 全表 COUNT( * )

    1、执行过程是怎样的?
    2、如何计算 count?影响 count 结果的因素有哪些?
    3、count 值存在哪里?涉及的数据结构是怎样的?
    4、为什么 InnoDB 只能通过扫表来实现 count( * )?(见本文最后的问题)
    5、全表COUNT( * )作为 table scan 类型操作的一个 case,有什么风险?
    6、COUNT(
    )操作是否会像“SELECT * ”一样可能读取大字段涉及的溢出页?
    *

    1、执行框架 – 循环: 读取 + 计数?

    1.1基本结论

    • 全表扫描,一个循环解决问题
    • 循环内: 先读取一行,再决定该行是否计入 count
    • 循环内是一行一行进行计数处理的

    1.2、说明:

    简单 SELELCT-SQL 的执行框架,类比 INSERT INTO … SELECT 是同样的过程
    在这里插入图片描述

    2、执行过程

    执行过程部分,分为 4 个部分:

    (1)COUNT( * ) 前置流程: 从 Client 端发 SQL 语句,到 MySQL-Server 端执行 SELECT 之前,为后面的一些阐述做一铺垫。

    (2)COUNT( * ) 流程: 简要给出代码层面的流程框架及 2 个核心步骤的重点调用栈部分。

    (3)读取一行: 可见性及 row_search_mvcc 函数,介绍可见性如何影响 COUNT( * ) 结果。

    (4)计数一行: Evaluate_join_record 与列是否为空,介绍计数过程如何影响 COUNT( * ) 结果。

    2.1、COUNT( * ) 前置流程回忆 – 从 Client 端发 SQL 到 sub_select 函数

    如何执行到 sub_select 函数这来的:

    在这里插入图片描述
    (1)MySQL-Client 端发送 SQL 语句,根据 MySQL 通信协议封包发送。

    (2)Mysql-Server 端接收数据包,由协议解析出 command 类型 ( QUERY ) 及 SQL 语句 ( 字符串 ) 。

    (3)SQL 语句经过解析器解析输出为 JOIN 类的对象,用于结构化地表达该 SQL 语句。

    PS: 这里的 JOIN 结构,不仅仅是纯语法结构,而是已经进行了语义处理,粗略地说,汇总了表的列表 ( table_list )、目标列的列表 ( target_list )、WHERE 条件、子查询等语法结构。

    在全表 COUNT( * )-case 中,table_list = [表“t”(别名也是“t”)],target_list = [目标列对象(列名为“COUNT( * )”)],当然这里没有 WHERE 条件、子查询等结构。

    (4)JOIN 对象有 2 个重要的方法: JOIN::optimize(), JOIN::exec(),分别用于进行查询语句的优化 和 查询语句的执行

    • join->optimize(),优化阶段 (稍后 myisam 下全表 count( * ) 操作会涉及这里的一点内容)
    • join->exec(),执行阶段 ( 重点 ),包含了 InnoDB 下全表count( * ) 操作的执行流程

    (5)join->exec() 经过若干调用,将调用到 sub_select 函数来执行简单 SQL,包括 COUNT( * ) 。

    (6)END of sub_select

    2.2、COUNT( * ) 流程 ( 于 sub_select 函数中 )

    层的流程与代码是比较简单的,集中在 sub_select 函数中,其中 2 类函数分别对应于前面”执行框架”部分所述的 2 个步骤 – 读取、计数。先给出结论如下:

    (1)读取一行:从相对顶层的 sub_select 函数经过一番调用,最终所有分支将调用到 row_search_mvcc 函数中,该函数就是用于从 InnoDB 存储引擎所存储的 B±tree 结构中读取一行到内存中的一个 buf (uchar * ) 中,待后续处理使用。

    这里会涉及行锁的获取、MVCC 及行可见性的问题。当然对 于 SELECT COUNT( * ) 这类快照读而言,只会涉及 MVCC 及其可见性,而不涉及行锁。详情可跳至“可见性与 row_search_mvcc 函数”部分。

    (2)计数一行: 代码层面,将会在 evaluate_join_record 函数中对所读取的行进行评估,看其是否应当计入 count 中 ( 即是否要 count++ )。

    简单来说,COUNT(arg) 本身为 MySQL 的函数操作,对于一行来说,若括号内的参数 arg ( 某列或整行 ) 的值若不是 NULL,则 count++,否则对该行不予计数。详情可跳至“ Evaluate_join_record 与列是否为空”部分。

    这两个阶段对 COUNT( * )结果的影响如下: (两层过滤)

  • 相关阅读:
    交通物流模型 | 基于多重时空信息融合网络的城市网约车需求预测
    我国数据安全治理研究
    Netty线程模型
    软件测试(二)用例
    24 行为型模式-访问者模式
    面向对象oop的理解
    怎样找到NPM里面开源库下载地址
    改进的蝗虫优化算法(Matlab代码实现)
    c# 字典与内存碎片化
    产品经理和项目经理谁才是项目管理界的NO.1?
  • 原文地址:https://blog.csdn.net/weixin_45520375/article/details/121338070