MySQL中神秘的TIME情况

大约翻译:对MySQL内部实现中一个看似微不足道的细节的详细分析引起了人们对自然而然的讨论,这些讨论通常涉及开发著名的开源项目的方法的正确性。这位葡萄牙工程师实际上发现了什么,他以接近侦探故事的格式讲述故事……



2020年,许多人成为了一种奇怪的时间感知现象的受害者,但是一些数据库管理系统却需要更长的时间。当我的一个朋友Accord是流行的Discord机器人)的一个朋友遇到与EF Core一起使用的MySQL连接器发生以下异常时,我首先注意到了这一点



MySqlException: Incorrect TIME value: '960:00:00.000000'


并不是太懂MySQL(由于较早会变得显而易见的原因,我更喜欢PostgreSQL),我想了一秒钟的小时数是错误的。合理地假设TIME值限制为24小时,或者跨多个天的值需要不同的语法-例如,40:00:00:00代表40天。但是事实却变得更加复杂和混乱。



下一步是查看MySQL文档它显示为:



MySQL接收并以'hh:mm:ss'格式显示或显示TIME值(或以'hhh:mm:ss'格式显示较大的小时值)。


到目前为止,一切都很好:我们有问题的TIME值很适合这种格式,尽管明确指定了hh它们的事实hhh引起了人们的怀疑(时钟值超过999是什么?)文档中的下一句话部分地解释了所有内容,并激发了诸如“ What the ...?”之类的问题:



TIME值的范围可以从'-838:59:59'到'838:59:59'。


好吧...有些奇怪的范围。为此必须有很好的技术原因。839小时是34.958(3)天,整个范围恰好是6040798秒。该文档的内容如下:



MySQL可以多种格式识别TIME值,其中一些可以包含小数秒,最多6个小数位(微秒)。


换句话说,整个间隔为6,040,798,000,000微秒。再次,一些奇怪的数字。它远不是2的幂(介于2 42和2 43之间),因此MySQL似乎正在使用某些唯一的内部表示格式。但是在进入这个问题之前,让我指出这种类型有多严重。



这就是MySQL在测量时间间隔上所需要提供的全部内容,整个时间跨度只有一个多月。这个“小小”有多大?如您所见,它甚至不是整数天的倍数。



更糟糕的是,EF Core提供程序中最受欢迎的MySQL默认将.NET转换TimeSpan为TIME,尽管事实是TimeSpan可以包含几十个千年的间隔(它使用64位整数,并且允许的精度为10 -8 s)。将此与TIME中的几个月比较。其他人已经遇到了



此问题,相应问题中的讨论包含对SQL Server行为的引用:“这模仿了SQL Server的行为”。我检查了-实际上,SQL Server时间类型范围是00:00:00.0000000至23:59:59.9999999,通常比奇怪的TIME范围合理得多。 但是,让我们回到MySQL。产生如此异常范围的原因是什么?在MySQL设备手册中



表示在5.6.4版中,TIME类型已更改,并且支持几分之一秒。整个部分使用三个字节。如果将这三个字节全部用于秒编码,则将导致超过2330小时的时间跨度-远远超过当前的最大值838小时(尽管在转换TimeSpan'a时这不是很有用)。



这意味着在MySQL中对时间进行编码的过程浪费了很多时间-可能是出于易于使用的目的(尽管我不确定在什么情况下这是有意义的)。如果DBMS(以及开发人员对用户的处理方式的想法)致力于处理字符串,并且开发人员希望加快演示的速度,这也许是有道理的hh:mm:ss



所以看:



1 — (1 = , 0 = )

1 ( )

10 — (0-838)

6 — (0-59)

6 — (0-59)

— 24 = 3


这说明了一切,不是吗?好吧,让我们仔细看看。 10个小时的时间...范围是从零到838。我要提醒您2 10 = 1024,而不是838。阴谋正在增加势头...



当然,我不是第一个提出这个问题的人(之前我已经在StackOverflow上问过这个问题)。似乎所有内容都在“已接受”的答案中进行了说明,但是,838小时的奇怪选择首先是通过“与很早以前编写的应用程序的向后兼容性”来解释的,然后才提到这与与MySQL 3的兼容性有关-顺便说一句Windows 98被认为是一种新颖,而Linux甚至还不到10年。



在MySQL 3中,TIME类型也使用3个字节,只是它以完全不同的方式来使用它。其中一位也保留给该符号,但其余的23位对应于按如下方式获得的整数:小时×10,000 +分钟×100 +秒。换句话说,两个最低有效数字是秒,接下来的两个是分钟,其余两个是小时。 2 * 23是83888608,即838:86:08,因此此格式的最大有效时间值为838:59:59。



这种格式甚至比当前格式更不方便,因为它几乎需要任何时间进行乘法和除法(字符串格式化和解析除外)-这再次证明MySQL过于关注字符串IO并且并不真正在乎类型的存在。这对于内部操作和非基于字符串的协议来说非常方便)。



MySQL开发人员已经能够修复此类型很多次,或者至少提供了一种没有现有限制的替代方法。自从MySQL 3到今天,TIME类型已经改变了两次,但是每次奇怪的范围都保持不变-可能是出于兼容性原因。



我不知所措,无法想象这样的情况:扩大类型的值范围可能破坏应用程序兼容性:MySQL中的类型是否具有特定的溢出行为?哪个理智的程序员将依靠内部数据库类型约束来验证其应用程序中的任何内容?如果有这样一个人,为什么他真的会突然决定将这个838小时的荒谬限制转移到他的应用程序的数据模型中而没有任何更改?老实说,我什至不想知道这些问题的答案。



尽管在MySQL的历史上进行了两次重大的变革,但是TIME类型仍然很笨拙且有限。我认为,该程序的重​​点是未使用的“保留给以后的扩展”。我希望从长远来看,它将指向旧的,旧的TIME值,届时MySQL和/或MariaDB将拥有一个明智的时间类型,例如PostgreSQL中的INTERVAL,其范围为±178,000,000年零毫秒。准确性。



译者的PS



另请参阅我们的博客:






All Articles