CTE查询数据量过大导致MySQL 8.0发生CORE问题解析

SQL如何成为数据科学家的核心数据操作系统 SQL不仅是关系型数据库的查询语言,更是一种面向集合、声明式的数据处理范式。其核心原理在于逻辑执行顺序(WHERE→GROUP BY→WINDOW→ORDER BY)、类型安全契约与集合运算思维。相比Python中冗长的pandas链式操作,SQL能以更简洁、可审计、可版本化的方式完成特征构造、分桶统计与数据质量校验,显著提升分析效率与结果可信度。在真实业务场景中,熟练运用CTE模块化、窗口函数动态排名、COALESCE防御性填充及SQL-Python协同编排,已成为数据分析师突破‘取数民工’瓶颈、实现端到 阅读详情

CTE查询数据量过大导致MySQL 8.0发生CORE问题解析

一、问题发现

在客户现场的一次问题报告中发现某个带有CTE语句进行查询的时候,数据量少的时候没问题,但是数据量大的时候会导致core。注意:这个问题只在 MySQL 8.0.32 版本才会复现,最新的8.4.4版本没有问题。

看下面例子:

1、准备表

CREATE TABLE t1 (
  ORG_ID decimal(12,0) NOT NULL,
  ORG_NAME varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin NOT NULL,
  ORG_CODE varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin NOT NULL,
  ORG_TYPE varchar(4) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin DEFAULT (_utf8mb4'01'),
  ORG_LEVEL varchar(4) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin DEFAULT NULL,
  PORG_ID decimal(12,0) DEFAULT NULL,
  ORG_STATE varchar(4) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin NOT NULL,
  COMMENTS varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin DEFAULT NULL,
  MANAGER_FLAG varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin DEFAULT NULL,
  SOET_WEEKLY varchar(12) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin DEFAULT NULL,
  CONTACT_NAME varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin DEFAULT NULL,
  CONTACT_TELEPHONE decimal(32,0) DEFAULT NULL,
  OTHER_TELEPHONE decimal(32,0) DEFAULT NULL,
  OFFICE_ADDR varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin DEFAULT NULL,
  PROVINCE varchar(3) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin DEFAULT NULL,
  CITY varchar(4) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin DEFAULT NULL,
  COUNTRY varchar(6) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin DEFAULT NULL,
  ORG_CLASS varchar(2) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin DEFAULT NULL,
  UPDATE_TIME datetime DEFAULT NULL,
  OPERATE_TYPE varchar(3) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin DEFAULT NULL,
  CREATE_TIME datetime DEFAULT NULL,
  KEY IDX_t1 (ORG_ID,PORG_ID),
  KEY INX_ESOP_ORG_OID (ORG_ID),
  KEY IDX_PROVINCE_CITY (PROVINCE,CITY)
);
INSERT INTO t1 VALUES(29100709,'数据库数据知识数据库数据知识数据库数据知识数据库数据知识数据库数据知识数据库数据知识数据库数据知识数据库数据知识数据库数据知识数据库数据知识',29100709,01,5,291007,1,NULL,NULL,NULL,NULL,NULL,NULL,NULL,290,9150,915009,02,'2018-02-09 04:19:00',1,'2017-11-15 00:00:00');
INSERT INTO t1 VALUES(29100708,'数据库数据知识数据库数据知识数据库数据知识数据库数据知识数据库数据知识数据库数据知识数据库数据知识数据库数据知识数据库数据知识数据库数据知识',29100709,01,5,291007,1,NULL,NULL,NULL,NULL,NULL,NULL,NULL,290,9150,915009,02,'2018-02-09 04:19:00',1,'2017-11-15 00:00:00');
INSERT INTO t1 VALUES(29100707,'数据库数据知识数据库数据知识数据库数据知识数据库数据知识数据库数据知识数据库数据知识数据库数据知识数据库数据知识数据库数据知识数据库数据知识',29100709,01,5,291007,1,NULL,NULL,NULL,NULL,NULL,NULL,NULL,290,9150,915009,02,'2018-02-09 04:19:00',1,'2017-11-15 00:00:00');
INSERT INTO t1 SELECT * FROM t1;
INSERT INTO t1 SELECT * FROM t1;

CREATE TABLE t2 (
  USER_ID decimal(14,0) NOT NULL,
  USER_NAME varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin NOT NULL,
  NICK_NAME varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin DEFAULT NULL,
  USER_CODE varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin NOT NULL,
  PASS_WORD varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin DEFAULT NULL,
  ORG_ID decimal(12,0) DEFAULT NULL,
  POSITION varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin DEFAULT NULL,
  FUNCTIONS varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin DEFAULT NULL,
  MOBILE_NO varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin DEFAULT NULL,
  EXTENSION_NO varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin DEFAULT NULL,
  EMAIL varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin DEFAULT NULL,
  STATE varchar(4) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin DEFAULT NULL,
  COMMENTS varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin DEFAULT NULL,
  SCORE decimal(10,0) DEFAULT NULL,
  IDCARD varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin DEFAULT NULL,
  AUDIT_STATE decimal(1,0) DEFAULT NULL,
  UPDATE_TIME datetime DEFAULT NULL,
  OPERATE_TYPE varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin DEFAULT NULL,
  MAPPINGCODE varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin DEFAULT NULL,
  CREATE_TIME datetime DEFAULT NULL,
  KEY IDX_t2_ID (USER_ID,USER_CODE),
  KEY INX_EUS_MCOD (MAPPINGCODE),
  KEY INX_EUERS_UNM (USER_NAME),
  KEY IDX_t2_MOBILE_NO (MOBILE_NO),
  KEY INX_ESOP_USR_ID (USER_ID),
  KEY IDX_t2_USER_CODE (USER_CODE),
  KEY IDX_ORG_ID (ORG_ID)
);
INSERT INTO t2(USER_ID, user_name, user_code, org_id) VALUES(1001, 'daizhong', 'daizhong', 1);
INSERT INTO t2(USER_ID, user_name, user_code, org_id) VALUES(1002, 'daizhong1', 'daizhong1', 29100709);
INSERT INTO t2(USER_ID, user_name, user_code, org_id) VALUES(1003, 'daizhong1', 'daizhong1', 29100708);
INSERT INTO t2(USER_ID, user_name, user_code, org_id) VALUES(1003, 'daizhong1', 'daizhong1', 29100707);
INSERT INTO t2 SELECT * FROM t2;
INSERT INTO t2 SELECT * FROM t2;

2、tmp_table_size为默认值场合

如下所示用默认tmp_table_size并且进行CTE派生表查询,可以发现结果正常显示一条数据,符合预期。

-- tmp_table_size参数为默认值的情况
greatsql> SHOW variables LIKE '%tmp_table_size%';
+----------------+----------+
| Variable_name  | Value    |
+----------------+----------+
| tmp_table_size | 16777216 |
+----------------+----------+

greatsql> WITH RECURSIVE cte_tree AS ( SELECT 1 AS LEVEL, t.org_id,t.org_id as porg_id,user_name, nick_name, user_code FROM t2 t UNION ALL SELECT LEVEL + 1 AS LEVEL, pt.org_id,pt.porg_id,pt.ORG_NAME,pt.CONTACT_NAME,pt.ORG_CODE FROM t1 pt JOIN cte_tree ct ON pt.org_id = ct.porg_id )  SELECT a.* FROM cte_tree a limit 1;
+-------+--------+---------+-----------+-----------+-----------+
| LEVEL | org_id | porg_id | user_name | nick_name | user_code |
+-------+--------+---------+-----------+-----------+-----------+
|     1 |      1 |       1 | daizhong  | NULL      | daizhong  |
+-------+--------+---------+-----------+-----------+-----------+

3、tmp_table_size修改小的场合

为了不让之前的临时表影响RAM内存的阈值,重新启动数据库。然后执行以下命令。

-- 设置tmp_table_size参数值比临时表数据大的情况
greatsql> SET tmp_table_size=1342;

-- 以下同样的命令导致core
greatsql> WITH RECURSIVE cte_tree AS ( SELECT 1 AS LEVEL, t.org_id,t.org_id as porg_id,user_name, nick_name, user_code FROM t2 t UNION ALL SELECT LEVEL + 1 AS LEVEL, pt.org_id,pt.porg_id,pt.ORG_NAME,pt.CONTACT_NAME,pt.ORG_CODE FROM t1 pt JOIN cte_tree ct ON pt.org_id = ct.porg_id )  SELECT a.* FROM cte_tree a limit 1;

core堆栈如下:
Thread 56 "mysqld" received signal SIGABRT, Aborted.
__GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
50        ../sysdeps/unix/sysv/linux/raise.c: No such file or directory.
(gdb) bt
#0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
#1  0x00007ffff7508859 in __GI_abort () at abort.c:79
#2  0x00007ffff7508729 in __assert_fail_base (
    fmt=0x7ffff769e588 "%s%s%s:%u: %s%sAssertion `%s' failed.\n%n", 
    assertion=0x55555dd91569 "m_opened_table != nullptr", 
    file=0x55555dd91510 "storage/temptable/include/temptable/handler.h", line=563, function=<optimized out>) at assert.c:94
#3  0x00007ffff7519fd6 in __GI___assert_fail (
    assertion=0x55555dd91569 "m_opened_table != nullptr", 
    file=0x55555dd91510 "storage/temptable/include/temptable/handler.h", line=563, 
    function=0x55555dd914d8 "void temptable::Handler::opened_table_validate()")
    at assert.c:103
#4  0x000055555bb98e96 in temptable::Handler::opened_table_validate (this=0x7fff2020f7e8)
    at storage/temptable/include/temptable/handler.h:563
#5  0x000055555bb95354 in temptable::Handler::info (this=0x7fff2020f7e8)
    at storage/temptable/src/handler.cc:751
#6  0x000055555bb93482 in temptable::Handler::open (this=0x7fff2020f7e8, 
    table_name=0x7fff2020b170 "greatdb/confper/tmp/#sqlf1fea_8_2")
    at storage/temptable/src/handler.cc:218
#7  0x0000555558fd1804 in handler::ha_open (this=0x7fff2020f7e8, table_arg=0x7fff2020edf0, 
    name=0x7fff2020b170 "greatdb/confper/tmp/#sqlf1fea_8_2", mode=2, 
    test_if_locked=516, table_def=0x0)
    at sql/handler.cc:2927
#8  0x00005555597c223e in open_tmp_table (table=0x7fff2020edf0)
    at sql/sql_tmp_table.cc:2422
#9  0x000055555923a798 in FollowTailIterator::Init (this=0x7fff2021e9c0)
    at sql/iterators/basic_row_iterators.cc:345
#10 0x0000555559255c13 in FilterIterator::Init (this=0x7fff2021ea20)
    at sql/iterators/composite_iterators.h:85
#11 0x0000555559b8fb69 in NestedLoopIterator::Init (this=0x7fff2021ea90)
    at sql/iterators/composite_iterators.cc:523
#12 0x0000555559b9805b in MaterializeIterator<DummyIteratorProfiler>::MaterializeQueryBlock (
    this=0x7fff2021ec08, query_block=..., stored_rows=0x7fffc83eedd8)
    at sql/iterators/composite_iterators.cc:1221
#13 0x0000555559b979f1 in MaterializeIterator<DummyIteratorProfiler>::MaterializeRecursive (
    this=0x7fff2021ec08)
    at sql/iterators/composite_iterators.cc:1129
#14 0x0000555559b960b9 in MaterializeIterator<DummyIteratorProfiler>::Init (
    this=0x7fff2021ec08)
    at sql/iterators/composite_iterators.cc:971
#15 0x0000555559b8e859 in LimitOffsetIterator::Init (this=0x7fff2021eca8)
    at sql/iterators/composite_iterators.cc:100
#16 0x00005555597dacd5 in Query_expression::ExecuteIteratorQuery (this=0x7fff201c20d0, 
    thd=0x7fff20001050) at sql/sql_union.cc:1814
#17 0x00005555597db113 in Query_expression::execute (this=0x7fff201c20d0, thd=0x7fff20001050)
    at sql/sql_union.cc:1877
#18 0x00005555597001c0 in Sql_cmd_dml::execute_inner (this=0x7fff20200a80, 
    thd=0x7fff20001050) at sql/sql_select.cc:872
#19 0x00005555596ff38e in Sql_cmd_dml::execute (this=0x7fff20200a80, thd=0x7fff20001050)
    at sql/sql_select.cc:612
#20 0x000055555966207c in mysql_execute_command (thd=0x7fff20001050, first_level=true)
    at sql/sql_parse.cc:5227


打印m_opened_table变量,发现结果为空,所以导致core
#4  0x000055555bb98e96 in temptable::Handler::opened_table_validate (this=0x7fff2c20ed88)
    at storage/temptable/include/temptable/handler.h:563
563          assert(m_opened_table != nullptr);
(gdb) p m_opened_table
$21 = (temptable::Table *) 0x0

二、问题调查过程

查询带有CTE派生表的时候内部会创建临时表用于保存临时数据,因此先看一下上面2个场合的临时表情况:

tmp_table_size值查询开始时候临时表情况查询过程中临时表情况说明
16777216内存表内存表没问题
1342内存表转为落盘表CORE

1、正常执行的代码流程调查

先看一下tmp_table_size=16777216时候没问题的代码流程,以下代码通过Handler::create()函数向kv_store这个map插入了一条新的table信息。

首先创建内部物化表,代码调用流程如下:
Table_ref::create_materialized_table -> instantiate_tmp_table -> create_tmp_table_with_fallback 
-> temptable::Handler::create -> temptable::Allocator
-> temptable::Prefer_RAM_over_MMAP_policy_obeying_per_table_limit::block_source

bool instantiate_tmp_table(THD *thd, TABLE *table) {
  // 这里创建临时表,包括引擎,调用Handler::create,注意这里handler=temptable::Handler
  if (create_tmp_table_with_fallback(thd, table)) return true;
  // 这里打开表,调用Handler::open打开临时表引擎
  open_tmp_table(table);
}

int Handler::create(const char *table_name, TABLE *mysql_table,
                    HA_CREATE_INFO *, dd::Table *) {
  try {
    size_t per_table_limit = thd_get_tmp_table_size(ha_thd());
    auto &kv_store = kv_store_shard[thd_thread_id(ha_thd())];
    // 这里创建表引擎的时候申请新的内存用于保存table信息,kv_store.emplace调用下面的block_source()函数申请新的内存。
    const auto insert_result = kv_store.emplace(
        std::piecewise_construct, std::forward_as_tuple(table_name),
        std::forward_as_tuple(mysql_table, m_shared_block,
                              all_columns_are_fixed_size, per_table_limit));

    ret = insert_result.second ? Result::OK : Result::TABLE_EXIST;

  } catch (Result ex) {
    ret = ex;
  } catch (...) {
    ret = Result::OUT_OF_MEM;
  }
}
// 申请新的内存
struct Prefer_RAM_over_MMAP_policy_obeying_per_table_limit {
  static Source block_source(uint32_t block_size,
                             TableResourceMonitor *table_resource_monitor) {
    assert(table_resource_monitor);
    assert(table_resource_monitor->consumption() <=
           table_resource_monitor->threshold());
    /* 以下各参数值:
       table_resource_monitor->consumption() = 0  这个值为已经申请到的内存值
       block_size = 1048576 这个值为当前新申请的内存值为1MB内存,为新申请的块大小
       table_resource_monitor->threshold() = 16777216 这个值为当前tmp_table_size值
       这个比较结果可以看出没有超出tmp_table_size值,因此正常新增1048576内存。
    */
    if (table_resource_monitor->consumption() + block_size >
        table_resource_monitor->threshold())
      throw Result::RECORD_FILE_FULL;

    return Prefer_RAM_over_MMAP_policy::block_source(block_size);
  }
};

接着FollowTailIterator迭代器通过open_tmp_table函数用temptable::Handler::open函数在kv_store这个map寻找刚才插入的那条记录成功。

int Handler::open(const char *table_name, int, uint, const dd::Table *) {
  try {
    auto &kv_store = kv_store_shard[thd_thread_id(ha_thd())];
    m_opened_table = kv_store.find(table_name);
    if (m_opened_table) {
      ret = Result::OK;
      opened_table_validate();
    } else {
      ret = Result::NO_SUCH_TABLE;
    }
  } catch (std::bad_alloc &) {
    ret = Result::OUT_OF_MEM;
  }

  info(HA_STATUS_VARIABLE);
  DBUG_RET(ret);
}

2、导致core的代码流程调查

设置完tmp_table_size=1342,这里代码流程跟上面是一样的,但是有一些参数值有变化。

bool instantiate_tmp_table(THD *thd, TABLE *table) {
  // 这里创建临时表,包括引擎,调用Handler::create,因为创建临时表内存超过阈值,因此这里引擎改为落盘表,hangler=ha_innobase
  if (create_tmp_table_with_fallback(thd, table)) return true;
  // 这里打开落盘表,调用Handler::open
  open_tmp_table(table);
}

static bool create_tmp_table_with_fallback(THD *thd, TABLE *table) {
  // 一开始临时表handler=temptable::Handler,但是申请内存超过阈值
  int error =
      table->file->create(share->table_name.str, table, &create_info, nullptr);
  if (error == HA_ERR_RECORD_FILE_FULL &&
      table->s->db_type() == temptable_hton) {
    // 这里修改临时表handler=ha_innobase
    table->file = get_new_handler(
        table->s, false, share->alloc_for_tmp_file_handler, innodb_hton);
    error = table->file->create(share->table_name.str, table, &create_info,
                                nullptr);
  }
}

int Handler::create(const char *table_name, TABLE *mysql_table,
                    HA_CREATE_INFO *, dd::Table *) {
  try {
    size_t per_table_limit = thd_get_tmp_table_size(ha_thd());
    auto &kv_store = kv_store_shard[thd_thread_id(ha_thd())];
    // 这里创建表引擎的时候申请新的内存用于保存table信息,kv_store.emplace调用下面的block_source()函数申请新的内存。
    // 这里因为新申请的内存大于tmp_table_size=1342这个阈值,因此报错RECORD_FILE_FULL,这回kv_store这个map因为报错因此没有插入table信息。
    const auto insert_result = kv_store.emplace(
        std::piecewise_construct, std::forward_as_tuple(table_name),
        std::forward_as_tuple(mysql_table, m_shared_block,
                              all_columns_are_fixed_size, per_table_limit));

    ret = insert_result.second ? Result::OK : Result::TABLE_EXIST;

  } catch (Result ex) {
    ret = ex;
  } catch (...) {
    ret = Result::OUT_OF_MEM;
  }
}
// 申请新的内存
struct Prefer_RAM_over_MMAP_policy_obeying_per_table_limit {
  static Source block_source(uint32_t block_size,
                             TableResourceMonitor *table_resource_monitor) {
    assert(table_resource_monitor);
    assert(table_resource_monitor->consumption() <=
           table_resource_monitor->threshold());
    /* 以下各参数值:
       table_resource_monitor->consumption() = 0  这个值为已经申请到的内存值
       block_size = 1048576 这个值为当前新申请的内存值
       table_resource_monitor->threshold() = 1342 这个值为当前tmp_table_size值
       这个比较结果可以看出这次新申请的内存超出tmp_table_size值,因此这次返回Result::RECORD_FILE_FULL的错误。
    */
    if (table_resource_monitor->consumption() + block_size >
        table_resource_monitor->threshold())
      throw Result::RECORD_FILE_FULL;

    return Prefer_RAM_over_MMAP_policy::block_source(block_size);
  }
};

接着继续执行sql,这次在FollowTailIterator迭代器打开临时表,注意,这里临时表引擎还是temptable::Handler而不是ha_innobase,那是因为FollowTailIterator迭代器用到的临时表跟上面MaterializeIterator迭代器用到的临时表是在prepare阶段,通过Common_table_expr::clone_tmp_table函数拷贝出来的,所以他们的handler虽然类型相同但是地址不同,只有table->share相同。

打开这张临时表,在kv_store寻找临时表信息的时候找不到对应临时表信息,导致m_opened_table为空,接着调用info()函数的时候core了。

实际上这时候FollowTailIterator迭代器用到的临时表的引擎应该跟上面的临时表一样改为innodb引擎,因为这里引擎没有跟着改动,加上kv_store没有存放相关临时表信息,因此导致这里core了。

打开临时表代码流程:
MaterializeIterator<DummyIteratorProfiler>::Init -> aterializeIterator<DummyIteratorProfiler>::MaterializeRecursive
-> MaterializeIterator<DummyIteratorProfiler>::MaterializeQueryBlock
-> NestedLoopIterator::Init
-> FilterIterator::Init
-> FollowTailIterator::Init
-> open_tmp_table
-> handler::ha_open -> temptable::Handler::open

int Handler::open(const char *table_name, int, uint, const dd::Table *) {
  try {
    auto &kv_store = kv_store_shard[thd_thread_id(ha_thd())];
    // 因为上面Handler::create创建的时候,kv_store没有插入临时表信息,因此这里找不到这张临时表的信息,导致这里m_opened_table为空。
    m_opened_table = kv_store.find(table_name);
    if (m_opened_table) {
      ret = Result::OK;
      opened_table_validate();
    } else {
      ret = Result::NO_SUCH_TABLE;
    }
  } catch (std::bad_alloc &) {
    ret = Result::OUT_OF_MEM;
  }

  info(HA_STATUS_VARIABLE);
  DBUG_RET(ret);
}

3、总结问题

根据上面分析可以知道,第二次修改tmp_table_size值为较小数值之后core,原因在于申请内存的时候tmp_table_size值超过阈值,因此在kv_store插入表信息失败,后面open临时表的时候,通过kv_store寻找这张表信息没有找到,因此最后m_opened_table为空指针。

从上面判断阈值公式其实可以看出这里判断方法有问题,因为实际申请的是96大小,但是这里却用块大小1MB来进行阈值比较。

    if (table_resource_monitor->consumption() + block_size >
        table_resource_monitor->threshold())
      throw Result::RECORD_FILE_FULL;

三、问题解决

结合上面分析,我们发现问题原因在于内存申请额时候用的判断阈值的公式有问题,导致提前报错使kv_store没有插入相关临时表信息,同时CTE的其中一个相关临时表引擎改变了,但是其它层的临时表引擎没改,这两个原因导致最后的core。

我们用较新的8.4.4版本代码尝试运行该sql发现没有问题,因此可见最新代码已经修复该bug。查看代码,可以发现有一个如下修复patch:

Change-Id: I90d7374971bdfc1fc178801d0c793382c6ab8a49

Bug #33814188 - Assertion consumption > threshold

MySQL 8.4.4版本代码作如下修复,就可以解决这个问题了。

修复storage/temptable/include/temptable/allocator.h文件的如下代码:

删除Prefer_RAM_over_MMAP_policy_obeying_per_table_limit::block_source函数,
申请内存的时候判断阈值从判断新增块大小改为判断实际新增内存大小,
这样第一次申请内存的时候就不会超过tmp_table_size,
在temptable::Handler::create可以正常向kv_store插入临时表信息。

同时在allocate()函数申请完内存之后增加如下阈值判断的操作。这样第二次申请内存的时候就会正常报错。
template <class T, class AllocationScheme>
inline T *Allocator<T, AllocationScheme>::allocate(size_t n_elements) {
  // 增加申请内存之后执行判断阈值操作的代码。这里从原来的block_size改为n_bytes_requested来判断,
  // 也就是从原来判断新增块大小改为判断实际新增内存大小,这样用实际新增大小来判断阈值。
  if (m_table_resource_monitor.consumption() + n_bytes_requested(※注意这个改动) >
      m_table_resource_monitor.threshold()) {
    throw Result::RECORD_FILE_FULL;
  }
}

修改之后的代码调用流程如下:

1、一开始创建正常handler=temptable::Handler临时表,并且向kv_store插入表信息成功。
2、通过handler::ha_write_row在向临时表写数据的时候,因为新申请的内存64kBytes超过了阈值
因此修改handler=ha_innobase,接着通过create_ondisk_from_heap函数创建落盘表。
3、create_ondisk_from_heap函数创建落盘表的时候会把之前CTE拷贝出来的相关表的引擎都一起改为
innodb表,因此接着FollowTailIterator迭代器的表执行open_tmp_table的时候打开的就跟
MaterializeIterator迭代器一样都是innodb表了,就不会有查找临时表handler的kv_store为空的情况了。

接着执行上面的查询,发现可以查出结果了。

greatsql> SET tmp_table_size=1342;
Query OK, 0 rows affected (0.00 sec)

greatsql> WITH RECURSIVE cte_tree AS ( SELECT 1 AS LEVEL, t.org_id,t.org_id as porg_id,user_name, nick_name, user_code FROM t2 t UNION ALL SELECT LEVEL + 1 AS LEVEL, pt.org_id,pt.porg_id,pt.ORG_NAME,pt.CONTACT_NAME,pt.ORG_CODE FROM t1 pt JOIN cte_tree ct ON pt.org_id = ct.porg_id )  SELECT a.* FROM cte_tree a limit 1;
+-------+--------+---------+-----------+-----------+-----------+
| LEVEL | org_id | porg_id | user_name | nick_name | user_code |
+-------+--------+---------+-----------+-----------+-----------+
|     1 |      1 |       1 | daizhong  | NULL      | daizhong  |
+-------+--------+---------+-----------+-----------+-----------+
1 row in set (1 min 1.63 sec)

四、问题总结

通过以上分析我们可以发现,带有CTE派生表的查询会在内部创建临时表用于储存中间数据,根据tmp_table_size值设置会影响临时表存放地方,如果tmp_table_size设置小了那么一开始就会从内存表改为创建落盘表,但是CTE内部涉及好几层迭代器,这时候每一层临时表的引擎都需要改,而导致core的代码只改了其中一层的临时表,没有把别的引擎一起改了,最后导致core。

通过分析可以发现,问题代码判断阈值的公式也有问题,因此才导致一开始其中一层临时表申请内存超过阈值判断错误,修改了引擎类型。

这个问题涉及了2个原因,一环套一环,分析类似问题的时候,需要持续深挖本质原因,同时结合查询计划的执行和临时表创建情况分析,还需要对内存申请过程熟悉才能更好解决问题。

DuckDB实战指南:用嵌入式分析数据库替代pandas和集群 DuckDB是一种面向OLAP场景的嵌入式列式分析数据库,其核心原理在于列式存储、向量化执行与零配置设计,天然适配现代CPU的SIMD指令集和多核并行能力。相比传统pandas数据处理或分布式集群方案,它在单机环境下即可实现TB级数据亚秒级聚合,显著降低运维复杂度与内存开销。技术价值体现在开发效率跃升(Jupyter直连、SQL即代码)、资源成本锐减(16GB笔记本跑100GB Parquet)及生产就绪性增强(自动溢出管理、S3原生支持)。典型应用场景包括ETL加速、临时分析、报表Pipeline构建及数 阅读详情

相关推荐

PostgresML实战:用SQL原生实现数据库内机器学习

数据库内机器学习(in-database ML)是一种将模型训练与推理直接嵌入数据存储层的技术范式,其核心原理是消除数据移动开销,利用数据库的查询优化器、ACID事务和原生聚合能力重构ML工作流。相比传统Python pipeline,它显著降低IO瓶颈,提升TB级数据场景下的端到端稳定性与安全可控性。技术价值体现在零导出预处理、SQL声明式特征工程、事务级模型版本管理及生产就绪的部署策略。典型应用场景包括实时推荐、BI嵌入预测、DBA驱动的MLOps以及SQL工程师主导的AI应用开发——本文即以Postg

weixin_33889245的博客 401

MySQL CTE:解锁SQL查询新模式

MySQL CTE(公用表表达式)是一种强大的查询工具,从8.0版本开始支持。它分为非递归和递归两种类型,能显著提升复杂查询的可读性和性能。非递归CTE适用于多步骤查询,而递归CTE特别适合处理层级数据。相比子查询,CTE只计算一次且可多次引用,性能更优。使用时需注意版本兼容性、递归终止条件和结果集大小等问题。合理运用CTE可以优化SQL结构,提高查询效率。

m0_73978383的博客 2229

【信息科学与工程学】【数据科学】 第四十四篇 数据湖 系列二 函数库02

加法模型: y(t) = g(t) + s(t) + h(t) + ε_t,其中g(t)为趋势项,s(t)为季节项,h(t)为节假日效应,ε_t为误差。管控目标: 写入延迟低,数据一致,不丢失。将新模型与当前生产模型并行运行,新模型接收同样的输入并产生预测,但不影响实际业务决策,仅用于收集性能数据进行比较。在数据加工任务运行时,实时捕获任务执行产生的血缘信息,并更新到血缘图,实现血缘的准实时同步。溯源深度: 可配置。将数据血缘图中的节点(如数据中心、云区域)映射到实际地理位置,在地图上展示数据的流动路径。

weixin_49199313的博客 291

mysql占用内存过大问题排查

如果数据库中有非常大的表,可能会占用大量内存,过多或不合理的索引也可能导致内存占用增加。如果有长时间运行的复杂查询或者大量未优化的查询,可能会占用大量内存。如果有长时间运行的复杂查询或者大量未优化的查询,可能会占用大量内存。通过以上步骤的排查,可以找出 MySQL 占用内存过高的原因,并采取相应的措施进行优化。

f897907070的博客 2760

【信息科学与工程学】计算机科学与技术——第七十七篇 系统架构设计02

编号类型模型配方企业资本运作的方法/规则/条件/依据及所有的步骤(​风险处理/异常处理/危机应对关联知识法律法规和裁决依据​​由发起人(通常是知名投资人、投行或产业资本)先设立一个只有现金、无实际业务的特殊目的收购公司(SPAC),通过IPO募集资金并在交易所上市。然后在规定时间内(通常24个月)寻找一家非上市公司进行合并(De-SPAC),使目标公司迅速实现上市,规避传统IPO的复杂流程和不确定性。1.:在开曼或特拉华注册空壳公司,注入少量种子资金。2.

weixin_49199313的博客 313

【信息科学与工程学】计算机科学与自动化——第一百九十八篇 高并发高可用的知识点01

以上补充约 110 行,涵盖分布式协调、服务网格细节、数据库高级特性、性能分析工具、混沌工程、安全渗透、合规审计、研发效能、新兴技术、架构模式、实用工具及经典论文。涵盖数据库索引与锁、NoSQL、前端性能、安全攻防、压测工具深入、监控深入、云原生存储、服务治理扩展、代码质量、DevOps 文化、行业标准、软技能及未来趋势。好的,继续补充更多高并发、高可用、高可靠的知识与技术,涵盖更深层的数据库内部机制、分布式事务、服务治理、运维自动化、安全与前沿技术等领域。

weixin_49199313的博客 732

Spark sql 常见问题及优化

目录数据倾斜1.null值的处理2.持续整理中... 数据倾斜 1.null值的处理 INSERT OVERWRITE TABLE tf_lte_prd_pd_inst_month PARTITION (month_id) SELECT prvnce_id, std_prvnce_cd ,... FROM ( SELECT '${V_DATE}' month_id ,..., C...

Jhon_yh的博客 3718

【信息科学与工程学】【数据中心】第三十二篇 云数据中心的计算/存储/网络的核心挑战01

状态方程: SOCk+1​=SOCk​−Qn​ik​Δt​;: 三相电流变换到αβ坐标系: iα​=32​​(ia​−21​ib​−21​ic​), iβ​=32​​(23​​ib​−23​​ic​). 计算瞬时有功功率p和无功功率q,经低通滤波器分离直流分量,反变换得基波电流,差值即为谐波指令。: 节点i的信念 bi​(xi​)=ϕi​(xi​)∏j∈N(i)​mji​(xi​), 消息 mij​(xj​)=∑xi​​ϕi​(xi​)ψij​(xi​,xj​)∏k∈N(i)\j​mki​(xi​).

weixin_49199313的博客 219

【mysql8的一个利器, CTE查询】

今天因为一个比较头疼的mysql查询问题, 去Stack Overflow提问, 然后底下有人答复说既然用了mysql8, 不要用子查询, 直接用. CTE查询是啥, 我直接百度了下, 然后发现, 这玩意太好用了, 但是说实话, 我可以肯定大多数人基本都不会去了解这些新出的功能,毕竟经手的项目大多数几年前的项目了,版本低. 但是说不定也有和我一样正在用mysql8版本的朋友, 所以分享出来1、MySQL8.0之前,进行复杂查询时需要使用子查询来实现,SQL语句不仅语句复杂性能低,而且不够清晰。

zzzgd_666的博客 1215

【信息科学与工程学】【通信工程】第一百八十八篇 通信网络中的数学物理01

因素及参数的数学建模与详细配置SDN将控制平面与数据平面分离,控制器负责全局视图与决策。控制器部署问题(Controller Placement Problem, CPP)常用图论建模:给定网络拓扑 G=(V,E),候选控制器位置集合 C⊆V,需选择k个控制器位置,使得交换机到最近控制器的平均时延或最坏时延最小化,同时考虑控制器间同步开销。

weixin_49199313的博客 605

C# MES源码深度解析:架构模块与二次开发实战

在智能制造加速落地的今天,MES(制造执行系统)已成为连接企业计划层与车间设备层的核心枢纽。C#凭借其Windows生态的天然优势、成熟的设备通讯库以及WPF的高效界面开发能力,成为众多制造企业构建MES的首选语言。面对一套C# MES源码,开发者往往困惑于其复杂的解决方案结构、繁多的类库以及PLC、OPC、SignalR等技术的交织。本文从系统架构出发,拆解工单管理、数据采集、实时看板、追溯防错等核心模块的实现思路,深入分析多线程UI更新、高并发削峰、事务边界等关键C#技术细节,并结合真实车间部署经验,剖

weixin_34378767的博客 430

mysql8 公用表表达式CTE的使用

公用表表达式CTE就是命名的临时结果集,作用范围是当前语句。 说白点你可以理解成一个可以复用的子查询,当然跟子查询还是有点区别的,CTE可以引用其他CTE,但子查询不能引用其他子查询。   一、cte的语法格式: with_clause: WITH [RECURSIVE] cte_name [(col_name [, col_name] ...)] AS (...

weixin_33713707的博客 294

用Python的fengwo模块复刻通达信筹码峰(COST/WINNER),选股效率提升10倍

本文介绍了如何使用Python的fengwo模块高效复刻通达信的筹码峰(COST/WINNER)计算,显著提升选股效率。通过C++内核与多线程优化,fengwo模块将传统Python实现的数小时计算压缩至分钟级,解决了量化交易中的性能瓶颈问题。文章详细解析了筹码峰计算原理、fengwo模块的技术架构及实战应用,助力量化开发者构建高效选股系统。

weixin_29062823的博客 438

星梦面板新增PG18与MySQL9数据库管理,游戏服主运维门槛骤降

数据库是游戏服务器运行的核心依赖,但传统命令行运维方式对中小服主并不友好。MySQL 9作为Innovation版本带来新特性,PostgreSQL 18则以稳定演进著称,两者在游戏数据存储场景各有优势。通过可视化面板将建库、授权、备份、端口配置等高频操作封装为图形界面,可显著降低数据库管理的技术门槛。本文围绕星梦面板新增的PG18与MySQL9支持,结合Docker部署、账号权限最小化、备份恢复等工程实践,帮助游戏服主快速掌握数据库选型与运维要点。

weixin_34290631的博客 400

ESP32、MicroPython、天问ASR-PRO,串口发送数据并播报

天问ASRPRO语音芯片内置脑神经网络处理器,支持DNN、TDNN、RNN等神经网络及卷积运算硬件运算,非软件运算,支持语音识别、声纹识别、语音增强、语音检测、单麦克风降噪增强、单麦克风回声消除、360度全方位拾音等功能。ASR-PRO可以图形化编程,降低开发难度,对小白友好。之前使用ASRPRO与ESP32进行过语音控制,这次由于一个项目需要对ESP处理过后的数据进行播报,就想到利用该模块。以下程序主要为测试代码,并非实际项目,仅供参考学习(若有错误,欢迎指正)。二者通过串口通信,波特率为9600。

weak_student的博客 5951

Dify安装使用说明

dify可以说是一个功能不错的LLMOps,可以通过dify集中管理模型,可以通过界面创建AI应用,可以上传文档形成知识库,可以创建自定义工具(API),并可以对外提供API。相关功能类似Openai的GPTs或智谱的智能体,其他大厂的大模型也有类似功能,不过貌似只有dify是开源的。这就为个人本地部署AI应用提供了便利。

龙井茶的Sky 1万+

数据工程工具链实战地图:20个核心工具分层解析与选型指南

数据工程工具链是支撑现代企业数据能力的基础设施,其本质是围绕数据生命周期——接入、存储、计算、治理与消费——构建的协同系统。理解各工具的技术原理(如Airflow的DAG编排、dbt的Transformation-as-Code、Flink的Event Time语义)是避免选型失当的前提;其技术价值在于将数据可靠性、模型可信度、实时准确性、质量可预防性及跨源可分析性从理想变为可落地的工程实践。典型应用场景包括金融实时风控、电商T+1报表、SaaS多源BI分析等。本文聚焦真实生产验证的20个高价值工具,尤其深

闲谈 449

Drupal远程代码执行漏洞(CVE-2018-7600)实战复现:从检测到写入WebShell的全过程

本文详细解析了Drupal远程代码执行漏洞(CVE-2018-7600)的实战复现过程,从环境搭建、漏洞检测到WebShell写入的全流程。该高危漏洞影响Drupal 6.x至8.x版本,允许攻击者未经授权执行任意代码。文章提供了多种POC构造方法和高级利用技巧,帮助安全研究人员深入理解漏洞原理并掌握验证技术。

weixin_29322553的博客 275

多维聚合实战:从GROUP BY到生产级立方体计算

多维聚合是BI、数据分析与特征工程的核心能力,本质是基于多个维度坐标对度量数据进行切片、钻取与跨粒度计算。其技术原理超越传统SQL的GROUP BY,依赖分层预计算、Rollup表、窗口函数与条件聚合等组合策略,以解决占比计算、动态TOP-N、层级下钻等高频复杂需求。关键技术价值在于保障查询性能、结果一致性与业务逻辑准确性,广泛应用于电商经营分析、金融风控宽表构建及IoT设备效能评估等场景。本文聚焦可落地的五类核心操作与三套生产级配置模板,覆盖Doris、StarRocks等主流OLAP引擎的多维聚合实践。

congbao6525的博客 426

Adams二次开发实战指南:从CMD到Python与插件开发全解析

本文全面解析Adams二次开发的三种核心方法:CMD命令、Python脚本和C++插件开发。通过实战案例展示如何实现参数批量修改、自动化仿真流程和定制功能开发,显著提升多体动力学仿真效率。特别适合需要处理复杂建模和重复性任务的工程师学习参考。

weixin_29235923的博客 128

台达PLC DIAdesigner-AX 编程笔记

1、登陆台达思图软件网站,选择Download,登陆注册好的账号,下载DIAInstaller,下载后安装(安装目录不要修改);

weixin_45823621的博客 6676

数据仓库面试题

******************************************数据仓库相关问题****************************************************** 1.建模方式是什么,有哪些 2.星型模型和雪花模型各自的优缺点是什么 3.维度建模的四个步骤是什么 3.数据仓库里 实体表/维表/事务表的概念,事务表的类型 4.数据仓库里数据规范有哪些 5.数据质量是怎么做的 https://blog.csdn.net/An342647823/articl.

qq_38889342的博客 1627

UVM实战 卷I学习笔记1——简单的UVM验证平台:只有driver

目录验证平台的组成最简单的验证平台——只有driverUVM如何搭建driver:加入factory机制加入objection机制加入virtual interface 个人《UVM实战卷I》学习随手笔记,此书作者:张强 验证平台的组成 一个验证平台要实现的基本功能: 模拟DUT的各种真实使用情况——给DUT施加各种激励→driver实现激励功能; 根据DUT的输出判断DUT行为是否与预期符合→scoreboard(也称为checker)——需注意判断的东西和判断的标准是什么; 收集DUT的输出并传递

weixin_41979380的博客 3890

LangChain在数据工程中的实战:结构化输出与可维护性设计

大语言模型(LLM)在数据工程中并非简单API调用,而是需深度融入ETL、BI和自动化分析流水线的关键组件。其核心挑战在于如何确保AI输出稳定、结构化、可解析,并能无缝对接Pandas、SQL引擎等传统数据工具。LangChain通过PromptTemplate、OutputParser、ChatMessageHistory和Chain四大抽象,重构了LLM交互的数据流范式,将非确定性生成转化为可测试、可灰度、可回滚的工程模块。尤其在SQL生成、报表自动解释、客户知识库问答等场景中,结构化输出与可维护性成为

weixin_30642561的博客 446

ROS Motion Planning运动规划库安装方法及进阶使用方法详细介绍

​今天偶然发现了一个优质的运动规划库:ai-winter/ros_motion_planning,比较适合从事ROS移动机器人运动规划研究领域的小伙伴学习和使用,相比于莱斯大学Kavraki实验室提供的开源的著名运动规划库OMPL、或着我之前介绍过的zhm-real开源的zhm-real/MotionPlanning和zhm-real/PathPlanning运动规划库,今天介绍的ROS Motion Planning运动规划库与ROS机器人中常用的Navigation导航框架的兼容性更好,库中的运动规划

慕羽★的博客 5577

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

.NET统计API设计:告别后端画图,构建前后端解耦的数据可视化方案

统计图表本质是数据可视化表达,其核心在于结构化数据供给而非渲染逻辑绑定。现代Web应用中,图表能力应由前端专业库(如ECharts)承担,后端.NET则聚焦于安全、高效、跨平台的数据聚合与标准化输出。基于Minimal API + OpenAPI契约 + 前端适配器的三层架构,可彻底规避System.Drawing跨平台限制、GDI+兼容性问题及前后端强耦合痛点。该模式天然支持缓存、权限下推、SSR导出等企业级需求,已在金融、政务系统中验证千人并发稳定性。本文详解如何用.NET 6+构建可复用、易测试、国产

天下无双 578

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

上一篇: 误操作后快速恢复数据 binlog 解析为反向 SQL
下一篇: GreatSQL备份报错“PROCESS权限不足“分析与解决
GreatSQL社区
博客等级 码龄5年 1210粉丝 390原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值