最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

PostgreSQL使用COPY協(xié)議高效批量數(shù)據(jù)寫入的實戰(zhàn)指南

 更新時間:2025年11月20日 09:45:40   作者:ezreal_pan  
這篇文章主要介紹了PostgreSQL的COPY協(xié)議,這是一種高效批量數(shù)據(jù)導(dǎo)入導(dǎo)出的二進制協(xié)議,適用于需要高效寫入大量數(shù)據(jù)的場景,COPY協(xié)議通過流式處理、事務(wù)安全和無參數(shù)限制等優(yōu)勢,顯著提升了數(shù)據(jù)寫入性能,并結(jié)合事務(wù)管理保證了數(shù)據(jù)一致性,需要的朋友可以參考下

問題背景

在開發(fā)過程中,我們經(jīng)常會遇到需要批量寫入大量數(shù)據(jù)到 PostgreSQL 數(shù)據(jù)庫的場景。當使用傳統(tǒng)的參數(shù)化插入語句時,可能會遇到如下錯誤:

pq: got 86575 parameters but PostgreSQL only supports 65535 parameters

這是因為 PostgreSQL 對單個查詢的參數(shù)數(shù)量有限制(通常為 65535)。傳統(tǒng)的解決方案是進行數(shù)據(jù)分片,分批寫入數(shù)據(jù)庫。但這種方法存在以下問題:

  • 需要手動管理分片邏輯
  • 多次數(shù)據(jù)庫往返,網(wǎng)絡(luò)開銷大
  • 事務(wù)管理復(fù)雜
  • 性能不夠理想

COPY 協(xié)議解決方案

COPY 協(xié)議簡介

PostgreSQL 的 COPY 協(xié)議是專門為高效批量數(shù)據(jù)操作設(shè)計的二進制協(xié)議,具有以下優(yōu)勢:

  1. 高性能:避免了 SQL 解析開銷,直接使用二進制格式傳輸數(shù)據(jù)
  2. 低內(nèi)存占用:流式處理,不需要在內(nèi)存中構(gòu)建龐大的 SQL 語句
  3. 事務(wù)安全:可以在事務(wù)中執(zhí)行,保證數(shù)據(jù)一致性
  4. 無參數(shù)限制:不受 PostgreSQL 參數(shù)數(shù)量限制

二進制協(xié)議原理

COPY 協(xié)議使用 PostgreSQL 的前后端協(xié)議進行數(shù)據(jù)傳輸,其工作流程如下:

  1. 啟動 COPY 模式:客戶端發(fā)送 COPY FROM STDIN 命令
  2. 數(shù)據(jù)傳輸:使用二進制格式按行發(fā)送數(shù)據(jù)
  3. 結(jié)束傳輸:發(fā)送特定的結(jié)束標記
  4. 確認完成:服務(wù)器返回處理結(jié)果

二進制格式避免了文本解析的開銷,直接使用網(wǎng)絡(luò)字節(jié)序傳輸數(shù)據(jù),大大提高了傳輸效率。

實戰(zhàn)實現(xiàn)

依賴庫

import (
    "github.com/lib/pq"
    "gorm.io/gorm"
)

核心實現(xiàn)代碼

// BatchCreate 批量創(chuàng)建消息接收者記錄 - 使用 COPY 協(xié)議
func (r *receiverRepo) BatchCreate(ctx context.Context, db *gorm.DB, data []*define.WecomMsgReceiver) (rowsAffected int64, err error) {
    db = r.WithTrace(ctx, db)
    db = db.Table(r.TableName())

    if len(data) == 0 {
        return 0, nil
    }

    // 過濾掉 nil 的數(shù)據(jù)
    validData := make([]*define.WecomMsgReceiver, 0, len(data))
    for _, item := range data {
        if item != nil {
            validData = append(validData, item)
        }
    }
    if len(validData) == 0 {
        return 0, nil
    }

    // 獲取底層 sql.DB
    sqlDB := db.DB()
    
    // 開始事務(wù)
    tx, err := sqlDB.BeginTx(ctx, nil)
    if err != nil {
        return 0, fmt.Errorf("開始事務(wù)失?。?+v", err)
    }
    defer func() {
        if err != nil {
            tx.Rollback()
        }
    }()

    // 創(chuàng)建 COPY writer
    stmt, err := tx.Prepare(pq.CopyIn(r.TableName(), "send_log_id", "user_id", "status", "created_at", "updated_at"))
    if err != nil {
        return 0, fmt.Errorf("準備 COPY 語句失?。?+v", err)
    }
    defer stmt.Close()

    // 批量寫入數(shù)據(jù)
    for _, item := range validData {
        _, err = stmt.Exec(item.SendLogID, item.UserID, item.Status, item.CreatedAt, item.UpdatedAt)
        if err != nil {
            return 0, fmt.Errorf("寫入數(shù)據(jù)失?。?+v", err)
        }
    }

    // 執(zhí)行 COPY
    _, err := stmt.Exec()
    if err != nil {
        return 0, fmt.Errorf("執(zhí)行 COPY 失?。?+v", err)
    }

    // 提交事務(wù)
    if err = tx.Commit(); err != nil {
        return 0, fmt.Errorf("提交事務(wù)失?。?+v", err)
    }
    
    rowsAffected = int64(len(validData))
    return rowsAffected, nil
}

代碼說明

  1. 數(shù)據(jù)驗證:首先過濾掉 nil 數(shù)據(jù),確保數(shù)據(jù)有效性 
  2. 事務(wù)管理:使用事務(wù)確保數(shù)據(jù)一致性,出錯時自動回滾
  3. COPY 準備:通過 pq.CopyIn 準備 COPY 語句,指定表名和列名
  4. 批量寫入:遍歷數(shù)據(jù)并執(zhí)行 Exec,但此時數(shù)據(jù)還在客戶端緩沖區(qū)
  5. 最終執(zhí)行:調(diào)用 stmt.Exec() 真正將數(shù)據(jù)發(fā)送到服務(wù)器
  6. 事務(wù)提交:提交事務(wù),完成批量寫入

完整測試用例

// 設(shè)置測試數(shù)據(jù)庫
func setupTestDB() (*gorm.DB, error) {
    ctx := context.Background()
    postgres, err := infrastructure.DialPostgres(ctx, infrastructure.PostgresConfig{
        Host:     "host",
        Port:     5432,
        Username: "postgres",
        Password: "xxxxx",
        Database: "xxxxx",
    })
    if err != nil {
        return nil, err
    }

    return postgres, nil
}

func setupLogger() factory.LogFactory {
    logger, _ := factory.NewJsonFactory(factory.NewLevel("info"), factory.NewZapOption(factory.AddCallerSkip(0)))
    return logger
}

func TestReceiverRepo_BatchCreate(t *testing.T) {
    db, err := setupTestDB()
    require.NoError(t, err)
    defer db.Close()

    // 創(chuàng)建日志工廠
    logger := setupLogger()

    // 創(chuàng)建 repository 實例
    repo := NewReceiverRepository(db, logger)

    // 準備測試數(shù)據(jù) - 20000 條記錄,使用負的 send_log_id 避免污染數(shù)據(jù)
    testData := make([]*define.WecomMsgReceiver, 0, 20000)
    now := time.Now()
    negativeSendLogID := int64(-100000) // 使用負的 send_log_id

    for i := 0; i < 20000; i++ {
        testData = append(testData, &define.WecomMsgReceiver{
            SendLogID: negativeSendLogID,
            UserID:    "test_user_" + fmt.Sprint(i),
            Status:    1,
            CreatedAt: now,
            UpdatedAt: now,
        })
    }

    ctx := context.Background()

    // 執(zhí)行批量插入
    rowsAffected, err := repo.BatchCreate(ctx, db, testData)

    // 驗證結(jié)果
    assert.NoError(t, err)
    assert.Equal(t, int64(20000), rowsAffected)

    // 驗證數(shù)據(jù)是否正確插入
    var count int64
    query := "SELECT COUNT(*) FROM wecom_msg_receiver WHERE send_log_id < 0 AND send_log_id >= ?"
    err = db.Raw(query, negativeSendLogID).Count(&count).Error
    assert.NoError(t, err)
    assert.Equal(t, int64(20000), count)

    // 清理測試數(shù)據(jù)
    deleteQuery := "DELETE FROM wecom_msg_receiver WHERE send_log_id < 0 AND send_log_id >= ?"
    result := db.Exec(deleteQuery, negativeSendLogID)
    assert.NoError(t, result.Error)
    assert.Equal(t, int64(20000), result.RowsAffected)

    // 驗證清理是否成功
    err = db.Raw(query, negativeSendLogID).Count(&count).Error
    assert.NoError(t, err)
    assert.Equal(t, int64(0), count)
}

性能對比

在實際測試中,COPY 協(xié)議相比傳統(tǒng)分批插入有顯著性能提升:

方案20000 條數(shù)據(jù)耗時內(nèi)存占用網(wǎng)絡(luò)請求次數(shù)
傳統(tǒng)分批插入~15 秒多次
COPY 協(xié)議~2 秒1 次

注意事項

  1. 錯誤處理:COPY 協(xié)議中某行數(shù)據(jù)錯誤可能導(dǎo)致整個批量操作失敗
  2. 數(shù)據(jù)類型:確保 Go 數(shù)據(jù)類型與 PostgreSQL 列類型匹配
  3. 連接池:長時間運行的 COPY 操作會占用數(shù)據(jù)庫連接
  4. 超時設(shè)置:對于大數(shù)據(jù)量,需要適當調(diào)整上下文超時時間

總結(jié)

通過使用 PostgreSQL 的 COPY 協(xié)議,我們成功解決了批量寫入時的參數(shù)數(shù)量限制問題,同時大幅提升了性能。這種方法特別適合數(shù)據(jù)遷移、日志批量處理等需要高效寫入大量數(shù)據(jù)的場景。

COPY協(xié)議結(jié)合事務(wù)管理,既保證了數(shù)據(jù)一致性,又能提供了接近原生的寫入性能,是PostgreSQL批量數(shù)據(jù)操作的優(yōu)選方案。

以上就是PostgreSQL使用COPY協(xié)議高效批量數(shù)據(jù)寫入的實戰(zhàn)指南的詳細內(nèi)容,更多關(guān)于PostgreSQL COPY批量數(shù)據(jù)寫入的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • postgresql synchronous_commit參數(shù)的用法介紹

    postgresql synchronous_commit參數(shù)的用法介紹

    這篇文章主要介紹了postgresql synchronous_commit參數(shù)的用法介紹,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • PGSQL 實現(xiàn)把字符串轉(zhuǎn)換成double類型(to_number())

    PGSQL 實現(xiàn)把字符串轉(zhuǎn)換成double類型(to_number())

    這篇文章主要介紹了PGSQL 實現(xiàn)把字符串轉(zhuǎn)換成double類型(to_number()),具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • PostgreSql中ALL與ANY區(qū)別小結(jié)

    PostgreSql中ALL與ANY區(qū)別小結(jié)

    ALL和ANY是PostgreSQL中用于與子查詢結(jié)果進行比較的強大操作符,本文主要介紹了PostgreSql中ALL與ANY區(qū)別,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-11-11
  • PostgresSQL安裝教程及初始使用操作方法

    PostgresSQL安裝教程及初始使用操作方法

    這篇文章介紹了如何在Linux系統(tǒng)上安裝和配置PostgreSQL數(shù)據(jù)庫,詳細介紹了PostgreSQL的數(shù)據(jù)類型,并解釋了如何使用序列來標識字段的自增長,感興趣的朋友跟隨小編一起看看吧
    2026-01-01
  • postgresql 實現(xiàn)取出分組中最大的幾條數(shù)據(jù)

    postgresql 實現(xiàn)取出分組中最大的幾條數(shù)據(jù)

    這篇文章主要介紹了postgresql 實現(xiàn)取出分組中最大的幾條數(shù)據(jù),具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-01-01
  • Postgres bytea類型 轉(zhuǎn)換及查看操作

    Postgres bytea類型 轉(zhuǎn)換及查看操作

    這篇文章主要介紹了Postgres bytea類型 轉(zhuǎn)換及查看操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • PostgreSQL日期時間字段類型選擇指南

    PostgreSQL日期時間字段類型選擇指南

    這段文章詳細介紹了在PostgreSQL中選擇合適的日期時間數(shù)據(jù)類型的方法,特別推薦使用timestampwithouttimezone類型來存儲精確到微秒的日期和時間,文章對比了多種日期時間數(shù)據(jù)類型的特點和適用場景,并并并強調(diào)了避免使用varchar等存儲日期時間的重要性
    2026-06-06
  • postgresql 實現(xiàn)啟動、狀態(tài)查看、關(guān)閉

    postgresql 實現(xiàn)啟動、狀態(tài)查看、關(guān)閉

    這篇文章主要介紹了postgresql 實現(xiàn)啟動、狀態(tài)查看、關(guān)閉的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-01-01
  • postgreSQL中的內(nèi)連接和外連接實現(xiàn)操作

    postgreSQL中的內(nèi)連接和外連接實現(xiàn)操作

    這篇文章主要介紹了postgreSQL中的內(nèi)連接和外連接實現(xiàn)操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-01-01
  • postgresql 查詢字符串中是否包含某字符的操作

    postgresql 查詢字符串中是否包含某字符的操作

    這篇文章主要介紹了postgresql 查詢字符串中是否包含某字符的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-01-01

最新評論

绿春县| 罗源县| 泸州市| 从江县| 沾益县| 大埔县| 富源县| 获嘉县| 黑龙江省| 邹城市| 峡江县| 顺平县| 勃利县| 屯门区| 老河口市| 威海市| 孙吴县| 仁布县| 泰安市| 西和县| 连城县| 上虞市| 康马县| 长汀县| 永胜县| 房产| 当阳市| 静宁县| 岳阳市| 子长县| 平顺县| 邹城市| 水城县| 浦城县| 江永县| 乌拉特后旗| 抚远县| 湖北省| 承德县| 逊克县| 开化县|