Dapper dot net中的缓冲参数是什么作用?

53
Dapper dot net有一个名为buffer的参数(一个布尔值),但据我所知,它唯一的作用就是在返回结果之前将其强制转换为列表。根据documentation的说法:Dapper的默认行为是执行SQL并在返回时缓冲整个读取器。在大多数情况下,这是理想的,因为它最小化了数据库中的共享锁,并减少了数据库网络时间。然而,当执行大型查询时,您可能需要最小化内存占用,并仅在需要时加载对象。要这样做,请将buffered:false传递到Query方法中。我不确定将结果强制转换为列表如何实现这一点。我错过了什么吗?我的唯一想法是它应该将ExecuteReaderCommandBehavior设置为CommandBehavior.SequentialAccess(但它没有)。

1
重复的内容 dapper缓存/缓冲区解释,其中@Marc给出了略微更长、更详细的答案(尽管实际上是在这个问题之后提出的)。 - vgru
@Groo,如果另一个问题是在这个问题之后提出的,那么另一个问题就是重复的,而不是这个问题。 - kristianp
3个回答

55
据我所知,这个方法唯一的作用就是将结果强制转换为列表后再返回。
你没有漏掉什么。这就是关键区别。但实际上这不是一个“强制转换”:实际返回的对象非常不同。基本上,有两种读取数据的方式:
- 在流式 API 中,每个元素逐个生成;这非常节省内存,但如果您对每个项目进行大量后续处理,则意味着您的连接/命令可能会持续 “活动” 很长时间。 - 在缓冲 API 中,所有行在任何事情被生成之前都会被读取。
如果您要读取大量数据(数千到数百万行),则非缓冲 API 可能更可取。否则将使用大量内存,并且在第一行甚至可用之前可能会有明显的延迟。然而,在大多数常见情况下,读取的数据量在合理范围内,因此合理地将其推入列表中,然后将其交给调用方。这意味着命令/读取器等已经完成了之后再返回。
另外,缓冲模式还避免了那些常见的“该连接上已经有一个打开的读取器”之类的问题。

9
我不得不反对@chris-marisic的观点...使用buffered:true时,在那一行代码(data.ToList())上我遇到了多个“内存不足”异常。这并不是一个“数十亿行X数十亿列”的查询,只是一个包含大约30列的普通5-6k行SQL结果。
它真的取决于你的配置,例如你的SQL和IIS是否运行在同一台物理机器上,以及IIS机器安装了多少内存,页面文件设置等等。如果web服务器有2GB或更少的内存,请考虑为超重报告设置“buffered:false”。

深入探讨一下:data.ToList() 实际上会创建一个完全在内存中的列表。因此,如果您使用了 buffered:true,则很可能会将列表在内存中存储两次。 - frankhommers
通过 data.ToList(),我指的是 Dapper 源代码中的一行实际代码。 - jazzcat
这是一个很好的例子,说明为什么应该使用分页,与缓冲和非缓冲没有任何关系。 - Chris Marisic

2
实际应用中最好永远不要使用buffered: false
我发现,即使读取数百万行数据,使用缓冲结果比不使用更快且更节省内存。也许在您的表具有500列并且您正在读取数千万或数亿行数据时,存在交叉点。
如果您的结果集小于数十亿个值,则没有任何理由使用buffered: false
在实际分析中,我惊讶地发现从Sql Server读取数千兆字节的数据在标准缓冲模式下既更快(2-6倍),又更节省内存。即使是最微小的操作,通过索引将对象添加到不调整大小的稀疏数组中,使用多千兆字节的稀疏数组从未缓冲切换到缓冲后,加载时间也提高了2倍。插入数百万条记录时,使用缓冲的字典看到了6倍的加载时间提高(字典使用表的int PK作为键,因此尽可能基本的哈希码计算)。
关于性能的所有事情都必须进行分析。但是,我可以告诉您非常确定的一点,始终从Dapper的默认缓冲行为开始。

2
Chris,当你谈论填充数组或写入字典时,你是在谈论如何处理Dapper已经返回的结果吗?所以从技术上讲,这就是对自己的后缓冲读取代码进行分析?还是你在谈论在更低的层次上处理来自SQL Server的结果? - Rich
@Rich 使用foreach(item in unbuffered) dictionary.Add(item.Id, item) 比使用bufferedquery.ToDictionary(x=> x.Id) 慢6倍。使用非缓冲方式明显存在延迟成本。(我正在读取数百万和亿级别的数据集,注意我还使用了字典的正确容量构造函数) - Chris Marisic
1
不知道你正在使用什么样的工作流程,但在数百万行上使用 buffered = true 是非常浪费资源的。如果部署到资源有限的机器上,由于页面调度,它可能会非常缓慢或者崩溃,特别是对于典型商业数据的行大小。 - Kasey Speakman
3
@ChrisMarisic 我们正在使用S3的Multipart Upload支持。我们一次上传一部分,S3在完成后将它们组合起来。可以说,无缓冲和缓冲Dapper查询都有很好的用例。我们大多数API调用都使用缓冲,但我们也限制结果计数以避免资源问题。 - Kasey Speakman
2
@ChrisMarisic 啊,“要么按我的方式,要么滚蛋。” 没关系。旁观者可以做出自己的判断。 - Kasey Speakman
显示剩余12条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接