当前位置: 首页 > news >正文

MySQL游标执行带有MINUS/INTERSECT查询导致core问题解析

MySQL游标执行带有MINUS/INTERSECT查询导致core问题解析

一、问题发现

在客户现场提交的一次问题中发现某个带有MINUS联合查询cursor语句进行查询的时候,用MINUS和INTERSECT进行联合查询会导致core,但是用UNION却不会。

注意:这里用的版本是debug版本会core,release版本会报错。这个问题在MySQL 8.0.32版本会复现,最新的8.4.4版本关掉HASH_SET_OPERATIONS开关以后同样复现。

看下面例子:

1、准备表和sp

8032版本执行以下命令:
CREATE TABLE t1 (a INT, b VARCHAR(3));
INSERT INTO t1 values(1,'aa'),(2,'bb'),(3,'cc'),(6,'ee') ;
CREATE TABLE t2 (a INT, b VARCHAR(3));
INSERT INTO t2 values(1,'aa'),(4,'bb'),(3,'cc'),(5,'dd') ;
SET sql_mode=oracle;
DELIMITER $$
CREATE or replace PROCEDURE p1()
IS
BEGIN
FOR v IN(
SELECT * FROM t1
minus
SELECT * FROM t2
) LOOP
SELECT v.a ;
END LOOP;
END;$$
DELIMITER ;

2、执行sp

执行sp可以看到core了。

-- CALL p1; 结果core了
core堆栈如下:
#0 __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
#1 0x00007ffff6a068e4 in __GI_abort () at abort.c:79
#2 0x00007ffff6a067cf in __assert_fail_base (
fmt=0x7ffff6b60e90 "%s%s%s:%u: %s%sAssertion `%s' failed.\n%n",
assertion=0x6073198 "inited NONE || (inited RND && scan)",
file=0x6071b68 "sql/handler.cc",
line=3072, function=<optimized out>) at assert.c:92
#3 0x00007ffff6a13f02 in GI_assert_fail (
assertion=0x6073198 "inited NONE || (inited RND && scan)",
file=0x6071b68 "sql/handler.cc",
line=3072, function=0x6073178 "int handler::ha_rnd_init(bool)")
at assert.c:101
#4 0x00000000034fb3e1 in handler::ha_rnd_init (this=0x7fff2c9ab490,
scan=true) at sql/handler.cc:3072
#5 0x0000000003a339e0 in Materialized_cursor::open (
this=0x7fff2c88bff8, thd=0x7fff2c001010)
at sql/sql_cursor.cc:375
#6 0x0000000003a333e5 in mysql_open_cursor (thd=0x7fff2c001010, result=
0x7fff2c604ac8, pcursor=0x7fff2c604ab8)
at sql/sql_cursor.cc:280
#7 0x00000000039ad4dc in sp_cursor::open (this=0x7fff2c604ab0,
thd=0x7fff2c001010)
at sql/sp_rcontext.cc:1262
#8 0x0000000003997f6e in sp_instr_cpush_rowtype::exec_core (
this=0x7fff2c881560, thd=0x7fff2c001010)
at sql/sp_instr.cc:1986
#9 0x0000000003993ae5 in sp_lex_instr::reset_lex_and_exec_core (
this=0x7fff2c881560, thd=0x7fff2c001010, nextp=0x7fffd45f2998,
open_tables=false)
at sql/sp_instr.cc:462
#10 0x000000000399472a in sp_lex_instr::validate_lex_and_execute_core (
this=0x7fff2c881560, thd=0x7fff2c001010, nextp=0x7fffd45f2998,
open_tables=false)
at sql/sp_instr.cc:769
#11 0x0000000003998f89 in sp_instr_copen::execute (this=0x7fff2c881a88,
thd=0x7fff2c001010, nextp=0x7fffd45f2998)
at sql/sp_instr.cc:2282
(gdb) f 4
#4 0x00000000034fb3e1 in handler::ha_rnd_init (this=0x7fff2c9ab490,
scan=true) at sql/handler.cc:3072
3072 assert(inited NONE || (inited RND && scan));
(gdb) p inited 这里引擎变为索引了,说明在前面的过程里引擎的索引没有执行HA_INDEX_END
$1 = handler::INDEX

3、8.4.4版本执行sp

8.4.4版本的 HASH_SET_OPERATIONS 开关默认开启的,因此这里不需要设置。

# 首先创建正常sp。
SET sql_mode=oracle;
DELIMITER $$
CREATE or replace PROCEDURE p1()
IS
BEGIN
FOR v IN(
SELECT * FROM t1
minus
SELECT * FROM t2
) LOOP
SELECT v.a ;
END LOOP;
END;$$
DELIMITER ;
# 接着执行这个sp,发现有结果,符合预期。
greatsql> CALL p1;
+------+
| v.a  |
+------+
| 2    |
+------+
1 row in set (0.01 sec)
+------+
| v.a  |
+------+
| 6    |
+------+
1 row in set (0.01 sec)

没问题是不是说明bug解决了呢?现在关掉HASH_SET_OPERATIONS开关,再次创建这个sp再运行一次。可以看到结果core了,说明这个bug并没有解决。

SET sql_mode=oracle;
DELIMITER $$
CREATE OR replace PROCEDURE p1()
IS
BEGIN
FOR v IN(
SELECT /*+ set_var(optimizer_switch='HASH_SET_OPERATIONS=off') */ * FROM t1
minus
SELECT * from t2
) LOOP
SELECT v.a ;
END LOOP;
END;$$
DELIMITER ;
CALL p1; # 这里core了
堆栈如下,可以发现跟8032版本的堆栈完全一样:
#0 __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
#1 0x00007ffff5bba8e4 in __GI_abort () at abort.c:79
#2 0x00007ffff5bba7cf in __assert_fail_base (
fmt=0x7ffff5d14e90 "%s%s%s:%u: %s%sAssertion `%s' failed.\n%n",
assertion=0x62be3b8 "inited NONE || (inited RND && scan)",
file=0x62bce28 "sql/handler.cc",
line=3151, function=<optimized out>) at assert.c:92
#3 0x00007ffff5bc7f02 in GI_assert_fail (
assertion=0x62be3b8 "inited NONE || (inited RND && scan)",
file=0x62bce28 "sql/handler.cc",
line=3151, function=0x62be398 "int handler::ha_rnd_init(bool)")
at assert.c:101
#4 0x000000000358db35 in handler::ha_rnd_init (this=0x7fff34047850, scan=true)
at sql/handler.cc:3151
#5 0x0000000003b0f0b2 in Materialized_cursor::open (this=0x7fff341017c8,
thd=0x7fff34000ec0)
at sql/sql_cursor.cc:381
#6 0x0000000003b0eab7 in mysql_open_cursor (thd=0x7fff34000ec0,
result=0x7fff340d4248, pcursor=0x7fff340d4238)
at sql/sql_cursor.cc:286
#7 0x0000000003a7a6ee in sp_cursor::open (this=0x7fff340d4230,
thd=0x7fff34000ec0)

二、问题调查过程

1、8.0.32版本core问题调查

打开游标的时候内部会创建临时表用于保存结果数据,因此先看一下上面打开游标的代码执行流程:

img

从上面流程图可以看出,执行查询的时候临时表进行了索引初始化,但是没有进行关闭,接着在打开游标的时候又进行了一次初始化,于是core了。

2、8.4.4版本core问题调查

8.4.4版本要分2个场景讨论,首先第一个不core的场景,也就是optimizer_switch='HASH_SET_OPERATIONS=on'的场景,从代码看跟8032版本不同处在于Query_result_materialize::start_execution的时候,table->share->keys数量等于0,而8032版本这个地方的keys数量等于1。因此在8.4.4版本table->file->ha_index_init的时候inited没有设置为INDEX而是保持为NONE,后面打开游标的时候初始化不会core。

bool instantiate_tmp_table(THD thd, TABLE table) {// Ensure that "in_use" is synchronized with the current sessionassert(table->in_use nullptr || table->in_use thd);table->in_use = thd;TABLE_SHARE *const share = table->s;// 跟8032代码相比多了这一行,这里把keys值设为0,因此后面临时表不创建索引,也就不会导致打开cursor的core。if (table->uses_hash_map()) share->keys = 0;

optimizer_switch='HASH_SET_OPERATIONS=off'的时候,代码流程跟8032一样,因此原因跟上图一致。

3、总结问题

对比上面1和2可以发现,8.4.4版本开启HASH_SET_OPERATIONS开关只是规避了问题,并没有解决问题。因此这个导致core的问题始终存在。

三、问题解决

结合上面分析,我们可以在第一次table->file->ha_index_init执行之后到结束的时候调用ha_index_end就可以了,这样接下来打开游标的时候引擎状态就是NONE,就不会core了。

添加如下代码,就可以解决这个问题了。

bool Query_result_materialize::send_eof(THD *) {bool rc = false;if (table->hash_field && table->file->inited == handler::INDEX)rc = table->file->ha_index_end();return rc;
}

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

img

上图绿色部分为修复新增的代码,当查询结束的时候执行一次索引状态重置,问题解决。

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

greatsql> call p1;
+------+
| v.a  |
+------+
| 2    |
+------+
1 row in set (0.01 sec)
+------+
| v.a  |
+------+
| 6    |
+------+
1 row in set (0.01 sec)

四、问题总结

通过以上分析我们可以发现,执行带有 MINUS 和 INTERSECT 联合查询的cursor的时候,游标储存结果的临时表的索引状态会多次改变,如果索引状态的开启和结束没有配套设置的话,会影响后面 cursor 的打开。同时,不同版本的 MySQL 会有不同情况,像本次例子中,HASH_SET_OPERATIONS 开关也会对结果有影响。这就需要研发人员耐心多看代码,多尝试不同情况的查询 SQL 来分析问题,而不是看到某一种场景没问题了以为 BUG 修复了,那样会导致潜在 BUG 流出,造成后续的更多影响。

http://www.zskr.cn/news/124408.html

相关文章:

  • 2025年年终上海小型搬家公司推荐:最新排名与全方位对比分析报告 - 十大品牌推荐
  • 2025年年终上海家庭搬家公司推荐:最新服务排名及核心维度深度解析 - 十大品牌推荐
  • 禅道CNVD-2023-02709
  • MBTI测试怎么选?2025年五大测试平台全解析,从新手到进阶 - 速递信息
  • 基于Meteostat API的全国主要城市气温舒适度研究(2023-2024)
  • 无线炉温跟踪仪生产商哪家好?实力厂家知名品牌推荐 - 品牌推荐大师1
  • mysql-8.0 二进制包部署
  • 2025年年终广州家庭搬家公司推荐:综合实力排行及深度服务评测 - 十大品牌推荐
  • 2025年抉择SKF轴承制造商推荐:SKF轴承定制服务厂家TOP5榜单 - mypinpai
  • PE重包装袋选购指南:全网好评产品排行榜单,目前评价好的PE重包装袋批发厂家推荐骏岚纸塑市场认可度高 - 品牌推荐师
  • 2025年质量好的电力电缆厂家选购全指南(完整版) - 品牌宣传支持者
  • 用Cursor自动生成完整函数教程
  • 2025年靠谱的楼梯升降机/升降机用户口碑最佳榜(高评价) - 品牌宣传支持者
  • AI编程工具常常使用的代码库索引技术是什么,他解决的核心问题是什么?
  • AI编程工具常常使用的代码库索引技术是什么,他解决的核心问题是什么?
  • 正式接入DeepSeek-V3.2,国产AI“双剑合壁”!
  • 2025年双片钉箱机行业翘楚,这五家厂家不容错过!行业内双片钉箱机实力厂家优质企业盘点及核心优势详细解读 - 品牌推荐师
  • 2025年口碑好的大型碾米机行业内知名厂家排行榜 - 品牌宣传支持者
  • 2025石材抛光磨头/花岗岩磨头/超薄板磨头/石材连续磨机磨头品牌推荐:适配多机型的优质供应商精选 - 工业企业赋能社
  • 2025年盘式真空干燥机制造企业权威推荐榜单:连续盘式干燥机/盘式连续干燥机/盘式干燥机源头厂家精选 - 品牌推荐官
  • DaVinci Resolve 20.0(达芬奇20)保姆级完整安装教程详细步骤包含:下载+安装+激活+入门指南
  • 包装设计公司推荐哪家? - 黑马榜单
  • 包装设计公司推荐哪家? - 黑马榜单
  • 2025年靠谱的非接触式伯努利KUMADE手指品牌厂家排行榜(热门) - 品牌宣传支持者
  • 2025年度印刷开槽模切机优质厂家采购指南排行,高速全自动水墨印刷开槽模切机/印刷机印刷开槽模切机制造厂家排行榜单 - 品牌推荐师
  • Shell简介:操作系统中的命令行桥梁
  • 2025-2026北京继承律师专业指南:财富传承风险规避与顶级律师甄选 - 苏木2025
  • 吸顶灯哪个品牌质量好?家用客厅卧室耐用吸顶灯十大品牌推荐 - 阿喂嘞lvv
  • 2025年辽宁公务员考试培训推荐,五大靠谱公考培训机构全解析,助你高效上岸! - mypinpai
  • 2025年评价高的塑料机械螺杆空压机厂家最新TOP实力排行 - 品牌宣传支持者