为啥每笔交易都算作一个客户?
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 吗?
2021-07-09:股票问题6。给定一个整数数组 prices,其中第 i 个元素代表了第 i 天的股票价格 ;整数 fee 代表了交易股票的手续费用。你可以无限次地完成交易,但是你每笔交易都需要付