MySQL更新数据时会报错,SQLSTATE[HY000]: General error: 1243 Unknown prepared statement handler (23039) given to mysqld_stmt_execute

Forsend

代码大概是这样的

    private function getDb()
    {
        return new \Workerman\MySQL\Connection('DB_HOST', 'DB_PORT', 'DB_USER', 'DB_PWD', 'DB_NAME');
    }

    public function func($id)
    {
        $db = $this->getDb();
        try {
            // 一些业务

            // 更新操作
            $db->query("UPDATE `table_name` SET field1 = 1 WHERE `id` = {$id}");

            // result通过业务拿到
            if ($result) {
                $db->query("UPDATE `table_name` SET field2 = 2 WHERE `id` = {$id}");
            }
        } catch (\Exception $exception) {
            echo $exception->getMessage();
        }
    }

第一个更新语句没问题,如果result为true,执行第二个更新时,会抛出异常,报错信息是

UPDATE `table_name` SET `field2` = 2 WHERE `id` = 12345678 
SQLSTATE[HY000]: General error: 1243 Unknown prepared statement handler (23039) given to mysqld_stmt_execute


不是必定触发,但是概率不小
然后我在网上搜了下报错信息,tp框架有类似的问题,解决办法是把PDO的PDO::ATTR_EMULATE_PREPARES属性改成true
按照这样把workerman源码改下也正常了,但具体原因不清楚

截图

PHP 7.2
mysql 5.7 有读写分离

2563 1 0
1个回答

MarkGo

我觉得可能是断线重连时产生的问题。
prepared statement handler 这个是通过mysql预处理进行查询;
类似一个查询执行2次命令,
第一次执行:
PREPARE STMT FROM 'SELECT * FROM table LIMIT ?';
第二次執行:
SET @a=1;EXECUTE STMT USING @a;

当第一次执行后发生断线重连时,运行的第二段sql则会报错,因为对应的预处理已经不存在了。

而设置PDO::ATTR_EMULATE_PREPARES = true,
则代表不使用mysql的预处理,通过PDO模拟预处理情况,最终只执行一次。

如果要印证猜测是否成立,可以看看mysql的errlog

  • Forsend 2022-07-27

    感谢解答。请问会不会跟MySQL读写分离有关,网上搜这个报错信息时,找到一些tp框架也会报这个错,都是开启了读写分离。我这边也用了读写分离中间件

  • MarkGo 2022-07-28

    这个应该不会吧,具体可能也要看中间件的处理方式,
    出现这个错误代表着预处理时模板语句没执行,但却执行了参数绑定导致出错。
    我也是用读写分离,但是我是代码级的,利用mariadb特性进行的,
    如一些耗时且没太高实时性要求的,就在sql前加入/SLAVE/select xxxxx
    并未出现过这些错误。

年代过于久远,无法发表回答
🔝