首页 > 数据库 >MySQL 聚合函数初探

MySQL 聚合函数初探

时间:2022-12-11 10:02:11浏览次数:55  
标签:聚合 file Aggregator sum MySQL tree FUNC 初探 table

MySQL 提供了许多聚合函数,常见的如sum,avg,count,min,max等。

那这些聚合函数在MySQL 底层是怎么实现的?

聚合函数(Aggregate Function)实现的大部分代码在item_sum.h和item_sum.cc。

聚合函数在代码中具体的枚举如下:

enum Sumfunctype {
COUNT_FUNC, // COUNT
COUNT_DISTINCT_FUNC, // COUNT (DISTINCT)
SUM_FUNC, // SUM
SUM_DISTINCT_FUNC, // SUM (DISTINCT)
AVG_FUNC, // AVG
AVG_DISTINCT_FUNC, // AVG (DISTINCT)
MIN_FUNC, // MIN
MAX_FUNC, // MAX
STD_FUNC, // STD/STDDEV/STDDEV_POP
VARIANCE_FUNC, // VARIANCE/VAR_POP and VAR_SAMP
SUM_BIT_FUNC, // BIT_AND, BIT_OR and BIT_XOR
UDF_SUM_FUNC, // user defined functions
GROUP_CONCAT_FUNC, // GROUP_CONCAT
JSON_AGG_FUNC, // JSON_ARRAYAGG and JSON_OBJECTAGG
ROW_NUMBER_FUNC, // Window functions
RANK_FUNC,
DENSE_RANK_FUNC,
CUME_DIST_FUNC,
PERCENT_RANK_FUNC,
NTILE_FUNC,
LEAD_LAG_FUNC,
FIRST_LAST_VALUE_FUNC,
NTH_VALUE_FUNC,
ROLLUP_SUM_SWITCHER_FUNC,
GEOMETRY_AGGREGATE_FUNC
};

本文以下列示例来讲解:

CREATE TABLE test_agg (c1 int NULL)

INSERT into test_agg values(1),(2),(3),(3),(4),(4),(5),(5),(5);

SELECT count(DISTINCT c1) from test_agg;

聚合函数的类设计大概如下

MySQL 聚合函数初探_ide


由上图可以发现MySQL 聚合函数实现是把distinct逻辑抽离出来,变成了aggregator_distinct和aggregator_simple,

服务于继承了Item_sum的所有聚合类。(当然Item_sum本身是继承于Item)

class Aggregator_simple : public Aggregator {
public:
Aggregator_simple(Item_sum *sum) : Aggregator(sum) {}
Aggregator_type Aggrtype() override { return Aggregator::SIMPLE_AGGREGATOR; }
bool setup(THD *thd) override { return item_sum->setup(thd); }
void clear() override { item_sum->clear(); }
bool add() override { return item_sum->add(); }
void endup() override {}
};
class Aggregator_distinct : public Aggregator {
public:
~Aggregator_distinct() override;
Aggregator_type Aggrtype() override { return DISTINCT_AGGREGATOR; }

bool setup(THD *) override;
void clear() override;
bool add() override;
void endup() override;
};

上面是2个类的部分代码,由此我们发现 ​​Aggregator_simple​​ 基本只是个调用wrap,表示非distinct的Item_sum处理,

直接调用的是聚合类的逻辑。

在 MySQL 中要实现聚合函数要有3个重要的步骤:setup, add, endup。

  • setup 在处理之前初始化
  • add 表示每条记录的处理
  • endup 收尾后最后计算聚合的结果。

回到代码

setup 阶段

(Aggregator_distinct::setup 截取部分代码)

if (!(table = create_tmp_table(thd, tmp_table_param, list, nullptr, true,
false, query_block->active_options(),
HA_POS_ERROR, "")))
if (all_binary) {
cmp_arg = (void *)&tree_key_length;
compare_key = simple_raw_key_cmp;
} else {
if (table->s->fields == 1) {
compare_key = simple_str_key_cmp;
cmp_arg = (void *)table->field[0];
} else {
uint32 *length;
compare_key = composite_key_cmp;
....
}
}
tree = new (thd->mem_root) Unique(compare_key, cmp_arg, tree_key_length,
item_sum->ram_limitation(thd));
if (!tree) return true;

由上知 setup 阶段主要做的是创建临时表和 tree ,设置比较函数。

add 阶段

(Aggregator_distinct::add 截取部分代码)

if (tree) {
return tree->unique_add(table->record[0] + table->s->null_bytes);
}
if (!check_unique_constraint(table)) return false;
if ((error = table->file->ha_write_row(table->record[0])) &&
!table->file->is_ignorable_error(error))

这边看到当 tree 存在时,MySQL 把记录加入 tree (实际为红黑树)中来去重复。

如果tree不存在,就用临时表来持久存储。在 ha_write_row 写入临时表之前会 check_unique_constraint 去重。

而 tree 和临时表就是 setup 阶段所创建的。setup 的 tree 是存在,什么时候销毁了呢。看下面:

inline bool unique_add(void *ptr) {
DBUG_TRACE;
DBUG_PRINT("info", ("tree %u - %lu", tree.elements_in_tree, max_elements));
if (tree.elements_in_tree > max_elements && flush()) return true;
return !tree_insert(&tree, ptr, 0, tree.custom_arg);
}
bool Unique::flush() {
Merge_chunk file_ptr;
elements += tree.elements_in_tree;
file_ptr.set_rowcount(tree.elements_in_tree);
file_ptr.set_file_position(my_b_tell(&file));
if (tree_walk(&tree, unique_write_to_file, this, left_root_right) ||
file_ptrs.push_back(file_ptr))
return true; /* purecov: inspected */
delete_tree(&tree);
return false;
}

可以看到MySQL 的策略是维护一颗红黑树这样的数据结构来去重。

当tree的数量过大时,内存放不下,就会flush到磁盘上,采用临时表来持久化,同时销毁tree。

endup 阶段

if (tree && tree->is_in_memory()) {
sum->count = (longlong)tree->elements_in_tree();
endup_done = true;
}
if (!tree) {
table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
if (table->file->ha_table_flags() & HA_STATS_RECORDS_IS_EXACT)
sum->count = table->file->stats.records;
else {
if (table->file->inited) table->file->ha_index_or_rnd_end();
ha_rows num_rows = 0;
table->file->ha_records(&num_rows);
if (table->hash_field) table->file->ha_index_init(0, false);
sum->count = static_cast<longlong>(num_rows);
}
endup_done = true;
}

可以看到最后取结果的时候

如果 tree 存在而且在内存中,直接取 tree 的节点个数。

如果 tree 不存在就取临时表的行数。

标签:聚合,file,Aggregator,sum,MySQL,tree,FUNC,初探,table
From: https://blog.51cto.com/u_12285298/5928103

相关文章

  • MySQL8.0登录提示caching_sha2_password问题解决方法
    背景用​​docker​​构建mysql容器后连接遇到以下问题问题Authenticationplugin'caching_sha2_password'cannotbeloaded:dlopen(/usr/local/mysql/lib/plugin/cachin......
  • 深入理解 MySQL 的事务隔离级别和 MVCC 机制
    前言我们都知道MySQL实现了SQL标准中的四个隔离级别,但是具体是如何实现的可能还一知半解,本篇博客将会从代码层面讲解隔离级别的实现方式,下面进入正题。事务考虑这样......
  • MySQL
    MySQL排序我们知道从MySQL表中使用SQLSELECT语句来读取数据。如果我们需要对读取的数据进行排序,我们就可以使用MySQL的ORDERBY子句来设定你想按哪个字段哪种方......
  • MySQL 5.7中文乱码与远程链接问题
    1.MySQL5.7中文乱码当我们直接在数据库里面输入中文时,保存后出现:Incorrectstringvalue:‘\xE9.....’forcolumn''atrow1出现上面的原因是因为字符编码设置有......
  • 《MySQL必知必会》之快速入门存储过程
    使用存储过程本章介绍什么是存储过程,为什么使用、如何使用,并介绍如何创建和使用存储过程的基本语法存储过程在实际应用中,往往需要执行多个表的多条sql语句存储过程就......
  • mysql约束
    Mysql约束约束用于确保数据库的数据满足特定的商业规则在Mysql中,约束包括:notnull、unique、primarykey、foreignkey和check五种primarykey(主键)的使用(主键列不......
  • Java操作ElasticSearch(五、聚合分桶)
    聚合功能通过SearchSourceBuilder的aggregation(AggregationBuilderaggregation)方法用来构建聚合条件其中要用到的各种聚合如:Term聚合Rang聚合Sum聚合等都......
  • MySQL 5.7中文乱码与远程链接问题
    (MySQL5.7中文乱码与远程链接问题)1.MySQL5.7中文乱码当我们直接在数据库里面输入中文时,保存后出现:Incorrectstringvalue:‘\xE9.....’forcolumn''atrow1出......
  • python连接数据库mysql
    前提:电脑已经安装数据库环境1、导入pymysql库2、建立连接:md=pymysql.connect(host='localhost',user='root',password='数据库密码',db='lili')3、新建游标:cur=md......
  • 力扣175(MySQL)-组合两个表(简单)
    题目:表: Person  表: Address编写一个SQL查询来报告Person表中每个人的姓、名、城市和州。如果personId的地址不在 Address 表中,则报告为空 null 。以......