执行语句是否总是占用结果集的内存?

时间:2010-11-12 16:37:05

标签: mysql perl resources prepared-statement dbi

一位同事告诉我,执行SQL语句总是将数据放入RAM / swap由数据库服务器。因此,选择大型结果集是不切实际的。

我认为这样的代码

my $sth = $dbh->prepare('SELECT million_rows FROM table');
while (my @data = $sth->fetchrow) {
    # process the row
}

逐行检索结果集,而不将其加载到RAM。 但我在DBI或MySQL文档中找不到任何引用。结果集是如何真正创建和检索的?它对简单的选择和连接是否有效?

4 个答案:

答案 0 :(得分:6)

这不是真的(如果我们讨论的是数据库服务器本身,而不是客户端层)。

MySQL可以缓冲整个结果集,但这不一定要完成,如果完成,则不一定在RAM

如果使用内联视图(SELECT FROM (SELECT …)),查询需要排序(显示为​​using filesort),或者计划需要创建临时表(显示为临时表),则结果集将被缓冲在查询计划中为using temporary

即使using temporaryMySQL仅在其大小未超过tmp_table中设置的限制时将表保留在内存中。当表超过此限制时,它将从memory转换为MyISAM并存储在磁盘上。

但是,您可以通过将MySQL指令附加到最外面的SQL_BUFFER_RESULT来明确指示SELECT缓冲结果集。

有关详细信息,请参阅docs

答案 1 :(得分:6)

你的同事是对的。

默认情况下,perl模块DBD :: mysql使用mysql_store_result,它确实读取所有SELECT数据并将其缓存在RAM中。除非你改变那个默认值,否则当你在DBI中逐行获取它时,它只是从那个内存缓冲区读取它们。

这通常是你想要的,除非你有非常大的结果集。否则,在从mysqld获取最后一个数据之前,它必须保持该数据准备就绪,我的理解是它会导致写入相同行的块(块?表?)。

请记住,现代机器有很多RAM。百万行结果集通常不是什么大问题。即使每行占用1 KB,也只有1 GB RAM加上开销。

如果您要处理数百万行BLOB,也许您确实需要mysql_use_result - 或者您希望以渐进使用LIMIT x,y的方式选择那些行。

有关详细信息,请参阅perldoc DBD::mysql中的mysql_use_result和mysql_store_result。

答案 2 :(得分:3)

不,这不是它的工作原理。

数据库不会在RAM / swap中保存行。

然而,它会尝试,并且mysql在这里努力尝试,尽可能地缓存(索引,结果等...)。您的mysql配置为不同类型的缓存(针对不同类型的存储引擎)提供了可用内存缓冲区的值 - 您不应该允许此缓存交换。

测试
底线 - 应该很容易使用客户端来测试它(我不知道perl的dbi,它可能,但我怀疑它,正在做一些迫使mysql加载准备的东西)。无论如何......测试一下:

如果你实际上在SELECT SQL_NO_CACHE million_rows FROM table上发布了一个准备工作,那么只能从数百万中获取几行。 然后,您应该将效果与SELECT SQL_NO_CACHE only_fetched_rows FROM table进行比较,看看票价如何。 如果表现具有可比性(且速度快),那么我相信你可以打电话给你同事的虚张声势。

另外,如果你启用了实际发给mysql的语句的日志并给我们一个转录本,那么我们(非perl人)可以给出关于mysql会做什么的更明确的答案。

答案 3 :(得分:1)

我对此并不十分熟悉,但它看起来像DBD :: mysql可以根据mysql_use_result属性预先获取所有内容或仅根据需要获取所有内容。请参阅DBD :: mysql和MySQL文档。