当前位置: 首页 > 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.jsqmd.com/news/112663/

相关文章:

  • 2025年珠海管道疏通联系方式汇总:全市专业服务机构官方联系方式与高效合作指引 - 十大品牌推荐
  • 2025年年终上海小型搬家公司推荐:最新排名与全方位对比分析报告 - 十大品牌推荐
  • 2025年年终深圳小型搬家公司推荐:实力榜单解析与多维度服务对比评测 - 十大品牌推荐
  • 2025年年终上海家庭搬家公司推荐:最新服务排名及核心维度深度解析 - 十大品牌推荐
  • Vscode连接服务器失败
  • 2025年年终北京家庭搬家公司推荐:权威榜单TOP5及核心指标比较分析 - 十大品牌推荐
  • MBTI测试怎么选?2025年五大测试平台全解析,从新手到进阶 - 速递信息
  • 禅道CNVD-2023-02709
  • LLC sec center tap winding current unbalance analysis
  • 基于Meteostat API的全国主要城市气温舒适度研究(2023-2024)
  • 无线炉温跟踪仪生产商哪家好?实力厂家知名品牌推荐 - 品牌推荐大师1
  • mysql-8.0 二进制包部署
  • 2025年年终广州家庭搬家公司推荐:综合实力排行及深度服务评测 - 十大品牌推荐
  • Skill语言入门的一些基础知识
  • Skill语言入门的一些基础知识
  • Playwright等待机制全面解析:自动等待与显式等待
  • 2025 年 12 月应急广播系统厂家权威推荐榜:公共安全与应急指挥的智能声网核心解决方案 - 品牌企业推荐师(官方)
  • 2025年抉择SKF轴承制造商推荐:SKF轴承定制服务厂家TOP5榜单 - mypinpai
  • 2025年抉择SKF轴承制造商推荐:SKF轴承定制服务厂家TOP5榜单 - mypinpai
  • 2025年口碑好的高强度标准紧固件信誉优质供应榜(可靠推荐) - 品牌宣传支持者
  • 2025试验机年末总结:五大顶尖试验机生产商推荐榜单一览! - 品牌推荐大师1
  • 2025试验机年末总结:五大顶尖试验机生产商推荐榜单一览! - 品牌推荐大师1
  • PE重包装袋选购指南:全网好评产品排行榜单,目前评价好的PE重包装袋批发厂家推荐骏岚纸塑市场认可度高 - 品牌推荐师
  • 2025年口碑好的牛肉面百强推荐榜 - 品牌宣传支持者
  • 2025 震动时效年终盘点 推荐生产厂家 / 优秀企业 专业制造商 / 源头工厂供货商优选 - 品牌推荐大师1
  • 魔方公寓闭店≠暴雷!2025上海租住选它的3个硬核理由 - 品牌推荐排行榜
  • 2025年质量好的电力电缆厂家选购全指南(完整版) - 品牌宣传支持者
  • Android中Spinner下拉列表单个item显示多行文本的处理
  • C++11内存模型
  • 用Cursor自动生成完整函数教程