提问人:Weilson Wonder 提问时间:5/26/2017 最后编辑:Weilson Wonder 更新时间:6/19/2021 访问量:4321
Golang slow scan() 多行
Golang slow scan() for multiple rows
问:
我在 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 秒,虽然查询速度很快,但循环成行。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:
我将 print 语句放在 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。
我仍然对这种奇怪的行为感到困惑,但我并不完全是行的。Next() 工作 - 每次我调用行时,它都会对数据库进行查询。Next()?
答:
你怎么想像这样做?
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)
}
}
希望这有帮助。
我自己走了一条奇怪的道路,同时巩固了我对工作方式以及可能影响绩效的因素的理解,所以考虑在这里为后代分享这个(尽管很久以前就问过这个问题)。rows.Next()
相关:
我仍然对这种奇怪的行为感到困惑,但我不完全是怎么做到的 行。Next() 工作 - 每次我都会对数据库进行查询吗 调用行。Next()?
它不会进行“查询”,但它会在每次迭代时通过驱动程序从数据库读取(传输)数据,这意味着它可能会受到例如网络性能不佳的影响。例如,如果您的数据库不是运行 Go 代码的本地位置,则尤其如此。 确认网络性能是否存在问题的一种方法是在数据库所在的同一台计算机上运行 go 应用程序(如果可能)。
假设在上述代码中扫描的列不是非常大或具有自定义转换 - 读取 ~400 行最多应花费 100 毫秒(在本地设置中)。
例如 - 我有一个案例,我需要读取大约 100k 行,每行大约 300B,这需要 ~4 秒(本地设置)。
评论
model.InfrastructureInit()
- 这条线在做什么?您扫描了多少列?也可能来自~400行的扫描。真的有必要收回那么多记录吗?model.InfrastructureInit()
infrastructure
rows.Close()