为啥每笔交易都算作一个客户?

Posted

技术标签:

【中文标题】为啥每笔交易都算作一个客户?【英文标题】:Why does each transaction count as a client?为什么每笔交易都算作一个客户? 【发布时间】:2017-10-23 20:33:45 【问题描述】:

我正在处理一堆文件,然后将结果转储到 PostgreSQL 中。我想同时处理许多工作人员,但不断收到错误“pq:抱歉,已经有太多客户”。每当工人> 100左右时,这似乎都会发生。 (为简单起见,下面的代码演示了该过程,但我只是在每个表中插入 1M 行,而不是处理文件)。

既然我在重复使用同一个 *db,为什么会出现这个错误?每笔交易都算作客户还是我做错了什么?

package main

import (
    "database/sql"
    "flag"
    "fmt"
    "log"
    "sync"

    "github.com/lib/pq"
)

func process(db *sql.DB, table string) error 
    if _, err := db.Exec(fmt.Sprintf(`DROP TABLE IF EXISTS %v;`, table)); err != nil 
        return err
    

    col := "age"
    s := fmt.Sprintf(`
        CREATE TABLE %v (
            pk serial PRIMARY KEY,
            %v int NOT NULL
    )`, table, col)

    _, err := db.Exec(s)
    if err != nil 
        return err
    

    tx, err := db.Begin()
    if err != nil 
        return err
    

    defer func() 
        if err != nil 
            tx.Rollback()
            return
        
        err = tx.Commit()
    ()

    stmt, err := tx.Prepare(pq.CopyIn(table, col))
    if err != nil 
        return err
    

    defer func() 
        err = stmt.Close()
    ()

    for i := 0; i < 1e6; i++ 
        if _, err = stmt.Exec(i); err != nil 
            return err
        
    

    return err



func main() 
    var u string
    flag.StringVar(&u, "user", "", "user")

    var pass string
    flag.StringVar(&pass, "pass", "", "pass")

    var host string
    flag.StringVar(&host, "host", "", "host")

    var database string
    flag.StringVar(&database, "database", "", "database")

    var workers int
    flag.IntVar(&workers, "workers", 10, "workers")

    flag.Parse()

    db, err := sql.Open("postgres",
        fmt.Sprintf(
            "user=%s password=%s host=%s database=%s sslmode=require",
            u, pass, host, database,
        ),
    )

    if err != nil 
        log.Fatalln(err)
    

    defer db.Close()
    db.SetMaxIdleConns(0)

    var wg sync.WaitGroup
    ch := make(chan int)

    for i := 0; i < workers; i++ 
        wg.Add(1)
        go func() 
            defer wg.Done()
            for i := range ch 
                table := fmt.Sprintf("_table%d", i)
                log.Println(table)
                if err := process(db, table); err != nil 
                    log.Fatalln(err)
                
            
        ()
    

    for i := 0; i < 300; i++ 
        ch <- i
    

    close(ch)
    wg.Wait()

我意识到我可以简单地增加 posgresql 设置,但想了解这个问题:How to increase the max connections in postgres?

【问题讨论】:

在“普通”Postgres 中,事务绝对不能算作“客户端连接”。我可以通过与数据库的单个连接来运行数百万个事务。您的程序显然没有(物理上)正确关闭与数据库的连接。 仅供参考,您被推迟到 err 的任务没有做任何事情。您只能分配给命名的返回值:play.golang.org/p/FKF2Z2KyzF @JimB 我不知道。谢谢! 您应该参数化您的 SQL 查询。 @squiguy 我得到 'pq: syntax error at or near "$1"'...似乎你不能将表名或列作为参数传递??? 【参考方案1】:

既然我在重复使用同一个 *db,为什么会出现这个错误?

我怀疑 Postgress 驱动程序正在为您的每个工作人员使用单独的连接,这在大多数情况下是一个明智的决定。

每笔交易都算作客户还是我做错了什么?

在您的情况下,是的,每个事务都算作客户端,因为您将 process() 作为 goroutine 调用。您正在创建与工作人员一样多的并发事务。由于您的每个事务都很长,所有这些事务都可能同时使用到数据库的单独连接,因此您达到了限制。

go func() 
        defer wg.Done()
        for i := range ch 
            table := fmt.Sprintf("_table%d", i)
            log.Println(table)
            if err := process(db, table); err != nil 
                log.Fatalln(err)
            
        
    ()

【讨论】:

为什么在我的情况下建立单独的连接很聪明,但在@a_horse_with_no_name 给出的示例中只有 1 个可以? “大多数情况下的明智决策”是指数据库驱动程序执行自己的数据库连接池(因为建立一个全新的数据库连接是一个相对缓慢的过程),并且它们的逻辑通常非常合理。 在这种情况下,驱动程序可能正在创建与数据库的全新连接,因为每个工作人员都会启动一个新事务(即,您要求数据库将每个工作保持隔离。) @kristen,我可以推荐阅读这本(免费获取)电子书吗? The Ultimate Guide to Building Database-Driven Apps with Go — 它很好地处理了此类池化问题。

以上是关于为啥每笔交易都算作一个客户?的主要内容,如果未能解决你的问题,请参考以下文章

与 Braintree 的每笔交易都需要支付 nonce 吗?

Paypal 商家接受或拒绝每笔交易

2021-07-09:股票问题6。给定一个整数数组 prices,其中第 i 个元素代表了第 i 天的股票价格 ;整数 fee 代表了交易股票的手续费用。你可以无限次地完成交易,但是你每笔交易都需要付

为啥 304 状态码算作“重定向”?

过滤过去 2 年每年至少有 3 笔交易的客户 Presto/SQL

从余额交易中获得条纹支付