如果表中有大列,即使没有选择这些列,SQL查询在内存方面会更“昂贵”吗?

时间:2016-04-25 14:12:12

标签: mysql sql postgresql

说有一张桌子。该表用于跟踪书的章节。该表具有以下结构:

CREATE TABLE Chapters(
   id           INT PRIMARY KEY     NOT NULL,
   storyId      INT references Books(id) 
   title        TEXT    NOT NULL,
   body         TEXT    NOT NULL
);

现在,在此表格中, 正文 列将包含大量正文文本。想象一下平均章节在平均小说中有多大,你会得到一个粗略的想法。它将保留数百千字节(甚至一兆字节)的字符串数据。

现在,有些情况下您不需要本章的“正文”,但需要其他内容,例如标题。例如,如果我试图建立一本书中章节的“书籍索引”,我会执行这样的查询:

SELECT title FROM Chapters WHERE storyId = 1

当然,查询会给我回复故事章节的标题。但是,查询是否会使用大量内存,因为它必须将生成的行(列和所有行)加载到内存中,并且表中的每一行都有一个“重”列(“主体”列)?

我问这个是因为(根据我的理解 - 如果我错了,请纠正我)这就是它在文档存储数据库中的工作方式。 MongoDB中的每一行(或“文档”)必须首先加载到内存中,即使您只想从中返回单个字段。因此,如果我在MongoDB中执行类似的查询,它会通过将大的“body”字段加载到内存中来“浪费”内存,即使我想要返回的唯一字段是“title”字段。

对于大多数SQL实现,这些问题是否相同?我特别要求PostgreSQL,但我也有兴趣知道MySQL是否有不同的方式。

5 个答案:

答案 0 :(得分:6)

如果您没有选择该列,那么它就不应该占用资源。根据您使用的特定类型的SQL的工作方式,额外的空间可能会导致更大的页面,因此服务器必须遍历更大的磁盘空间才能找到您需要的行,但在您的行中例如,您通过ID(可能是索引)进行选择,因此即使这样也不会发生。即使在确实发生这种情况的情况下,额外的列也不会被放入内存中,只需在服务器搜索您需要的行时跳过它。

对于SQL的某些变体,像TEXT这样的东西甚至不会与数据行的其余部分一起存储 - 使用的指针指向磁盘上的位置。实际上是保持。在这些情况下,你甚至不会有更大页面的问题。

当然,所有这些都将特定于您正在使用的SQL变体的内部。我不是MySQL或PostgreSQL的专家,所以如果我的任何解释都不适用于那些特定的SQL实现,那么任何人都可以纠正我。

答案 1 :(得分:3)

即使您没有选择该列, 也会占用资源。在如果MyISAM表将使用更多VFS的情况下,对于Innodb将使用更多的缓冲池。为了缓存目的,记录(无论是从索引还是表中检索)都是完整的(但在MyISAM的情况下,VFS在提供另一层抽象的页面上运行,但是整个记录一次性读取,但可能会分期从缓存中逐出。)

即使它被立即丢弃,仍然会有性能影响 - 因为这些是可变长度记录,DBMS必须将流上的句柄前进到下一条记录,除了它使用索引的情况取消引用一个表,它不能直接跳到正确的位置(即使它可以,寻求是昂贵的)。即,对于全表扫描或索引扫描,记录的大小会影响性能。将MyISAM表转换为使用fixed-length records通常会对性能产生显着影响(但不要尝试使用BLOB和CLOB的表)。

包含记录的表的另一个问题是记录迁移,每个记录的大小可能会发生显着变化。记录按照定义的顺序累积,但在更新时,特定记录可能会变得太大而无法容纳先前占用的孔。然后将记录迁移到表的末尾。这也会对性能产生重大影响,也是固定大小记录更快的另一个原因。

答案 2 :(得分:2)

在PostgreSQL中,如果大字段大于物理页面大小(默认为8kB),则不会有性能损失。行必须适合单个物理页面,所以如果你有一个不错的章节大小,那么你将很容易超过这个限制。这样大的行可以压缩和/或烘烤(是的,像面包一样)。

TOAST存储大型字段,而其他字段以通常方式存储。这样可以避免在提取其他字段时I / O等上的任何开销。优秀的PG文档更详细地说明了这一点:

  

与更多方案相比,该方案具有许多优点   简单的方法,例如允许行值跨越页面。   假设查询通常通过比较来限定   相对较小的关键值,执行者的大部分工作都将是   使用主行条目完成。 TOASTed属性的重要值   只会在结果时拉出(如果完全选中)   set被发送到客户端。因此,主表要小得多   它的更多行适合共享缓冲区高速缓存   没有任何外线存储。排序集也缩小,排序   更常见的是完全在记忆中完成。

答案 3 :(得分:1)

答案是

  1. 是的确影响了性能。
    1. 在大多数情况下,只要您的数据库健康,您的表现就不会受到影响。
    2. ***我对健康的定义是a)表被正确编入索引,数据库被正确地进行碎片整理并且数据库设计正确。

答案 4 :(得分:1)

来自docs

  

对于在ROW_FORMAT=DYNAMICROW_FORMAT=COMPRESSED中创建的表格,BLOBTEXTVARCHAR列的值可能完全存储在页外,具体取决于它们的长度和整行的长度。对于存储在页外的列,聚簇索引记录仅包含指向溢出页的20字节指针,每列一个。是否在页外存储任何列取决于页面大小和行的总大小。当行太长而无法完全适合聚簇索引的页面时,InnoDB会选择最长的列进行页外存储,直到该行适合聚簇索引页。如上所述,如果某行在压缩页面上不适合自己,则会发生错误。

这意味着您的大多数TEXT字段都会偏离页面,并且不会严重影响不使用它们的查询。

当您执行以下查询时:

SELECT  title
FROM    Chapters
WHERE   storyId = 1

在正确索引的数据库上,会发生以下情况:

  1. 引擎会在storyId = 1的辅助索引中查找storyId的所有条目。在您的案例storyIdid中,索引是索引键和主键上的B树。您可以将其视为表的一个子集,它只包含两个字段:storyId, Id,始终对它们进行排序,并在原始表更新时更新。这允许快速定位任何给定storyId的值。

  2. 引擎获取上一步中找到的id的所有值,并为每个值查找表中的titleInnoDB中的表也是B-Tree,即它们是由主键id排序的。通过id找到每个给定记录的速度也很快,因为当您更新表时,订单会由引擎维护。

  3. 如果title存储在页外,引擎会获取第一个溢出页面的地址并遍历单链表,直到获得所有数据。如果页面上有title,则引擎会立即将其返回。

  4. 现在,还记得我在第2步说“快”吗?

    确切的速度取决于网页的大小。

    B-Tree的设计方式是它的深度,即你可以通过id找到页面的跳数,取决于它存储的页面数量(不是记录)以及它存储的时间长度主键是。

    这意味着如果您的记录很大(即页面上存储了长列),则每页获取的记录数量会减少,因此会有更多页面保存相同数量的记录,并且PK上的索引查找会减少高效。

    但是,如果title存储在页面上,则可以通过您不需要执行一个(或多个)其他页面查找来获得其值来减轻这一点。