MySQL:大型VARCHAR与TEXT?


845

我在MySQL中有一个消息表,该表记录了用户之间的消息。除了典型的ID和消息类型(所有整数类型)之外,我需要将实际消息文本另存为VARCHAR或TEXT。我将前端限制设置为3000个字符,这意味着消息插入数据库的时间绝不会超过此时间。

是否有使用VARCHAR(3000)或TEXT的理由?只是编写VARCHAR(3000)有点不合常理。我曾经在Stack Overflow上浏览过其他类似的文章,但是最好获得特定于这种常见消息存储类型的视图。


27
有点老了,但是我来到这里是因为遇到一个让我考虑的问题。在我的情况下,前端形式限制为2,000个字符,但是存储方法中隐含的编码将国际字符编码为多个字符(每个字符显然可以在3到12之间的任何位置)。所以我的2,000突然变成24,000。需要考虑的事情...
James S

3
我发现对于许多并发插入,文本要快得多。
Ray S.

1
@JamesS:utf8mb4 ...>。<
不可分割

10
@RickJames考虑发布更新的答案,而不是关闭问题
Yvette

3
@YvetteColomb-我添加了一个答案。我主要想摆脱已接受的答案,因为它已经过时了。我参加问答活动是因为有人引用了错误的信息,并说“ 754赞成,所以一定是正确的”。好的,我也编辑了批准的答案。(尽管感觉不合适)
Rick James

Answers:


811
  • TEXT并且BLOB 可以通过将该表存储在表之外,而该表仅具有指向实际存储位置的指针。它的存储位置取决于许多因素,例如数据大小,列大小,row_format和MySQL版本。

  • VARCHAR与表内联存储。VARCHAR如果大小合理,则速度更快,折衷的速度会更快,这取决于您的数据和硬件,因此您希望使用数据对实际场景进行基准测试。


148
+1:如果频繁检索数据(大多数查询都包含),则VARCHAR(内联存储)通常更快。但是,对于通常无法检索(即未由任何查询引用)的大量数据,最好不要内联存储数据。对于行内存储的数据,行大小有上限。
spencer7593 2011年

21
@Pacerier:避免“内联”存储的真正好处是可以增加块中可存储的行数,这意味着表行在InnoDB缓冲区高速缓存中占用的块更少(内存占用更少),并且意味着更少块与磁盘之间的传输(减少的I / O)。但是,如果存储在“行外”的列在很大程度上未被查询引用,那么这仅是性能上的好处。如果大多数查询都引用了那些“离线”列,那么这种好处就会大大消失。如果列适合最大行大小并且经常被引用,则首选内联。
spencer7593 2013年

231
“大小合适时,VARCHAR更快”。什么是“合理的”字符数100?1000?十万?
蒂姆·彼得森2013年

125
对于InnoDB,此答案不正确。如果给定行上的值适合页面大小(16KB,并且每个页面必须至少包含两行),则VARCHAR和BLOB / TEXT都将与其他列内联存储。如果该字符串太大,则会溢出到其他页面。有关详细说明,请参见mysqlperformanceblog.com/2010/02/09/blob-storage-in-innodb
Bill Karwin 2014年

14
@BillKarwin ...如果我正确理解,那么InnoDB varcharblob/ text上的小型文本项之间应该没有性能差异吗?那么,仅将每种类型都设置为varchar一个text类型并让DB管理内联与溢出是否明智?
ryvantage 2014年

473

您能预测用户输入多长时间吗?

VARCHAR(X)

案例:用户名,电子邮件,国家/地区,主题,密码


文本

案例:消息,电子邮件,评论,格式化文本,html,代码,图像,链接


中文字

案例:大型JSON正文,中短长度的书籍,CSV字符串


长文本

案例:教科书,程序,日志文件的年限,哈利·波特与火焰杯,科学研究记录


7
可预测性实际上是这里的一个附带条件。实际上,最大预期长度应该是决定因素。您提到的更可预测的项目只是那样,因为它们比其他项目
Andrew Barber 2012年

29
@ andrew-barber这是我的意思。其他所有文章都很好地解释了差异,但没有解释当您实际上必须在两者之间做出选择时的情况。我试图指出使用varchar可以预见地短是一个不错的选择,而使用文本任意地长则是一个不错的选择。
Michael J. Calkins 2012年

1
如果所有列都很短且可预测(例如,MAC地址,IMEI等……是永远不变的事物),则使用CHAR列,并且可以使行大小固定,如果使用MyISAM,则可以大大加快操作速度InnoDb也可以,尽管我不确定。
马特

1
@ MichaelJ.Calkins MySQL 5.6中发生的事情。现在,您还可以在InnoDB中进行全文搜索。见dev.mysql.com/doc/refman/5.6/en/fulltext-search.html
PhoneixS

7
字符数限制:TINYTEXT:255;文字:65,535;MEDIUMTEXT:16,777,215;长文本:4,294,967,29。
Victor Stoddard

218

只是为了阐明最佳做法:

  1. 文本格式的消息几乎应始终存储为TEXT(它们最终会任意长)

  2. 字符串属性应存储为VARCHAR(目标用户名,主题等)。

我知道您有一个前端限制,这很好,直到没有限制为止。*咧嘴*诀窍是将数据库与连接到数据库的应用程序分开考虑。仅仅因为一个应用程序对数据进行了限制,并不意味着数据本身就受到限制。

消息本身是什么使它们不能超过3000个字符?如果这只是一个任意的应用程序约束(例如,对于文本框之类的东西),请TEXT在数据层使用字段。


“什么才好,直到没有才好”是什么意思?“不是”指的是什么?
Pacerier,2015年

7
@Pacerier为您提供詹姆斯可能不是的示例:以Twitter为例,直到最近Twitter的PM限制为140个字符。他们认为这不再合情理,因此选择完全取消该限制。如果他们不考虑这个问题(我很确定他们可能会这么做...),他们将遇到上面概述的情况。
PaulSkinner,2015年

9
我只是建立了新数据库,我以为没有人可以在我们的小注释框中输入2000个以上的字符,然后,正如James所指出的,今晚它突然“不好”,因为用户输入了一个非常有效的注释,长度为2600个字符。我曾用varchar(2000)认为它不可能比这更长,而且我错了。所以是的,直到不是那样,这很棒。在我们的情况下,仅用了几天就显示出来。以下规则是Michael J. Calkins,我想从现在开始我将继续使用。消息,评论的文字。
Lizardx

1
@Pacerier“这很棒,直到不好为止”。换句话说,它几乎一直都有效并且很棒……除了那些不太好的例外情况。
有限赎罪

@Pacerier在所选答案的注释中提到了另一个有趣的示例,基本上他的前端限制为2,000个字符,但是引入的字符在一个代码页中,实际上,该代码页使用的字节数比普通字母多,因此他的数据库最终需要空间仅仅因为他必须考虑所引入字符的实际字节大小,才可以使用24k个字符。
RaptorX '16

32

免责声明:我不是MySQL专家...但这是我对问题的理解。

我认为TEXT存储在mysql行之外,而我认为VARCHAR存储为该行的一部分。mysql行有一个最大行长度。因此,您可以使用VARCHAR限制一行中可以存储多少其他数据。

同样由于VARCHAR构成了行的一部分,我怀疑查看该字段的查询会比使用TEXT块的查询稍快。


38
行长度限制为65,535个字节[ dev.mysql.com/doc/refman/5.0/en/column-count-limit.html ]。如果您的列是utf8编码的,则意味着3000个字符的varchar列最多可占用9000个字节。
Jan Fabry 2010年

7
UTF-8字符最多可以包含4个字节,因此我想您的意思是12,000个字节(除非这里有我不理解的MySQL问题)。
raylu 2011年

13
@raylu MySQL的UTF-8是“伪UTF-8”,因为它最多每个字符最多支持3个字节,因此无法在MySQL的UTF-8中直接将Unicode字符存储在BMP平面之外。这在MySQL 5.5中已修复。
Pacerier

2
我相信此断言仅对MyISAM有效。我找不到确切的来源,但我相信InnoDB TEXT也会在表中存储内联。
dotancohen 2013年

2
@dotancohen我在这里找到了一个资料来源,说明使用InnoDB存储可变长度数据可能会有所不同(可以存储在行内或行内)mysqlserverteam.com/externally-stored-fields-in-innodb
KiX Ortillan,2015年

30

简短的回答: 没有实用性,性能或存储差异。

长答案:

VARCHAR(3000)(或任何其他大限制)和之间(在MySQL中)基本上没有区别TEXT。前者将截断3000个字符 ; 后者将截断为65535 字节。(我区分字节字符,因为一个字符可以占用多个字节。)

对于较小的限制VARCHAR,与相比有一些优势TEXT

  • “较小”表示191、255、512、767或3072等,具体取决于版本,上下文和CHARACTER SET
  • INDEXes在可索引的列数方面受到限制。(767或3072 字节;这取决于版本和设置)
  • 由复杂SELECTs系统创建的中间表以两种不同的方式处理-MEMORY(更快)或MyISAM(更快)。如果涉及“大”列,则会自动选择较慢的技术。(8.0版中将进行重大更改;因此,此项目符号可能会有所更改。)
  • 与上一项相关,所有TEXT数据类型(与相对VARCHAR)直接跳到MyISAM。也就是说,TINYTEXT对于生成的临时表,它自动比等效表更糟VARCHAR。(但这将讨论引向了第三方向!)
  • VARBINARY就像VARCHAR; BLOB就像TEXT

反驳其他答案

最初的问题是一件事(使用哪种数据类型)。接受的答案回答了其他问题(记录外存储)。该答案现在已过期。

当启动回答该线程时,InnoDB中只有两种“行格式”。此后不久,又引入了两种格式(DYNAMICCOMPRESSED)。

对于存储位置TEXTVARCHAR()基于大小,而不是数据类型的名称。有关大型text / blob列的开/关记录存储的最新讨论,请参见this


1
这里有一些很好的见解。这应该是公认的答案。
Kosta Kontos

2
@KostaKontos-感谢您的称赞和错字修复。当我认为需要更好的答案时,即使8年800次投票来不及,我也会添加一个答案。
瑞克·詹姆斯

7

前面的答案在主要问题上的坚持不够:即使在非常简单的查询中,例如

(SELECT t2.* FROM t1, t2 WHERE t2.id = t1.id ORDER BY t1.id) 

可能需要一个临时表,如果VARCHAR涉及到一个CHAR字段,它将转换为临时表中的一个字段。因此,如果您在表中说有一个VARCHAR(65000)字段500 000行,则仅此列将使用6.5 * 5 * 10 ^ 9字节。这样的临时表无法在内存中处理,而是被写入磁盘。可以预期的影响是灾难性的。

来源(带有指标):https : //nicj.net/mysql-text-vs-varchar-performance/ (这是指“标准”(?)MyISAM存储引擎中TEXTvs 的处理VARCHAR。在其他方面可能有所不同,例如InnoDB。)


3
InnoDB:5.7版也是如此。在8.0中,varchar临时长度是可变的。
瑞克·詹姆斯

3

VARCHAR和TEXT之间存在巨大差异。虽然可以为VARCHAR字段建立索引,但不能为TEXT字段建立索引。VARCHAR类型字段是内联存储的,而TEXT是脱机存储的,实际上只有TEXT数据的指针存储在记录中。

如果必须索引字段以进行快速搜索,更新或删除,而不是使用VARCHAR(无论大小如何)。VARCHAR(10000000)永远不会与TEXT字段相同,因为这两种数据类型本质上是不同的。

  • 如果您仅将字段用于归档
  • 你不在乎数据速度检索
  • 您关心速度,但是您将在搜索查询中使用运算符'%LIKE%',因此建立索引不会有太大帮助
  • 您无法预测数据长度的限制

而不是去TEXT。


部分误导性信息:TEXT列不能完全索引。在索引中包括TEXT列时,必须指定长度。此外,在VARCHAR> 255的情况下,无法对VARCHAR整体进行索引,因为索引大小有最大长度。
eRadical

2

Varchar用于小数据,例如电子邮件地址,而Text用于大数据,例如新闻文章,Blob用于二进制数据,例如图像。

Varchar的性能更强大,因为它完全依靠内存运行,但是varchar(4000),例如,如果数据太大,则情况并非如此。

另一方面,文本不会保留在内存中,并且会受到磁盘性能的影响,但是您可以通过在单独的表中分离文本数据并应用左联接查询来检索文本数据来避免这种情况。

Blob的速度要慢得多,因此仅当您没有太多数据(例如10000张图像,将花费10000条记录)时才使用它。

请遵循以下提示以实现最大速度和性能:

  1. 使用varchar作为名称,标题,电子邮件

  2. 使用文本处理大数据

  3. 在不同表格中分开文字

  4. 在ID(例如电话号码)上使用左联接查询

  5. 如果您要使用Blob,请应用与“文字”中相同的提示

这将使表在数据大于10 M且保证最大大小为10GB的表上花费毫秒的时间。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.