【背景】
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占有大小取決於微秒的精度。
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處會返回失敗。