MEDIUMINT在MySQL中应该避免使用吗?
MEDIUMINT在MySQL中应该避免使用吗?
在以下博文的评论中,我看到一个建议不要使用MEDIUMINT
的评论:
即使在MySQL中也不要使用[24位INT]。它愚蠢、慢,并且实现它的代码是让人恐怖的。
Stack Overflow上的一个回答还指出SQL Server、PostgreSQL和DB2不支持MEDIUMINT
:
是否应该避免使用MEDIUMINT
,还是应该在最能代表我存储的数据的情况下继续使用它?
在大的方案中,获取一行数据是一个很大的成本。简单的函数、表达式以及数据格式的时间在查询中是微不足道的。
另一方面,如果你的数据集太大无法保持在缓存中,获取行的I/O开销就更加显著。一个简单的经验法则是,非缓存行的时间比缓存行的时间长10倍。因此,缩小数据集(例如使用更小的*INT)可能会给你带来巨大的性能优势。
这个论点适用于...INT、FLOAT vs DOUBLE、DECIMAL(m,n)、DATETIME(n)等。对于[VAR]CHAR/BINARY(...)和TEXT/BLOB,需要进行不同的讨论。
对于那些有汇编语言背景的人来说…
- 表格很有可能会有各种各样的数字和字符串,从而阻碍了对值进行"对齐"的尝试。
- MySQL一直以来都处理各种硬件(big/little-endian,16/32/64位)并保持二进制兼容性。请注意提供的代码是如何避免对齐和字节序问题的。并且如果硬件只有16位,它会让编译器处理32位问题。
- 测试特殊情况的代码可能会比编写通用代码更加复杂。
- 我们通常只谈论整体行处理时间的不到1%。
因此,编写代码的唯一明智的方法是按字节级别处理,并忽略寄存器大小,并假设所有值都是未对齐的。
对于优化,按重要性顺序:
- 计算磁盘访问次数。访问磁盘是查询中最昂贵的部分。
- 计算所访问的行数。查找一行(通过BTree等)需要一些CPU。但是请注意,很少有安装是CPU受限的;那些受限的往往有较差的索引。(经验法则:InnoDB数据或索引块通常包含100行。)
- 现在才是解析行的时候。
经验法则:如果一个初步的优化没有带来10%的改进(通过粗略计算),就不要浪费时间在上面。相反,寻找一些更大的改进。例如,索引和汇总表通常提供10倍的性能提升(而不仅仅是10%)。
谢谢,那么一个MySQL的MEDIUMINT(3字节)整数呢?它会导致CPU额外的工作来对齐数据吗?这就是我在这里被告知的。
我之前的背景使我非常了解"字对齐"。但是,我声称,在这个环境中,它真的微不足道。我在我的回答中补充了一些内容。
在MySQL中,MEDIUMINT被存储为三个字节的值。但是当MySQL需要进行计算时,三个字节的MEDIUMINT会被转换为八个字节的无符号长整型(我假设现在没有人在32位上运行MySQL)。这样做有利有弊,但你明白“它很愚蠢,很慢,实现它的代码很可怕”的理由并不是技术上的,对吧?
我会说当数据在磁盘上的大小很重要时,MEDIUMINT是有意义的。也就是说,当一个表有很多记录,即使一个字节的差异(4个字节的INT与3个字节的MEDIUMINT)都意味着很多。这是一个相对罕见的情况,但是可能发生。
mach_read_from_3和mach_read_from_4是InnoDB用来从InnoDB记录中读取数字的原语。它们都返回ulint。我敢打赌,在任何工作负载下,你都不会注意到差异。
看一下代码:
ulint mach_read_from_3( /*=============*/ const byte* b) // in: pointer to 3 bytes { ut_ad(b); return( ((ulint)(b[0]) << 16) | ((ulint)(b[1]) << 8) | (ulint)(b[2]) ); }
你认为它比这个慢多了吗?
ulint mach_read_from_4( /*=============*/ const byte* b) // in: pointer to four bytes { ut_ad(b); return( ((ulint)(b[0]) << 24) | ((ulint)(b[1]) << 16) | ((ulint)(b[2]) << 8) | (ulint)(b[3]) ); }
好的,谢谢。我猜我是在确认MySQL处理mediumint的方式是否有什么特别糟糕的地方,或者它是否真的是非标准的,应该避免使用。听起来如果它适合存储的数据,那么使用它是可以的。
嗨,你是说,如果我在MySQL中使用这个:min(MEDIUMINTField)
,结果将是八个字节,而在Java中,例如,我需要一个long
类型的变量吗?
这取决于客户端库决定使用哪种类型来存储结果。