我有一个具有唯一约束的表

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/

10-15 00:35
查看更多