mysql5.6 TIME,DATETIME,TIMESTAMP

it2022-05-16  72

【背景】

5.6.4以后时间类型(TIME,DATETIME,TIMESTAMP)支持微秒

DATETIME范围 :'1000-01-01 00:00:00.000000' to '9999-12-31 23:59:59.999999'

TIMESTAMP范围: values is '1970-01-01 00:00:01.000000' to'2038-01-19 03:14:07.999999'

 

1) 5.6 支持指定小数精度

use test

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;

+-------------+------------------------+------------------------+| 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 test

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;

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

3)5.6时间函数(CURTIME(), SYSDATE(), or UTC_TIMESTAMP())可以指定微秒精度

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

4)存储

5.6.4以前,TIME,DATETIME,TIMESTAMP 分别固定占用3,8,4字节

5.6.4以后,TIME,DATETIME,TIMESTAMP占有大小取决于微秒的精度。

TIME3 bytes + fractional seconds storageDATETIME5 bytes + fractional seconds storageTIMESTAMP4 bytes + fractional seconds storage

而微秒的存储长度和精度的关系如下

 

Fractional Seconds PrecisionStorage Required00 bytes1, 21 byte3, 42 bytes5, 63 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处会返回失败。

转载于:https://www.cnblogs.com/justfortaste/p/4119722.html


最新回复(0)