首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >使用golang迭代超过10,000个postgresql行需要很长时间(20分钟),如何加快速度?

使用golang迭代超过10,000个postgresql行需要很长时间(20分钟),如何加快速度?
EN

Stack Overflow用户
提问于 2022-01-27 06:55:17
回答 1查看 586关注 0票数 0

请参阅下面的更新

我有一个函数,它使用gorm查询postrgesql数据库,然后遍历返回的所有行,并执行一些简单的计算,例如检查自上次通知和价格变化以来的时间差等。

令人失望的是,每10行需要1.2秒,这是线性的,谢天谢地

所以10,000行大约需要20分钟--我测试过了--并且缩放非常线性,5,000行大约需要10分钟等等。

如何减少这种情况,以便在几秒钟内完成我想要完成的任务?我计划在1,000,000行上迭代,这是我的期望,而--我希望能够在60秒或更短的时间内完成这个任务--超过100万行

背景故事:

我最初使用python框架构建了这个应用程序,我获得了一些类似的性能,与slower...but一样,python与动态输入的python相比,速度非常快,速度也非常愚蠢,但是大约2倍的性能,真的是这样吗?

所以,现在我用golang重新构建了整个应用程序,我仍然得到了这个糟糕的数字,现在是时候向我心爱的stackoverflow社区寻求帮助了。

对于如何最好地完成这一任务,我都会听取所有的建议。就像我说的,我可以处理100万行,所以我正在寻找一种未来的证明解决方案,以及一些类似于这样做的最佳方法。

我有下面任务所做的功能,这样你们就可以确切地看到迭代中涉及到的内容。

代码语言:javascript
运行
复制
package main

import (
    ...
    ...
    ...
    "fmt"
    "time"
)

func SendNotification() error {

    var productPrices []models.ProductPrice
    database.DB.Where("status = ?", "enabled").Find(&productPrices)

    timeNow := time.Now()
    for _, productPrice := range productPrices {
    
        if productPrice.LastNotified.IsZero() != true {
            lastNotified := productPrice.LastNotified
            timeNotified, _ := time.Parse(time.RFC3339, lastNotified.String())
            timeDuration := timeNow.Sub(timeNotified)
            timeDurationSeconds := timeDuration.Seconds()
            
            if uint64(timeDurationSeconds) < productPrice.Cooldown {
                continue
            }
        }

        // call an external API endpoint to get the current price of product based on product name
        setPrice := providers.GetProductPrice(providers.ProviderOne, productPrice.Name)

        if float64(setPrice) == 0 {
            continue
        }

        if productPrice.Type == "above" && productPrice.Price >= float64(setPrice) {
            continue
        }
        
        if productPrice.Type == "below" && productPrice.Price <= float64(setPrice) {
            continue
        }
        
        var user models.User

        database.DB.Where("id = ?", productPrice.UserId).First(&user)

        userTo := user.Email
        notificationMessage := fmt.Sprintf(`<!DOCTYPE html>
        <html lang="en">
        <body>
            <p> Welcome back %s
        </body>
        </html>`, user.Email)

        emailSubject := fmt.Sprintf("%s product notification for %s", productPrice.Name, user.Email)


        if productPrice.NotificationMethod == "email" {
            NotificationMethodEmail(SendEmail, userTo, notificationMessage, emailSubject)
        }
        if productPrice.NotificationMethod == "sms" {
            NotificationMethodSMS(SendSMS, userTo, notificationMessage)
        }
    }

    fmt.Println("processed this number of rows: ", len(productPrices), "\n")
    return nil  
}

func SendNotificationCron() {
    for {
        // i deally i will be running this every 2 minutes
        // when am able to speed things up to less than 60 seconds
        time.Sleep(30 * time.Minute)
        startTime := time.Now()
        SendNotification()
        endTime := time.Now()
        cronDuration := endTime.Sub(startTime)
        fmt.Println("duration: ", cronDuration, "\n")
    }
}


func main() {
    go notifications.SendNotificationCron()
}

下面是测试结果

代码语言:javascript
运行
复制
golang-task       | processed this number of rows:  10 
golang-task       | 
golang-task       | duration:  1.377023066s 
golang-task       | 
golang-task       | processed this number of rows:  10 
golang-task       | 
golang-task       | duration:  1.097972596s

golang-task       | prcoessed this number of rows:  4000 
golang-task       | 
golang-task       | duration:  7m59.548090252s 
golang-task       | 
golang-task       | prcoessed this number of rows:  4000 
golang-task       | 
golang-task       | duration:  7m41.425781293s

golang-task       | processed this number of rows:  8000 
golang-task       | 
golang-task       | duration:  14m10.168135161s
golang-task       | 
golang-task       | processed this number of rows:  8000 
golang-task       | 
golang-task       | duration:  16m2.544522843s  

您的帮助是非常感谢的,提前谢谢。

更新:

1

问题与每行进行外部API调用的行有关。

我按照注释中的建议更新了DB查询,使之使用JOIN,但仍然需要类似的时间来完成。

因此,我决定删除外部API块,并硬编码一个浮点值,用于所有行迭代,下面是删除外部API调用部分之后得到的内容。

代码语言:javascript
运行
复制
golang-task       | prcoessed this number of rows:  1000 
golang-task       | 
golang-task       | duration:  20.645238ms 
golang-task       | 
golang-task       | prcoessed this number of rows:  1000 
golang-task       | 
golang-task       | duration:  23.839578ms

golang-task       | prcoessed this number of rows:  10000 
golang-task       | 
golang-task       | duration:  252.026702ms 
golang-task       | 
golang-task       | prcoessed this number of rows:  10000 
golang-task       | 
golang-task       | duration:  234.026715ms

所以似乎问题是外部API调用部分。在这种情况下,我现在能做什么呢? 1:https://gorm.io

EN

回答 1

Stack Overflow用户

发布于 2022-01-27 08:40:16

您正在为每个用户进行查询。所以你的程序会因为这个而线性地减速。这个问题有一个基本的解决办法。我不知道你的数据结构,所以把这个答案作为解决问题的指南。

你必须加入到这里来:

database.DB.Where("status = ?", "enabled").Find(&productPrices)

您必须删除以下查询:

database.DB.Where("id = ?", productPrice.UserId).First(&user)

并从productPrices.User获取数据,而不是为用户查询n次。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/70874464

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档