MySQL Fractional timestamp and Partition pruning

MySQL Fractional timestamp

  • timestamp 를 milliseconds 혹은 microseconds까지 저장하기 위해서 fractional part 를 명시할 수 있다.
  • Reference : https://dev.mysql.com/doc/refman/5.7/en/fractional-seconds.html
  • timestamp(3): 3자리 fractional part를 저장한다. milliseconds(ms)
  • timestamp(6): 6자리 fractional part를 저장한다. microseconds(µs)

test table with timestamp(3)

root@localhost:(none) 11:17:56>show create table test.atest\G
*************************** 1. row ***************************
       Table: atest
Create Table: CREATE TABLE `atest` (
  `no` bigint(20) NOT NULL AUTO_INCREMENT,
  `a_no` bigint(20) NOT NULL,
  `created_at` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
  PRIMARY KEY (`no`,`created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
1 row in set (0.00 sec)


root@localhost:test 13:58:20>select * from atest;
+----+------+-------------------------+
| no | a_no | created_at              |
+----+------+-------------------------+
|  1 |    1 | 2018-06-04 09:56:35.931 |
|  2 |    2 | 2018-06-04 09:56:35.931 |
|  3 |    3 | 2018-06-04 09:56:35.931 |
|  4 |    4 | 2018-06-04 09:56:35.931 |
+----+------+-------------------------+
4 rows in set (0.00 sec)

Partitoining with fractional timestamp

try to alter partition table

alter table atest
PARTITION BY RANGE (unix_timestamp(created_at))
(PARTITION p20180601 VALUES LESS THAN (1530370800) ENGINE = InnoDB,
PARTITION p20180701 VALUES LESS THAN (1533049200) ENGINE = InnoDB);

ERROR 1491 (HY000): The PARTITION function returns the wrong type

안된다..

wrong type이라니, 확인해보자.

root@localhost:test 15:19:25>select now(3),unix_timestamp(now(3));
+-------------------------+------------------------+
| now(3)                  | unix_timestamp(now(3)) |
+-------------------------+------------------------+
| 2018-07-18 15:19:32.469 |         1531894772.469 |
+-------------------------+------------------------+
1 row in set (0.00 sec)

어떻게 하면 fractional timestamp기준으로 partitioning할 수 있을까. 정답: 소수점 떼기, FLOOR!

FLOOR(UNIX_TIMESTMP(your_partitioning_key))

alter table atest
PARTITION BY RANGE (floor(unix_timestamp(created_at)))
(PARTITION p20180601 VALUES LESS THAN (1530370800) ENGINE = InnoDB,
PARTITION p20180701 VALUES LESS THAN (1533049200) ENGINE = InnoDB);

root@localhost:test 15:41:07>show create table atest\G
*************************** 1. row ***************************
       Table: atest
Create Table: CREATE TABLE `atest` (
  `no` bigint(20) NOT NULL AUTO_INCREMENT,
  `a_no` bigint(20) NOT NULL,
  `created_at` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
  PRIMARY KEY (`no`,`created_at`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4
/*!50100 PARTITION BY RANGE (floor(unix_timestamp(created_at)))
(PARTITION p20180601 VALUES LESS THAN (1530370800) ENGINE = InnoDB,
 PARTITION p20180701 VALUES LESS THAN (1533049200) ENGINE = InnoDB) */
1 row in set (0.00 sec)

UNFORTUNATELY, Partition pruning is not working well

try to range scan

root@localhost:test 15:43:09>explain select * from atest where created_at between ('2018-06-01') and ('2018-06-10');
+----+-------------+-------+---------------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions          | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+---------------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | atest | p20180601,p20180701 | ALL  | NULL          | NULL | NULL    | NULL |    8 |    12.50 | Using where |
+----+-------------+-------+---------------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

try to equal scan

root@localhost:test 15:43:13>explain select * from atest where created_at ='2018-06-04 09:56:35.931';
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | atest | p20180601  | ALL  | NULL          | NULL | NULL    | NULL |    4 |    25.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

BUG 였음. FLOOR(decimal)의 partitioning expression으로 되어있지만, pruning에 제약이 있음.

Conclusion

  • 인덱스를 타게 해야한다.
  • 가능하다면, Partition selection을 강제한다.
  • 빨리 bug fix 되길 바란다.