【问题标题】:MySQL 5.5 : Which one of the following is a better storage for a text/varchar field in innodb?MySQL 5.5:以下哪一项是 innodb 中 text/varchar 字段的更好存储?
【发布时间】:2013-02-21 07:40:16
【问题描述】:

要求:

Page#1 -> 显示用户和他们最近 10 篇博文的 1-2 行预览

Page#2 -> 显示带有全文的单个博文。

方法一:

MySQL table ->   userid -> varchar 50
                 post_id -> integer
                 post_title -> varchar 100
                 post_description -> varchar 10000

对于 page#1,从 blog_table 中选择 user_id、post_title、post_description。 post_description 的子字符串用于在列表中显示预览。

对于 page#2 ,选择 user_id 、 post_title 、 post_description where post_id = N

方法二:

 MySQL table ->   userid -> varchar 50
                  post_id -> integer
                  post_title -> varchar 100
                  post_brief -> varchar 250
                  post_description -> text

对于 page#1,从 blog_table 中选择 user_id、post_title、post_brief。

对于 page#2 ,选择 user_id 、 post_title 、 post_description where post_id = N

是否存储两列,一列作为 varchar,一列作为文本(因为它访问文件系统,并且只应在需要时查询),值得性能优势吗?

因为方法 2 将仅存储指向行中文本的指针,而方法 1 将存储完整的 varchar 10K 字符串在行中。它是否会影响可以驻留在 RAM 中的表数据量,从而影响查询的读取性能?

【问题讨论】:

  • 您不应该使用 VARCHAR 作为用户 ID 将此列更改为 INT
  • 您开始了赏金活动,指出当前的答案没有包含足够的细节。我不确定你在寻找什么样的细节。如果没有很多关于您的确切情况的更多详细信息,我认为没有很多细节可以添加到我的答案中 - 例如平均文本长度、记录数、您的硬件等。
  • 嗨 Hazzit,感谢您的贡献。但是您的答案中缺少我正在寻找的几条信息。文本将具有指向行中文件位置的指针,varchar 将在行数据中包含完整的 65K 字符。这对驻留在内存中的表数据量有何影响。
  • @DhruvPathak 我添加了一些关于内存要求的信息。
  • 您还应该记住,拥有一个单独的“简短文本”通常是有益的,因为许多文本不能自动缩短或在完成后失去意义。考虑一下包含 html 标记的文本,或者可能是顶部的免责声明。

标签: mysql innodb


【解决方案1】:

SQL 查询的性能主要取决于 JOIN、WHERE 子句、GROUP BY 和 ORDER BY,而不是检索到的列。如果检索到的数据明显更多,这些数据可能必须通过网络才能由您的编程语言处理,这些列才会对查询的速度产生显着影响。这里不是这样。

简短回答:两种建议设置之间的性能差异可能非常小。

为了获得良好的速度,您的 post_id 列应该有一个(唯一的)索引。您没有按任何其他列进行选择、排序或分组,因此数据可以直接来自表格,这是一个非常快速的过程。

您在这里谈论的是“页面”,所以我猜这些将呈现给用户 - 您似乎不太可能希望在同一页面上向人类显示数千篇博客文章的表格,因此您的陈述中实际上可能确实有 ORDER BY 和/或 LIMIT 子句,而这些子句并未包含在您的问题中。

但是让我们更深入地了解一下整个事情。假设我们实际上是直接从硬盘读取大量 TEXT 列,我们不会达到驱动器的最大读取速度吗?只检索一个 VARCHAR(250) 会不会更快,尤其是因为它为您节省了额外的 LEFT() 调用?

我们可以很快得到 LEFT() 调用。字符串函数非常快——毕竟只是 CPU 切断了一些数据,这是一个非常快的过程。它们产生明显延迟的唯一时间是在 WHERE 子句、JOIN 等中使用它们时,但这并不是因为这些函数很慢,而是因为它们必须运行很多次(可能是数百万次)才能甚至会产生单行结果,甚至更多,因为这些用途通常会阻止数据库正确使用其索引。

所以最后归结为:MySQL 从数据库中读取表内容的速度有多快。而这又取决于您使用的存储引擎及其设置。 MySQL 可以使用许多存储引擎,包括(但不限于)InnoDB 和 MyISAM。这两个引擎都为大对象(如 TEXT 或 BLOB 列)提供不同的文件布局(但有趣的是,还有 VARCHAR)。如果 TEXT 列存储在与行的其余部分不同的页面中,则存储引擎必须为每一行检索两个页面。如果它与其余部分一起存储,它将只是一页。对于顺序处理,这可能是性能上的重大变化。

这里有一些背景阅读:

长答案:这取决于:)

您必须在自己的硬件上进行大量基准测试才能真正确定哪种布局实际上更快。鉴于第二种设置在其附加列中引入了冗余,因此在大多数情况下它的性能可能会更差。当且仅当表结构允许较短的 VARCHAR 列适合磁盘上的同一页面,而较长的 TEXT 列适合另一个页面时,它将执行得更好。

编辑:有关 TEXT 列和性能的更多信息

对于 BLOB 和内存处理似乎存在一个常见的误解。相当多的页面(包括 StackOverflow 上的一些答案——我会尝试找到它们,并给出额外的评论)指出 MySQL 无法在内存中处理 TEXT 列(和所有其他 BLOB),因此总是性能猪。那不是真的。真正发生的事情是这样的:

如果您运行一个涉及 TEXT 列的查询并且该查询需要处理一个临时表,那么 MySQL 将不得不在磁盘上创建该临时表,而不是比在内存中,因为 MySQL 的 MEMORY 存储引擎无法处理 TEXT 列。见this related question

MySQL documentation 声明了这一点(该段落对于从 3.2 到 5.6 的所有版本都是相同的):

查询结果中的 BLOB 或 TEXT 列实例 使用临时表处理会导致服务器在 磁盘而不是内存,因为 MEMORY 存储引擎不 支持这些数据类型(参见第 8.4.3.3 节,“MySQL 如何使用 内部临时表”)。使用磁盘会导致性能损失, 因此,只有在查询结果中包含 BLOB 或 TEXT 列时 真的需要。例如,避免使用 SELECT *,它选择所有 列。

这是最后一句话让人们感到困惑 - 因为那只是一个坏例子。一个简单的SELECT *不会受到这个性能问题的影响,因为它不会使用临时表。例如,如果相同的选择由非索引列排序,则 必须使用临时表并且将受到此问题的影响。在 MySQL 中使用EXPLAIN 命令来确定查询是否需要临时表。

顺便说一句:这些都不会影响缓存。 TEXT 列可以像其他任何内容一样被缓存。即使查询需要一个临时表并且必须存储在磁盘上,如果系统有资源这样做,结果仍然可以缓存,并且缓存不会失效。在这方面,TEXT 列与其他任何内容一样。

编辑 2:更多关于 TEXT 列和内存要求...

MySQL 使用存储引擎从磁盘检索记录。然后它将缓冲结果并按顺序将它们交给客户端。下面假设这个缓冲区最终在内存中而不是在磁盘上(原因见上文)

对于 TEXT 列(和其他 BLOB),MySQL 将缓冲一个指向实际 BLOB 的指针。这样的指针仅使用几个字节的内存,但需要在将行交给客户端时从磁盘中检索实际的 TEXT 内容。 对于 VARCHAR 列(以及除 BLOB 之外的所有其他列),MySQL 将缓冲实际数据。这通常会使用更多内存,因为您的大部分文本将不仅仅是几个字节。 对于计算列,MySQL 也会缓冲实际数据,就像使用 VARCHAR 一样。

对此有几点注意事项:从技术上讲,BLOB 在移交给客户端时也会被缓冲,但一次只能缓冲一个 - 对于大型 BLOB 可能不是全部。由于此缓冲区在每一行之后被释放,因此不会产生任何重大影响。此外,如果 BLOB 实际上与行的其余部分存储在同一页中,则它可能最终被视为 VARCHAR。老实说,我从未要求在单个查询中返回 很多 BLOB,所以我从未尝试过。

现在让我们实际回答(现已编辑的)问题:

第 1 页。用户概述和简短的博客文章 sn-ps。

您的选择几乎就是这些查询

SELECT userid, post_title, LEFT(post_description, 250) FROM `table_method_1`  <-- calculated based on a VARCHAR column
SELECT userid, post_title, LEFT(post_description, 250) FROM `table_method_2`  <-- calculated based on the TEXT column
SELECT userid, post_title, post_brief FROM `table_method_2`                   <-- precalculated VARCHAR column
SELECT userid, post_title, post_description FROM `table_method_2`             <-- return the full text, let the client produce the snippet

前三个的内存需求相同。第四个查询将需要 less 内存(TEXT 列将作为指针缓冲),但 更多 流量到客户端。由于流量通常通过网络传输(在性能方面很昂贵),因此这往往比其他查询慢 - 但您的里程可能会有所不同。 TEXT 列上的 LEFT() 函数可以通过告诉存储引擎使用内联表格布局来加速,但这将取决于所存储文本的平均长度。

第 2 页。一篇博文

SELECT userid, post_title, post_description FROM `table_method_1` WHERE post_id=... <-- returns a VARCHAR
SELECT userid, post_title, post_description FROM `table_method_2` WHERE post_id=... <-- returns a TEXT

开始时内存要求很低,因为只会缓冲一行。由于上述原因,第二个将需要稍微少一点的内存来缓冲行,但需要一些额外的内存来缓冲单个 BLOB。

无论哪种情况,我很确定您不会关心仅返回单行的 select 的内存要求,所以这并不重要。

总结

如果您有任意长度的文本(或任何需要超过几千字节的文本),您应该使用 TEXT 列。这就是他们的目的。 MySQL 处理这些列的方式大部分时间都是有益的。

日常使用只需要记住两件事:

  • 如果您实际上不需要它们,请避免选择 TEXT 列、BLOB 列和所有其他可能包含大量数据的列(是的,包括 VARCHAR(10000))。当您只需要几个值时,“SELECT * FROM whatever”的习惯会给数据库带来很多不必要的压力。
  • 当您正在选择 TEXT 列或其他 BLOB 时,请确保选择不使用临时表。如有疑问,请使用 EXPLAIN 语法。

当您遵守这些规则时,您应该会从 MySQL 获得相当不错的性能。如果您需要进一步优化,则必须查看更精细的细节。这将包括存储引擎和相应的表格布局、有关实际数据的统计信息以及有关所涉及硬件的知识。根据我的经验,我通常可以摆脱对性能的贪婪,而无需深入挖掘。

【讨论】:

  • 答案没有提到任何关于 varchar 存储在内存中的内容,并且 text 每次都是磁盘访问。
  • 你是对的。答案没有提到这一点,因为它不是普遍正确的。在某些情况下,VARCHAR 列可以缓存在内存中,但在其他情况下不会。
  • 我刚刚编辑了我的答案以反映您的评论。 (见“编辑”标记)
【解决方案2】:

方法 2 看起来更好,但如果您在其中存储 HTML,post_brief 也可以是 TEXT 列,如果是纯文本,您可以将所有内容存储在一列中并使用

SELECT user_id, post_title, LEFT(post_description,255) AS post_brief FROM blog_table.

以 MySQL 5.6 为例,它更快,您可以在 InnoDB 中使用 FULLTEXT 索引,因此在搜索帖子时会有很大帮助

【讨论】:

    【解决方案3】:

    选项 2 对我来说也不错。由于博文会很大,在这些列上应用函数也需要时间。

    如果你问我,post_description 的数据类型应该是blob/text。尽管 blob 列不支持搜索,但这会是更好的选择。

    只有两列的缺点是,你必须确保 desc 和 brief 是同步的(也许你也可以把它作为一个特性)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-05
      • 1970-01-01
      • 2011-09-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多