微秒时间戳的DECIMAL长度是多少?

11

我希望能将PHP的microtime作为MySQL中的时间戳进行存储。

,最好使用DECIMAL进行存储,但我找不到理想的大小。

有人知道microtime(true)返回的最大值是多少吗?这样我就可以将其作为我的数据类型长度。

我应该选择可变的DECIMAL长度吗?


2
使用长度为14,向左10个字符,向右4个字符。 - Samuel Cook
@SamuelCook 你从哪里得到的? - user1382306
2
现在的时间是 time(),如果只有10位数字,而 microtime() 则只会浮动4位小数。 - Samuel Cook
4个回答

16

简而言之,使用 microtime(false) 并将结果以一百万分之一秒为单位存储在 MySQL bigint 中。否则,您必须学习所有有关浮点运算的知识,这是一项非常复杂的任务。

PHP 的 microtime 函数从一个系统调用中获取 Unix 时间戳(目前约为十六进制 50eb7c00 或十进制 1,357,609,984),并从另一个系统调用中获取微秒时间。然后将它们转换成字符串。然后,如果使用 (true) 调用它,它会将该数字转换为一个 64 位 IEEE 745 浮点数,PHP 称之为 float

截止到今天,需要在小数点左侧使用十个十进制数字来存储整数 UNIX 时间戳。直到公元 2280 年左右,您的后代才需要十一位数字。需要使用小数点右侧六个数字来存储微秒。

您无法获得完全的微秒精度。大多数系统将其子秒系统时钟保留在范围为 1 到 33 毫秒之间的分辨率中。这取决于系统。

MySQL 版本 5.6.4 及更高版本允许您指定具有微秒分辨率的 DATETIME(6) 列。如果您正在使用此类 MySQL 版本,则绝对是最好的方式。

在版本 5.6.4 之前,您需要使用 MySQL DOUBLE(IEEE 754 64 位浮点数)来存储这些数字。MySQL FLOAT(IEEE 754 32 位浮点数)的幂指数不足以完全准确地存储当前秒的 UNIX 时间。

为什么要存储这些时间戳?您希望做什么?

  WHERE table.timestamp = 1357609984.100000

或者类似的查询来查找特定的项目?如果在您的处理链中使用浮点数或双精度数(即使只使用 microtime(true)一次),那么这是充满危险的。即使您认为它们应该相等,它们也会因为某些原因不相等而臭名昭著。相反,您需要使用像这样的东西。 0.001被称为数字处理行业中的“epsilon”。

  WHERE table.timestamp BETWEEN 1357609984.100000 - 0.001
                            AND 1357609984.100000 + 0.001

如果将时间戳存储为十进制或百万分之一秒的bigint列,就不会出现这个问题。

IEEE 64位浮点数具有53位尾数 - 精度。当前的UNIX Epoch时间戳(自1970年1月1日00:00Z以来的秒数)乘以一百万使用了51位。因此,如果我们关心低位比特,DOUBLE中没有太多额外的精度。另一方面,对于几个世纪而言,精度不会耗尽。

int64(BIGINT)的精度远未用尽。如果我实际上只是为了在MySQL中对它们进行排序而存储微秒级时间戳,我会选择DATETIME(6),因为我可以免费获得许多日期算术运算。如果我正在做一个内存高负载应用程序,我会使用int64。


我正在使用PHP进行所有排序,参考http://highscalability.com/blog/2010/3/23/digg-4000-performance-increase-by-sorting-in-php-rather-than.html。请不要试图告诉我这个想法有多糟糕。通过仅使用PHP进行计算,我将本地wampserver的等待时间从约40毫秒降至始终<10毫秒。在这种情况下,我正在使用PHP的microtime处理时间差异。感谢您提供的精确说明!由于PHP似乎在查询时将所有内容转换为字符串,所以我应该存储在varchar中吗? - user1382306
另外,varchar和decimal之间有大小差异吗? varchar 15 = decimal 14,4吗?感谢您的广博知识! - user1382306
存储在64位浮点数据项中的时间戳(PHP float,MySQL double)将正确排序。而浮点数的数值相等性则会变得比较棘手。 - O. Jones
我喜欢你说话的方式,先生。 - Aditya M P
如果我仅使用微秒级时间戳来对发生在亚秒时间内的日志事务进行排序(ORDER BY),那么使用DOUBLE还是BIGINT有关系吗? - user151841

1

这取决于您需要的精度。如果毫秒足够,而且您不希望运行时间超过999999秒,您可以使用DECIMAL(10,4)。您可以按照自己的需求进行调整。

关于理论最大值,它受系统上float的大小限制(32位、64位)。有关详细信息,请参见PHP Float。但正如我所说,这是理论上的。没有计时器能以那种精度给出时间。


microtime(true) 的输出长度是否达到了最大值? - user1382306
1
我更新了我的回答。PHP手册说:“浮点数的大小取决于平台,尽管最大值约为1.8e308,精度约为14个十进制数字是一个常见的值(64位IEEE格式)。” - Nic
抱歉,这不是我的专业领域,请见谅。您能否发布32位和64位的长度? - user1382306
1
这并不容易,因为浮点数有不同的表示方式,并且它是一个非常大的数字。正如我所说,这是理论上的,没有计时器可以给你那样精确的时间。你应该按照@SamuelCook的建议去做,或者如果你觉得必须这样做,可以再加几个数字以保险起见。 - Nic
1
PHP浮点数的精度被认为是依赖于平台的,但通常是64位IEEE 754。MySQL的FLOAT数据类型是32位IEEE 754。如果将64位浮点数转换为32位浮点数,则会丢失精度。UNIX时间戳现在使用31位来表示整数秒。因此,在MySQL IEEE 754浮点数中表示秒/微秒时间戳需要DOUBLE。FLOAT将会丢失精度。 - O. Jones

1
在MySQL 5.6.4及以上版本中,本地的DATETIMETIMESTAMP类型可以支持小数秒。因此,您可以在DATETIME(6)TIMESTAMP(6)列中存储具有微秒分辨率的时间戳。

要将 PHP microtime() 返回值转换为 MySQL datetime 格式,您可以使用 MySQL FROM_UNIXTIME() 函数,或者如果您正在使用 PHP DateTime 类,则可以使用 DateTime::format()。请注意,PHP date() 函数目前不支持微秒级时间戳。(它确实有一个微秒格式代码u,但它总是将其映射到000000。)

对于旧版 MySQL,无法在其本机 datetime 类型中存储微秒,您应该使用 DOUBLEBIGINT(以微秒表示的值,即乘以 1,000,000)或 DECIMAL(16,6)(这应该足够使用几百年)。


-1

为什么不直接将其存储为 float 呢?这就是 microtime(true) 返回的内容,因此它是最佳选择。


我知道,但是我的问题中的链接说浮点数近似而十进制只截断。我宁愿存储整个东西。应该用double吗? - user1382306
2
如果源数据已经是浮点数,那么这个浮点数没有舍入问题。实际上,将其存储为十进制数只会导致更多的舍入问题。 - Tom van der Woerdt
2
IEEE 754的32位浮点数(MySQL FLOAT)精度不足以处理微秒级时间戳。 MySQL double具有充足的精度,足以支持日后很久很久。MySQL规范说:“为了最大程度的可移植性,需要存储近似数值数据的代码应使用没有精度或数字位数规定的FLOAT或DOUBLE PRECISION。”换句话说,括号中在FLOAT或DOUBLE声明后面的数字并没有什么用处,只会让程序员认为他们理解了某些他们实际上并不理解的东西。 - O. Jones
2
抱歉,我得给这个点个踩,因为MySQL的FLOAT是32位的,而PHP的float则依赖于系统,但通常是64位的。 - O. Jones

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接