数据库调用goroutine失败而没有错误

时间:2017-01-02 05:20:47

标签: mysql go goroutine

我编写了一个脚本来将大量数据从一个数据库迁移到另一个数据库并使其工作正常,但现在我想尝试使用goroutines来通过使用并发数据库调用来加速脚本。由于更改为调用go processBatch(offset)而不仅仅是processBatch(offset),我可以看到启动了一些goroutine,但脚本几乎立即完成,实际上什么也没做。每次调用脚本时,启动的goroutine数量也会有所不同。没有错误(我可以看到)。

我仍然是goroutines和Go的新手,所以任何关于我可能做错的指示都非常感激。我已从以下代码中删除了与并发或数据库访问无关的所有逻辑,因为它在没有更改的情况下运行正常。我还留下了一个评论,我认为它失败了,因为没有在该行下面运行(Print不输出)。我也尝试使用sync.WaitGroup来交错数据库调用,但它似乎没有改变任何东西。

var (
    legacyDB     *sql.DB
    v2DB         *sql.DB
)

func main() {

    var total, loops int
    var err error

    legacyDB, err = sql.Open("mysql", "...")
    if err != nil {
        panic(err)
    }
    defer legacyDB.Close()

    v2DB, err = sql.Open("mysql", "...")
    if err != nil {
        panic(err)
    }
    defer v2DB.Close()

    err = legacyDB.QueryRow("SELECT count(*) FROM users").Scan(&total)
    checkErr(err)

    loops = int(math.Ceil(float64(total) / float64(batchsize)))

    fmt.Println("Total: " + strconv.Itoa(total))
    fmt.Println("Loops: " + strconv.Itoa(loops))

    for i := 0; i < loops; i++ {
        offset := i * batchsize

        go processBatch(offset)
    }

    legacyDB.Close()
    v2DB.Close()
}

func processBatch(offset int) {

    query := namedParameterQuery.NewNamedParameterQuery(`
        SELECT ...
        LIMIT :offset,:batchsize
    `)
    query.SetValue(...)

    rows, err := legacyDB.Query(query.GetParsedQuery(), (query.GetParsedParameters())...)
    // nothing after this line gets done (Println here does not show output)
    checkErr(err)
    defer rows.Close()

    ....

    var m runtime.MemStats
    runtime.ReadMemStats(&m)
    log.Printf("\nAlloc = %v\nTotalAlloc = %v\nSys = %v\nNumGC = %v\n\n", m.Alloc/1024/1024, m.TotalAlloc/1024/1024, m.Sys/1024/1024, m.NumGC)
}

func checkErr(err error) {
    if err != nil {
        panic(err)
    }
}

1 个答案:

答案 0 :(得分:3)

正如Nadh在评论中提到的那样,这是因为当main函数完成时程序退出,无论是否还有其他goroutines运行。要解决这个问题,* sync.WaitGroup就足够了。 WaitGroup用于您有多个并发操作的情况,并且您希望等到它们全部完成。可以在此处找到文档:https://golang.org/pkg/sync/#WaitGroup

不使用全局变量的程序示例实现看起来像替换

fmt.Println("Total: " + strconv.Itoa(total))
fmt.Println("Loops: " + strconv.Itoa(loops))

for i := 0; i < loops; i++ {
    offset := i * batchsize

    go processBatch(offset)
}

fmt.Println("Total: " + strconv.Itoa(total))
fmt.Println("Loops: " + strconv.Itoa(loops))

wg := new(sync.WaitGroup)
wg.Add(loops)

for i := 0; i < loops; i++ {
    offset := i * batchsize

    go func(offset int) {
        defer wg.Done()
        processBatch(offset)
    }(offset)
}

wg.Wait()