MD5 字段的最佳数据类型是什么?

bob*_*opy 45 postgresql index database-design uniqueidentifier datatypes

我们正在设计一个众所周知的读取量大的系统(每分钟读取数万次)。

  • 有一个表names作为一种中央注册表。每行都有一个text字段representation和一个唯一的字段,key它是该字段的 MD5 哈希值representation1该表目前有数千万条记录,预计在应用程序的生命周期内会增长到数十亿条。
  • 还有许多其他表(具有高度变化的模式和记录计数)引用该names表。这些表之一中的任何给定记录都保证有一个name_key,它在功能上是names表的外键。

1:顺便说一句,正如您所料,此表中的记录一旦写入便不可变。

对于表以外的任何给定表names,最常见的查询将遵循以下模式:

SELECT list, of, fields 
FROM table 
WHERE name_key IN (md5a, md5b, md5c...);
Run Code Online (Sandbox Code Playgroud)

我想优化读取性能。我怀疑我的第一站应该是最小化索引的大小(尽管我不介意在那里被证明是错误的)。

问题:和列
的最佳数据类型是什么? 有理由使用over吗?或者?keyname_key
hex(32)bit(128)BTREEGIN

Erw*_*ter 56

数据类型uuid非常适合的任务。它只占用 16 个字节,而不是 RAM 中的 37 个字节用于varcharortext表示。(或磁盘上的 33 个字节,但奇数在许多情况下需要填充以使其有效地为40个字节。)并且该uuid类型具有更多优势。

例子:

SELECT md5('Store hash for long string, maybe for index?')::uuid AS md5_hash;
Run Code Online (Sandbox Code Playgroud)

看:

如果您不需要 md5 的加密组件,您可能会考虑其他(更便宜的)散列函数,但我会在您的用例中使用 md5(主要是只读的)。

一句警告:对于你的情况(immutable once written)一个函数依赖(伪自然)PK是好的。但在可能更新的情况下同样会很痛苦text。想想纠正一个错字:PK 和所有依赖的索引、“其他几十个表”中的 FK 列和其他引用也必须改变。表和索引膨胀、锁定问题、更新缓慢、引用丢失……

如果text可以在正常操作中改变,代理PK将是更好的选择。我建议一bigserial列(范围-9223372036854775808 to +9223372036854775807-这是9百万的三次223万亿3720000亿36十亿的东西),为不同的值billions of rows。在任何情况下都可能是个好主意:对于数十个 FK 列和索引,使用8字节而不是16字节!)。或随机UUID更大的基数或分布式系统。您总是可以另外存储所述 md5 (as uuid)以快速从原始文本中查找主表中的行。有关的:

至于您的查询


要解决@Daniel 的评论:如果您更喜欢不带连字符的表示,请删除连字符以进行显示:

SELECT replace('90b7525e-84f6-4850-c2ef-b407fae3f271', '-', '')
Run Code Online (Sandbox Code Playgroud)

但我不会打扰。默认表示就好了。问题真的不是这里的表示。

如果其他方应该采用不同的方法并将没有连字符的字符串混合在一起,那也没有问题。Postgres 接受几种合理的文本表示作为uuid. 手册

PostgreSQL 还接受以下替代形式的输入:使用大写数字、用大括号括起来的标准格式、省略部分或全部连字符、在任何一组四位数字后添加连字符。例子是:

A0EEBC99-9C0B-4EF8-BB6D-6BB9BD380A11
{a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11}
a0eebc999c0b4ef8bb6d6bb9bd380a11
a0ee-bc99-9c0b-4ef8-bb6d-6bb9-bd38-0a11
{a0eebc99-9c0b4ef8-bb6d6bb9-bd380a11}
Run Code Online (Sandbox Code Playgroud)

更重要的是,该md5()函数返回text,你会用decode()转换为bytea和的默认表示是:

SELECT decode(md5('Store hash for long string, maybe for index?'), 'hex')

\220\267R^\204\366HP\302\357\264\007\372\343\362q
Run Code Online (Sandbox Code Playgroud)

您将不得不encode()再次获得原始文本表示:

SELECT encode(my_md5_as_bytea, 'hex');
Run Code Online (Sandbox Code Playgroud)

最重要的是,由于内部开销,存储 as 的值bytea将占用 RAM 中的 20 个字节(磁盘上为 17 个字节,填充24个字节),这对于简单索引的大小和性能尤其不利。varlena

一切都uuid这里有利。

  • @AndrewWolfe:完全合法,IMO。不要被*名称*带走。它是一个 16 字节的实体,带有一组方便的提供的类型转换和输入/输出逻辑。手头的案例甚至实际上需要一个“唯一标识符”。您也可以在 `text` 列中存储各种字符数据 - 即使它根本不是“文本”。 (4认同)
  • @PirateApp,先解码:`SELECT encode(decode('tZmffOd5Tbh8yXaVlZfRJQ==', 'base64'), 'hex')::uuid;`。 (3认同)
  • @PirateApp:128 位数量中不存在“无效”UUID。我添加了一些内容来详细说明这一点。 (2认同)