MySQL バッドノウハウ

潟若若菴遵

[MySQL]

はじめに

嘘があっても泣かない。

関数について

日付関連

そもそも now() と sysdate() ではなにが違うのか? NOW() が、ステートメントを実行開始する時刻を返すのと違い、 SYSDATE() は、それが実行された時刻を返します(MySQL5.0以降?)

11.5. 日付時刻関数
http://dev.mysql.com/doc/refman/5.1/ja/date-and-time-functions.html

によると

SYSDATE() は、それが実行された時間を戻します。これは NOW() の動作によって異なり、ステートメントが
実行を開始する時間を示す定数時間を戻します。( ストアド ルーチンまたはトリガ内で、NOW() はルーチン
またはトリガ文が実行を開始する時間を戻します。) 

そのほか、 SET TIMESTAMP 文は NOW()  によって戻された値に影響を及ぼしますが、SYSDATE()  によって
戻された値には影響しません。つまり、バイナリログのタイムスタンプ設定は、SYSDATE()  の呼び出しには
効果をもたらさないということになります。 

なんのこっちゃ?って感じですが、そのすぐ下の例を見るとすこし分かった気になります。

  • 例:
mysql> select now(), sleep(2), now();
+---------------------+----------+---------------------+
| now()               | sleep(2) | now()               |
+---------------------+----------+---------------------+
| 2007-08-14 12:17:25 |        0 | 2007-08-14 12:17:25 | 
+---------------------+----------+---------------------+
1 row in set (2.00 sec)
 
mysql> select sysdate(), sleep(2), sysdate();
+---------------------+----------+---------------------+
| sysdate()           | sleep(2) | sysdate()           |
+---------------------+----------+---------------------+
| 2007-08-14 12:17:28 |        0 | 2007-08-14 12:17:30 | 
+---------------------+----------+---------------------+
1 row in set (2.01 sec)

つまり、where 句で SYSDATE() を使うと、レコード数回 SYSDATE() が実行されるらしく、遅くなります。たとえば、16000レコードのデータに対して

select count(*) from event_master \
               where event_start_datetime < subdate (now(),1 );
select count(*) from event_master \
               where event_start_datetime < subdate (sysdate(),1 );

を実行した場合、

  • SYSDATE 0.08〜0.09 sec
  • NOW() 0.03〜0.04 sec

という結果になりました。安直に考えれば、sysdate の代わりに now を使うと、 2倍程度の高速化になることになります(大雑把すぎますが。逆に考えれば、16000回も実行してもこの程度しか差がないとも言えます)ともあれ、特別な理由がない限り、sysdate()よりも now() を使った方がよさそうです。

またパフォーマンスだけでなく、レプリケーション利用時にも注意が必要です。

11.5. 日付時刻関数
http://dev.mysql.com/doc/refman/5.1/ja/date-and-time-functions.html

SYSDATE()  は同じステートメントの中でも、異なる値を戻すことができ、ま
た SET TIMESTAMP  に影響を受けないため、これは非決定性であり、従ってス
テートメントに基づくバイナリ ロギングが使用されている場合、複製は安全
でないということになります。これが問題になる場合は、行ベースのロギング
を使用するか、または --sysdate-is-now  オプションでサーバを起動して、
SYSDATE()  が NOW()  のエイリアスになるようにしてください。 

5.4.1.6. レプリケーションとシステム機能
http://dev.mysql.com/doc/refman/5.1/ja/replication-features-functions.html

を読んでわかるように、sysdate() では、レプリケーション環境で、整合性が取れなくなります。逆にこれを利用して、レプリケーションの同期遅延を計るぜ!ってことをやる方もおられるようです。

行ベースのロギングを使用している場合には、この点の心配はありません

の記載があるように、binlog_format を ROW にするとレプリケーションセーフになるようですが試してません。基本的に sysdate() じゃなきゃだめだ!ってことはないと思うので、sysdate() ではなく、now() を使った方がよさそうです。すでにsysdate()でプログラムを書いちゃったよ!という場合でも

[mysqld]
sysdate-is-now

を追加するか、--sysdate-is-nowオプション付きで mysqld を起動すれば、now() が sysdate() の代わりに利用されますので、プログラムの修正なしに対応が可能です。