MySQL中查询字段的数量会影响查询效率

分类:编程技术 时间:2024-02-20 15:42 浏览:0 评论:0
0
本文将详细讲解MySQL中查询字段的数量如何影响查询效率。小编觉得很实用,所以分享给大家,作为参考。希望您读完本文后有所收获。

1.问题由来

我们知道不同的执行计划在效率上会有差异,但是在这个例子中执行计划是完全一样的,都是全表扫描,唯一的区别就是执行计划的数量字段。其次,测试中使用了where条件进行过滤(Using where),过滤后没有返回任何数据。我们常说的where过滤其实是在MySQL层。当然,在某些情况下,使用ICP会提前对Innodb层的数据进行过滤。这里我们不考虑ICP。我会在后面的文章中详细介绍ICP的流程。文章还会给出where过滤接口供大家参考恩斯。

以下截图来自两位朋友,感谢他们的测试和提问。另外,对于大数据访问,可能会涉及到物理IO。由于Innodb缓冲区的原因,第一次访问和后续访问效率不同是正常的,需要多次测试。

测试 1:

测试 2 :

通过这两个测试可以发现,随着字段不断减少,效率越来越高,主要区别在发送数据下。 ,我在参考文章中大致描述了这种状态:
https://www.jianshu.com/p/46ad0aaf7ed7
https://www.jianshu.com/p/4cdec711adef

简单来说,Innodb数据的获取以及Innodb数据到MySQL层数据的传输都包含在其中。

2.简单流程介绍

Bel下面我主要是结合字段,从多少和全表扫描两个方面来简单介绍一下流程。其实核心接口之一就是row_search_mvcc,大概包含以下功能:

通过预取缓存获取数据

开启事务

定位索引位置(包括使用AHI快速判断bit)

是否启用readview

通过持久化游标不断访问下一条数据

添加Innodb表锁和Innodb行锁< /p>

可见性判断

根据主键返回表(返回表时可能需要加行锁)

ICP优化

SEMI更新优化

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

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

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

case MARK_COLUMNS_READ: bitmap_set_bit(read_set, field->field_index);

从栈帧来看,构建read_set的过程位于state下'在里面' 。请参阅最后的堆栈帧 1。

2、访问时也会构建第一次定位。一个模板(mysql_row_templ_t)(Innodb层)

这个模板是ma仅在Innodb层数据转换到MySQL层时使用。它记录了使用的字段数量以及字段的字符集。 、字段类型等。接口 build_template_field 用于构建此模板。请参阅最后的堆栈帧 2。
但是需要注意的是,这里构建的模板会通过my我们使用上面提到的read_set来确定模板需要构建多少个字段,然后调用build_template_field函数。下面是最重要的代码,位于build_template_needs_field接口中。

bitmap_is_set(table->read_set, static_cast(i)

可以看到是在测试这个字段是否出现在read_set中,如果没有则跳过该字段. .下面是函数build_template_needs_field的注释:

判断m_prebuilt结构体'template'中是否需要某个字段。@return字段使用,如果不需要该字段则返回NULL */< /pre>

我们需要的字段此处已建立访问权限

3.第一次定位数据,将光标定位到主键索引第一行,为全表扫描(Innodb层)做准备

对于这种执行方式全表扫描使得定位数据变得简单。我们只需要找到主键索引中的第一条数据即可。它与我们通常使用的(ref/range)定位方法不同,不需要二分法的支持。 。因此,全表扫描的初始定位调用函数是btr_cur_open_at_index_side_func,而不是我们平时说的btr_pcur_open_with_no_init_func。
如果粗略地看一下函数btr_cur_open_at_index_side_func的作用,我们很容易看出它是定位目标的通过B+树结构。第一个块在叶子节点开始,然后调用funpage_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)也将在这里发挥作用。这是一个字段过滤过程。我们先看一个循环

for (i = 0; i < prebuilt ->n_template; i++)

其中prebuilt->n_template是字段模板的数量。我们之前说过,通过read_set的过滤,不会为我们不需要的字段创建模板。因此,这里的模板数量和我们访问的字段数量是一样的。

然后在这个loopc下会调用row_sel_store_mysql_field_fun,然后调用row_sel_field_store_in_mysql_format_func将字段一一转换为MySQL格式。让我们来看看可以进行如下类型转换:

 case DATA_INT: /* 将 Innobase 中的整数数据转换为小端格式,符号位恢复为正常 */ ptr = dest + len; for (;;) { ptr--; *ptr = *data;//值复制内存复制 if (ptr == dest) { break;与数据++; }

我们可以发现,这是一个实际的转换,需要内存空间。请参阅最后的堆栈帧 4。
此时我们大概知道,查询的字段越多,这里的转换过程就会越长,而这些都是实际的内存拷贝

最后一行数据会存储在row_search_mvcc的形参缓冲区中,并返回到MySQL层。本正式文件的注释参数如下:

@param[out] buf 缓冲区,用于以 MySQL 格式获取行
6。 where过滤第一条数据(MySQL层)

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

found= MY_TEST(condition->val_int()); //通过调用条件进行比较,并返回记录的比较

如果不符合条件,则返回False。这里的比较最终会调用Item_func的各个方法。如果等于 Item_func_eq,则查看堆栈帧 5 末尾的堆栈帧。

7.访问下一个数据

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

将光标移动到下一行

p>

访问数据

根据数据进行转换到模板并返回给MySQL层

根据where条件进行过滤

整个过程会一直持续到所有主键索引数据访问完成。但需要注意的是,上层界面有一些变化。 ha_innobase::index_first会变成ha_innobase::rnd_next,统计数据也会从Handler_read_first变成Handler_read_rnd_next。这方面你可以参考我的文章:
https://www.jianshu.com/p/25fed8f1f05e
而且row_search_mvcc的流程肯定会发生变化。这里我就不详细说了。但实际的数据转换和过滤过程并没有改变。

注意,除了步骤1之外,这些步骤基本上都是在发送数据下。

3.回到问题本身

好了,到这里了,现在大家大概已经知道全表扫描访问数据的流程了,我们来看看全表扫描在字段数量上的异同表扫描过程:

区别:

构造的read_set不同。字段越多,read_set中'1'的位数就越多

创建的模板不同。字段越多,使用的模板就越多。

每行数据转换成MySQL格式的方式是不同的。字段越多,模板就越多,因此转换每个字段的循环次数就会越大。很多,并且这是针对每一行进行处理的。

返回到MySQL层的行消耗的内存越大

相同点:

访问的行数相同

访问过程相同

其中过滤方式为同样

在整个不同点中,我认为最耗时的部分应该就是将每一行数据转换为MySQL格式的成本,因为每一行的每个字段都需要这样转换,这就是除以发送数据的状态,网上有很多10个以上字段的表,如果我们只需要访问其中的几个字段,最好写实际的字段,而不是'*',以避免这种情况

4.写在最后

虽然文章使用全表扫描是针对列进行解释的,但无论如何我们应该减少访问的字段数量,只访问需要的字段。

5. 备用堆栈帧

堆栈frame 1 read_set build

#0 TABLE::mark_column_used (this=0x7ffe7c996c50, thd=0x7ffe7c000b70, field=0x7ffe7c997c88, mark=MARK_COLUMNS_READ) 位于 /root/mysqlall/percona-server-locks-细节- 5.7.22/sql/table.cc:6344#1 0x00000000015449b4 in find_field_in_table_ref (thd=0x7ffe7c000b70, table_list=0x7ffe7c0071f0, name=0x7ffe7c006a38“id”, length=2, item_ name=0x7ffe7c0 06a38 "id", db_name=0x0, table_name=0x0、ref=0x7ffe7c006bc0、want_privilege=1、allow_rowid=true、cached_field_index_ptr=0x7ffe7c0071a0、register_tree_change=true、actual_table=0x7fffec0f46d8)位于/root/mysqlall/percona-server-locks-detail-5.7.22/sql/sql_base。 cc:7730#2 0x00000000015 44efc in find_field_in_tables (thd=0x7ffe7c000b70、item=0x7ffe7c0070c8、first_table=0x7ffe7c0071f0、last_table=0x0、ref=0x7ffe7c006bc0、report_error=IGNORE_ EXCEPT_NON_UNIQUE、want_privilege=1、register_tree_change=true)位于 /root/mysqlall/percona- server-locks-detail-5.7.22 /sql/sql_base.cc:7914 #3 0x0000000000faadd8 在 Item_field::fix/root/mysqlall/percona-server-locks-detail-5.7.22/s ql/item.cc:5857#4 0x00000000015478ee in setup_fields (thd= 0x7ffe7c000b70, ref_pointer_array=...、fields=...、want_privilege=1、sum_func_list=0x7ffe7c005d90、allow_sum_func=true、column_update=false)位于/root/mysqlall/percona-server-locks-detail-5.7.22/sql/sql_base。 cc:9047#5 0x000000000161 419d in st_select_lex::prepare ( this=0x7ffe7c005c30, thd=0x7ffe7c000b70) 位于 /root/mysqlall/percona-server-locks-detail-5.7.22/sql/sql_resolver.cc:190

堆栈帧2构造模板

#0 build_template_field (prebuilt=0x7ffe7c99b880, clust_index=0x7ffe7c999c20, index=0x7ffe7c999c20, table=0x7ffe7c996c50, field=0x7ffe7c997c88, i=0, v_no=0) 在 /root/mysqlall/percona-server -locks-detail-5.7.22/storage/innobase/handler/ha_innodb.cc:7571#1 0x00000000019d1dc1 在 ha_innobase::build_template (this=0x7ffe7c997610,whole_row=false)在/root/mysqlall/percona-server -locks-detail -5.7.22/storage/innobase/handler/ha_innodb.cc:8034#2 0x00000000019d60f5 在 ha_innobase::change_active_index (this=0x7ffe7c997610, keynr=0) 位于 /root/mysqlall/ percona-server-locks-detail-5.7.22/storage/innobase/handler/ha_innodb.cc:9805#3 0x00000000019d682b in ha_innobase::rnd_init (this=0x7ffe7c997610, scan=true) at /root/mysqlall/percona-server -locks-detail-5.7.22/storage/innobase/handler/ha_innodb。 cc:10031#4 0x0000000000f833b9 in handler::ha_rnd_init (this=0x7ffe7c997610, scan=true) 在 /root/mysqlall/percona-server-locks-detail-5.7.22/sql/handler.cc:3096#5 0x000 00000014e24d1 中init_read_record (info=0x7ffe7cf47d60、thd=0x7ffe7c000b70、table=0x7ffe7c996c50、qep_tab=0x7ffe7cf47d10、use_record_cache=1、print_error=true、disable_rr_cache=false)位于/root/mysqlall/percona-server-locks-detail-5.7.22/ sql/ Records.cc:315

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

#0 page_cur_set_before_first (block=0x7fff4d02f4a0, cur=0x7ffe7c99bab0) at /root/mysqlall/percona-server-locks-detail-5.7.22/storage/innobase/include/page0cur.ic:99#1 0x0000000001c5187 f in btr_cur_open_at_index_side_func ( from_left=true,索引=0x7ffe7c999c20,latch_mode=1,光标=0x7ffe7c99baa8,级别=0,文件=0x239d388“/root/mysqlall/percona-server-locks-detail-5.7.22/storage/innobase/include/btr0pcur.ic”,行= 562,mtr=0x7fffec0f3570)位于/root/mysqlall/percona-server-locks-detail-5.7.22/storage/innobase/btr/btr0cur.cc:2422#2 0x0000000001b6e9c9在btr_pcur_open_at_index_side(from_left=true,索引=0x7ffe7c99 9c20,锁存模式=1、pcur=0x7ffe7c99baa8、init_pcur=false、level=0、mtr=0x7fffec0f3570) 位于 /root/mysqlall/percona-server-locks-detail-5.7.22/storage/innobase/include/btr0pcur.ic:562#3 row_search_mvcc 中的 0x0000000001b79a35 (buf=0x7 ffe7c997b50“\ 377”,模式=PAGE_CUR_G,预建=0x7ffe7c99b880,match_mode=0,方向=0)位于/root/mysqlall/percona-server-locks-detail-5.7.22/storage/innobase /行/row0sel.cc:5213#4 0x00000000019d5493 in ha_innobase::index_read (this=0x7ffe7c997610, buf=0x7ffe7c997b50 "\377", key_ptr=0x0, key_len=0, find_flag=HA_READ_AFTER_KEY) 位于 /root/mysqla ll/percona -服务器锁-detail-5.7.22/storage /innobase/handler/ha_innodb.cc:9536#5 0x00000000019d66ea 在 ha_innobase::index_first (this=0x7ffe7c997610, buf=0x7ffe7c997b50 "\377") 位于 /root/mysqlall/percona-server-locks -detail-5.7.22/storage/innobase/handler/ha_innodb.cc:9977#6 0x00000000019d6934 在 ha_innobase::rnd_next (this=0x7ffe7c997610, buf=0x7ffe7c997b50 "\377") 位于 /root/ mysqlall/percona-server-锁-detail-5.7.22/storage/innobase/handler/ha_innodb.cc:10075#7 0x0000000000f83725 位于 handler::ha_rnd_next (this=0x7ffe7c997610, buf=0x7ffe7c997b50 "\377") 处 /root/mysqlall/percona-server-lock s -detail-5.7.22/sql/handler.cc:3146#8 rr_sequential (info=0x7ffe7cf47d60) 中的 0x00000000014e2b3d 在 /root/mysqlall/percona-server-locks-detail-5.7.22/sql /records.cc:521< /pre>

堆栈帧 4 MySQL 格式转换sion

#0 row_sel_field_store_in_mysql_format_func (dest=0x7ffe7c997b51 "", templ=0x7ffe7c9a27f8, index=0x7ffe7c999c20, field_no= 0, data=0x7fff4daec0a1 "\200", len=4, prebuilt=0x7ffe7 c99b880,sec_field = 18446744073709551615)在/root/mysqlall/percona-server-locks-detail-5.7.22/storage/innobase/row/row0sel.cc:2888#1 0x0000000001b754b9 in row_sel_store_mysql_field_func (mysql_rec=0x7ffe7c997b50“\377”,预建=0x7ffe7c99b8 80、记录=0x7fff4daec0a1 “\200”,索引=0x7ffe7c99 9c20,偏移量=0x7fffec0f3a80,field_no=0,templ=0x7ffe7c9a27f8,sec_field_no=18446744073709551615)位于/root/mysqlall/percona-server-locks-detail-5.7.22/storage/in nobase/行/ row0sel.cc:3255#2 row_sel_store_mysql_rec中的0x0000000001b75c85(mysql_rec = 0x7ffe7c997b50“\ 377”,pre = 0x7ffe7builtc 99b880,rec = 0x7fff4daec0a1“\ 200”,vrow = 0x0,rec_clust = 0,索引=0x7ffe7c999c20,偏移量=0x7fffec0f3a80,clust_templ_for_sec=假)位于 /root/mysqlall/percona-server-locks-detail-5.7.22/storage/innobase/row/row0sel.cc:3434#3 0x0000000001b7bd61 in row_search_mvcc (buf =0x7ffe7c997b50 "\ 377", mode=PAGE_CUR_G, prebuilt=0x7ffe7c99b880, match_mode=0, Direction=0) 位于 /root/mysqlall/percona-server-locks-detail-5.7.22/storage/ innobase/row/row0sel.cc:6123#4 ha_innobase::index_read 中的 0x00000000019d5493 (this=0x7ffe7c997610, buf=0x7ffe7c997b50 "\377", key_ptr=0x0, key_len=0, find_flag=HA_READ_AFTER_KEY) 位于 /root/ mysqla ll/percona -server-locks-detail-5.7.22/storage /innobase/handler/ha_innodb.cc:9536#5 0x00000000019d66ea in ha_innobase::index_first (this=0x7ffe7c997610, buf=0x7ffe7c997b50 "\377") at /root/mysqlall/percona -server-locks-detail-5.7.22/storage/innobase/handler/ha_innodb.cc:9977#6 0x00000000019d6934 在 ha_innobase::rnd_next (this=0x7ffe7c997610, buf=0x7ffe7c997b50 "\377 ") 位于 /root/mysqlall/ percon一个-Server-locks-detail-5.7.22/storage/innobase/handler/ha_innodb.cc:10075#7 0x0000000000000000f83725在处理程序中-服务器锁-detail-5.7.22/sql/handler.cc:3146#8 rr_sequential (info=0x7ffe7cf47d60) 中的 0x00000000014e2b3d 在 /root/mysqlall/percona-server-locks-detail-5.7.22/sql/records .cc:521# 9 0x0000000001584264 in join_init_read_record (tab=0x7ffe7cf47d10) at /root/mysqlall/percona-server-locks-detail-5.7.22/sql/sql_executor.cc:2487#10 0x00000000 01581349 在 sub_select (join=0x7ffe7cf 47660, qep_tab=0x7ffe7cf47d10, 记录结束= false)在/root/mysqlall/percona-server-locks-detail-5.7.22/sql/sql_executor.cc:1277#11 0x0000000001580cce在do_select(join=0x7ffe7cf47660)在/root/mysqlall/percona-server-locks- detail-5.7.22/sql/sql_executor.cc:950

堆栈帧5 String的相等性比较

#0 Arg_comparator: :compare_string (this=0x7ffe7c0072f0) at /root/mysqlall/percona-server-locks-detail-5.7.22/sql/item_cmpfunc.cc:1669#1 0x0000000000fde1e4 在 Arg_comparator::compare (this=0x7ffe7c0072 f0) 位于 /root/mysqlall /percona-server-locks-细节-5.7.22/sql/item_cmpfunc.h:92#2 0x0000000000/root/mysqlall/percona-server-locks-detail-5.7 中 Item_func_eq::val_int (this=0x7ffe7c007218) 中的 fcb0a1。 22 /sql/item_cmpfunc.cc:2507#3 0x0000000001581af9 在evaluate_join_record (join=0x7ffe7c0077d8, qep_tab=0x7ffe7cb1dc70) 位于/root/mysqlall/percona-server-locks-detail-5.7.22/sql/sql_executor.cc:1492 #4 0x000000000158145a 位于 /Root/mysqlall/percona- Server-detail-5.7.22/sql/Executor.cc:1297 #5 0x000000000158 的 sub_select (join=0x7ffe7c0077d8 、 qep_tab=0x7ffe7cb1dc70、end_of_records=false ) do_select 中的 0cce(加入 = 0x777777777D8) /root/root/root/root/root/root/root/mysqlall/Percona-Server-Locks-Detail-5.7。 22/sql/sql_executor.cc:950#6 0x000000000157eb8a 在 JOIN::exec (this=0x7ffe7c0077d8) 位于 /root/mysqlall/percona-server-locks-detail-5.7.22/sql/sql_executor.cc:199

这篇关于《MySQL中查询字段数量会影响查询效率》的文章就分享到这里。希望以上内容能够对大家有所帮助,方便大家学习n 更多 长知识了,如果您觉得文章不错,请转发,让更多的人看到。

1. 本站所有资源来源于用户上传或网络,仅作为参考研究使用,如有侵权请邮件联系站长!
2. 本站积分货币获取途径以及用途的解读,想在本站混的好,请务必认真阅读!
3. 本站强烈打击盗版/破解等有损他人权益和违法作为,请各位会员支持正版!
4. 编程技术 > MySQL中查询字段的数量会影响查询效率

用户评论