背景

      在MySQL数据库中,我们利用show table status命令可以得到表的状态信息,其中一列信息为create_time,表示表的创建时间。对于不同的存储引擎(如InnoDB/MyISAM/MEMORY)我们都能得到create_time的数值。我们知道不同的存储引擎表的文件结构是不同的,因此实现表的创建时间create_time的机制也是不同的。下面着重探讨InnoDB和MyISAM在create_time上的区别。

    实验

      我们先做一些实验来看看create_time的特点。在InnoDB引擎下创建一个表:

    1. CREATE TABLE `tb` (
    2. `seq_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
    3. `a` varchar(32) DEFAULT NULL,
    4. `b` varchar(32) DEFAULT NULL,
    5. `c` varchar(32) DEFAULT NULL,
    6. `d` char(255) DEFAULT NULL,
    7. Primary key (seq_id),
    8. KEY a (a),
    9. KEY bc (b,c),
    10. KEY cb (c,b)
    11. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

    在MyISAM引擎下也创建结构相同的一个表,如何可以更新create_time呢?我们在表上做以下3种操作,观察create_time的变化。

      1. 对表进行增删改查操作,InnoDB、MyISAM的create_time不变。

      2. 对表进行alter table add column e varchar(32),InnoDB、MyISAM的create_time都更新到当前时间。

      3. 对表进行truncate table tb,InnoDB的create_time不变、MyISAM的create_time更新到当前时间。

      通过这些操作我们发现虽然2种引擎的内部实现不同,但前2种操作的现象是一样的。对表进行增删改查并不重建表,因此create_time没有更新。而alter table会更新create_time的原因是新建了一个原表的副本,在副本上实现alter table的功能(增加新列等等),最后删除原表,用副本替代原表。因此alter table下create_time是原表副本的创建时间。

      第3种操作,两者的现象不同,这是为什么呢?我们从代码实现上分析原因。

    show table status的create_time的实现

    1.InnoDB   在InnoDB下执行show table status获得create_time来自于代码:

    1. ./storage/innobase/handler/ha_innodb.cc
    2. ...
    3. if (os_file_get_status(path,&stat_info)) {
    4. stats.create_time = (ulong) stat_info.ctime;
    5. }
    6. ...
    7.   stats.create_time最终来自于以下代码的statinfo变量:
    8. ./storage/innobase/os/os0file.c
    9. ...
    10. ret = stat(path, &statinfo);
    11. ...

       stat为C语言的库函数,含义是将文件路径path定位的文件(TABLENAME.frm)的状态信息(包括了创建时间create_time)存入statinfo。

      通过对源码的分析,我们知道在InnoDB引擎,create_time来源于.frm文件的创建日期。在truncate table之后,InnoDB并没有重建.frm文件,因此show table status的create_time不变。

    1. MyISAM下   在MyISAM下执行show table status获得create_time来自于代码:

      1. ./storage/myisam/ha_myisam.cc:
      2. ...
      3. stats.create_time= (ulong) misam_info.create_time;
      4. ...

        对应的misam_info.create_time来源于:

      1. ./storage/myisam/mi_open.c:
      2. ...
      3. mi_state_info_read(disk_cache, &share->state);
      4. ...

        即MyISAM通过读.MYI文件来获得state信息(包含了create_time),也就是说MyISAM下show table status的create_time最终来源于MYI文件中的state信息。

      在MyISAM下,创建表(create table..)的create_time来源于以下代码:

    1. ./storage/myisam/mi_create.c
    2. ...
    3. if (! (flags & HA_DONT_TOUCH_DATA))
    4. share.state.create_time= (long) time((time_t*) 0);
    5. ...

      MyISAM下的create_time来源于share变量,每次执行这部分代码都会更新share.state.create_time。share是MyISAM引擎下的全局信息,share.state区间信息包含了键和数据文件长度、时间戳(即create_time)和打开表的次数等等参数。share.state会记入MYI文件,代码如下:

    1. ./storage/myisam/mi_create.c
    2. ...
    3. DBUG_PRINT("info", ("write state info and base info"));
    4. if (mi_state_info_write(file, &share.state, 2) ||
    5. mi_base_info_write(file, &share.base))
    6. goto err;
    7. ...

      执行truncate table也会经过以上2处的代码,更新.MYI文件的state区间的信息,然后show table status时读入.MYI文件最新的state信息(包含了create_time),因此create_time会被更新。