MySQL 源码|94 - 优化器:临时表配置对象及字段类型统计|V20241020

MySQL源码优化器函数与类剖析

目录文档:MySQL 源码|源码剖析文档目录

涉及内容:

在优化器主函数 JOIN::optimize() 中,多次调用了 count_field_types 函数用于统计字段类型的数量,并将其结果记录在用于临时表配置的 Temp_table_param 类型对象中。在调用 JOIN::optimize() 函数时,提供了 Temp_table_param 类型的临时表配置对象,即 JOIN::tmp_table_param

Temp_table_param

Temp_table_param 对象包括在创建和使用临时表时所用的参数,借助此对象创建的临时表仅供查询执行引擎内部使用。Temp_table_param 类中的主要逻辑源码如下:

class Temp_table_param {
 public:
  Mem_root_array<Copy_field> copy_fields;
  uchar *group_buff;
  Func_ptr_array *items_to_copy; /* Fields in tmp table */
  KEY *keyinfo;
  ha_rows end_write_records{HA_POS_ERROR};
  uint func_count;
  uint sum_func_count;
  uint hidden_field_count;
  uint group_parts, group_length, group_null_parts;
  bool allow_group_via_temp_table{true};
  uint outer_sum_func_count;
  bool using_outer_summary_function;
  CHARSET_INFO *table_charset;
  bool schema_table;
  bool precomputed_group_by;
  bool force_copy_fields;
  bool skip_create_table;
  bool bit_fields_as_long;
  bool can_use_pk_for_unique;
  bool force_hash_field_for_unique{false};
  bool m_window_frame_buffer{false};
  enum {
    TTP_UNION_OR_TABLE,
    TTP_EXCEPT,
    TTP_INTERSECT
  } m_operation{TTP_UNION_OR_TABLE};
  bool needs_set_counter() { return m_operation != TTP_UNION_OR_TABLE; }
  bool m_last_operation_is_distinct{false};
  Window *m_window;
  void cleanup() { copy_fields.clear(); }
};

其中的数据成员包括:

  • copy_fieldsMem_root_array<Copy_field>
  • group_buffuchar *
  • items_to_copyFunc_ptr_array *
  • keyinfoKEY *):在创建临时表之后,指向根据表的用途(如分组、去重等)所创建的表上的索引。这样的索引最多只有一个。
  • end_write_recordsha_rows):临时表的行数上限(最大行数),或者使用 HA_POS_ERROR 表示没有限制。当写入表时,此限制由 MaterializeIterator 强制执行。
  • func_countuint):查询中的项目数量,包括聚合函数(如 SUM)、非聚合函数(如 RAND)、窗口函数以及字段。 同时也会计算在窗口函数或聚合函数中引用的函数,例如 SELECT SUM(RAND()) 的计数为 2。由 count_field_types 函数设置。
  • sum_func_countuint):查询中具有聚合函数的字段数量。需要注意的是,优化器可能会选择通过将这些字段替换为常量来优化掉它们,在这种情况下,sum_func_count 需要被更新。由 optimize_aggregated_query 函数和 count_field_types 函数设置。
  • hidden_field_countuint
  • group_partsuint
  • group_lengthuint
  • group_null_partsuint
  • allow_group_via_temp_tablebool):是否允许运行 GROUP BY 并写入到临时表中,即同时进行多种聚合操作而不需要有序的输入。这通常是允许的,但目前不支持用于聚合 UDF、带有 DISTINCT 的聚合函数或 ROLLUP。需要注意的是,即使这种情况是允许的,优化器也可能选择不使用临时表,因为直接读取索引会更加高效。
  • outer_sum_func_countuint):外部聚合函数的数量,即在这个子查询外部的查询块中被聚合的集合函数的数量。由 count_field_types 函数设置。外部聚合函数,即在当前子查询之外的更高层级的查询结构中执行的聚合操作。
  • using_outer_summary_functionuint):当至少有一个外部聚合函数时启用,当使用 DISTINCT 时需要。由 create_tmp_table 函数设置。
  • table_charsetCHARSET_INFO *
  • schema_tablebool
  • precomputed_group_bybool):如果 GROUP BY 及其聚合函数已经通过某种表访问方法(例如通过 loose index scan)计算完毕,则此值为真。在这种情况下,查询执行不应再进行聚合操作,并应将聚合函数视为普通函数处理。
  • force_copy_fieldsbool
  • skip_create_tablebool):如果为真,则在创建结果表时实际上不创建表处理器。这允许范围优化器之后添加索引。用于物化派生表 / 视图。由 Table_ref::update_derived_keys 函数设置。
  • bit_fields_as_longbool):如果为真,则在 create_tmp_table 中调用 create_tmp_field,将所有 BIT 字段转换为 64 位长整型。这是针对 MEMORY 表无法对 BIT 列建立索引这一限制的解决方法。
  • can_use_pk_for_uniquebool):唯一索引是否可以提升为主键。
  • force_hash_field_for_uniquebool):是否应始终通过隐藏的哈希字段来实现唯一键,而不是使用唯一索引。这对于混合 UNION ALLUNIONDISTINCT 的查询来说是必须的,详见 create_result_table 函数。
  • m_window_frame_bufferbool):这个临时表是否用于窗口的帧缓冲。
  • m_operation(枚举值):用于 INTERSECTEXCEPT 计算。
  • m_last_operation_is_distinctbool):用于 INTERSECTEXCEPT 计算。
  • m_windowWindow *):如果是某个窗口的输出表,则指向窗口。

其中的函数成员包括:

  • needs_set_counter():返回是否需要设置一个计数器来统计重复项(用于 EXCEPT 和 INTERSECT 计算)。
  • cleanup():清空 copy_fields 数据成员。
count_field_types 函数

count_field_types 函数用于计算字段、函数以及求和函数(类型为 SUM_FUNC_ITEM 的项)的数量,以便被 create_tmp_table() 使用,并将这些数量存储在第 2 个参数 paramTemp_table_param 类型对象中。如果需要的话,它还会更新 allow_group_via_temp_table 的属性。

count_field_types 函数的原型如下:

// sql/sql_select.h
void count_field_types(const Query_block *query_block, Temp_table_param *param,
                       const mem_root_deque<Item *> &fields,
                       bool reset_with_sum_func, bool save_sum_fields);

count_field_types 函数中的参数含义如下:

  • query_blockQuery_block *):查询的 Query_block
  • paramTemp_table_param *):临时表的参数
  • fieldsmem_root_deque<Item *> &):需要用于统计的字段的列表
  • reset_with_sum_funcbool):是否需要重置函数的 with_sum_func
  • save_sum_fieldsbool):是否在接受到相同的参数时,按 create_tmp_table() 函数预期的方式进行统计,即在字段列表中保留 Item_sum_* 函数

count_field_types 函数中的逻辑如下:

Step 1|初始化 Temp_table_param 中数据成员:将 sum_func_count(查询中具有聚合函数的字段数量)置为 0,将 func_count(查询中的项目数量)置为 fields 参数的数量,将 hidden_field_count 置为 0,将 outer_sum_func_count(外部聚合函数的数量)置为 0。

param->sum_func_count = 0;
param->func_count = fields.size();
param->hidden_field_count = 0;
param->outer_sum_func_count = 0;

Step 2|如果 GROUP BY 及其聚合函数已经通过松散索引扫描并计算完毕,则 param->precomputed_group_bytrue,此时将 save_sum_fields 置为 true

save_sum_fields |= param->precomputed_group_by;

Step 3|遍历每个字段以统计字段信息,具体地,对于每个字段执行以下逻辑:

  • 如果是非窗口函数的聚集函数:real_type == Item::SUM_FUNC_ITEM && !real->m_is_window_function
    • 如果当前字段不是常量(!field->const_item()
      • 如果聚集函数(sum_item)不允许通过临时表聚集,则将 Temp_table_param 也置为不允许通过临时表聚集
      • 累加查询中聚集函数的字段数量(sum_func_count
      • 使用聚集函数中的参数数量(sum_item->argument_count()),累加查询中的项目数量(file_count
    • 如果当前字段是常量,且需要在接受到相同的参数时,按 create_tmp_table() 函数预期的方式进行统计(save_sum_fields),即在字段列表中保留 Item_sum_* 函数
      • 累加查询中的项目数量(func_count
      • 累加查询中聚集函数的字段数量(sum_func_count
  • 如果是窗口函数的聚集函数:real_type == Item::SUM_FUNC_ITEM && real->m_is_window_function
    • 使用窗口函数中的参数数量(window_item->argument_count()),累加查询中的项目数量(file_count
  • 如果不是聚集函数:
    • 如果需要重置函数的 with_sum_func,则调用 field->reset_aggregation() 函数
    • 如果当前字段是聚集字段,则累加外部聚合函数的数量(outer_sum_func_count
for (Item *field : fields) {
  Item *real = field->real_item();
  Item::Type real_type = real->type();

  if (real_type == Item::SUM_FUNC_ITEM && !real->m_is_window_function) {
    if (!field->const_item()) {
      Item_sum *sum_item = down_cast<Item_sum *>(field->real_item());
      if (sum_item->aggr_query_block == query_block) {
        if (!sum_item->allow_group_via_temp_table)
          param->allow_group_via_temp_table = false;  // UDF SUM function
        param->sum_func_count++;
        param->func_count += sum_item->argument_count();
      }
    } else if (save_sum_fields) {
      if (field->type() != Item::SUM_FUNC_ITEM) {
        param->func_count++;  // TODO: Is this really needed?
        param->sum_func_count++;
      }
    }
  } else if (real_type == Item::SUM_FUNC_ITEM) {
    Item_sum *window_item = down_cast<Item_sum *>(real);
    param->func_count += window_item->argument_count();
  } else {
    if (reset_with_sum_func) field->reset_aggregation();
    if (field->has_aggregation()) param->outer_sum_func_count++;
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

长行

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值