Golang在for循环中执行MySQL查询性能缓慢问题探讨

Golang在for循环中执行MySQL查询性能缓慢问题探讨 我使用Golang配合这个驱动——github.com/go-sql-driver/mysql——来连接MySQL。Go的性能表现非常糟糕,或者是我遗漏了什么。

我们有一个包含帖子的表。我们获取帖子,然后针对每个帖子查询收藏表(检查用户是否收藏了该帖子以及是否喜欢它)。

posts := make([]*MembersModel.Post, 0, 6)

postsResults, err := Config.DB.Query(“SELECT id, b64, title, description, total_likes, total_favorites, published_date FROM posts ORDER BY id DESC LIMIT 6”)

defer postsResults.Close()

if err != nil {
    fmt.Println(err)
    panic(err.Error()) 
}

for postsResults.Next() {
    var postID int
    var b64 string
    var title string
    var description string
    var total_likes int
    var total_favorites int
    var published_date string

    postsResults.Scan(&id, &b64, &title, &description, &total_likes, &total_favorites, &published_date)

    var count int
    var favorited string

    fetchBookmarks := Config.DB.QueryRow("SELECT COUNT(*) FROM favorites where userID = ? and postID = ? and status = ?", userID, postID, "added").Scan(&count)

    if fetchBookmarks != nil {
        fmt.Println("error")
    }

    if count == 0 {
        favorited = "no"
    } else {
        favorited = "yes"
    }

    var countSubmitted int
    var likedPost string

    fetchLikes := Config.DB.QueryRow("SELECT COUNT(*) FROM likes where userID = ? and postID = ? and status=?", userID, postID, "liked").Scan(&countSubmitted)

    if fetchLikes != nil {
        fmt.Println("error")
    }

    if countSubmitted == 0 {
        likedPost = "no"
    } else {
        likedPost = "yes"
    }


    post := &MembersModel.JobList{
        PostID:        b64,
        Title: title,
        Description:     description,
        Likes:   total_likes,
        PubDate:   published_date,
        Bookmarked:   favorited,
        Liked:     likedPost,
    }

    posts = append(posts, post)

}

获取这些结果的平均时间——10秒!

如果排除循环内的MySQL调用,获取这些结果的时间是300毫秒

(喜欢-收藏表只有10行数据)


更多关于Golang在for循环中执行MySQL查询性能缓慢问题探讨的实战教程也可以访问 https://www.itying.com/category-94-b0.html

5 回复

这段代码有什么问题。

更多关于Golang在for循环中执行MySQL查询性能缓慢问题探讨的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


你收到你问题的答案了吗?

感谢您的回复。您能以上述代码为例吗?您将如何使用预处理语句来创建它们?以及如何复用单个连接?

对于每种类型的子查询,使用单一的预处理语句,而不是临时创建。

此外,你每次从连接池中获取一个新连接,这是另一个成本因素,尝试复用单个连接。

问题出在N+1查询模式上。每次循环都执行两次独立的数据库查询,导致性能急剧下降。应该使用JOIN或批量查询来优化。

优化方案:使用JOIN查询一次性获取所有数据:

query := `
    SELECT 
        p.id,
        p.b64,
        p.title,
        p.description,
        p.total_likes,
        p.total_favorites,
        p.published_date,
        CASE WHEN f.postID IS NOT NULL THEN 'yes' ELSE 'no' END as favorited,
        CASE WHEN l.postID IS NOT NULL THEN 'yes' ELSE 'no' END as likedPost
    FROM posts p
    LEFT JOIN favorites f ON p.id = f.postID 
        AND f.userID = ? 
        AND f.status = 'added'
    LEFT JOIN likes l ON p.id = l.postID 
        AND l.userID = ? 
        AND l.status = 'liked'
    ORDER BY p.id DESC 
    LIMIT 6`

rows, err := Config.DB.Query(query, userID, userID)
if err != nil {
    fmt.Println(err)
    panic(err.Error())
}
defer rows.Close()

posts := make([]*MembersModel.Post, 0, 6)
for rows.Next() {
    var postID int
    var b64 string
    var title string
    var description string
    var total_likes int
    var total_favorites int
    var published_date string
    var favorited string
    var likedPost string
    
    err := rows.Scan(&postID, &b64, &title, &description, 
        &total_likes, &total_favorites, &published_date,
        &favorited, &likedPost)
    if err != nil {
        fmt.Println(err)
        continue
    }
    
    post := &MembersModel.JobList{
        PostID:      b64,
        Title:       title,
        Description: description,
        Likes:       total_likes,
        PubDate:     published_date,
        Bookmarked:  favorited,
        Liked:       likedPost,
    }
    
    posts = append(posts, post)
}

如果无法使用JOIN,可以使用批量查询:

// 先获取所有帖子
postsRows, err := Config.DB.Query(`
    SELECT id, b64, title, description, total_likes, total_favorites, published_date  
    FROM posts 
    ORDER BY id DESC 
    LIMIT 6`)
if err != nil {
    fmt.Println(err)
    panic(err.Error())
}
defer postsRows.Close()

var postIDs []int
var tempPosts []struct {
    ID            int
    B64           string
    Title         string
    Description   string
    TotalLikes    int
    TotalFavorites int
    PublishedDate string
}

for postsRows.Next() {
    var p struct {
        ID            int
        B64           string
        Title         string
        Description   string
        TotalLikes    int
        TotalFavorites int
        PublishedDate string
    }
    
    err := postsRows.Scan(&p.ID, &p.B64, &p.Title, &p.Description, 
        &p.TotalLikes, &p.TotalFavorites, &p.PublishedDate)
    if err != nil {
        fmt.Println(err)
        continue
    }
    
    postIDs = append(postIDs, p.ID)
    tempPosts = append(tempPosts, p)
}

// 批量查询收藏状态
favoritesMap := make(map[int]bool)
if len(postIDs) > 0 {
    query := "SELECT postID FROM favorites WHERE userID = ? AND postID IN (?" + 
        strings.Repeat(",?", len(postIDs)-1) + ") AND status = 'added'"
    
    args := make([]interface{}, len(postIDs)+1)
    args[0] = userID
    for i, id := range postIDs {
        args[i+1] = id
    }
    
    rows, err := Config.DB.Query(query, args...)
    if err != nil {
        fmt.Println(err)
    } else {
        defer rows.Close()
        for rows.Next() {
            var postID int
            rows.Scan(&postID)
            favoritesMap[postID] = true
        }
    }
}

// 批量查询点赞状态
likesMap := make(map[int]bool)
if len(postIDs) > 0 {
    query := "SELECT postID FROM likes WHERE userID = ? AND postID IN (?" + 
        strings.Repeat(",?", len(postIDs)-1) + ") AND status = 'liked'"
    
    args := make([]interface{}, len(postIDs)+1)
    args[0] = userID
    for i, id := range postIDs {
        args[i+1] = id
    }
    
    rows, err := Config.DB.Query(query, args...)
    if err != nil {
        fmt.Println(err)
    } else {
        defer rows.Close()
        for rows.Next() {
            var postID int
            rows.Scan(&postID)
            likesMap[postID] = true
        }
    }
}

// 组装最终结果
posts := make([]*MembersModel.Post, 0, len(tempPosts))
for _, p := range tempPosts {
    favorited := "no"
    if favoritesMap[p.ID] {
        favorited = "yes"
    }
    
    likedPost := "no"
    if likesMap[p.ID] {
        likedPost = "yes"
    }
    
    post := &MembersModel.JobList{
        PostID:      p.B64,
        Title:       p.Title,
        Description: p.Description,
        Likes:       p.TotalLikes,
        PubDate:     p.PublishedDate,
        Bookmarked:  favorited,
        Liked:       likedPost,
    }
    
    posts = append(posts, post)
}

使用JOIN方案可以将查询次数从13次(1+6×2)减少到1次,性能提升最明显。批量查询方案将查询次数减少到3次,也能显著改善性能。

回到顶部