我正在使用PostgreSQL(9.4)数据库在Ruby on Rails中开发应用程序。对于我的用例,表的列将被非常频繁地查找,因为应用程序的重点是在模型上搜索非常具体的属性。

我目前正在决定是否使用integer类型或直接对列使用典型的字符串类型(例如character varying(255),这是Rails中的默认值),因为我不确定索引的性能差异。

这些列是枚举。对于具有的可能值的数量,它们具有固定的大小。大多数枚举长度不超过5,这意味着该索引在整个应用程序的生命周期中或多或少是固定的。因此,整数索引和字符串索引在节点数上是相同的。

但是,要索引的字符串可能长约20个字符,这在内存中大约是整数的5倍(如果整数是4个字节,并且字符串是纯ASCII码(每个字符1个字节),则成立)。我不知道数据库引擎如何进行索引查找,但是如果它需要“扫描”字符串直到完全匹配,那么从本质上讲这意味着字符串查找比整数查找慢5倍。直到匹配整数查找为止的“扫描”将是4个字节而不是20个字节。这就是我的想象:

查找值为(整数)4:


扫描..........................查找|正在获取记录...
| BYTE_1 | BYTE_2 | BYTE_3 | BYTE_4 | BYTE_5 | BYTE_6 | BYTE_7 | BYTE_8 | ... |


查找值是(字符串)“ some_val”(8个字节):


扫描.................................... ................................................... .....
发现|正在获取记录...
| BYTE_1 | BYTE_2 | BYTE_3 | BYTE_4 | BYTE_5 | BYTE_6 | BYTE_7 | BYTE_8 | ... |


我希望这是有道理的。基本上,因为整数占用的空间较小,所以它的“匹配”速度比字符串对应的速度更快。也许这是一个完全错误的猜测,但是我不是专家,所以这就是为什么我问你们!我想我刚刚找到的这个答案似乎支持我的假设,但是我想确定。

该列中可能使用的任何一个值都不会改变,因此索引本身不会改变(除非我为枚举添加了新值)。在这种情况下,使用integervarchar(255)是否会有性能差异,还是使用整数类型更有意义?


我问的原因是Rails的enum类型映射字符串键的整数,但它们并不意味着是面向用户的列。本质上,您无法验证枚举值是否有效,因为无效值会导致ArgumentError,然后才能运行任何验证。使用string类型将允许进行验证,但是如果有性能损失,我宁愿绕开验证问题。

评论

如果您只有5个枚举,则可能不值得在该列中添加索引。我不是专家,但基数值很低。例如,这就像为每个姓氏为:“ Singh,Mary或Mohammed”的人准备一本带有索引条目的电话簿....您将获得数百万个结果,这增加了存储这些索引值的大量开销没有任何相应的好处。如果我犯了错误,我会让数据库专家发表评论。

#1 楼

简短的答案:integer在各个方面都比varchartext更快。对于小桌子和/或短键并没有多大关系。差异随着键的长度和行数的增加而增加。


字符串... 20个字符长,在内存中大约是
整数的5倍(如果整数是4个字节,并且字符串是每个字符1个字节的纯ASCII
,则表示成立)


确切地说,字符类型(textvarchar)对于磁盘上的20个ASCII字符和RAM中的23个字节,它们恰好占据21个字节。详细评估:


varchar(n)的开销是多少?

同样重要的是:COLLATION规则会使字符数据排序更加昂贵-与数字数据类型不同:


当所有值均为36个字符时,使用char vs varchar可使索引查找明显更快

索引大小可能是导致性能差异最大的原因在大多数情况下。考虑每个索引元组的开销(基本上与表相同):4个字节用于项目标识符,8个字节用于索引元组标头。因此,integer的索引元组总计为20个字节(包括4个字节的对齐填充),而varchar(20)的索引元组具有20个ASCII字符,则为36个字节(还包括填充)。详细信息:


配置PostgreSQL的读取性能

除所有理论外,最好只是进行测试:


测量PostgreSQL表行的大小

Postgres 9.5引入了一种对字符数据的长字符串(关键字“缩写关键字”)进行排序的优化方法。但是Linux上某些C库函数中的错误迫使该项目对Postgres 9.5.2中的非C归类禁用该功能。发行说明中的​​详细信息。

但是,如果您实际上使用的是Postgres enum类型,则这些注意事项中的大多数都是无关紧要的,因为无论如何这些都是内部使用integer值实现的。手册:


一个enum值在磁盘上占用四个字节。


此外:varchar(255)以前对于SQL Server的早期版本有意义,它可以使用更有效的数据内部输入,最多255个字符。但是255个字符的奇数长度限制在Postgres中根本没有特殊意义。

评论


对于varchar(255)与例如varchar(260),SQL Server中没有隐藏的优化。 SQL Server 6.x可能存在这种情况,但很长一段时间以来并非如此。

– a_horse_with_no_name
16年5月9日在5:45

@a_horse_with_no_name:谢谢,我作了相应澄清。

–欧文·布兰德斯特(Erwin Brandstetter)
16年5月9日在6:06

很抱歉花了这么长时间接受这个,我在这个项目的开发上一直很慢;)

–克里斯·西里菲斯(Chris Cirefice)
16年8月15日在16:02

这个答案对Postgres 10仍然有效吗?

–玛蒂
18年5月19日在9:01

@Matty:仍然有效。而且,第11页也没有任何变化。

–欧文·布兰德斯特(Erwin Brandstetter)
18年5月19日在13:34