MySQL内核技术之“结果发送”
本节我们主要讨论COUNT的结果处理与发送。典型的SQL语句为:SELECT COUNT(*) FROM foo;
查询结果处理流程
结果累加调用链
JOIN::exec()--> do_select()--> sub_select()--> evaluate_join_record()--> end_send_group()--> init_sum_functions--> reset_and_add()--> aggregator_add()--> Item_sum_count::add()
返回结果调用链
JOIN::exec()--> do_select()--> sub_select()--> evaluate_join_record()--> end_send_group()--> send_data--> send_result_set_row()--> Item::send()--> Item_sum_count::val_int()
注意:上面的end_send_group
实际上是evaluate_join_record()
中的这行代码:rc= (*qep_tab->next_select)(join, qep_tab+1, 0);
整个逻辑是sub_select()
中有个大循环,每次读出一条数据,然后进行evaluate_join_record()
。在evaluate_join_record()
中进行where
判断等,然后调用end_send
或者end_send_group
进行最后处理。因为这里是COUNT
因此,使用end_send_group()
。end_send_group
调用reset_and_add
进行累加。
发送完毕调用链(这个执行完客户端就有数据显示了)
dispatch_command()--> thd->send_statement_status()
多线程并发执行改造
现在我们来看看如何多结果处理流程进行改造。从上面分析可以看出,每次读出一条数据就要进入evaluate,调用end_send_group产看是否应该累加还是累加已经完毕。因此我们要在end_send_group
加入新的逻辑,累加过程不变,但是累加完毕后,并不发送而是把结果放入select->m_parallel_results
里,同时把done位置为true
而主线程要查看worker是否都完成了,即把结果放入select->m_parallel_results,如果是则进行所有的结果累加,还是加到item_sum中的count上。然后走正常的流程。
附录
MySQL的COUNT操作对应了Item_sum_count
类,这个类是基于Item_sum
类的。那么对应一个JOIN结构而言,里面包含了一个(其实是2个)Item_sum:分别是。Item_sum_count就被赋给了Item_sum这个成员变量。
执行的累加结果存在Item_sum_count中的count变量里,结束后被返回给客户端。因为没有GROUP BY,所以返回的结果只有一条记录。
改造的方法:因为一个JOIN对应了query的上下文,优化后的结果,如果同时被不同的thread执行,那么应该有多个count结果,而不是一个,这样我们就可以增加一个标志位为parallel,同时增加Item_sum_count中的member:count数组,其数量对应了多少个thread。每个thread执行的结果放在这对应的count中,执行完毕后worker thread退出,而由主thread统计结果并发送最后的count给client。