何时在INT上使用TINYINT?


91

通常,我总是使用Ints。我知道,从理论上讲,这不是最佳实践,因为您应该使用可以保证存储数据的最小数据类型。

例如,最好tinyint在知道将要存储的唯一数据是1、0或null(极少有机会将其扩展到2或3)后使用。

但是,我知道这样做的唯一原因是出于存储目的-在一行中使用1个字节而不是4个字节。

除了节省硬盘驱动器上的空间之外,仅使用tinyint(或smallint什至bigint)有什么影响int


2
这是一个非常好的问题(+1)。MySQL具有SELECT ... PROCEDURE ANALYSE(),实际上建议表对于给定的SELECT应该具有的最小数据类型。这部分是我回答背后的灵感。
RolandoMySQLDBA 2011年

3
很好的问题,但准确地说,tinyint范围是0-255。位字段是0或1(或NULL)。tinyint的存储成本为1个字节。表中的每8位字段将花费1字节的存储空间。msdn.microsoft.com/en-us/library/ms187745.aspxmsdn.microsoft.com/en-us/library/ms177603.aspx
billinkc 2011年

@billinkc对。这就是为什么我提到了将列扩展为包含值2或3的可能性。如果包含2或3,则必须使用tinyint(以最小的比例)。
理查德

1
“例如,当您知道将要存储的唯一数据是1、0或null(极有可能将其扩展到2或3)时,最好使用tinyint。” 我会用一个ENUM这样的事情。这些存储为位字段,正如其他许多人在此处指出的那样,每条记录的少量节省总计将构成整个数据库的大量节省-如果对列进行索引,则更是如此。

2
@ user6665 I'd use an ENUM for such a thing.不在SQL Server中,您不会,因为它没有任何类型的枚举。
underscore_d

Answers:


92

磁盘空间很便宜……这不是重点!

不要再考虑存储空间,而要考虑缓冲池和存储带宽。在最末端,CPU缓存和内存总线带宽。链接的文章是该系列文章的一部分,着重介绍了集群键选择不佳的问题(INT vs GUID vs Sequential GUID),但它着重强调了字节可能造成的差异。

最重要的信息是设计问题。直到您到达VLDB区域,差异才会显示在具有适当规格的服务器上的单个数据库中,但是如果您可以节省一些字节,那为什么不这样做。

我想起了先前问题中描述的环境。每个SQL实例400多个数据库,大小从50mb-50GB不等。在该环境中,每个记录,每个表,每个数据库清理几个字节可能会产生很大的不同。


29

除了其他答案...

行和索引条目存储在8k页中。因此,每行3个字节的一百万行不是磁盘上的3 MB:它会影响每页的行数(“页面密度”)。

这同样适用于nvarchar到varchar,smalldatetime到datetime,int到tinyint等

编辑,2013年6月

http://sqlblog.com/blogs/joe_chang/archive/2013/06/16/load-test-manifesto.aspx

这篇文章指出

重要标准是基数和页与行的比率。

因此,数据类型的选择很重要


5
好点子。绝对最坏的例子是一个4028字节的行,其中包含要添加列的完全固定长度的列。添加smallint会将您带到4030(每页2行),但是int会将您推到边界(每页1行,每页浪费4028字节)。
Mark Storey-Smith

我曾经在int vs bigint上进行过性能测试。保存一百万条记录,比较时间和存储,然后一个接一个地检索它们,再次衡量性能。我没有发现主要差异。我将对int vs tinyint做相同的性能测试。我确实认为对于80%的应用程序可以忽略它,从而导致更一致的数据类型和更少的维护成本。
Saeed Neamati

1
@SaeedNeamati你可能想重读文章马克的答案(“ 你听说过......让我们刚刚得到这个工作? -我们会担心以后的表现......我听到这一切的时候...... ”)和GBN在这里。我认为带回家的是,任何低效的选择都将以适当的比例显示其条纹,OP的直觉也没有错。
ruffin

14

不仅要考虑表存储。如果使用int列是复合键的一部分的索引,则自然会希望索引页尽可能完整,这是由于索引项越小越好。

我绝对希望发现使用较小的数据类型来检查BTREE页面中的索引条目会更快一些。但是,索引条目中涉及的任何VARCHAR都会抵消(抵消)由于使用TIN而使用TINYINT所带来的性能提升。

尽管如此,如果索引条目具有复合条目并且全部都是整数,则整数按字节排列越小,则越好且越快。


13

当数据库变得更大时,所有事情变得变得复杂:

  • 维护时段需要扩大或重新安排
  • 备份(一天结束时的完整备份成为荒谬的浪费时间,因此您需要差异备份或什至日志备份,并每周执行一次完整备份,也许每月一次)
  • 性能维护成为一个耗时的工作(在数百万行的表上创建索引不会花费很短的时间来执行),并且需要重新安排时间,如果表很宽,情况会变得更糟...
  • 而且通过网络传输该100Gb备份并不是我的小菜一碟-特别是如果网络(出于某种未知原因)在断开连接到75Gb标记时固执的话……(发生在我正在安装的设备上正在备份到网络上的映射驱动器-网络)...

与此相关的数据类型是什么?一切。如果使用的行大小大于必需的行数,则数据库页面将比需要的空间更早填充,或者如果行大小不能在该页面上记录多于一条的记录,则甚至浪费空间。结果是需要写入和读取更多的页面,更多的RAM内存用于缓存(更大的记录需要更大的内存)。并且由于您指定的数据类型大于磁盘所需的数据类型,因此索引将遭受相同的问题-特别是如果您将2个BIGINT列的主键群集在一起,因为创建的任何其他索引都会在其定义上隐式复制该主键。

如果您知道某个表中的某些列将具有数百万个行,甚至有一个小表可以将FK扩展到数百万行,则不需要4个字节的整数来存储数据,但是2个字节将足够-使用SMALLINT。如果范围在0-255之间的值足够,请使用TINYINT。是/否标志?有BIT


9

尽管for tinyint与vs int有明显的区别,例如磁盘空间,页面拆分和维护时间,但for不会有任何区别varchar

那么为什么不将所有文本字段都声明为varchar(4000),因为它只会占用所需的空间?甚至可以保证您的数据将永远不会被截断。

答案当然是:

  1. 说明您的意图(因为没人会理解为什么名称字段应为4000个字符)
  2. 要进行验证,以确保没有人输入完整的传记作为名称。

这些完全相同的原因也适用tinyint


3
这是一个较旧的线程,但是澄清和验证不是唯一的原因。如果您将VARCHAR(4000)设置为应为VARCHAR(20),则查询计划将认为您的内存和CPU要求是该列的应有倍数。我还没有花时间去做,但是我猜想您可能可以通过查看VARCHAR(20)的查询计划,然后更改为VARCHAR(4000)并检查估计的成本来看到这一点。

3
@GeorgeShouse 在这里演示
Martin Smith
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.