最近使用Go语言开发了一个简单的文件上传服务器。关键代码如下:
func saveFile(r io.Reader, n int64) error {
objectID := "1cb700e18a864328a561641736af934c" // just an example
objectPath := "/mnt/1c/b7/1cb700e18a864328a561641736af934c" // just an example
var createDirStart, createFileStart, copyStart time.Time
createDirStart = time.Now()
if err := os.MkdirAll(filepath.Dir(objectPath), 0755); err != nil {
return err
}
createFileStart = time.Now()
f, err := os.Create(objectPath)
if err != nil {
return err
}
defer func() {
syncStart := time.Now()
if err == nil {
err = f.Sync()
}
closeStart := time.Now()
errc := f.Close()
if err == nil {
err = errc
}
if err != nil {
os.Remove(objectPath)
}
fmt.Printf("objectId:%s, dirCost:%v, createFileCost:%v, copyCost:%v, syncCost:%v, totalCost:%v\n",
objectID, createFileStart.Sub(createDirStart), copyStart.Sub(createFileStart), syncStart.Sub(copyStart),
closeStart.Sub(syncStart), time.Since(createDirStart))
}()
copyStart = time.Now()
if _, err = io.CopyN(f, r, n); err != nil {
return err
}
return nil
}
对于每个上传的文件,都会生成一个 UUID 作为其 ID。为了保证一个目录下的文件不太多,我用UUID的前两个字符作为一级目录,后两个字符作为二级目录。二级目录里面的文件名就是UUID本身,大小一般不超过500MB,通常为10MB。通过打印每一步所花费的时间,我发现IO.copy花费的时间最多,最多达到几十秒,但此时系统压力并不大。通过用blktrace跟踪,发现Q2G项的MAX指标非常大。如果有专家能帮我看看问题出在哪里,我将不胜感激。非常感谢。
我的操作系统版本: Linux 版本 5.4.0-153-generic (gcc 版本 9.4.0 (Ubuntu 9.4.0-1ubuntu1~20.04.1)) #170-Ubuntu SMP 2023 年 6 月 16 日星期五 13:43:31 UTC 2023
我的Go版本: go版本go1.22.4 linux/amd64
我的硬件SSD版本: 型号:INTEL SSDPF2KX038TZ 固件版本:JCV10300 PCI 供应商/子系统 ID:0x8086 IEEE OUI 标识符:0x5cd2e4
摘自红帽性能调优指南:
如果Q2G很高,说明有很多请求同时排队。 这可能表明存储无法跟上 I/O 负载。
因此,您应该使用不同的机器对此进行基准测试 - Go 不太可能是导致 Queue-to-GetRequest 时间较长的罪魁祸首。也许超级用户的人知道更多,我认为Moishe Pippik博士的评论可能会触动你的神经:
超过缓存大小的大型写入可能会f a r变慢。