如何以尽可能短的大小存储数字?

How to store number in shortest possible size?

我将在我的网站中添加评论,我想要一个树结构,这样每个评论都可以有一个 parent。这在检索评论时会产生问题,因为每个评论都必须遍历 children,而这在数据库存储方面是不可接受的。

这个众所周知的性能问题有多种解决方案,我想使用简单、快速的路径前缀扫描 (SELECT * FROM comments WHERE path LIKE 'commentID/%'),在其中我将通过其所有 parents 和评论的 id 作为值。

实际上它看起来像:

[comment id]/[comment id]/[comment id]/.. = [comment id]

这样我可以一次找到某个路径的所有评论,也可以在需要时立即删除 children。

评论id将是无符号整数,由数据库指定为auto-incrementing键。

我正在研究的问题是如何尽可能短地表示评论 ID,这样路径索引就不会太长。

我希望使用无符号的 64 位长整数,它以二进制格式占用 8 个字节。如果是三个前驱,路径值将是 24 个字节 + 2 个字节作为定界符。如果我要使用字符串表示,那么它取决于数字的大小。如果每个 parent 的 id 是 5 位长 (12345),路径值将是 17 个字节长,这比用 9 个字节表示数字的二进制表示更有效。但是一旦 id 超过 8 位,字符串变体的效率就会降低,它比二进制格式更长。

所以我的问题是是否有另一种方法可以短整数表示?我在考虑双射函数。

一个明显的答案是使用 32 位整数而不是 64 位整数。您真的认为您网站上的 232 条评论会更多吗?无意冒犯,但我假设您没有实施 Facebook。到目前为止,您甚至还没有实现 Stack Overflow,which only has 83 million comments

如果您确实需要支持 264 个不同的值,我看不出双射函数会有什么帮助。双射相关的两个集合中的值每个至少需要 64 位,并且您不会保存任何内容。

另一个答案是改变你存储分层数据的方式,这样你就不用担心评论路径的长度了。您正在使用一种管理树状数据的方法,有时称为物化路径,这自然会对路径的深度施加限制,这就是您的问题所在。

在没有物化路径的情况下执行此操作的一种方法是仅使用存储层次结构的正常方式:每个评论都有对其自身父项的引用。然后你需要学习使用递归SQL查询,即supported in MySQL 8.0.

或者您可以使用其他替代方法来存储层次结构。我喜欢使用闭包 Table,请参阅我对 What is the most efficient/elegant way to parse a flat table into a tree?

的回答

哪种设计最适合您的应用取决于您需要针对这些数据进行的查询类型。您必须试验每个解决方案,看看它对您需要执行的用途的效果如何,例如添加评论、搜索评论线程、删除评论或线程等。

我希望 98% 的“路径”深度不超过 3:“123/456/789”。因此,通过使用 VARCHAR,这将占用 2+11 个字节(长度为 2;字符串为 11)。换句话说,我不认为 space 是个大问题。

我查看了一个有 71K 个问题的论坛。有 185K 条回复。忽略“threading”(对响应的响应),它表示 71K/185K (38%) 是一个数字(la“123”)。我没有关于线程的统计数据,但我上面 98% 的声明可能相当接近正确。

底线:我认为缩小表示“线程”的字符串是一个小问题。

(我有一个经验法则:如果提议的更改似乎没有节省 10%,请继续处理其他问题。)线程字符串可能远远少于磁盘的 10% space被所有其他东西所采用——尤其是文本。在我的调查中,“元信息”是 11MB;文本为 260MB。您的线程字符串将是 11MB 的一小部分。

为了保存 space,我将 compress() 文本(上面的 260MB)存储到 MEDIUMBLOB 中。散文(或代码或 XML 或 json)通常会缩小 3:1(仅 87MB)。在客户端执行compress/uncompress,从而卸载服务器和网络。

另一方面,您可能希望评论有一个 FULLTEXT 索引。所以不能压缩。