我有一个非常简单的查询,返回几千行,只有两列:
SELECT "id", "value" FROM "table" LIMIT 10000;
发出sql.Query()
后,我使用以下代码遍历结果集:
data := map[uint8]string{}
for rows.Next() {
var (
id uint8
value string
)
if error := rows.Scan(&id, &value); error == nil {
data[id] = value
}
}
如果我直接在数据库上运行完全相同的查询,我会在几毫秒内得到所有结果,但Go代码需要更长的时间才能完成,有时几乎是10秒!
我开始评论代码的几个部分,似乎rows.Scan()
是罪魁祸首。
扫描将当前行中的列复制到指向的值 由dest。
如果参数的类型为* [] byte,则Scan会在该参数中保存一份副本 相应的数据。该副本由调用者拥有,可以 修改并无限期地持有。使用一个可以避免副本 改为类型为* RawBytes的参数;请参阅RawBytes的文档 限制其使用。如果参数的类型为* interface {}, 扫描复制底层驱动程序提供的值 转换。如果值是[]字节类型,则复制并且 来电者拥有结果。
如果我使用*[]byte
,*RawBytes
或*interface{}
,可以预期任何速度提升吗?
查看the code,看起来convertAssign()
function正在执行许多此特定查询所不需要的内容。所以我的问题是:如何让Scan
进程更快?
我考虑过将函数重载以期望预定类型,但在Go中不可能......
有什么想法吗?
答案 0 :(得分:2)
是的,您可以使用RawBytes
而rows.Scan()
将避免内存分配/复制
关于convertAssign()
函数 - 是的,它在Go 1.2中不是最优的,
但是他们在1.3中做出了重大改进
- http://code.google.com/p/go/issues/detail?id=7086
- sync.Pool的无锁实现
我有一些RawBytes
用法示例 - https://gist.github.com/yvasiyarov/9911956
此代码从MySQL表中读取数据,进行一些处理并将其写入CSV文件。 昨晚需要1分24秒才能生成4GB的CSV数据(约3000万行)
所以我很清楚在代码之外还有什么问题:rows.Scan()
更糟糕的使用方法不能让你延迟10秒。