目录 · 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 STATUS、SHOW VARIABLES、EXPLAIN 这类语句,在不少 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 是接受的,只是返回零行。
三条能带走的
- 没参数的语句别走
execute()。 图统一没有意义,代价是在兼容引擎上莫名其妙地挂。 - 报错信息说「协议不支持」的时候,先自己验一遍——同一条 SQL 用
query()发一次。两分钟的事,能省掉翻半天引擎文档。 - 能力探测放在连接建立的时候做,别写死一种语法然后在出错分支里猜。
EXPLAIN的语法在 MySQL 系里至少有三种写法。
如果你也在 Node 里连过 MySQL 协议的兼容引擎,很想知道你们还踩到过哪些 prepared 协议相关的坑。