一位同事告诉我,执行SQL语句总是将数据放入RAM / swap由数据库服务器。因此,选择大型结果集是不切实际的。
我认为这样的代码my $sth = $dbh->prepare('SELECT million_rows FROM table');
while (my @data = $sth->fetchrow) {
# process the row
}
逐行检索结果集,而不将其加载到RAM。 但我在DBI或MySQL文档中找不到任何引用。结果集是如何真正创建和检索的?它对简单的选择和连接是否有效?
答案 0 :(得分:6)
这不是真的(如果我们讨论的是数据库服务器本身,而不是客户端层)。
MySQL
可以缓冲整个结果集,但这不一定要完成,如果完成,则不一定在RAM
。
如果使用内联视图(SELECT FROM (SELECT …)
),查询需要排序(显示为using filesort
),或者计划需要创建临时表(显示为临时表),则结果集将被缓冲在查询计划中为using temporary
。
即使using temporary
,MySQL
仅在其大小未超过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文档。