5

我在 Golang 中运行查询,从我的 Postgresql 数据库中选择多行。

我正在为我的查询使用以下导入

"database/sql"
"github.com/lib/pq"

我已经缩小到我的循环,以便将结果扫描到我的结构中。

// Returns about 400 rows
rows, err = db.Query('SELECT * FROM infrastructure')
if err != nil {
    return nil, err
}

var arrOfInfra []model.Infrastructure
for rows.Next() {
    obj, ptrs := model.InfrastructureInit()
    rows.Scan(ptrs...)
    arrOfInfra = append(arrOfInfra, *obj)
}
rows.Close()

上面的代码运行大约需要 8 秒,虽然查询速度很快,但 rows.Next() 中的循环需要整个 8 秒才能完成。

有任何想法吗?我做错了什么,还是有更好的方法?

我的数据库配置

// host, port, dbname, user, password masked for obvious reasons
db, err := sql.Open("postgres", "host=... port=... dbname=... user=... password=... sslmode=require")
if err != nil {
    panic(err)
}

// I have tried using the default, or setting to high number (100), but it doesn't seem to help with my situation
db.SetMaxIdleConns(1)
db.SetMaxOpenConns(1)

更新 1:

我将打印语句放在 for 循环中。下面是我更新的片段

for rows.Next() {
    obj, ptrs := model.InfrastructureInit()
    rows.Scan(ptrs...)
    arrOfInfra = append(arrOfInfra, *obj)
    fmt.Println("Len: " + fmt.Sprint(len(arrOfInfra)))
    fmt.Println(obj)
}

我注意到在这个循环中,它实际上会中途暂停,并在短暂休息后继续。它看起来像这样:

Len: 221
Len: 222
Len: 223
Len: 224
<a short pause about 1 second, then prints Len: 225 and continues>
Len: 226
Len: 227
...
..
.

稍后会在另一个行计数时再次发生,并在几百条记录后再次发生。


更新 2:

下面是我的 InfrastructureInit() 方法的片段

func InfrastructureInit() (*Infrastructure, []interface{}) {
    irf := new(Infrastructure)
    var ptrs []interface{}
    ptrs = append(ptrs,
        &irf.Base.ID,
        &irf.Base.CreatedAt,
        &irf.Base.UpdatedAt,
        &irf.ListingID,
        &irf.AddressID,
        &irf.Type,
        &irf.Name,
        &irf.Description,
        &irf.Details,
        &irf.TravellingFor,
    )
    return irf, ptrs
}

我不确定是什么导致了这种缓慢,但我目前在我的服务器上放置了一个快速补丁,以使用 redis 数据库并预缓存我的基础设施,将其保存为字符串。现在似乎没问题,但我现在必须同时维护 redis 和我的 postgres。

我仍然对这种奇怪的行为感到困惑,但我并不完全了解 rows.Next() 的工作方式 - 每次我调用 rows.Next() 时它都会查询数据库吗?

4

2 回答 2

0

你怎么想就这样做?

defer rows.Close()

var arrOfInfra []*Infrastructure
for rows.Next() {
    irf := &Infrastructure{}

    err = rows.Scan(
        &irf.Base.ID,
        &irf.Base.CreatedAt,
        &irf.Base.UpdatedAt,
        &irf.ListingID,
        &irf.AddressID,
        &irf.Type,
        &irf.Name,
        &irf.Description,
        &irf.Details,
        &irf.TravellingFor,
    ) 

    if err == nil {
        arrOfInfra = append(arrOfInfra, irf)
    }
}

希望这有帮助。

于 2018-06-20T08:54:40.360 回答
0

rows.Next()在巩固对工作方式以及可能影响性能的因素的理解的同时,我自己也走了一条奇怪的道路,因此考虑在此分享此内容以供后代使用(尽管很久以前就提出了这个问题)。

相关:

我仍然对这种奇怪的行为感到困惑,但我并不完全了解 rows.Next() 的工作方式 - 每次我调用 rows.Next() 时它都会查询数据库吗?

它不会进行“查询”,但它会在每次迭代时通过驱动程序从数据库读取(传输)数据,这意味着它可能会受到网络性能不佳等的影响。例如,如果您的数据库不在您运行 Go 代码的地方,则尤其如此。确认网络性能是否存在问题的一种方法是在您的数据库所在的同一台机器上运行您的 go 应用程序(如果可能)。

假设在上述代码中扫描的列不是非常大或具有自定义转换 - 读取约 400 行最多应该花费100 毫秒的时间(在本地设置中)。

例如 - 我有一个案例,我需要读取大约100k行,每行大约300B,这需要大约 4 秒(本地设置)。

于 2021-04-13T06:16:03.157 回答