MySQL内核技术之“聚合操作”

本文可以结合我的上一篇文章“结果发送”结合来看。因为上一篇文章着重针对COUNT操作的结果发送,文本继续以COUNT操作为例阐述MySQL的聚合操作。

简要来说调用关系:

JOIN::optimize-->
JOIN::make_join_plan()-->
JOIN::estimate_rowcount()-->
add_group_and_distinct_keys()-->
is_indexed_agg_distinct()-->
JOIN::make_sum_func_list()

make_sum_func_list函数如下:

bool JOIN::make_sum_func_list(List<Item> &field_list,
                              List<Item> &send_result_set_metadata,
                  bool before_group_by, bool recompute)
{
  List_iterator_fast<Item> it(field_list);
  Item_sum **func;
  Item *item;
  DBUG_ENTER("make_sum_func_list");

  if (*sum_funcs && !recompute)
    DBUG_RETURN(FALSE); /* We have already initialized sum_funcs. */

  func= sum_funcs;
  while ((item=it++))
  {
    if (item->type() == Item::SUM_FUNC_ITEM && !item->const_item() &&
        (!((Item_sum*) item)->depended_from() ||
         ((Item_sum *)item)->depended_from() == select_lex))
      *func++= (Item_sum*) item;
  }
  if (before_group_by && rollup.state == ROLLUP::STATE_INITED)
  {
    rollup.state= ROLLUP::STATE_READY;
    if (rollup_make_fields(field_list, send_result_set_metadata, &func))
      DBUG_RETURN(TRUE);            // Should never happen
  }
  else if (rollup.state == ROLLUP::STATE_NONE)
  {
    for (uint i=0 ; i <= send_group_parts ;i++)
      sum_funcs_end[i]= func;
  }
  else if (rollup.state == ROLLUP::STATE_READY)
    DBUG_RETURN(FALSE);                         // Don't put end marker
  *func=0;                    // End marker
  DBUG_RETURN(FALSE);
}

其中,*func++= (Item_sum*) item是赋值操作,即把对应的item直接转化成item_sum类赋值给func,即JOIN的sum_funcs(func= sum_funcs)。

这里的item是传入的field_list(join->all_fields),这个field_list是在yacc parse而来,即yacc.cc parse的时候就new出了对应的item_sum_count。

对MySQL的改动如何来做

因为我们加入了新的线程来并发执行,所以会在optimizer里new出新的JOIN。从JOIN的constructor可以看到:all_fields(select->all_fields),是直接通过select赋值的。如果新的线程对应的JOIN不改动的话,默认就要共享all_fields这个变量,肯定是不行的。

因此我们在创建JOIN后,要重置对应的sum_funcs和sum_funcs_end。我们可以参照make_sum_func_list的方法进行重置:

  item_sum** func;
  func= sum_funcs;
  while ((item=it++))
  {
    if (item->type() == Item::SUM_FUNC_ITEM && !item->const_item() &&
        (!((Item_sum*) item)->depended_from() ||
         ((Item_sum *)item)->depended_from() == select_lex))
      *func++= (Item_sum*) item;
  }

使用item_sum_count自带的copy_or_same函数,来创建新的sum_funcs。同时也要reset sum_funcs_end:

sum_funcs_end= (Item_sum***) (sum_funcs + func_count + 1);

还有一点需要注意的是,在end_send_group中,如果结果为0会有如下调用,其中的no_rows_in_result()原代码用的是fields来调用,这样会把别的线程结果同时清零。我们需要把这里进行改动

    // Calculate aggregate functions for no rows
    List_iterator_fast<Item> it(*fields);
    Item *item;

    while ((item= it++))
      item->no_rows_in_result();    

类型判断

很多时候我们要判断聚合函数的类型。那么首先我们要先判断当前操作是否是聚合操作,然后再判断聚合操作的类型。

因为

Item类有一个纯虚函数: virtual enum Type type() const =0; 其对应的Type在该类中定义为:

  enum Type {INVALID_ITEM= 0,
             FIELD_ITEM, FUNC_ITEM, SUM_FUNC_ITEM, STRING_ITEM,
         INT_ITEM, REAL_ITEM, NULL_ITEM, VARBIN_ITEM,
         COPY_STR_ITEM, FIELD_AVG_ITEM, DEFAULT_VALUE_ITEM,
         PROC_ITEM,COND_ITEM, REF_ITEM, FIELD_STD_ITEM,
         FIELD_VARIANCE_ITEM, INSERT_VALUE_ITEM,
             SUBSELECT_ITEM, ROW_ITEM, CACHE_ITEM, TYPE_HOLDER,
             PARAM_ITEM, TRIGGER_FIELD_ITEM, DECIMAL_ITEM,
             XPATH_NODESET, XPATH_NODESET_CMP,
             VIEW_FIXER_ITEM};

进一步,Item_sum从Item派生出来,除了type()函数外,他还带有一个函数virtual enum Sumfunctype sum_func () const=0;。 在Item_sum中定义了Sumfunctype的类型:

  enum Sumfunctype
  { COUNT_FUNC, COUNT_DISTINCT_FUNC, SUM_FUNC, SUM_DISTINCT_FUNC, AVG_FUNC,
    AVG_DISTINCT_FUNC, MIN_FUNC, MAX_FUNC, STD_FUNC,
    VARIANCE_FUNC, SUM_BIT_FUNC, UDF_SUM_FUNC, GROUP_CONCAT_FUNC
  };

我们可以通过这些类型来判读当前具体的聚合操作。初次之外,我们还可以利用lex的一些helper函数来进行辅助判断,如is_single_grouped():

  /**
    @return true if this query block is implicitly grouped and returns exactly
    one row, which happens when it does not have a HAVING clause.
  */
  bool is_single_grouped() const
  {
    return m_agg_func_used &&
           group_list.elements == 0 &&
           m_having_cond == NULL;
  }

该函数返回true的条件是没有group by,也没有have。

    原文作者:Michael
    原文地址: https://segmentfault.com/a/1190000011564739
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞