Go 語言并發(fā)清洗數(shù)據(jù)

某徒弟每日工作就是把數(shù)據(jù)庫(kù)里上十萬條數(shù)據(jù)取出來進(jìn)行一些操作(更新字段、檢查鏈接狀態(tài)等),把 Go 當(dāng) PHP 寫,一個(gè) for 循環(huán),一兩個(gè)小時(shí)過去了才能出結(jié)果(可能他就是想這么摸魚吧)。他說并發(fā)編程容易寫錯(cuò),需求又急:),幸好我之前寫過一點(diǎn),整一個(gè) demo 給他參考一下。

WaitGroup 和 數(shù)據(jù)庫(kù)分頁(yè)

package main

import (
    "fmt"
    "sync"
    "time"
)

func main() {
    start := time.Now()
    var wg sync.WaitGroup
    // 總條數(shù),一般從數(shù)據(jù)庫(kù) COUNT 出來
    count := 8823
    // 每頁(yè)處理的條數(shù)
    pageSize := 1000
    // 總頁(yè)數(shù) 向上取整
    page := (count + pageSize - 1) / pageSize

    // 每頁(yè)開一個(gè) goroutine
    for i := 0; i < page; i++ {
        wg.Add(1)
        go func(i int) {
            defer wg.Done()
            // 計(jì)算當(dāng)前頁(yè)的偏移量
            offset := i * pageSize
            // demo 拼接 Sql 然后 按ID順序 查出數(shù)據(jù)遍歷處理 記錄日志方便知道清洗的位置
            fmt.Println("sql 里的 limit ", offset, ",", pageSize)
        }(i)
    }
    wg.Wait()

    end := time.Since(start)
    fmt.Println("總共花了", end)
}

數(shù)據(jù)量更大,不能直接載入機(jī)器內(nèi)存

package main

import (
    "fmt"
    "sync"
    "time"
)

func main() {
    start := time.Now()

    // 大量數(shù)據(jù)一次開多個(gè) goroutine 全部取出數(shù)據(jù)放到機(jī)器內(nèi)存里,數(shù)據(jù)庫(kù)和內(nèi)存都可能會(huì)崩
    // 采用同步加異步的方式處理
    // 外部循環(huán)同步,處理完再開始下一輪 內(nèi)部循環(huán)并發(fā)執(zhí)行
    // 總條數(shù)
    count := 121231
    // 外部循環(huán)每次處理的條數(shù),考慮機(jī)器內(nèi)存可以適當(dāng)調(diào)整
    pageOutSize := 10000
    // 外部循環(huán)次數(shù)
    pageOut := (count + pageOutSize - 1) / pageOutSize

    for i := 0; i < pageOut; i++ {
        // 內(nèi)循環(huán)
        var wg sync.WaitGroup
        // 內(nèi)循環(huán)每頁(yè)處理的條數(shù)
        pageInnerSize := 1000
        // 內(nèi)部循環(huán)需要處理的總條數(shù)
        innerCount := pageOutSize
        if i == pageOut-1 {
            // 最后一頁(yè)了 只需要處理剩下的條數(shù)即可
            innerCount = count - pageOutSize*(pageOut-1)
        }
        // 內(nèi)循環(huán)的總頁(yè)數(shù),每頁(yè)開啟一個(gè) goroutine
        pageInner := (innerCount + pageInnerSize - 1) / pageInnerSize
        for j := 0; j < pageInner; j++ {
            wg.Add(1)
            go func(j int) {
                defer wg.Done()
                // 計(jì)算偏移量,需要考慮外部循環(huán)的輪次
                offset := i*pageOutSize + j*pageInnerSize
                // demo 拼接 Sql 然后 按ID順序 查出數(shù)據(jù)遍歷處理 記錄日志方便知道清洗的位置
                fmt.Println("sql 里的 limit ", offset, ",", pageInnerSize)
            }(j)
        }
        wg.Wait()
    }

    end := time.Since(start)
    fmt.Println("總共花了", end)
}

如果遇到錯(cuò)誤需要終止執(zhí)行,可以考慮將 WaitGroup 換成 errgroup,看具體需求。不足之處歡迎留言指正:)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容