MySQLでのタイムゾーン処理:アプリケーションをタイムゾーンの迷子にさせないために

MySQL tutorial - IT technology blog
MySQL tutorial - IT technology blog

タイムゾーンのズレ — 古くて新しい、常にホットな問題

タイムゾーンのズレは、「徹夜のサポート」を引き起こす主な原因の一つです。よくあるシナリオ:ローカル環境では正常に動作しているのに、AWS(通常はデフォルトでUTC)にデプロイした途端、レポートのデータが突然9時間もズレてしまう。顧客が午前10時に注文したのに、システムには深夜1時と記録されるといったケースです。

初期段階での設計ミスは、データベースのスケールアップ時に「致命傷」となります。後から数十億件のレコードを移行するには、数時間ではなく数週間かかることもあります。私は、5,000万行のデータを変換するスクリプトを実行するために、チームが48時間不眠不休で対応するのを目の当たりにしたことがあります。すべては、UTCで標準化せずにローカル時刻で保存していたことが原因でした。

クイックスタート:一瞬で確認・設定する

推測に頼らず、次のコマンドでデータベースがどのタイムゾーンで動作しているか今すぐ確認しましょう。

-- グローバル、セッションのタイムゾーンと現在時刻を確認
SELECT @@global.time_zone, @@session.time_zone, NOW();

結果が SYSTEM の場合、MySQLはオペレーティングシステム(OS)と同じ時刻を使用しています。これはデフォルト設定ですが、サーバーをリージョン間(例:シンガポールから米国など)で移動させる際に非常にリスクが高くなります。

現在のセッションを強制的にUTC基準にするには、以下を使用します。

SET time_zone = '+00:00';
SELECT NOW(); -- 時刻が即座に国際標準時(UTC)と一致します

恒久的に設定したい場合は、my.cnf (Linux) または my.ini (Windows) の [mysqld] セクションに以下の行を追加します。

[mysqld]
default-time-zone = '+00:00'

TIMESTAMPかDATETIMEか?運命を決める選択

エンジニアの間では、これら2つの型について激しい議論が交わされることがよくあります。それぞれに「代償」があります。

1. TIMESTAMP型 (4バイト)

  • 仕組み: MySQLが保存時に現在のタイムゾーンからUTCに自動変換し、取得時に元に戻します。
  • 制限: 2038年1月19日に「パンク」します(2038年問題)。
  • メリット: 接続するクライアントのタイムゾーンに合わせて自動的に調整されます。

2. DATETIME型 (5-8バイト)

  • 仕組み: そのまま保存されます(見たままが保存されます)。変換は行われません。
  • 制限: 西暦9999年まで保存可能です。
  • デメリット: サーバーの地域が変わった際にアプリ層でロジックを処理しないと、データの意味が食い違ってしまいます。

実践的なアドバイス: 現代的なシステムでは、DATETIMEとサーバーのUTC運用を組み合わせるのがベストです。これによりデータの透明性が高まり、デバッグが容易で、2038年問題の心配もなくなります。

応用:名前付きタイムゾーンでプロフェッショナルに

+09:00 のような無機質な数字を覚える代わりに、'Asia/Tokyo' のような名称を使用できます。ただし、MySQLにはデフォルトでこれらの名前テーブルが用意されていません。Linuxでは、以下のコマンドでOS from DBにデータを読み込む必要があります。

mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root -p mysql

読み込み後は、タイムゾーンの設定が非常に直感的になります。最大の利点は、コードを修正することなく、欧米市場の夏時間(DST)をMySQLが自動的に処理してくれることです。

グローバルアプリケーションのための「3層」戦略

時刻のズレを完全に防ぐために、以下の標準的な構成を適用しましょう。

  1. データベース層(Database Layer): 常に time_zone = '+00:00' に固定します。保存には DATETIME のみを使用します。
  2. アプリケーション層(Application Layer): バックエンドは保存前にすべての入力をUTCにパースします。例:Node.jsなら moment.utc()dayjs.utc() を使用します。
  3. クライアント層(Client Layer): フロントエンドはAPIからUTC文字列を受け取り、ブラウザを使用してユーザーのローカル時刻で表示します。

警告:タイムゾーンのせいでクエリを遅くさせない

最も一般的な間違いは、WHERE 句の中で変換関数を使用することです。1,000万件のレコードがあるテーブルで以下のクエリを実行すると、フルテーブルスキャンが発生し、サーバーのCPU使用率が100%に跳ね上がります。

Share: