MySQL engine层到server层字段过滤优化

简介:

1.1 问题描述

  执行计划的不同肯定会带来效率的不同,但是在本例中执行计划完全一致,都是全表扫描,不同的只有字段个数而已。其次,测试中都使用了where条件进行过滤(Using where),过滤后没有数据返回,常说的where过滤实际上是在server层,当然某些情况下使用ICP会提前在Innodb层过滤数据,这里不考虑ICP。
  对于大数据量访问来讲可能涉及到物理IO,首次访问和随后的访问因为Innodb buffer的关系,效率不同是正常,需要多测试几次。
_
_
_
  通过上面的测试,可以发现随着字段的不断减少,效率越来越高,并且主要的区别都在sending data下面。简单的说Innodb数据的获取和Innodb数据到server层数据的传递都包含在其中。

2.2 理论依据

https://dev.mysql.com/doc/dev/mysql-server/latest/

全表访问数据的流程

  这里将简单描述一下这种全表扫描的流程,实际上其中有一个核心接口就是row_search_mvcc,它大概包含了如下功能:

  • 通过预取缓存获取数据
  • 打开事务
  • 定位索引位置(包含使用AHI快速定位)
  • 是否开启readview
  • 通过持久化游标不断访问下一条数据
  • 加Innodb表锁、加Innodb行锁
  • 可见性判断
  • 根据主键回表(可能回表需要加行锁)
  • ICP优化
  • SEMI update优化

  下面对MySQL处理字段多少时的优化流程做出介绍:

1、通过select字段构建read_set(server 层)

  首先需要构建一个叫做read_set的位图,来表示访问的字段位置及数量。

2、初次访问定位的时候还会构建一个模板(mysql_row_templ_t)(innodb 层)

  本模板主要用于当Innodb层数据到server层做转换的时候使用,其中记录了使用的字段数量、字段的字符集、字段的类型等等。

3、初次定位数据,定位游标到主键索引的第一行记录,为全表扫描做好准备(innodb层)

  对于这种全表扫描的执行方式,定位数据就变得简单了,只需要找到主键索引的第一条数据就好了。对于全表扫描的初次定位调用函数为btr_cur_open_at_index_side_fun。

  btr_cur_open_at_index_side_func的功能就是通过B+树结构,定位叶子结点的开头第一个块,然后调用函数page_cur_set_before_first,将游标放到了所有记录的开头,目的只有一个为全表扫描做好准备。

4、获取Innodb层的第一条数据(Innodb层)

  拿到了游标过后就可以获取数据了。但是这里获取的数据只是一个指针,言外之意可以理解为整行数据,其格式也是原始的Innodb数据,其中还包含了一些伪列比如(rollback ptr和trx id)。这里实际上和访问的字段个数无关。

5、将第一行记录转换为MySQL格式(Innodb层)

  这一步完成后可以认为记录已经返回给了server层,这里就是实际的数据拷贝了,并不是指针,整个过程放到了函数row_sel_store_mysql_rec中。

  前面的模板(mysql_row_templ_t)也会在这里发挥它的作用,这是一个字段过滤的过程,先来看一个循环
for (i = 0; i < prebuilt->n_template; i++),其中prebuilt->n_template就是字段模板的个数,通过read_set的过滤,对于不需要的字段是不会建立模板的。因此这里的模板数量是和访问的字段个数一样的。

  然后在这个循环下面会调用row_sel_store_mysql_field_func然后调用row_sel_field_store_in_mysql_format_func将字段一个一个转换为MySQL的格式。其中一种类型的转换如下:

    case DATA_INT:
        /* Convert integer data from Innobase to a little-endian
        format, sign bit restored to normal */

        ptr = dest + len;

        for (;;) {
            ptr--;
            *ptr = *data;//值拷贝 内存拷贝
            if (ptr == dest) {
                break;
            }
            data++;
        }

  可以发现这是一种实际的转换,也就是需要花费内存空间的。查询的字段越多那么着这里转换的过程越长,并且这里都是实际的内存拷贝,最终这行数据会存储到row_search_mvcc的形参buffer中返回给server层。

6、对第一条数据进行where过滤(server层)

  拿到数据后当然还不能作为最终的结果返回给用户,需要在server层做一个过滤操作,这个条件比较位于函数evaluate_join_record的开头。

  如果和条件不匹配将会返回False。这里比较会最终调用Item_func的各种方法,如果等于则是Item_func_eq。

7、访问下一条数据(server 层)

  上面已经展示了访问第一条数据的大体流程,接下面需要做的就是继续访问下去,如下:

移动游标到下一行
访问数据
根据模板转换数据返回给server层
根据where条件过滤

  整个过程会持续到全部主键索引数据访问完成。

  并且row_search_mvcc的流程肯定也会有变化。但是实际的获取数据转换过程和过滤过程并没有改变。注意这些步骤除了步骤1,基本都处于sending data下面。

  到这里已经大概知道全表扫描的访问数据的流程了,就来看看一下在全表扫描流程中字段的多少到底有哪些异同点:

不同点

  • 构建的read_set不同,字段越多read_set中为‘1’的位数越多
  • 建立的模板不同,字段越多模板数量越多
  • 每行数据转换为MySQL格式的时候不同,字段越多模板越多,那么循环转换每个字段的循环次数也就越多,并且这是每行都要处理的。返回给server层的行内存消耗越大。

相同点

  • 访问的行数一致
  • 访问的流程一致
  • where过滤的方式一致

  在整个不同点中,认为最耗时的部分应该是每行数据转换为MySQL格式的消耗最大,因为每行每个字段都需要做这样的转换,这也刚好是除以sending data状态下面。线上大于10个字段的表比比皆是,如果只需要访问其中的少量字段,最好还是写实际的字段而不是‘*’,来规避这个问题。

总结
  本文中以全表扫描为列进行了解释,但是实际上任何情况下都应该缩减访问字段的数量,应该只访问需要的字段。

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
目录
相关文章
|
6月前
|
SQL 缓存 关系型数据库
MySQL 慢查询是怎样优化的
本文深入解析了MySQL查询速度变慢的原因及优化策略,涵盖查询缓存、执行流程、SQL优化、执行计划分析(如EXPLAIN)、查询状态查看等内容,帮助开发者快速定位并解决慢查询问题。
266 0
|
4月前
|
缓存 关系型数据库 MySQL
降低MySQL高CPU使用率的优化策略。
通过上述方法不断地迭代改进,在实际操作中需要根据具体场景做出相对合理判断。每一步改进都需谨慎评估其变动可能导致其他方面问题,在做任何变动前建议先在测试环境验证其效果后再部署到生产环境中去。
227 6
|
5月前
|
存储 SQL 关系型数据库
MySQL 核心知识与索引优化全解析
本文系统梳理了 MySQL 的核心知识与索引优化策略。在基础概念部分,阐述了 char 与 varchar 在存储方式和性能上的差异,以及事务的 ACID 特性、并发事务问题及对应的隔离级别(MySQL 默认 REPEATABLE READ)。 索引基础部分,详解了 InnoDB 默认的 B+tree 索引结构(多路平衡树、叶子节点存数据、双向链表支持区间查询),区分了聚簇索引(数据与索引共存,唯一)和二级索引(数据与索引分离,多个),解释了回表查询的概念及优化方法,并分析了 B+tree 作为索引结构的优势(树高低、效率稳、支持区间查询)。 索引优化部分,列出了索引创建的六大原则
146 2
|
5月前
|
存储 SQL 关系型数据库
MySQL 动态分区管理:自动化与优化实践
本文介绍了如何利用 MySQL 的存储过程与事件调度器实现动态分区管理,自动化应对数据增长,提升查询性能与数据管理效率,并详细解析了分区创建、冲突避免及实际应用中的关键注意事项。
226 0
|
7月前
|
存储 SQL 关系型数据库
京东面试:mysql深度分页 严重影响性能?根本原因是什么?如何优化?
京东面试:mysql深度分页 严重影响性能?根本原因是什么?如何优化?
京东面试:mysql深度分页 严重影响性能?根本原因是什么?如何优化?
|
6月前
|
关系型数据库 MySQL
MySQL数据表添加字段(三种方式)
本文解析了数据表的基本概念及字段添加方法。在数据表中,字段是纵向列结构,记录为横向行数据。MySQL通过`ALTER TABLE`指令支持三种字段添加方式:1) 末尾追加字段,直接使用`ADD`语句;2) 首列插入字段,通过`FIRST`关键字实现;3) 指定位置插入字段,利用`AFTER`指定目标字段。文内结合`student`表实例详细演示了每种方法的操作步骤与结构验证,便于理解与实践。
|
9月前
|
存储 关系型数据库 MySQL
MySQL细节优化:关闭大小写敏感功能的方法。
通过这种方法,你就可以成功关闭 MySQL 的大小写敏感功能,让你的数据库操作更加便捷。
711 19
|
10月前
|
SQL 关系型数据库 MySQL
基于SQL Server / MySQL进行百万条数据过滤优化方案
对百万级别数据进行高效过滤查询,需要综合使用索引、查询优化、表分区、统计信息和视图等技术手段。通过合理的数据库设计和查询优化,可以显著提升查询性能,确保系统的高效稳定运行。
481 9
|
3月前
|
缓存 关系型数据库 BI
使用MYSQL Report分析数据库性能(下)
使用MYSQL Report分析数据库性能
161 3
|
3月前
|
关系型数据库 MySQL 数据库
自建数据库如何迁移至RDS MySQL实例
数据库迁移是一项复杂且耗时的工程,需考虑数据安全、完整性及业务中断影响。使用阿里云数据传输服务DTS,可快速、平滑完成迁移任务,将应用停机时间降至分钟级。您还可通过全量备份自建数据库并恢复至RDS MySQL实例,实现间接迁移上云。