RDS for Oracleでのsysdateに要注意

2014.01.13

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

このブログの内容は、AWSの機能アップデートにより古い情報となりました。詳しくはこちらの記事を御覧ください。

こんにちは、望月です。最近Oracleに入門し、諸々お勉強しております。
さて、AWSでOracleを扱うとなれば、当然RDSの利用を考えることになると思いますが、新規構築の際も移行の際も、RDSを利用する場合に気をつけなければならないのがタイムゾーンの存在です。

RDSにおけるタイムゾーン

RDSに限らず、EC2もデフォルトのタイムゾーンはUTCになっています。EC2の場合はOSにログインできるため、Linuxの場合なら下記コマンドでUTCからJSTにタイムゾーンを変更することが可能です。

$ sudo ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

ですが、RDSではOSのタイムゾーンを変更することは不可能です。OracleのsysdateはOSの時刻を返すため、sysdateを利用して取得する値はUTCでの値が帰ってくることになります。

$ LANG=C date
Mon Jan 13 10:19:41 JST 2014

$ sqlplus oracleuser/password@oracle.xxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com:1521/orcl

SQL*Plus: Release 11.2.0.3.0 Production on Mon Jan 13 10:19:47 2014

Copyright (c) 1982, 2012, Oracle.  All rights reserved.


Connected to:
Oracle Database 11g Release 11.2.0.2.0 - 64bit Production

SQL> SELECT TO_CHAR(sysdate, 'YYYY-MM-DD HH24:MI:SS') from dual;

TO_CHAR(SYSDATE,'YY
-------------------
2014-01-13 01:19:51 #<=UTCの時刻が取得される
[/bash]

<p>sysdateはOracleを利用してアプリ開発する上では呼吸をするように自然に使う関数だと思います。しかも、既存アプリの場合はJSTでデータが取得できることが前提の作りになっていることが多いと思いますので、その前提が崩れるとなると単純移行では済まなくなってしまいます。<br/>
新規開発の場合は、「DBのタイムゾーンはUTCになるから、アプリで変換してねー」で済む話のですが、 <a class="simple-footnote" title="それでもアプリ開発の負担は多いと重いますが" id="return-note-88070-1" href="#note-88070-1"><sup>*1</sup></a>既存アプリを移行する場合はどういった対策を打てばいいのでしょうか。以下に3点を示してみました。この他に有効な回避策があったり、何か文中に誤りがあれば、コメントやTwitterなどで教えてください!</p>

<h2 id="toc-1-sysdatecurrent_date">1 : sysdateのかわりにcurrent_dateを利用する</h2>
<p>RDS for Oracleでは、セッションタイムゾーンを変更するためのストアドプロシージャが予め用意されています。
<ul>
	<li><a href="http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Appendix.Oracle.CommonDBATasks.html#Appendix.Oracle.CommonDBATasks.TimeZoneSupport" target="_blank" rel="noopener noreferrer">Appendix: Common DBA Tasks for Oracle - Setting the Database Time Zone</a></li>
</ul>
<p>ただし、このストアドプロシージャで変更できるのは「DB上の」タイムゾーンであることに注意をしてください。つまり、「OSの」タイムゾーンを利用するsysdateには、これを実行しても影響を及ぼしません。<br/>
そのかわり、Oracleにはcurrent_dateという関数が用意されております。current_dateはセッションタイムゾーン上での現在時刻を取得するため、上述のストアドによるタイムゾーン変更の影響を受けます。以下のSQLで試してみました。
</p>

$ LANG=C date
Mon Jan 13 10:26:13 JST 2014

$ sqlplus oracleuser/password@oracle.xxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com:1521/orcl

SQL*Plus: Release 11.2.0.3.0 Production on Mon Jan 13 10:19:47 2014

Copyright (c) 1982, 2012, Oracle.  All rights reserved.


Connected to:
Oracle Database 11g Release 11.2.0.2.0 - 64bit Production

SQL> exec rdsadmin.rdsadmin_util.alter_db_time_zone('+9:00'); # <= DBタイムゾーンをJSTに変更

PL/SQL procedure successfully completed.

SQL> SELECT TO_CHAR(sysdate,      'YYYY-MM-DD HH24:MI:SS') SYS_DATE, 
  2         TO_CHAR(current_date, 'YYYY-MM-DD HH24:MI:SS') CUR_DATE from dual;

SYS_DATE	    CUR_DATE
------------------- -------------------
2014-01-13 01:26:27 2014-01-13 10:26:27

SQL> exit
Disconnected from Oracle Database 11g Release 11.2.0.2.0 - 64bit Production
$

タイムゾーンの影響を受けて、JSTでの時刻が取得されていることが確認できました。ただ、アプリケーションから発行されるsysdateを全てcurrent_dateに置き換えるというのは、かなりの大改修になります。

2 : アプリ側でタイムゾーン変換処理を行う

これもかなりアプリケーション側の大改修が必要になる箇所かと思います。DBにはUTCで時刻は格納される前提で、アプリケーション側でタイムゾーンの変換を行います。OracleのDate型はタイムゾーン情報を持たないので、アプリ側で決まった時間だけ足し引きするような形になります。

3 : RDS for Oracleを諦め、Oracle on EC2を利用する

これはかなり奥の手だと思います。上述のコマンドでEC2のタイムゾーンを変換した上でOracleを構築すれば、sysdateを利用してそのままJST時刻を取得することができます。
しかし当然ですが、RDSのマネージドサービスが利用できなくなりますので、Multi-AZ Failoverなどの冗長化対策やDaily Snapshotなどのバックアップ方策等を全て自分たちで管理しなければならなくなります。DBの管理が煩雑だからAWS RDSの利用を検討したのに、結局管理コストが変わらないのであれば元も子もないですよね。。

まとめると

RDS for MySQLの場合は、以前弊社ブログで紹介した方式にて回避することができますが、Oracleの場合はRDS以上に一筋縄ではいかない問題となります。
オンプレ環境で動いているアプリケーションの移行の際は必ずタイムゾーンの問題が起こります。長期的に見た場合は、JST固定でなくUTCからタイムゾーンを変換する方式にしておいたほうがベターな選択肢だとは思いますが、改修コスト等も考えて、移行するかどうかの部分から検討項目に入れておくと、後々のトラブル回避につながるのではないでしょうか。

脚注

  1. それでもアプリ開発の負担は多いと重いますが