You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MariaDB并行执行含DEFAULT()的预查询触发1615错误求助

问题原因分析

这个错误是MariaDB在多连接并行场景下,处理关联了Sequence的表的预准备语句时的元数据缓存竞争问题:

  • 当查询使用DEFAULT(column)函数时,预准备语句会依赖表的元数据(列默认值定义)
  • 关联了Sequence的表,在多连接并行访问时,元数据可能被标记为"失效"(即使表结构没有实际变更)
  • 此时预准备语句的依赖元数据发生变化,触发MariaDB抛出1615 (HY000): Prepared statement needs to be re-prepared错误
  • 调整table_definition_cache无效,说明问题并非缓存容量不足,而是元数据一致性的并发处理逻辑存在缺陷
解决/规避方案

临时规避方案(无需升级数据库)

  • 替换DEFAULT()函数的查询方式:改为从INFORMATION_SCHEMA.COLUMNS直接查询列默认值,示例SQL:
    SELECT column_default 
    FROM INFORMATION_SCHEMA.COLUMNS 
    WHERE table_schema = DATABASE() 
      AND table_name = 'table1' 
      AND column_name = 'name'
    
  • 驱动层面添加自动重试逻辑:捕获1615错误后,重新准备语句并执行:
    • Golang示例修改:
      func request(request string, db *sql.DB) {
          retryCount := 3
          var stmt *sql.Stmt
          var err error
          var rows *sql.Rows
      
          for i := 0; i < retryCount; i++ {
              stmt, err = db.Prepare(request)
              if err != nil {
                  if i == retryCount-1 {
                      panic(err.Error())
                  }
                  time.Sleep(10 * time.Millisecond)
                  continue
              }
              rows, err = stmt.Query()
              if err != nil {
                  if !strings.Contains(err.Error(), "1615") || i == retryCount-1 {
                      panic(err.Error())
                  }
                  stmt.Close()
                  time.Sleep(10 * time.Millisecond)
                  continue
              }
              break
          }
          if rows != nil {
              rows.Close()
          }
          if stmt != nil {
              stmt.Close()
          }
      }
      
    • PHP mysqli示例:捕获mysqli_sql_exception,判断错误码为1615时重新执行准备和查询操作
  • 临时解除列与Sequence的关联:如果业务允许,暂时取消列和Sequence的绑定,待问题修复后再重新关联
  • 限制单连接执行:仅适合测试场景,生产环境不推荐

长期解决方案

  • 升级MariaDB版本:查询MariaDB官方bug列表,确认该问题是否已在后续版本(如11.5+、10.6.x补丁版)修复,升级到对应稳定版
  • 提交bug报告:如果当前使用的版本未修复该问题,向MariaDB官方提交包含复现步骤的bug报告,推动问题修复
复现代码与报错

Golang复现代码

package main

import (
    "database/sql"
    _ "github.com/go-sql-driver/mysql"
    "time"
)

const myrequest = `SELECT
        DEFAULT(name)
        FROM table1`

func createDB() *sql.DB {
    db, err := sql.Open("mysql", "dev:dev@tcp(localhost:3306)/dev")
    if err != nil {
        panic(err.Error())
    }
    return db
}

func request(request string, db *sql.DB) {
    stmt, err := db.Prepare(request)

    if err != nil {
        panic(err.Error())
    }
    _, err = stmt.Query()
    if err != nil {
        panic(err.Error())
    }
}

func main() {
    db := createDB()
    for i := 0; i < 100; i++ {
        go func() {
            request(myrequest, db)
        }()
    }

    time.Sleep(1 * time.Second)
}

报错输出

panic: Error 1615 (HY000): Prepared statement needs to be re-prepared

goroutine 76 [running]:
main.request({0x640f56?, 0x0?}, 0x0?)
        /home/bperrard/Documents/booking/testmariadb/main.go:336 +0x7c
main.main.func1()
        /home/bperrard/Documents/booking/testmariadb/main.go:344 +0x25
created by main.main in goroutine 1
        /home/bperrard/Documents/booking/testmariadb/main.go:343 +0x25

内容的提问来源于stack exchange,提问作者bpeexc

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 06:42:51