限制最大准备语句数

问题

我编写了一个将数据从 BigQuery 同步到 MySQL 数据库的应用程序。我尝试每 3 小时分批插入大约 10-20k 行(每批最多 10 项)。出于某种原因,当它尝试将这些行更新插入 MySQL 时,我收到以下错误:

不能创建超过 max_prepared_stmt_count 个语句:

错误 1461:不能创建超过 max_prepared_stmt_count 个语句(当前值:2000)

我的“相关代码”

// ProcessProjectSkuCost receives the given sku cost entries and sends them in batches to upsertProjectSkuCosts()

func ProcessProjectSkuCost(done <-chan bigquery.SkuCost) {

    var skuCosts []bigquery.SkuCost

    var rowsAffected int64

    for skuCostRow := range done {

        skuCosts = append(skuCosts, skuCostRow)


        if len(skuCosts) == 10 {

            rowsAffected += upsertProjectSkuCosts(skuCosts)

            skuCosts = []bigquery.SkuCost{}

        }

    }

    if len(skuCosts) > 0 {

        rowsAffected += upsertProjectSkuCosts(skuCosts)

    }

    log.Infof("Completed upserting project sku costs. Affected rows: '%d'", rowsAffected)

}


// upsertProjectSkuCosts inserts or updates ProjectSkuCosts into SQL in batches

func upsertProjectSkuCosts(skuCosts []bigquery.SkuCost) int64 {

    // properties are table fields

    tableFields := []string{"project_name", "sku_id", "sku_description", "usage_start_time", "usage_end_time",

        "cost", "currency", "usage_amount", "usage_unit", "usage_amount_in_pricing_units", "usage_pricing_unit",

        "invoice_month"}

    tableFieldString := fmt.Sprintf("(%s)", strings.Join(tableFields, ","))

我的问题:


为什么我max_prepared_stmt_count在立即执行准备好的语句时点击了(请参阅函数upsertProjectSkuCosts)?


我只能想象这是某种并发性,它在准备和执行所有这些语句之间同时创建大量准备好的语句。另一方面,我不明白为什么会有这么多并发,因为通道中的通道ProcessProjectSkuCost是一个大小为 20 的缓冲通道。


慕姐8265434
浏览 121回答 1
1回答

泛舟湖上清波郎朗

您需要关闭里面的声明upsertProjectSkuCosts()(或重新使用它 - 请参阅本文末尾)。当您调用 时db.Prepare(),会从内部连接池中获取一个连接(如果没有任何空闲连接,则会创建一个新连接)。stmt.Exec()然后在该连接上准备该语句(如果调用时该连接不可用,则该语句也会在另一个连接上准备)。因此,这会在您的数据库中为该连接创建一个语句。这个语句不会神奇地消失——在一个连接中有多个准备好的语句是完全有效的。Golang可以看到stmt超出范围,看到它需要某种清理然后进行清理,但 Golang 不会(就像它不会为您关闭文件和类似的东西一样)。所以你需要自己使用stmt.Close(). 你打电话时stmt.Close(),驱动程序将向数据库服务器发送命令,告诉它不再需要该语句。defer stmt.Close()最简单的方法是在检查后添加err以下内容db.Prepare()。您还可以做的是,准备一次语句并使其可用于upsertProjectSkuCosts(通过将stmtinto传递upsertProjectSkuCosts或通过构造upsertProjectSkuCosts结构的 func,以便结构可以具有 的属性stmt)。如果你这样做,你不应该调用stmt.Close()- 因为你不再创建新的语句,你正在重新使用现有的语句。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go