zoukankan      html  css  js  c++  java
  • 2038/01/19 11:14:07 MySQL

    给出这样一个标题,实在有些唐突,不过这是我最近一直在思考的一个问题的一个边界值,这个问题就是:在MySQL中如何给时间字段定义数据类型。

    在以前的mysql数据库设计中,我们把时间型的字段都设计成了bigint型,而基本上所有插入这里边的值都是间接或直接从time()这个函数得到的。

    一、关于time()

    php中time()函数返回的返回的是一个Unix时间戳,其官方文档 如此解释这个函数:


    int
    time ( void )

    Returns the current time measured in the number of seconds since the Unix Epoch (January 1 1970 00:00:00 GMT).

    就是当前的时间的秒数减去1970年1月1日 00:00:00时的秒数所得到的一个整形差值。

    从这个函数的返回值来看,这是个整型值,也就是说,它得到的结果是一个有范围的值。在php中,对于整形的定义没有有符号和无符号之分,虽然其字长和平台有关,但一般的最大值都是为231 -1=2147483647。运行下面的php代码:

    print strftime("%Y/%d/%m", 2147483647);

    就会得到和标题一样中的结果了,从现在来看的话,这个日期似乎也不是那么久远了。

    在php中,对于时间的处理函数大部分都存在这种限制。比如,这样的代码会出现意想不到的效果:

    print strftime("%Y/%d/%m %H:%M:%S/n", 2147483648);
    // 输出为:1901/14/12 04:51:44

    $last = strtotime("2038/01/19 11:14:08");
    var_dump($last); // 输出为bool(false)

    也就是说,超过这个日期之后的日期,用php相关的函数处理会出错。

    回到数据库设计来说,我们把一个只会返回int型的数据插入到了bigint型的字段中了。那么,这会不会形成对数据库空间的浪费吗?有没有更加合理的方案呢?这正是我最近一直在思考的问题。

    二、关于TIMESTAMP类型

    在MySQL中存在一系列的时间类型日期时间类型,包括DATETIME、DATE、 TIMESTAMP、TIME和YEAR。在这其中,TIMESTAMP最接近于我们的系统中要使用的时间字段,TIMESTAMP值返回后显示为 “YYYY-MM-DD HH:MM:SS”格式的字符串,如果要获得数字值,在sql语句时应在其字段名称后加“+0”,比如select create_date+0 from table。TIMESTAMP类型的长度为4个字节,和int类型的一样,而且它们的日期范围是完全一致,所以,我觉得TIMESTAMP存取的实际数 值应该就是日期的时间戳,只不过在select出来的时候mysql做了一些转化而已。而且,在用php和mysql配合时,如果有字段为 TIMESTAMP的话,还存在进行一些额外的转化。在《php和mysql时间互换 》这篇文章中就总结了几种常用的互换方法:

    第一种方法:使用 date()和strtotime()函数

           $mysqltime=date('Y-m-d H:i:s',$phptime);

           $phptime=strtotime($mysqldate);

    第二种方法:在查询语句中使用mysql函数转换:UNIX_TIMESTAMP(DATETIME=>PHP TIMESTAMP)和FROM_UNIXTIME(PHP TIMESTAMP=>DATETIME).

            $sql="SELECT UNIX_TIMESTAMP(datetimefield) FROM table WHERE ...";

            $sql="UPDATE table set datetimefield=FROM_UNIXTIME($phptime) WHERE ..";

    其实,该文还提到了第三种方法,这也就是我通过对这个类型字段的研究得出的结论——在mysql中使用整数字段来保存php的timestamp类型。

    三、关于用int类型字段保存时间

     

    在我们的系统里边,有一个表是用来抓取blog的文章的,每天都会增长很多记 录,到现在应该有上亿的数据记录了,我们对其进行了分表的处理。假设一个有1亿条记录的表里边的字段里边有一个时间字段被处理成bigint类型了,那么 每一条记录对应地会浪费掉4个字节,那么这1亿条记录会浪费掉4*100,000,000/1024/1024M≈381M的空间。这从数据库优化的角度 上还是可以考虑一试的。

    当然,从另外一个角度来说,数据库是需要考虑其扩展性的。假如真到了2038年 1月19日,那么这些日期字段要再来扩充的话,是不是会比较麻烦呢?实际上,既然对这些表进行了分表处理,那么,至少对于这个大限之日之前的一些数据表, 我们是不用考虑要修改其数据字段的类型。另外,虽然php中对于整形变量并没有有符号无符号之别,但是在mysql数据库中,却是存在无符号整形的概念。 从MySQL的官方文档 ,我们可以看到这样的描述。

    Type Bytes Minimum Value Maximum Value
    (Signed/Unsigned) (Signed/Unsigned)
    TINYINT 1 -128 127
    0 255
    SMALLINT 2 -32768 32767
    0 65535
    MEDIUMINT 3 -8388608 8388607
    0 16777215
    INT 4 -2147483648 2147483647
    0 4294967295
    BIGINT 8 -9223372036854775808 9223372036854775807
    0 18446744073709551615

    也就是说,如果将int字段设置成unsigned的话,其值的范围可以提升1倍,初略算了一下,按照time()函数求时间戳的思路(以后应该会考虑扩展其返回数值的范围的),4294967295这个值,大概可以支撑到2106年去。我想,这个范围的话,足可以将我们的系统应付到我们可以预见的有生之年吧。

    ps 昨天还尝试直接把系统改到2038年,没想到的是,从未出错的gtalk竟然给我报了一个错,而且,杀毒软件诺顿也同样凑了一下热闹。看来,各种软件对于时间的处理可能也存在同样的限制。

  • 相关阅读:
    今天开始用 VSU 2010
    Visual Studio 2010 模型设计工具 基本应用
    Asp.Net访问Oracle 数据库 执行SQL语句和调用存储过程
    Enterprise Library 4.1 Security Block 快速使用图文笔记
    解决“System.Data.OracleClient 需要 Oracle 客户端软件 8.1.7 或更高版本。”(图)
    一个Oracle存储过程示例
    Enterprise Library 4.1 Application Settings 快速使用图文笔记
    Oracle 10g for Windows 简体中文版的安装过程
    Oracle 11g for Windows 简体中文版的安装过程
    Oracle 9i 数据库 创建数据库 Net 配置 创建表 SQL查询 创建存储过程 (图)
  • 原文地址:https://www.cnblogs.com/ylqmf/p/2208909.html
Copyright © 2011-2022 走看看