emahiro/b.log

日々の勉強の記録とか育児の記録とか。

MySQL で時刻を時刻型で保存するか Unix Timestamp で保存するかを考えた話

Overview

MySQL でよくある CreatedAt と UpdatedAt を時刻型で保存するのか、Unix Timestamp で保存するかを考えてみたのでその備忘録です。

なお考えるにあたって以下のブログを参考にさせてもらいました。

www.m3tech.blog

前提

この議論を考えるにあたっての前提は以下です。

Unix Timestamp

Pros

  • MySQL 上のデータサイズが時刻型より小さい。
  • 数字型(INT型 or BIGINT型)の場合コード上で取り扱いやすい (*1)
  • Go を使う場合 Time 型を使うよりメモリに優しい。

*1 について

数字で時刻を取り扱うことが可能なので時間の前後を数字の大小として比較することが可能です。

// User.CreatedAt が Datetime のとき

now := time.Now()

// ユーザーが作られた時間が現在より前かどうかを検査したいとき
if user.CreatedAt.Before(now) {
    // TBD
}

と書くところを以下のように書ける。

// User.CreatedAt が int64 のとき

now := time.Now().Unix()
// ユーザーが作られた時間が現在より前かどうかを検査したいとき
if user.CreateAt < now {
   // TBD
} 

Before/After でも分かりやすいが数値で比較する方がより直感的で分かりやすいと思います。

Cons

  • TIMESTAMP型 を使うと 2038年問題 がある。2020年代でこの型を使うのはあまり現実的ではない。なお、この 2038 年問題は INT (もしくは BIGINT 型) で Timestamp を保存することで回避可能ではあある。

TIMESTAMP には、'1970-01-01 00:00:01' UTC から '2038-01-19 03:14:07' UTC の範囲があります。

ref: https://dev.mysql.com/doc/refman/8.0/ja/datetime.html

  • unix time は直感的にそれが何時何分なのかが分かりづらい。
    • from_unixtime() 関数 を使えば、Datetime 同様の直感性は手に入れらますが、毎回これを差し込むことになります。

時刻型(DATETIME)

Pros

  • MySQL 上で時刻が分かりやすい。
  • 時刻で絞ったクエリを書きたい場合 createdAt < "2022-04-08" のような直感的なクエリを書くことが可能。

Cons

  • Unix timestamp よりデータ型が大きいです。(と言っても意識するレベルではないと思いますが。)
  • Timezone の影響を受ける。
    • これも原則時刻型は UTC にして保存しておく、等の設計の工夫が求められるポイントになる。

結局どちらを選んだか?

今回は業務で実装するシステムもあり Datetime を選びました。理由の大きいところとしてはやはり運用のしやすさ、時刻型の Pros の 2 つ目の理由が大きいです。
データサイズやコード上での取り回しやすさを考えるとエンジニアとしては Unix Timestamp を採用したくなります。
Pros/Cons には入れませんでしたが、Unix Timestamp でフォーマットの影響を受けないという単純さも魅力です。パースはGUI側で取り回す(= フロント側の責務に持っていく)方が今時っぽい関心の分離方法だと思います。

しかし、事業をする上でやはりすぐにクエリを引きたい、という要求は後々ニーズとして大きくなるものです。分析でも使いますし、障害対応のケースなどでも影響範囲を出すためにクエリを引くことはあります。

初期のデータモデルが後々まで尾を引くことになるので、最初から多少のデメリットはあってもシンプルなクエリを書けることを優先しました。(データサイズもクラウドがスタンダードの現代にあっては多少は許容できるデメリットだと判断しました。)

まとめ

チームで軽く雑談で聞いてみましたが、Unix Timestamp 側の観点でいた自分にとっては運用観点を知るいい機会になりました。
運用は本当に大事で運用のことを少しでも考えて設計できるかはエンジニアとしての実力が出るところなので1つの観点を知るきっかけになってもらえれば幸いです。