跳到主要内容
CNode

mysql2 的 prepared 协议踩坑:同一条 SQL,execute() 报错 query() 正常,我们靠这个把四个面板搞挂了

分享
Llibredb发布于 6 小时前
0360
目录 · 5 个章节

这是一个我们自己写出来的 bug,排查花了不少时间,教训挺通用的,记一下。

症状

我们用 mysql2 连一批 MySQL 协议兼容的数据库。对其中一个,四个功能同时挂掉,报的都是同一句话:

This command is not supported in the prepared statement protocol yet

一开始的判断是「这个数据库不支持这些语句」。错了。同样的 SQL 换个发法就能跑。

原因

mysql2 有两个方法,走的是两条完全不同的协议:

方法协议说明
connection.execute(sql, params)二进制 prepared先 PREPARE 再 EXECUTE
connection.query(sql)文本协议直接把语句发过去

我们图省事,所有语句一律走 execute(),包括那些根本没有参数的。

问题在于,SHOW STATUSSHOW VARIABLESEXPLAIN 这类语句,在不少 MySQL 协议兼容引擎的 prepared 协议里是没实现的。MySQL 自己支持得比较全,所以本地测试完全看不出来;换成兼容引擎就炸。

关键点:报错信息说的是协议不支持,不是语句不支持。 这两句话长得像,方向完全相反。我们照着前者查了半天引擎文档,其实该改的是自己的调用方式。

影响范围

我们把几个 MySQL 协议兼容引擎都测了一遍,这个问题影响的面比想的大:

  • SingleStore:四个功能挂掉——连接测试、健康检查、概览、监控面板,全是这一个原因。
  • StarRocks:概览和性能指标两个读取失败,同样是这个原因。
  • Databend:对象浏览器一直是空的。它的目录你直接查完全查得到,是我们那些带参数的读取走了 prepared 协议,而它没实现这套协议。

改法很简单:没有参数的语句改走文本协议 query(),带参数的继续走 execute() 后者必须保留——参数化是防注入的手段,不能为了兼容性丢掉。

改完之后,上面 SingleStore 的四个、StarRocks 的两个都恢复了。

一个长得一模一样、但根本不是同一回事的坑

同一轮里还有一个失败,报错信息几乎一样,我们顺手也归到协议问题里了。结果改了协议它还是失败。

那是 EXPLAIN 面板。我们发的是:

EXPLAIN FORMAT=JSON SELECT ...

在 SingleStore 上,这句话在两种协议下都报 ER_PARSE_ERROR。因为它的语法不是 EXPLAIN FORMAT=JSON,而是:

EXPLAIN JSON SELECT ...

是语法问题,穿着协议问题的外衣。换协议自然没用。

现在的做法是:连接建立的时候先探一下这台服务器接受哪种 EXPLAIN 写法,然后按结果发,而不是写死一种再去猜为什么失败。

同类的还有:TiDB 直接拒绝 EXPLAIN FORMAT='json',得发朴素的 EXPLAIN;Apache Doris 的语法里 SHOW STATUS LIKE '...' 是解析错误(mismatched input 'LIKE'),而不带 LIKE 的裸 SHOW STATUS 是接受的,只是返回零行。

三条能带走的

  1. 没参数的语句别走 execute() 图统一没有意义,代价是在兼容引擎上莫名其妙地挂。
  2. 报错信息说「协议不支持」的时候,先自己验一遍——同一条 SQL 用 query() 发一次。两分钟的事,能省掉翻半天引擎文档。
  3. 能力探测放在连接建立的时候做,别写死一种语法然后在出错分支里猜。EXPLAIN 的语法在 MySQL 系里至少有三种写法。

如果你也在 Node 里连过 MySQL 协议的兼容引擎,很想知道你们还踩到过哪些 prepared 协议相关的坑。

查看回复

回复 (0)

暂无回复,成为第一个参与讨论的人。

参与回复

登录后即可参与回复。登录