我有一个具有唯一约束的表
CREATE UNIQUE INDEX "bd_hash_index" ON "public"."bodies" USING btree ("hash");
我还有一个Go程序,该程序在 channel 上获取“body”值,通过散列过滤出重复项,然后仅将非重复项插入数据库。
像这样:
import (
"crypto/md5"
"database/sql"
"encoding/hex"
"log"
"strings"
"time"
)
type Process struct {
DB *sql.DB
BodiesHash map[string]bool
Channel chan BodyIterface
Logger *log.Logger
}
func (pr *Process) Run() {
bodyInsert, err := pr.DB.Prepare("INSERT INTO bodies (hash, type, source, body, created_timestamp) VALUES ($1, $2, $3, $4, $5)")
if err != nil {
pr.Logger.Println(err)
return
}
defer bodyInsert.Close()
hash := md5.New()
for p := range pr.Channel {
nowUnix := time.Now().Unix()
bodyString := strings.Join([]string{
p.GetType(),
p.GetSource(),
p.GetBodyString(),
}, ":")
hash.Write([]byte(bodyString))
bodyHash := hex.EncodeToString(hash.Sum(nil))
hash.Reset()
if _, ok := pr.BodiesHash[bodyHash]; !ok {
pr.BodiesHash[bodyHash] = true
_, err = bodyInsert.Exec(
bodyHash,
p.GetType(),
p.GetSource(),
p.GetBodyString(),
nowUnix,
)
if err != nil {
pr.Logger.Println(err, bodyString, bodyHash)
}
}
}
}
但定期我得到错误
在我的日志文件中。我无法想象它的状态,因为在执行插入操作之前,我会检查哈希的唯一性。
我确定当我调用
go processDebugBody.Run()
时,bodys表为空。该 channel 被创建为具有以下内容的缓冲 channel :
processDebugBody.Channel = make(chan BodyIterface, 1000)
最佳答案
当您使用sql.DB
在事务之外执行查询时,如果连接出现问题,它将自动重试。在当前的实现中,高达10倍。例如,注意 maxBadConnRetries
中的sql.Exec
。
现在,仅当底层驱动程序返回driver.ErrBadConn
并且规范说明以下内容时,它才真正发生:
我认为驱动程序的实现在执行此规则时有些粗心,但是背后可能包含一些逻辑。前几天我一直在研究lib/pq
的实现,并注意到这种情况是可能的。
正如您在评论中指出的那样,在看到重复项之前,您已经发出了一些SSL错误,因此这似乎是一个合理的猜测。
要考虑的一件事是使用事务。如果在提交事务之前丢失了连接,则可以确保将其回滚。此外,在连接断开时不会自动重新发送事务处理的语句,因此可以解决此问题–但是,您很可能会发现SSL错误直接传播到您的应用程序,因此您需要自己重试。
我必须告诉你,在使用Go 1.3的postgres上也看到SSL重新协商错误,这就是为什么我暂时禁用内部DB的SSL(连接字符串中的sslmode=disable
)的原因。我想知道版本1.4是否已解决该问题,因为changelog上的一件事是crypto/tls软件包现在支持RFC 7301(应用程序层协议(protocol)协商扩展的ALPN状态)中定义的ALPN。
关于postgresql - Go插入时偶尔出现PostgreSQL “Duplicate key value violates unique constraint”错误,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/30231978/