当前位置: 首页 > 知识库问答 >
问题:

限制最大准备语句计数

柴兴修
2023-03-14

问题所在

我写了一个应用程序,它将数据从BigQuery同步到MySQL数据库。我试着每3小时插入大约1020k行(每批最多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, ","))

    // placeholderstring for all to be inserted values
    placeholderString := createPlaceholderString(tableFields)
    valuePlaceholderString := ""
    values := []interface{}{}
    for _, row := range skuCosts {
        valuePlaceholderString += fmt.Sprintf("(%s),", placeholderString)
        values = append(values, row.ProjectName, row.SkuID, row.SkuDescription, row.UsageStartTime,
            row.UsageEndTime, row.Cost, row.Currency, row.UsageAmount, row.UsageUnit,
            row.UsageAmountInPricingUnits, row.UsagePricingUnit, row.InvoiceMonth)
    }
    valuePlaceholderString = strings.TrimSuffix(valuePlaceholderString, ",")

    // put together SQL string
    sqlString := fmt.Sprintf(`INSERT INTO
        project_sku_cost %s VALUES %s ON DUPLICATE KEY UPDATE invoice_month=invoice_month`, tableFieldString, valuePlaceholderString)
    sqlString = strings.TrimSpace(sqlString)

    stmt, err := db.Prepare(sqlString)
    if err != nil {
        log.Warn("Error while preparing SQL statement to upsert project sku costs. ", err)
        return 0
    }

    // execute query
    res, err := stmt.Exec(values...)
    if err != nil {
        log.Warn("Error while executing statement to upsert project sku costs. ", err)
        return 0
    }

    rowsAffected, err := res.RowsAffected()
    if err != nil {
        log.Warn("Error while trying to access affected rows ", err)
        return 0
    }

    return rowsAffected
}

// createPlaceholderString creates a string which will be used for prepare statement (output looks like "(?,?,?)")
func createPlaceholderString(tableFields []string) string {
    placeHolderString := ""
    for range tableFields {
        placeHolderString += "?,"
    }
    placeHolderString = strings.TrimSuffix(placeHolderString, ",")

    return placeHolderString
}

我的问题:

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

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

共有1个答案

荆钱明
2023-03-14

您需要关闭upsertProjectSkuCosts()中的语句(或者重新使用它-请参阅本文末尾)。

调用db.Prepare()时,会从内部连接池中获取连接(如果没有任何可用连接,则会创建新连接)。然后在该连接上准备语句(如果调用stmt.Exec()时该连接不是空闲的,则该语句也在另一个连接上准备)。因此,这将在数据库中为该连接创建一条语句。这个语句不会神奇地消失——在一个连接中有多个准备好的语句是完全有效的。Golang可以看到stmt超出了范围,看到它需要某种清理,然后进行清理,但Golang没有(就像它不会为您关闭文件之类的)。因此,您需要自己使用stmt.Close()来完成此操作。调用stmt.Close()时,驱动程序将向数据库服务器发送一个命令,告诉它不再需要该语句。

最简单的方法是在err检查下面的db.Prepare()之后添加defer stmt.Close()

您还可以做的是,准备一次语句,并使其可用于upsertProjectSkuCosts(通过将stmt传递到upsertProjectSkuCosts中,或者通过使upsertProjectSkuCosts成为结构的一个函数,使结构可以具有stmt的属性)。如果执行此操作,则不应调用stmt.Close()——因为您不再创建新语句,而是在使用现有语句。

还要看看我们是否应该关闭Golang中的DB.Prepare()?和https://groups.google.com/forum/#!主题/golang nuts/ISH22xZE-s

 类似资料:
  • Im使用具有2000多个参数标记的构建大型数据库调用。 我收到这个错误 我试着搜索API文档和google,但找不到如何配置它。 有没有人知道是否有可能达到这个限度?我知道这将是一个缓慢的数据库调用,但这是好的现在。 从长远来看,这是否会给我带来任何问题?批量运行是否会更好?

  • 问题内容: 我使用具有2000多个参数标记的大型数据库调用进行构建。 我收到这个错误 我尝试搜索API文档和Google,但是找不到如何配置它。 有谁知道是否有可能提高这个限制?我知道这将是一个缓慢的数据库调用,但是现在还可以。 从长远来看,这还会给我造成任何问题吗,我最好分批运行吗? 问题答案: 接缝处好像停留在2000年。这是驱动程序来源的一部分。 这是一个博客,其中包含有关如何解决该问题的示

  • 我正在使用我生成的一个准备好的语句,但在Java抛出的语句上出现了语法错误。然而,当我将PS的toString复制并粘贴到数据库的phpmyadmin中时,它的执行完美无缺。有什么想法会出错吗,我很难理解? 编辑:更改为PS.ExecuteUpdate(查询);还是不起作用。

  • 问题内容: 嗨,我正在尝试学习使用准备好的语句来避免SQL注入等的正确方法。 当我执行脚本时,我从脚本中收到一条消息,提示已插入0行,我希望这表示已插入1行,并且当然会更新表。我不完全确定自己准备好的陈述,因为我已经做过一些研究,我的意思是每个例子都各不相同。 更新表格时,我需要声明所有字段吗?还是只更新一个字段就可以了? 任何信息将非常有帮助。 index.php classes / class

  • 我在调试中不断看到这个错误。登录cassandra, 在这个里面 因此,根据Cassandra中的这个标签,https://github . com/krasserm/akka-persistence-Cassandra/issues/33,我看到这是由于输入有效负载大小的增加,所以我将cassandra.yml中的< code > commit log _ segment _ size _ in