技术共享| MySQL有多少查询字段影响查询效率

2020-01-23 11:31 来源:科创网

原标题:技术共享| MySQL:查询字段数量对查询效率的影响

作者:高鹏

文章末尾有他的作品《深入理解 MySQL 主从原理 32 讲》,对MySQL主从和GTID相关技术知识有着深刻透彻的理解。

我的一个朋友最近问了这个问题。我只是看了一眼,留下了这样的记录。

本文最后给出了一些函数接口和一些调用堆栈,为感兴趣的朋友提供参考,并为您自己提供一个注释。

一、问题由来

我们知道不同的执行计划肯定会导致不同的效率,但是在这个例子中,执行计划是完全一致的,并且都是全表扫描,只有字段的数量不同。其次,在测试中,条件用于过滤(使用位置),过滤后不返回任何数据。我们经常说过滤实际上在MySQL层的什么地方。当然,在某些情况下,电感耦合等离子体用于预先过滤Innodb层中的数据。此处不考虑国际比较方案。我将在后面的文章中详细描述比较方案的过程。本文还将提供过滤位置的界面,供每个人参考。

下面的截图来自两位朋友,感谢他们的测试和提问。此外,对于大数据访问,可能涉及物理输入输出。由于Innodb缓冲区之间的关系,第一次访问和后续访问之间的效率差异是正常的,需要更多的测试。

测试1:

16953f3a408341b5a459b543f3ff08aa.jpeg

47abbc9cf49d422bad43507e3e61d41c.jpeg

8a9a197529564d27b02428564eb54caf.jpeg

测试2:

9b0b412764874fa7af043e7f053bf1b2.jpeg

通过这两个测试,我们可以发现随着字段的不断减少,效率越来越高,主要区别在于发送数据。我在参考文章中简要描述了这种状态:

http://www.jianshu.com/p/46ad0aaf7ed7

http://www.jianshu.com/p/4cdec711adef

简单地说,Innodb数据的获取和Innodb数据到MySQL层数据的传输都包括在内。

二、简单的流程介绍

我将主要从两个方面介绍一个简单的过程:字段数和全表扫描。事实上,核心接口之一是row_search_mvcc,它大致包括以下功能:

通过预取缓存打开事务定位索引位置(包括使用AHI快速定位)获取数据是否打开读取视图使用Innodb表锁连续访问下一个数据并基于主键返回表添加Innodb行锁可见性判断(可能返回表需要行锁)ICP优化半更新优化

作为访问数据必须经历的接口,这个功能也值得仔细研究。

1. 通过 select 字段构建 readset(MySQL 层)

首先需要构建一个名为read_set的位图来指示所访问字段的位置和数量。与写集一起,它在记录binlog事件中也起着重要作用。请参考我的《深入理解 MySQL 主从原理》中关于binlog_row_image参数的部分。这里构建的主要接口是TABLE :3360 MARK _ COLUMN _ USED函数,每个需要访问的字段调用该函数来设置自己的位图。以下是其中之一:

从堆栈帧中,构建读取集的过程位于“init”状态下。堆叠框架见末端堆叠框架1。

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

该模板主要用于将Innodb层数据转换为MySQL层。它记录使用的字段数量、字段字符集、字段类型等。接口build_template_field用于构建此模板。堆叠框架见末端堆叠框架2。但是,应该注意,这里构建的模板将决定在调用build_template_field函数之前,需要通过上面提到的read_set在模板中构建多少个字段。下面是最重要的代码,它位于build_template_needs_field接口中。

您可以看到该字段正在被测试,以查看它是否出现在read_set中。如果没有,跳过此字段。以下是对函数build_template_needs_field的注释:

到这里我们需要访问的字段已经确立下来了

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

对于这种全表扫描执行模式,定位数据变得简单。我们只需要找到主键索引的第一个数据。它不同于正常(参考/范围)定位模式,不需要二分法支持。因此,全表扫描的第一个位置调用函数是BTR _ CUR _ OPEN _ AT _ INDEX _ SIDE _ FUNC,而不是我们通常所说的BTR _ PCUR _ OPEN _ WITH _ NO _ INIT _ FUNC。

如果我们粗略地看一下函数BTR _ CUR _ OPEN _ AT _ INDEX _ SIDE _ FUNC,我们可以很容易地看到它是通过B树结构在叶节点的开头定位第一个块,然后首先调用函数page_cur_set_before_first,并将光标放在所有记录的开头,唯一的目的是为全表扫描做准备。堆叠框架见末端堆叠框架3。请注意,这就是我们所说的row_search_mvcc。

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

可以在获取光标后获取数据,这里的简单代码如下:

rec=btr _ pcur _ get _ rec(pcur);//从持久光标整行数据

但是应该注意的是,这里获取的数据只是一个指针,其含义可以理解为整行数据,其格式也是原始Innodb数据,其中还包含一些伪列,比如(rollback ptr和trx id)。这与访问的字段数量无关。

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

完成此步骤后,我们可以认为记录已经返回到MySQL层。这是实际的数据拷贝,不是指针。整个过程被放入函数row_sel_store_mysql_rec。

我们面前的模板(mysql_row_templ_t)也将在这里发挥作用。这是场过滤的过程。让我们先看一个循环。

其中prebuild-n _ template是字段模板的数量。正如我们之前说过的,通过read_set过滤器,不会为我们不需要的字段创建模板。所以这里的模板数量与我们访问的字段数量相同。

然后,在这个循环下,将调用row_sel_store_mysql_field_func,然后调用ROW _ SEL _ FIELD _ STORE _ IN _ mysql _ FORMAT _ FUNC将字段一个接一个地转换为MySQl格式。让我们来看看下面的转换类型之一:

我们可以发现这是一个实际的转换,需要内存空间。堆叠框架见末端堆叠框架4。到这里我们大概知道了,查询的字段越多那么这里转换的过程越长,并且这里都是实际的内存拷贝,而不是指针。

最后,这一行数据将存储在row_search_mvcc的参数缓冲区中,并返回到MySQL层。该参数的注释如下:

找到=MY_TEST(条件值_ int);//比较调用条件并返回记录的比较

6. 对第一条数据进行 where 过滤(MySQL 层)

在获得数据后,当然不能将其作为最终结果返回给用户。我们需要在MySQL层进行过滤操作。该条件比较位于函数evaluate_join_record的开头,其中比较结果如下:

如果不匹配该条件,将返回False。这里的比较最终将调用Item_func的各种方法,如果相等,它就是Item_func_eq,堆栈帧显示在结束堆栈帧5中。

7. 访问下一条数据

我已经展示了访问上面第一个数据的一般过程,接下来我需要做的是继续访问,如下所示:

整个过程将继续,直到访问所有主键索引数据。但是,应该注意的是,在上层接口中有一些变化,从HA1 _ INNOBASE :3360 INDEX _ FIRST到HA1 _ INNOBASE :3360 RND _ NEXT,并且统计数据从Handler_read_first变化到Handler_read_rnd_next。请参考我的文章:

http://www.jianshu.com/p/25fed8f1f05e

并且row_search_mvcc的过程肯定会改变。这里不再重复。然而,实际的采集数据转换过程和过滤过程没有改变。

注意了这些步骤除了步骤1,基本都处于 sending data 下面。

三、回到问题本身

嗯,这里我们知道了访问数据进行全表扫描的过程。让我们看看全表扫描过程中的字段数量,看看有什么相似之处和不同之处:

不同点:

而构造的read_set。字段越多,read_set中“1”的位数越多,创建的模板就越多,字段越多,模板就越多 当把每一行数据转换成MySQL格式时,它是不同的。字段越多,模板越多,每个字段的周期就越多。这就是每一行要处理的。

相同点:

访问的行数是一致的,访问流是一致的,过滤的方式也是一致的

总的来说,我认为最耗时的部分应该是每行数据都需要转换成MySQL格式,因为每行的每个字段都需要转换,只需除以发送数据的状态。我们线上到处都是10个以上字段的表格。如果我们只需要访问少量的字段,为了避免这个问题,我们最好写实际的字段,而不是' * '。

四、写在最后

虽然本文解释了全表扫描,但是我们实际上应该减少在任何情况下要访问的字段的数量,并且应该只访问需要的字段。

五、备用栈帧(以下图片需要放大)

栈帧1 read_set 构建

96c15df866454d30a16549a2079efa2e.jpeg

栈帧2 构建模板

030a1ba5b33d4d0291e57a68f68a07e7.png

栈帧3 全表扫描初次定位栈帧

ad13aa5eb88c4c29939839fa537778c4.jpeg

栈帧4 MySQL 格式的转换

fd8ed72cd3ab42eaac1aa101824d47d5.png

栈帧5 String 的等值比较

066-9900那些想学习MySQL主从原则的人一定不能错过。

叶老师的新课程MySQL性能优化合理利用索引,降低锁影响,提高事务并发度

标签: 访问 字段 Innodb

热门文章

热点图文