【问题标题】:Choosing data type for MySQL?为 MySQL 选择数据类型?
【发布时间】:2011-08-01 03:23:57
【问题描述】:

我这几天一直在研究和阅读 SQL 数据类型(我知道...我知道,这不是很长),我很难掌握的一件事是如何选择最好的可扩展性、效率和易于访问的数据类型。

我认为如何选择基本数据类型(即 int 与 varchar)非常简单,但是如何在 blob 和文本类型之间进行选择。

MySQL 手册页很棒,但它们不是我们计算机极客喜欢的...高效。

我认为,如果我们能够编制一份 MySQL 数据类型列表、每种数据类型的一般优势/劣势以及何时选择该数据类型是有意义的,那就太棒了。

【问题讨论】:

  • google.com/search?q=performance+of+mysql+data+types(第一页上的任何内容)
  • 我喜欢这个问题,并希望得到一个深思熟虑的答案,这确实有助于提高我的服务器的速度(如果我选择了错误的类型)。同时,这篇文章与此有关(以及选择错误数据类型的问题)m.pinboard.in/blog/173
  • @Ben 当然是谷歌的链接,你不会认为通过阅读我的问题,我已经花了一些时间在谷歌上搜索和阅读什么是最好的。问题是没有以一种有效的方式对这些信息进行编译,并很好地描述了何时使用这些信息。如果您阅读 5 或 6 篇文章,您将获得大量信息,但我们可以在 wiki 样式列表中轻松匹配这些信息。它可以是可编辑的,并且比通过谷歌结果筛选要好得多。搜索到的最好的文章是 peachpits,它仍然不完整。 :/ 我认为这里的答案可能会更好。
  • @zobgib 这不是 StackOverflow 的意义所在。请参阅常见问题解答:stackoverflow.com/faq
  • “我认为如果我们能编制一份清单,那就太棒了”——那你恐怕来错地方了

标签: mysql sql database performance


【解决方案1】:

MySQL 字符串类型有两种变体:一种没有字符集标签,另一种有字符集标签。

一个固定长度的字符串,最后用空格填充,是 CHAR(n)。没有字符集标签的匹配类型是 BINARY(n)。将字符串“hello”存储在CHAR(255) CHARSET utf8 中将占用 765 个字节(用空格填充到全长的字符串,存储为 utf8,最坏的情况是 3 个字节/字符的空间使用分配 3*255 个字节)。

具有一个或两个长度字节且没有填充的可变长度字符串是 VARCHAR((n)。没有字符集标签的匹配类型是 VARBINARY(n)。将字符串“hello”存储在 VARCHAR(255) CHARSET utf8 中将占用 6 个字节(1 个长度字节加上 5 个字节用于实际文本)。以相同类型存储字符串 クリス 将占用 10 个字节(1 个长度字节加上 3 个字符,每个字符用 3 个字节来表示它们)。

mysql> select hex('クリス'), length(hex('クリス'))/2 as bytes;
+--------------------+--------+
| hex('クリス')      | bytes  |
+--------------------+--------+
| E382AFE383AAE382B9 | 9.0000 |
+--------------------+--------+
1 row in set (0.02 sec)

具有 1、2、3 或 4 个长度字节的可变长度字符串是 TINYTEXT、TEXT、MEDIUMTEXT 和 LARGETEXT。没有字符集标签的匹配类型有 TINYBLOB、BLOB、MEDIUMBLOB 和 LARGEBLOB。

TEXT/BLOB-like 类型与 VARCHAR/VARBINARY-like 类型的不同之处在于数据的存储方式和位置,请参阅http://www.mysqlperformanceblog.com/2010/02/09/blob-storage-in-innodb/ 了解有关 TEXT/BLOB-like 类型如何根据版本和ROW_FORMAT 设置。出于性能原因,您需要最新版本的 InnoDB 和“梭子鱼”格式表。

MySQL 无法处理任何大小超过 max_allowed_pa​​cket(默认值:1M)的数据,除非您在服务器端构建复杂且内存密集的解决方法。这进一步限制了 TEXT/BLOB 类类型可以做什么,并且通常使 LARGETEXT/LARGEBLOB 类型在默认配置中无用。

对于没有字符集标签的类型(BINARY、VARBINARY 和 %BLOB%),MySQL 将接受接收到的数据并将其写入磁盘。对于具有字符集标签的类型,MySQL 将查看您向服务器宣布的客户端字符集SET NAMES,以及定义的字符集标签是什么列。然后它将从连接字符集转换为列字符集并写入转换后的数据。您可以使用 HEX() 函数进行检查,例如SELECT HEX(str) FROM t WHERE id = ....

在检索时,使用SET NAMES 声明的连接字符集可能与写入时不同。 MySQL 将再次根据为此连接宣布的字符集检查列字符集标签,如有必要,将转换为连接字符集。

无论如何,与此类数据发生的磁盘 I/O 所花费的时间相比,这种转换的性能损失可以忽略不计,就性能而言,您选择哪种类型几乎无关紧要。相反,规则是:如果您正在处理文本数据,请选择带有字符集标签的类型,如果您不是,请选择不带字符集标签的类型。


一个经常被问到的相关问题:我应该选择 CHAR 还是 VARCHAR(分别是 BINARY 或 VARBINARY)?

对于 InnoDB,答案始终是:选择可变长度数据类型。 InnoDB 中的固定长度数据类型没有性能优势,但是如果您选择固定长度数据类型然后没有使用其中的所有空间,则会有巨大的大小损失。另外,固定长度的 SQL 字符串类型在结尾处使用空格进行填充和修剪的规则非常奇怪,您可能不会费心去学习这些规则。对于 MySQL,情况可能会有所不同,但几乎不会。


另一个相关问题:我应该为我的字符串选择 VARCHAR 还是 TEXT(分别为 VARBINARY 或 BLOB)?

答案是使用最新版本的 InnoDB、梭子鱼格式表,然后是 TEXT/BLOB。其原因在http://www.mysqlperformanceblog.com/2011/04/07/innodb-row-size-limitation/ 中有详细解释。这样做的结果是:使用前 Barracuda 格式的 VARCHAR 或 TEXT/BLOB,如果单行中有太多 InnoDB 行大小限制,则存在溢出 InnoDB 行大小限制的风险。


最后:我应该在数据库中存储文件/图像/其他大型 blob 或文本数据吗?

答案是:通常不会。与从文件系统提供文件相比,从数据库 (http://mysqldump.azundris.com/archives/36-Serving-Images-From-A-Database.html) 提供文件是一项昂贵的操作。如果可能的话,你会想要这样做。有一种方法可以解决这个问题,http://www.blobstreaming.org/,但这是一种先进的技术,需要您完全控制自己的执行环境,而在托管环境中永远不会出现这种情况。


四舍五入:MEMORY 引擎表中没有可变长度数据类型。因此,如果您在 EXPLAIN 输出中看到“使用临时”,这意味着

  • VARCHAR 在该临时表中转换为 CHAR
  • VARBINARY 转换为 BINARY

如果此进程的临时表变得大于 tmp_table_size 或 max_heap_table_size,它会即时转换为 MyISAM 格式并进入磁盘。

示例:您正在定义一个 Ruby Active Record 类 User,其中包含标记为 :string 的十个字段。这些中的每一个最终都是VARCHAR(255) CHARSET utf8 在您的Users 表中。

在您的代码库的其他地方,Users 的使用方式涉及using temporary 计划。您在负载下的磁盘操作中会立即死亡,因为Users 表的每一行现在至少在 MEMORY 中使用 7650 个字节,其中大部分是用作填充的空格。这会强制将临时表转换为 MyISAM 并写入磁盘。

  • 任何 %TEXT% 或 %BLOB% 类型都不能在 MEMORY 中表示,因此临时表会作为 MyISAM 进入磁盘,即使根据上述限制它本来足够小以保存在内存中。

这意味着任何具有 TEXT 或 BLOB 类型的查询以及具有“使用临时”的计划都需要重写,以避免临时表撞到磁盘。

【讨论】:

    【解决方案2】:

    关于 BLOB 与 TEXT(因为这是您帖子中唯一具体的问题):BLOB 用于二进制数据,而 TEXT 用于文本数据。

    使用适合您需求的最具体类型的列通常非常简单,如果它们都不适合您的使用,则回退到通用类型。

    【讨论】:

      【解决方案3】:

      对于 MySQL,有一个称为分析的过程,该过程将评估数据启发式方法,其理念是告知数据类型的最佳选择,并建议枚举的范围或值。

      一个快速的动态 concat 脚本,用于生成要运行的 SQL

      select CONCAT(' SELECT ', COLUMN_NAME, ' FROM ', TABLE_NAME, ' procedure analyse() ;' ) 
      FROM INFORMATION_SCHEMA.COLUMNS   
      WHERE table_schema ="yourDbName"   
      AND DATA_TYPE ="varchar"   
      AND CHARACTER_MAXIMUM_LENGTH > 190   
      AND COLUMN_KEY not in (' ') ;
      

      ** 上面的 SQL 不会评估 PK——假设它们不是文本字段

      当希望根据数据使用情况更改数据类型或通过移动或存储更小的数据包来提高效率时,该过程很有用。

      Percona 博客有一个很好的适用于 Drupal 的过程分析示例。 https://www.percona.com/blog/2009/03/23/procedure-analyse/

      其中一些研究是针对与更长的 utf8mb4 索引相关的压缩进行的 http://techblog.constantcontact.com/devops/space-the-final-frontier-a-story-of-mysql-compression/

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-02-25
        • 2015-01-07
        • 2020-12-29
        • 2021-05-23
        • 2015-06-20
        • 1970-01-01
        • 2011-07-21
        • 1970-01-01
        相关资源
        最近更新 更多