mysql5.6 TIME,DATETIME,TIMESTAMP

【背景】

5.6.4之後時間類型(TIME,DATETIME,TIMESTAMP)支持微秒html

DATETIME範圍 :'1000-01-01 00:00:00.000000' to '9999-12-31 23:59:59.999999'mysql

TIMESTAMP範圍: values is '1970-01-01 00:00:01.000000' to'2038-01-19 03:14:07.999999'sql

 

1) 5.6 支持指定小數精度

use testapp

CREATE TABLE fractest( c1 TIME(2), c2 DATETIME(2), c3 TIMESTAMP(2) );
INSERT INTO fractest VALUES ('17:51:04.777', '2014-09-08 17:51:04.777', '2014-09-08 17:51:04.777');
SELECT * FROM fractest;ide

+-------------+------------------------+------------------------+
| c1          | c2                     | c3                     |
+-------------+------------------------+------------------------+
| 17:51:04.78 | 2014-09-08 17:51:04.78 | 2014-09-08 17:51:04.78 |
+-------------+------------------------+------------------------+函數

 

2)5.6.4之前 插入的數據支持微秒,但插入存儲的數據會忽略微秒

use testui

CREATE TABLE fractest( c1 TIME, c2 DATETIME, c3 TIMESTAMP );
INSERT INTO fractest VALUES ('17:51:04.777', '2014-09-08 17:51:04.777', '2014-09-08 17:51:04.777');
SELECT * FROM fractest;spa

+----------+---------------------+---------------------+
| c1       | c2                  | c3                  |
+----------+---------------------+---------------------+
| 17:51:04 | 2014-09-08 17:51:04 | 2014-09-08 17:51:04 |
+----------+---------------------+---------------------+debug

3)5.6時間函數(CURTIME()SYSDATE(), or UTC_TIMESTAMP())能夠指定微秒精度

mysql> select CURTIME(2);
+-------------+
| CURTIME(2)  |
+-------------+
| 11:26:56.43 |
+-------------+code

4)存儲

5.6.4之前,TIME,DATETIME,TIMESTAMP 分別固定佔用3,8,4字節

5.6.4之後,TIME,DATETIME,TIMESTAMP佔有大小取決於微秒的精度。

TIME 3 bytes + fractional seconds storage
DATETIME 5 bytes + fractional seconds storage
TIMESTAMP 4 bytes + fractional seconds storage

而微秒的存儲長度和精度的關係以下

 

Fractional Seconds Precision Storage Required
0 0 bytes
1, 2 1 byte
3, 4 2 bytes
5, 6 3 bytes

例如上例中的c1 TIME: 佔4字節,c2 DATETIME佔6字節,TIMESTAMP 佔7字節,TIMESTAMP佔用5字節

相關函數能夠參考my_datetime_packed_to_binary

 

5)新老時間類型在源碼中的表現 

5.6 內部增長了一些新的時間類型

MYSQL_TYPE_TIMESTAMP2

MYSQL_TYPE_DATETIME2,
MYSQL_TYPE_TIME2,

用於支持微秒的存儲。

而老的時間類型

MYSQL_TYPE_TIMESTAMP,
MYSQL_TYPE_DATETIME,
MYSQL_TYPE_TIME

仍然保留和支持,從而兼容老的時間數據

 

5.6 新建的表時間字段默認使用新的類型,參考以下代碼

sql/sql_yacc.yy:6514

  | DATETIME type_datetime_precision
    { $$= MYSQL_TYPE_DATETIME2; }

 

6)binlog與新時間類型

 binlog的Table_map_log_event中會記錄表的元數據信息,包括庫,表,列信息等。新時間類型的微秒精度信息就做爲列的元數據(m_field_metadata)進行存儲。相似的大字段列的列元數據存儲大字段的實際長度(Field_blob::do_save_field_metadata)。

 

【問題重現】

1 master 上執行

  use zy

  CREATE TABLE t1 (id int primary key, c1 TIME, c2 DATETIME, c3 TIMESTAMP );

  set sql_log_bin=0;

  alter table t1 modify c3 timestamp(4);

  set sql_log_bin=1;

  INSERT INTO t1 VALUES (10, '17:51:04.98887', '2014-09-08 17:51:04.866666', '2014-09-08 17:51:04.777');

 

2 slave上執行

  show slave status\G        

  Last_Errno: 1677

  Last_Error: Column 3 of table 'zy.t1' cannot be converted from type 'timestamp' to type 'timestamp'

 

【分析】

  

1)先嚐試修復,修改slave_type_conversions='ALL_LOSSY';參數slave_type_conversions能夠參考 http://dev.mysql.com/doc/refman/5.5/en/replication-options-slave.html#sysvar_slave_type_conversions 

mysql> show variables like 'slave_type_conversions';                                                                                          

+------------------------+-------+

| Variable_name          | Value |

+------------------------+-------+

| slave_type_conversions |       |

+------------------------+-------+

1 row in set (0.00 sec) 

mysql> set global slave_type_conversions='ALL_LOSSY';

Query OK, 0 rows affected (0.00 sec)
 
show slave status\G

  Last_Errno: 1610

  Last_Error: Could not execute Write_rows event on table zy.t1; Corrupted replication event was detected, Error_code: 1610; handler error No Error!; the event's master log mysql-bin.000002, end_log_pos 550

  

 發現備庫用備庫的表結構信息解析binlog行數據(unpack_row)時出錯,所以,此方法修復失敗。

 

2)查看源碼:

Rows_log_event::do_apply_event

  table_def::compatible_with

   can_convert_field_to

  ....

 

   if (field->real_type() == source_type)//本例主備類型一致

  {

    if (metadata == 0) // Metadata can only be zero if no metadata was provided  // 本例主庫精度爲4

    {

      /*

        If there is no metadata, we either have an old event where no

        metadata were supplied, or a type that does not require any

        metadata. In either case, conversion can be done but no

        conversion table is necessary.

       */

      DBUG_PRINT( "debug" , ("Base types are identical, but there is no metadata"));

      *order_var= 0;

      DBUG_RETURN( true );

    }

 

    DBUG_PRINT( "debug" , ("Base types are identical, doing field size comparison"));

    if (field->compatible_field_size(metadata, rli, mflags, order_var))

      DBUG_RETURN(is_conversion_ok(*order_var, rli));

    else

      DBUG_RETURN( false );

  }

   else if (metadata == 0 && //這裏有對新老時間類型的兼容處理

           ((field->real_type() == MYSQL_TYPE_TIMESTAMP2 &&

             source_type == MYSQL_TYPE_TIMESTAMP) ||

            (field->real_type() == MYSQL_TYPE_TIME2 &&

             source_type == MYSQL_TYPE_TIME) ||

            (field->real_type() == MYSQL_TYPE_DATETIME2 &&

             source_type == MYSQL_TYPE_DATETIME)))

  {

    /*

      TS-TODO: conversion from FSP1>FSP2.

      Can do non-lossy conversion

      from old TIME, TIMESTAMP, DATETIME

      to new TIME(0), TIMESTAMP(0), DATETIME(0).

    */

    *order_var= -1;

    DBUG_RETURN( true);

  } 

  上面代碼進行類型兼容性判斷,本例因爲精度不一致在is_conversion_ok處會返回失敗。

相關文章
相關標籤/搜索