目次


PHPアプリケーションをMySQLからDB2に移行するプロセス

第3部PHPコードの変換

IBM社内のイントラネット・アプリケーションの事例に基づく移行について

Comments

コンテンツシリーズ

このコンテンツは全#シリーズのパート#です: PHPアプリケーションをMySQLからDB2に移行するプロセス

このシリーズの続きに乞うご期待。

このコンテンツはシリーズの一部分です:PHPアプリケーションをMySQLからDB2に移行するプロセス

このシリーズの続きに乞うご期待。

本シリーズについて

My SQLは、現在PHPプログラミングで最も多く使用されているデータベース・サーバーですが、DB2もPHPの機能に対応した汎用度の高いデータベースであり、多くのアプリケーションに最適な機能を提供する点ではMySQLを上回る大きなメリットを提供します。

本シリーズでは、PHPアプリケーションをDB2に移行する際のメリットについて説明したうえで、実際の移行例に基づいて移行の準備方法および実行方法、メンテナンス方法、発生する恐れのあるリスクへの対応方法について説明します。プロジェクトをスムーズに進めるために有用なコードやコンフィギュレーションのサンプルならびに各種関連情報も提供します。

実際の環境でスムーズに移行を行った例と、ここから得た教訓を参照することで、明確に記載されたステップに基づいて移行を簡単にでき、大きなメリットが得られることを確認できます。

本シリーズは4部に分かれています。IBM社内でibm.comのコンテンツ製作のために本番システムで使用されるミッション・クリティカルなPHPアプリケーション(グローバルに4,000名のユーザーが存在)をMySQLからDB2にスムーズに移行したプロジェクトから得た教訓について説明します。

  • 第1部では、移行準備について説明します。
  • 第2部では、データベース移行について説明します。
  • 第3部では、PHPコード変換について説明します。
  • 第4部では、アプリケーションの実装とメンテナンスについて説明します。

本資料の説明内容

本資料は、PHPアプリケーションをMySQLからDB2に移行する際の一般的なステップについて理解を深めることを目的としています。あわせて、参照可能な関連情報について紹介し、IBMのプロジェクト・チームが2010年のはじめに実行したプロジェクトについて説明します。

MySQLからDB2への移行について調べた経験のあるユーザーであれば、製品の説明資料、パフォーマンス・ベンチマーク、DB2の説明資料に記載されている機能、IBM Redbookの比較資料(参考文献を参照)からDB2が提供するメリットについて既にご存知かもしれません。

さらに、機能をフルに装備した無償のデータ・サーバー、DB2 Express-Cも有益な製品です。本製品は、クラウドまたはAmazon EC2上でIBM Smart Business Business Development and Testを活用して簡単にインストールと評価を行えます。本ソリューションに関するリンクも、参考文献のセクションに記載しました。

ここでは、IBM社内で頻繁に使用されるPHPによるイントラネット・アプリケーション(ibm.comのWebサイトのさまざまなページでコンテンツ管理用に使用)を2010年にスムーズに移行した実際の例について説明します。

本資料を読むと、同様の移行プロセスの概要把握し、実行すべき項目のタイミングと条件の確認、発生する恐れのあるリスクの認識、各ステップにおけるサポートの受け方について確認できます。これらの情報を確認すれば、ユーザーのPHPアプリケーションが現在MySQLで構築されている場合、確信を持ってDB2を選択し最大限に活用できます。

本記事の適用について

本記事は、IBM社内で実施したMySQLからDB2への移行のプロセスで得られた教訓と同様の処理に利用可能な関連情報について説明します。本記事は、すべてのシナリオに対応しているわけではなく、移行に関する包括的ガイドではありません。

お客様にとって最適なアプローチを決定するには、”MySQL to DB2 Conversion Guide”もしくはソフトウェア移行プロジェクト事務局(SMPO)で、無料で移行に関する見積もりを行えます。参考文献にて、そのリンクが提供されています。

コードの移行について

本資料は、IBMの事例に基づいて、PHPアプリケーションのコードを移行することで、MySQLのドライバーと構文をDB2のドライバーと構文に変換する5つの主なステップについて説明します。変換を実施する前に必要に応じて本シリーズの第1部を参照して全体的な移行プロセスのスケジュールを確認できます。

ステップ1: コード変換の第1ステップを実行する

  • DB2に対応できるようにPHPのコンフィギュレーションをアップデートする。
  • DB2の構文に対応できるように個々のSQL文をアップデートする。
  • 必要に応じて、DB2のユーザーが定義した関数を使用してネイティブのMySQLの関数をエミュレートする。
  • 必要に応じてSQLからPHPにロジックを移行する。

ステップ2: コード変換の第2ステップを実行する

  • 適切な分離レベルをサポートするために必要な変更を検証する。
  • クエリーを論理的な作業単位ごとに再構築することで、データの一貫性とパフォーマンスを向上する。

ステップ3:ステークホルダーの協力に基づき、ビジネス部門の使用シナリオのテストを開始する

  • ステークホルダーの協力を得て、以前のシステムで実行していた使用シナリオを実行する。
  • テストの結果判明した障害を把握し、欠陥を分析して、開発者による修正を適用する。

ステップ4: ボトルネックに対応し、ベースラインの機能との比較を行う

  • 機能検証の実施後、ユーザーのフィードバックに基づいてシステムのパフォーマンスを改善する。
  • DB2の自動問題修正機能を活用する(本移行プロセスではDB2の実装が最も重大な変更点であるため)。
  • オペレーティング・システムのリソースの使用率を参照のうえ、PHPのボトルネックに対応する。

ステップ 5: コード変換のベースライン情報を検証する

  • 上記の各ステップを繰り返し実施した後、コードの変換が完了したことを確認する。
  • システムのバックアップを行い、バージョン管理システムでアプリケーションのバージョンを管理する。
  • 次のステップで行うアプリケーションの実装に備えて、必要な準備が完了していることを検証する。

本事例のPTTで提供される既存のPHPコードについて

本事例のアプリケーションでは、プロジェクト・トラッキング・ツール(PTT)のソースコードは数百のPHPファイルで構成されています。コードには、関数のライブラリー、オブジェクト指向のコード(データ転送オブジェクトやマネージャー・クラスとして使用される)、およびユーザーインターフェースを提供するさまざまなHTMLテンプレートのフラグメントやヘルパーが含まれます。

PTTのデータベースはibm.comで公開されるワークフロー情報をサポートするさまざまな機能に使用されています。PHP Webのフロントエンド経由で、世界中の4,000名を超えるユーザーが本データベースにアクセスし、データベースの修正を行っています。ある一時点では、最大数百名の同時アクセスユーザーが存在しています。

コードは、mod_phpを共有モジュールとして読み込む単一のApacheのWebサーバー上に実装されています。

本事例では、主に組み込まれたSQLに変更を加えることで、既存のコードを新規のDBシステム用にアップデートしました。DB2ドライバーを使用するためのPHPのコンフィギュレーションのアップデートと、新規の接続文字列を使用するためのデータベース接続の微調整のために、その他の小規模な変更も行いました。本資料では、アプリケーションの品質、構造および保全性の向上を目標とした、より優れたモデル・ビュー・コントローラー(MVC)アーキテクチャーを実現するために、アプリケーション構造に適用した改善項目についても説明しています。

変換に必要なソフトウェアのインストール

本事例のコードを変換するために、Windowsワークステーションに変換のステップを実行するための以下のソフトウェア・コンポーネントをインストールします。

移行元データベースのMySQL(いずれかのバージョン)

コードに対して行った全ての変更を検証するために、旧システムのコピーを参照用に保管しておく必要があります。新システムに対して適用した変更を旧システムと並べて比較するためです。本シリーズの第2部で使用したものと同じシステムを使用できます。

データ・サーバーのドライバーをローカルかテスト・サーバーにインストールしたDB2(いずれかのバージョン)

ワークステーション上に新規の移行先のデータベースを構築するために、DBのインストールを行います。通常、本データベースは本番システムで使用されているデータベースと同じエディションである必要はありませんが、全ての機能について互換性を保つために、同じエディションを使用することをお勧めします。本資料に記載される事例に基づいて、DB2 Enterprise Server Edition Version 9.7.2をインストールします。PHPクライアントのライブラリーを提供するために必要なデータ・サーバーのドライバーが利用可能であることを確認します。本シリーズの第2部で使用したものと同じシステムを使用できます。

ibm_db2の拡張機能またはPDO_IBM (PHP Data Objects)を備えたPHP(いずれかのバージョン)

Zend Serverの最新バージョンをダウンロードし、DB2の拡張機能のオプションを選択します。本オプションを使用するには、追加パッケージのインストールが必要です。

Zend StudioまたはEclipse PHP Development Tools (PDT)をはじめとするIDE

Zend StudioやEclipse PHP Development Tools等のPHPと互換性のあるIDEを使用して、PHPの開発を簡略化します。PHPアプリケーションを保有しているため、適切なツールを既に保有している可能性があるからです。

設定内容と得られた情報を詳細に記録しておくことで、展開のステップを繰り返し実行できます。重要な目標を達成した主なマイルストーンにおいてWindowsオペレーティング・システムのスナップショットを仮想イメージに保存することで、コンフィギュレーションのバックアップとして活用し、今後データベースの改善を行う際に活用可能なベースライン情報として使用できます。

物理マシンのコンフィギュレーションのイメージをキャプチャーしたい場合は、無償提供されるVMware vCenter Converterを活用できます。その他の方法としては、クラウドを活用して変更もできます。Amazon EC2によるDB2 AMIを使用したり、IBM SmartCloud(以前は、IBM Development and Test on the IBM Cloudと呼ばれていました)の契約もできます。仮想コンピューターを使用することで、サーバー・ハードウェアの調達を行い、オペレーティング・システムとDB2をインストールする作業を省けるため、所要時間を削減し、移行プロセスを迅速化し、余裕を持ってニーズに最も合致するコンフィギュレーションを試すことが可能になります。これらの製品に関するリンクは、参考文献にあります。

ステップ1: コード変換の第1ステップを実行する

コード変換の第1ステップでは、PHPとDB2に基づく新規のインフラのコンフィギュレーションを設定し、アプリケーションに基づいてPHPとSQLがデータにアクセスする方法を変換します。本ステップには、以下のサブステップが含まれます。

PHPのドライバーとコンフィギュレーションをアップデートする

既存のPHPのコンフィギュレーションを修正することで、DB2のドライバーに対応し、サーバー・データベースのエンコードに適合できるように設定を行い、詳細な警告メッセージやエラー通知機能によって重要なシステム検証メッセージを提供できるようにします。

DB2用にPHPコードをアップデートするには、通常ibm_db2の拡張機能が使用されるコードの該当箇所を変更するか、PDO (PHP Data Objects)インターフェースを提供するPDO_IBMの接続文字列をアップデートします。MySQLベースのシステムにおいてPDOをデータベース抽象化ライブラリーとして使用している場合は、コードに対する変更はそれほどありません。1つの接続文字列を変更するだけで済みます。プロシージャーを提供するibm_db2の拡張機能を使用している場合でも、アップデートのプロセスは単純なものです。通常、コード内に存在する”mysql_”または”mysqli_”で始まる関数を、”db2_”のプレフィックスが付いた同様の関数で置き換えることができます。Developing PHP Applications for IBM Data Servers (参考文献セクションに表示されるIBM Redbookを参照) の6.3章で、関数を比較した表とサンプルコードを確認できます。

いずれの接続方法もDB2サーバーに接続するためのブリッジとしてDB2クライアントを使用するため、リモート・サーバーの設定に合致するようにDB2クライアントのコンフィギュレーションを設定する必要があります。特に、DB2クライアントのコード・ページがDB2サーバーのコード・ページと同一であることを確認する必要があります。同一でない場合は、文字セットで問題が発生します。本事例では、リスト1のコマンドを実行することでクライアントのエンコードをUTF-8に設定します。本設定は、本シリーズの第2部のリスト1のCREATE DATABASEコマンドに相当するものです。

リスト1. DB2のクライアントのコード・ページをUTF-8に設定する
db2set db2codepage=1208

ランタイム・クライアントをインストールした各ワークステーションと各サーバーで本設定が有効化されていることを確認するために、リスト2のコマンドを実行します。

リスト2. DB2のコンフィギュレーション変数を参照する
db2set -all

最後に、変換のプロセス中には全てのエラーと警告のメッセージをブラウザでキャプチャーおよび表示させることが必要です。このため、php.iniファイルにおいてリスト3のとおりパラメーターをアップデートします。本番システムを提供する時点ではパラメーターの設定を変更し、それほど詳細でない警告メッセージを表示するようにします。本シリーズの第4部では、本番システムの提供時に実施すべきエラー報告のメカニズムの例について説明しています。

リスト3. php.iniファイルでエラー制御パラメーターを設定する
display_errors = On
error_reporting = E_ALL & ~E_NOTICE

SQL構文を変換する

次に、DB2の構文に対応できるように個々のSQL文をアップデートするか、データのアクセス方法を若干変更します。ほとんどのアプリケーションの場合、本プロセスでは大量のコードのアップデートおよび検証のためのテストを行います。MySQL to DB2 Conversion Guide (IBM Redbook)の8.1章の“Data Manipulation Language differences and similarities”で、一連の構文の変換方法を確認できます。以下においては、本事例のアプリケーションで行った変換について説明しています。

SELECT句のワイルドカード

複数のテーブル内全ての列や、特定の列を選択するワイルドカード(アスタリスク文字)を使用したSELCT文はMySQLでは使用できますが、DB2では使用できません。この場合、テーブル名(またはエイリアス)を指すプレフィックスをワイルドカードに追加し、対象となる各テーブルの列を指定するか、1つのワイルドカードで各テーブルの全ての列を指定します。リスト4では、クエリーの修正方法を説明しています。

リスト4. MySQLとDB2で使用可能な列とテーブルに関するワイルドカードの比較
-- MySQLの場合:
SELECT R.NAME, * 
FROM USER U, ROLE R 
WHERE U.ROLE_ID = R.ID;

-- DB2の場合:
SELECT R.NAME,U.* 
FROM USER U, ROLE R 
WHERE U.ROLE_ID = R.ID;

-- もしくは
SELECT R.NAME,U.ID, U.NAME, U.ROLE_ID, U.DEPART_ID 
FROM USER U, ROLE R 
WHERE U.ROLE_ID = R.ID;
SELECT句において、LIMITを使用して検索結果の最大サイズを指定する

LIMITは、クエリーで戻される最大行数を指定するためのMySQLのみで使用される非標準のキーワードです。DB2では、本機能を提供するために“FETCH FIRSTn ROWS ONLY”の構文を使用します。リスト5は、DB2用にクエリーを修正する方法を示しています。

リスト5. MySQLとDB2において、クエリーの結果戻される最大行数を指定する
-- MySQLの場合:
SELECT * FROM ROLELIMIT 10;

-- DB2の場合:
SELECT * FROM ROLEFETCH FIRST 10 ROWS ONLY;
GROUP BY句

MySQLでは、列の一覧において集計関数が使用する列名を指定せずにGROUP BY句を使用できます。しかし、DB2でそうすることはできません。検索の結果としてあいまいな情報が提供され、多くのユーザーにとって許容できないためです。リスト6では、DB2 のクエリーで列を明示的に指定する方法について説明しています。

リスト6. MySQLとDB2において使用可能なGROUP BYの集計関数の比較
-- MySQLの場合:
SELECT R.ID, R.NAME, COUNT(U.ID) AS NUM 
FROM USER U, ROLE R 
WHERE U.ROLE_ID = R.ID 
GROUP BY R.ID;

-- DB2の場合:
SELECT R.ID,MIN(R.NAME), COUNT(U.ID) AS NUM 
FROM USER U, ROLE R 
WHERE U.ROLE_ID = R.ID 
GROUP BY R.ID;

-- または
SELECT R.ID, R.NAME, COUNT(U.ID) AS NUM 
FROM USER U, ROLE R 
WHERE U.ROLE_ID = R.ID 
GROUP BY R.ID,R.NAME;

リスト6においてはR.IDとR.NAMEは本テーブルにおける主キーであるため、GROUP BY句をアップデートすることができます。リスト7では、リスト6のクエリーほど簡単に処理できないGROUP BYクエリーについて説明しています。

リスト7. DB2用に修正する必要のあるMySQLによる曖昧なGROUP BYクエリー
-- MySQLの場合:
SELECT R.ID, R.NAME, COUNT(U.ID) AS NUM 
FROM USER U, ROLE R 
WHERE U.ROLE_ID = R.ID 
GROUP BY R.NAME;

この場合は、R.IDは本テーブルの主キーであるものの、R.NAMEは主キーではありません。ROLEテーブル内に重複するR.NAME値が存在する場合、R.IDをMIN(R.ID)で置き換えることができないため、GROUP BY句にR.IDを追加できません。どのような検索結果が必要であるかによって、本SQLの変換方法が変わります。リスト8では、この場合に実行可能なオプションについていくつか説明しています。

リスト8. DB2においてGROUP BYクエリーを変換する
-- DB2の場合:
-- オプション1: MySQLと同じクエリー結果を取得したい場合は、SQLは以下のとおりとなります。 
SELECT(SELECT RL.ID FROM ROLE RL WHERE RL.NAME = R.NAME FETCH FIRST 1 ROW ONLY) AS ID,
R.NAME, COUNT(U.ID) AS NUM 
FROM USER U, ROLE R 
WHERE U.ROLE_ID = R.ID 
GROUP BY R.NAME;
				
-- オプション2: ロール名が同じ場合、同じロールとして扱います。 
-- この場合、R.IDが存在しないため、MySQLとは多少異なるクエリー結果が表示されます。
SELECT R.NAME, COUNT(U.ID) AS NUM 
FROM USER U, ROLE R 
WHERE U.ROLE_ID = R.ID 
GROUP BY R.NAME;
				
-- オプション3: ロールIDが異なる場合、異なるロールとして扱います。
-- この場合、MySQLとは全く異なるクエリー結果が表示されます。
SELECT R.ID, R.NAME, COUNT(U.ID) AS NUM 
FROM USER U, ROLE R 
WHERE U.ROLE_ID = R.ID 
GROUPBY R.ID, R.NAME;
MySQLが提供するREPLACE INTO句とDB2が提供するMERGE句

MySQLでは、REPLACE INTO句が提供されます。DB2でもMERGE句が提供されますが、当該クエリー目的は似通っているものの全く同一ではありません。同一のクエリー結果を得るためには、同じ主キーまたは一意値が存在する場合は、新規のレコードを作成するか既存のレコードを置き換える必要があります。リスト9では、テーブル内の一意値をチェックのうえ、値をアップデートまたは挿入する方法について説明しています。

リスト9. MySQLのREPLACE INTO構文をDB2用に変換する
-- MySQLの場合:
REPLACE INTO ROLE (ID, NAME, DESCRIPTION)
SELECT ID, NAME, DESCRIPTION 
FROM ROLE_TMP;

-- DB2の場合:
MERGE INTO ROLE R
USING (SELECT ID, NAME, DESCRIPTION FROM ROLE_TMP) RT
ON (R.ID = RT.ID)
WHEN MATCHED THEN
UPDATE SET (ID, NAME, DESCRIPTION) = (RT.ID, RT.NAME, RT.DESCRIPTION)
WHEN NOT MATCHED THEN
INSERT (ID, NAME, DESCRIPTION) VALUES (RT.ID, RT.NAME, RT.DESCRIPTION);
JOIN句

2つのテーブルに対して外部結合を実行しつつ、第三のテーブルからデータを抽出する場合、DB2の結合キーワードの隣に外部結合で使用するテーブルを配置します。MySQLでは、これらのテーブルをどのような順序で配置しても構いません。リスト10では、実施すべき変更について説明しています。

リスト10. MySQLとDB2のJOIN構文を比較する
-- MySQLでは、以下のいずれのケースも可能:
SELECT * 
FROM USER U, ROLE R 
LEFT JOIN DEPARTMENT D 
ON U.DEPT_ID = D.ID;

-- または
SELECT * 
FROM ROLE R, USER U 
LEFT JOIN DEPARTMENT D 
ON U.DEPT_ID = D.ID;

-- DB2では、以下のケースのみが可能:
SELECT * FROM ROLE R, USER U 
LEFT JOIN DEPARTMENT D 
ON U.DEPT_ID = D.ID;
エスケープ文字

“\”で始まる文字シーケンスはMySQLではバックスラッシュでエスケープしたシングルクオート(')の文字シーケンスを指しますが、DB2ではシングルクオートにもう1つのシングルクオートを追加して('')、ダブル・シングルクオートとする必要があります。リスト11では、シングルクオートをアポストロフィーとして使用するエスケープ・シーケンスの例を示しています。

リスト11. MySQLはシングルクオートの文字列をエスケープするためにバックスラッシュを使用し、DB2はダブル・シングルクオートを使用する
-- MySQLの場合: 
SELECT * FROM ROLE 
WHERE DESCRIPTION = 'It\'s a super admin role';

-- DB2の場合:
SELECT * FROM ROLE 
WHERE DESCRIPTION = 'It''s a super admin role';
DB2において値の幅を超えているデータを確認する

MySQLではSELECT句においてデータ型のデータの幅の検証を行わないものの、DB2では行います。したがって、リスト12で示される2月1日から2月30日までの情報を検索するSQLは、DB2では機能しないものの、MySQLでは機能します。

リスト12. MySQLでは、データ型ごとのWHERE句の値の幅をチェックしない
-- MySQLでは、値の幅として2月30日を設定可能 
SELECT * FROM USER 
WHERE BIRTHDAY BETWEEN '1980-02-01' AND'1980-02-30';
デフォルト値を持たないNOT FULL列へのINSERT

MySQLではNOT NULL列に値を提供しないINSERT文を使用しても、コマンドは正常に機能します。テーブルを構築する際に列にデフォルト値を指定していない場合でも、MySQLでは自動的にデフォルト値が生成されます。しかし、DB2ではデフォルト値が設定されていない場合は、INSERT文に含まれるNOT NULL列に値を設定する必要があります。リスト13のSQL文は、各製品が提供する異なる挙動について説明しています。

リスト13. MySQLとDB2では、NULL値の挿入の形態が異なる
-- 以下はMySQLとDB2の両方で正常に機能する。 
CREATE TABLE TEST1 (ID INTEGER, NAME VARCHAR(20) NOT NULL);

-- 以下はMySQLでは正常に機能するが、DB2では機能しない。
INSERT INTO TEST1 (ID) VALUES(1);

-- 以下はMySQLとDB2の両方で正常に機能する。 				
CREATE TABLE TEST2 (ID INTEGER, NAME VARCHAR(20) NOT NULLDEFAULT '');

-- 以下はMySQLとDB2の両方で正常に機能する。
INSERT INTO TEST2 (ID) VALUES(1);
MySQLの関数をDB2の同様の関数に変換する

MySQL独自のSQLに加えて、MySQLが標準で提供する関数も使用できます。これらに代えて、SQLの標準関数やDB2が提供する同様の関数の使用もできます。“MySQL to DB2 Conversion Guide”(参考文献セクションに掲載されるIBM Redbook)の”Appendix A: Mapping MySQL built-in functions and operators”をご参照ください。

前述のとおり、DB2で同様の関数が存在しない場合は、SQLを再作成することでデータへのアクセス方法を変更できます。または以下のセクションの説明のとおり、DB2の関数のエミュレートを行うか、ロジックをPHPコードに移行するかを選択できます。

MySQLの機能をエミュレートするために、ユーザーが作成した関数を使用する

DB2構文内のSQL文をアップデートすることでMySQLのクエリーと同じ検索結果を提供できない場合は、DB2のSQLにおいてユーザーが作成した関数(UDF)をワークアラウンドとして実装して、MySQLの標準機能をエミュレートできます。

本事例においてDB2用にアプリケーションのアップデートを行う際に、MySQLとPHP の機能を使用したコード内には、特別なアプローチを必要とするような箇所がいくつか存在していました。特にデータの変換を行うような場合には、一部の機能をMySQLからPHPに移行できます。または、”UNIX_TIMESTAMP()”や”NOW()”のようなMySQLに標準で組み込まれている機能と同様の機能を提供するためにDB2でユーザーが作成した機能を構築するような場合には、DB2内で機能をエミュレートできます。“MySQL to DB2 Conversion Guide”(参考文献セクションに掲載されるIBM Redbook)のAppendix Bでは、これらの機能の違いについて説明しており、変更方法を説明しています。Daniel Krookがお客様の移行プロセスに基づいて収集した移行のヒントも役立ちます(参考文献を参照)。

DB2のDMLはMySQLのDMLとは異なります。本資料は組み込まれたSQLをMySQL用からDB2用に変換する方法について説明していますが、MySQLに組み込まれた関数をはじめとする全ての構文をMySQLからDB2に変換する必要はありません。 in the “MySQL to DB2 Conversion Guide”(参考文献セクションに掲載されるIBM Redbook)の”Appendix A: Mapping MySQL built-in functions and operators”では、MySQLに組み込まれた多くの標準の関数について説明しています。一般的に、これらの関数の多くはDB2のUDFに変換できます。頻繁に使用される標準の関数のうち、どれを変換すべきか決定する必要があります。本事例のケースでは、MySQLの関数やレジスター(頻繁に使用している関数やレジスターの場合)を頻繁に変換するためにDB2のUDFを使用することは有効な手段でした。以下は、変換の対象となるMySQLの関数とレジスターの一覧です。

  • CURRENT_DATE() / CURDATE()
  • DATE_FORMAT()
  • DATEDIFF()
  • FROM_UNIXTIME()
  • NOW()
  • PERIOD_DIFF()
  • TO_DAYS()
  • UNIX_TIMESTAMP()
  • WEEKDAY()
  • YEARWEEK()

"DB2 basics: Fun with dates and times"(参考文献を参照)に記載されているとおり、これらの関数の多くは幸運なことに既に実装されており、自由に再利用できる状態にあります。DB2 v9.7.2以降のバージョンを使用している場合、新規に提供されるDB2_COMPATIBILITY_VECTOR=MYSの互換性ベクトルの設定もできます。

DB2では、MySQLと同様に、Cプログラミング言語を使用せずSQLで独自の関数を作成できるため、簡単にユーザーが関数を作成することができます。

リスト14は、MySQLのNOW()関数(標準の関数ではないものの、頻繁に使用される関数)をエミュレートするために作成可能なUDFの例を示しています。本事例のアプリケーションのさまざまな箇所で本エミュレーションを使用できます。

リスト14. DB2において、MySQLの標準のNOW()関数をエミュレートするUDFを作成する
CREATE FUNCTION NOW()
	RETURNS TIMESTAMP
	NO EXTERNAL ACTION

	BEGIN ATOMIC
		RETURN SELECT CURRENT TIMESTAMP
		FROM SYSIBM.DUAL;
	END

MySQL to DB2 Conversion Guide”(参考文献セクションに掲載されるIBM Redbook)のAppendix Bには、頻繁に使用されるMySQLの標準関数をエミュレートするためのその他多くのUDFについて説明しています。

必要に応じて、ロジックをPHPからSQLに移行する

場合によってはMySQLの関数や構文をDB2の形式に簡単に変換できないことがあります。そのような場合には、PHPが当該機能を備えているかどうかを確認したうえで、該当する関数や拡張機能を使用する必要があります。例えば、MySQLはINET_ATON()というSQL関数を提供していますが、DB2にはこれと同等の関数は存在しません。INET_ATON()用にUDFを作成すると、エラーが発生するか作業に大きな労力が必要になることがあります。幸運なことに、PHPは同じ機能を提供するip2long()関数を提供し、本関数は特定のデータベース・ベンダーに依存する関数ではありません。

ステップ2: コード変換の第2ステップを実行する

PHPコードをアップデートすることにより、組み込まれたSQL文を修正し、MySQLと同様な方法でデータの読み取りと更新が行えるようになった時点で、クエリーのリファクタリングを行い、データの一貫性とパフォーマンスを向上するための追加プロセスを実施する必要があります。本ステップには、以下のサブステップが含まれます。

同時実行に関して適切な方針を選択する

MySQLベースのアプリケーションに組み込まれた個々のクエリーがDB2上で同等の機能を提供するように変換された時点で、DB2の同時実行とデータの一貫性の機能を活用できるように当該クエリーをさらに改善する必要があります。データベースに同時接続をする際に適切な分離レベルを設定するにあたって、どのような変更が必要か確認します。

DB2には、同時実行を管理する分離レベルが4種類存在します。分離レベルにより、トランザクションにおいて処理中のデータ変更を他のユーザーに非表示にする方法が設定されます。分離レベルは以下のとおりです。

Repeatable Readの分離レベル(RR)

トランザクションが完了するまで、全ての行がロックされる。

Read Stabilityの分離レベル(RS)

トランザクションが完了するまで、述部の条件を満たす行がロックされる。

Cursor Stabilityの分離レベル(CS)

カーソルが置かれている行のみがロックされる。本分離レベルがデフォルトとして設定される。

Uncommitted Readの分離レベル(UR)

データに変更が加えられていない限り、行はロックされない。

パフォーマンスとデータの一貫性の観点から、アプリケーションにどの分離レベルを設定すべきかを検討する必要があります。分離レベルをUncommitted Read (UR)からRepeatable Read (RR)に移行すると、同時実行のパフォーマンスは下がるものの、データの一貫性は改善します。RRの分離レベルは、パフォーマンスを犠牲にしてデータの一貫性を最大化するためです。逆に、URはデータの一貫性を多少犠牲にして高レベルのパフォーマンスを実現します。は、パフォーマンスのデータの一貫性の関係を示しています。

図1. DB2の分離レベル
Diagram with RR, RS, CS, and UR blocks vertically descending. Data integrity scale is highest at RR.                     Performance scale is highest at UR

個々のクエリーから接続やセッションに至るまで、さまざまなレベルで分離レベルを設定できます。読み取り処理を大量に実施するワークロードの場合、接続レベルでUncommitted Read (UR)の分離レベルを設定することで、各ステートメントで分離レベルを設定する必要がなくなります。読み取り処理と書き込み処理が混在しているワークロードの場合は、デフォルトでCursor Stability (CS)ではなく、個別のクエリーに対して分離レベルを設定したほうが効率的です。

さらに、データ・アクセスを最適化するために、使用すべきカーソルの種類を検討する必要があります。ibm_db2とPDO_IBMの両方のドライバーは、前進のみのカーソルとスクロール可能なカーソルの2種類に対応しています。デフォルトで設定されているのは、前進のみのカーソルです。通常デフォルト設定は大部分の使用シナリオに対応でき、この場合シーケンシャルのクエリーの実行結果に繰り返しアクセスできます。パフォーマンスの観点からは、前進のみのカーソルのほうがスクロール可能なカーソルよりも優れています。しかし、場合によっては、前向きと後ろ向きでカーソルの読み取りを行う必要があり、この場合はスクロール可能なカーソルを使用する必要があります。本事例では、リスト15のコマンドを使用して、クエリーごとにカーソルの種類を指定しました。

リスト15. ibm_db2関数とPDOドライバーにおいて、DB2のスクロールタイプを設定する
-- For ibm_db2
db2_exec($connection_resource, $sql, array('cursor' => DB2_SCROLLABLE));

-- For PDO_IBM
$DB_PDO->prepare($sql, array(PDO::ATTR_CURSOR, PDO::CURSOR_SCROLL));

Developing PHP Applications for IBM Data Servers”(参考文献セクションに掲載されるIBM Redbok)の4.2章の”Using PHP with DB2 database”では、より詳細な情報について説明しています。

データの品質とパフォーマンスを向上するために、クエリーを統合する

次に、個別のクエリーを管理するのではなく、クエリーを論理的なグループにまとめます。コードの移行を行う本サブステップでは、SQL文で古いアプリケーションと同じデータを取得でき、古いアプリケーションと同様にデータの修正が行えることが判明した時点で、データ・アクセスを行うクエリーとアップデートをグループにまとめる必要があります。これによって、DB2のデータの一貫性を確保したうえで、MySQLに匹敵するパフォーマンスを実現できます。

ステートメント数に対するデータベース接続数を削減する

一般的に、DB2に比べてMySQLのほうがデータベースへの接続は迅速に行われます。したがって、複数のクエリーに対して1つの接続を再利用することによって、HTTPリクエストごとの総接続数を削減することで、プロセッサー、ディスク、およびネットワークのパフォーマンスを改善できます。本事例においては、複数のクエリーを統合しています。統合の例としては、複数のルックアップ・クエリーを使用するのではなくアップフロントでより広範な結合を実施することで、ユーザー・アカウントの情報を取得するためのクエリーを統合する例が挙げられます(リスト16を参照)。

リスト16. クエリーの統合を行う
-- 2種類の異なるクエリーを使用すると、データ・サーバーのページに2度アクセスする必要がある。
SELECT FIN_PROJECT_MANAGER AS PM, PROJNAME FROM PROJECT WHERE ID = $id;
SELECT EMAIL, FIRSTNAME, LASTNAME FROM USER WHERE ID = $pm;

-- 単一の統合したクエリーを使用すると、同じ情報を1度のアクセスで取得できる。
SELECT U.EMAIL, U.FIRSTNAME, U.LASTNAME
FROM USER U, PROJECT P
WHERE U.ID = P.FIN_PROJECT_MANAGER 
AND PROJECT.ID = $id
より効率的な管理を行えるMVCアーキテクチャーへの移行

モデル・ビュー・コントローラー(MVC)アーキテクチャーを使用した効果的な3層構造のWebアプリケーションでは、コントローラーがユーザーからのWebリクエストを処理し、その後モデル上のコマンドを呼び出して検索結果を準備します。検索結果のオブジェクトはビュー(単一のHTMLテンプレートで表示されることが多い)に転送され、ページ上で戻されたデータを表示します。旧アプリケーションではMVCのパターンを使用せず、アプリケーションがテンプレートに直接クエリーを組み込むことで、3種類の処理を1つのページで効果的に実現していました。

MVCアーキテクチャーのベストプラクティスに基づいて複数のクエリーをまとめる(例: ユーザー・アカウントに関する全ての情報と当該ユーザーの全てのプロジェクトを、モデルに対する単一の呼び出しプロセスに統合する)ことで、ビューのページではデータのフェッチを繰り返す必要がなくなり、該当するデータのみを表示すればよくなります。本手法によって、アプリケーションはデータに左右されるアプリケーションからビジネス・オブジェクトを活用するアプリケーションへと変貌でき、データ・アクセスのロジックをレイアウトや設計のマークアップから分離できるため、コードの構造を改善できるようになります。

コンピューティングのワークロードは分散したほうがよい

本事例では単一のWebサーバーと単一のデータベース・サーバーに基づくものであったため、Webサーバーでは大容量のHTTPトラフィックの処理にほとんどのリソースを割いていました。本WebサーバーはPHPモジュール経由でサーバー側のロジックを実行していました。Webサーバーがmod_phpで使用するCPUとメモリーの使用率を削減してHTTPサーバーのパフォーマンスを向上させるために、一部のビジネス・ロジックの処理をデータ・サーバーに分散しました。 これによりネットワーク・トラフィックの量の削減(Webサーバーとデータ・サーバー間のトラフィックが削減することにより実現)という追加のメリットが発生するため、データにアクセスしたレベルでデータを処理できるようになり、スループットが改善しました。

サーバーでクエリーをまとめることによって、データの一貫性を高める

トランザクションの一貫性を高めるための最も重要なプロセスは、ストアード・プロシージャー、トリガー、およびユーザーが作成した関数を使用するデータ・サーバー上で、ロジックをトランザクションごとの個別の単位にまとめることです。以下のようなプロセスを実現すると、PTTのようなアプリケーションでタスクを実行するプロセスの信頼性が高まります。

  • 全てのデータをデータベースに一括転送する。
  • 複数のINSERT文をまとめて実行する。
  • ユーザーに対して単一の完了メッセージやエラーメッセージを戻す。

トランザクションをサポートできるデータベースが存在しない場合、本事例のケースでは上記のデータベースの修正のいずれかまたは全てで障害が発生し、システムが不安定になり、ユーザーまたは管理者の介入によりデータの不整合(オーナーやその他の重要な情報が関連付けられないまま不十分な形でタスクが構築される等)を解決する必要が発生する場合があります。リスト17では、2つの関連するアップデートを1つにまとめる処理について説明しています。

リスト17. 複数のアップデートを1つのストアード・プロシージャーにまとめる
CREATE PROCEDURE BILLING_TYPE_UPDATE (IN p_date DATE)
BEGIN
    -- Update for project's billing type
    FOR row AS 
       SELECT * 
       FROM proj_billingtype_snapshot
       WHERE end_date IS NULL
       AND start_date = p_date
       
	    DO
	       -- Execute two updates in one transaction
	       t1: begin atomic   -- Transaction begins
	      
	       -- First update
	       UPDATE fin_attributes
	       SET proj_type = row.billing_type
	       WHERE project_id = row.proj_id;
	        
	       -- Second update
	       UPDATE fin_attributes_archive
	       SET proj_type = row.billing_type
	       WHERE project_id = row.proj_id
	       AND year = YEAR(NOW());
	          
	       end t1;            -- Transaction ends
    END FOR;
END

全てのDB2のバージョンはトランザクションをサポートしていますが、デフォルトのMyISAMをはじめとするほとんどのMySQLのストレージ・エンジンはトランザクションをサポートしていません。本事例のアプリケーションはデフォルトのMyISAMのテーブル型を使用して構築されており、開発者はコードレベルでデータの一貫性を実現しようとしていました。したがって、DB2への移行にはメリットがありました。移行によってデータベースにDBが得意とするトランザクション管理ができるためです。移行を行うことで、本アプリケーションに含まれるPHPのページで連携する必要のあるコードやさまざまなコンポーネントの総容量を削減し、より簡略化されたシステム構造の構築もできました。“PHP Applications for IBM Data Servers”(参考文献に掲載されるIBM Redbook)の5.2.8章の“Transactions and isolation level”を参照することで、DB2の同時実行に関するより詳細な情報を確認できます。

ステップ3: ステークホルダーの協力に基づいて、ビジネス部門の使用シナリオのテストを開始する

この時点で、テスト可能な機能システムが、システム全体もしくは少なくとも特定のコンポーネントで構成される関連機能のセットで完了しているはずです。例えば、本PTTシステムにおいては、親プロジェクトに関連する新規のタスクを構築するプロセスは、それ自体がかなり独立したプロセスです。製品の名称や説明をはじめとするコンテンツの一部を翻訳サービス部門に送付するプロ個別にテストが可能なプロセスです。変換を行う本ステップには、以下のサブステップが含まれます。

ユーザー・アクセプタンス・テストを作成する

本ステップでは、ステークホルダーの協力を得て、古いシステムで実行していた一連の使用シナリオを実行します。一連の同じプロセスを実行することで、新システムでも同じ機能が提供できることを確認します。

本事例では、過去数年間にわたって非常に標準化された形式でユーザー・アクセプタンス・テストを実施してきました。したがって新規の機能を検証し、既存のコードで回帰問題を避けるための過去のテストアーカイブが存在していました。テスト・ポートフォリオの拡張を行い、PTTの機能が提供する主要機能に対応できるようにしました。これらのテストによって、ビジネス上の目標を達成する一連の関連ステップをまとめて検証でき、実際のシステムの使用シナリオに沿ったテストを行えます。本テストが静的な使用シナリオのテストと異なる点は、テストがスクリーンショットやテーブルとともにスプレッドシートの形式で保存され、テーブルの行にはテスターが特定の一連のステップや個々のステップテーブルが成功したかを入力できる状態になっていることです。は、タスクが予定どおり作成できるか検証するためのユーザー・アクセプタンス・テストの例を示しています。

表1. ユーザー・アクセプタンス・テストの例
テスト予測される結果テストの結果
1. 新規のタスクを作成する1.1 システムにログインする最初のページが表示される成功
1.2. 1.2. 新規のタスクのページを開く新規のタスクのフォームがロードされる成功
1.3 フォームに入力し、保存する完了のメッセージとユニークなタスクIDへのリンクが表示される成功
2. タスクを承認する2.1 システムにログインする最初のページが表示される成功
2.2. 一連のタスクを確認する一連のタスクがロードされる成功
2.3 タスクの横の適切なボタンをクリックする完了のメッセージが表示され、電子メールが送信される不成功

テスターは十分な時間を割いてユーザー・アクセプタンス・テストを実施し、発生する問題を記録する必要があります。機械的にテストを実施するだけでは、エラーの発生を見逃す可能性があります。また、誰がいつテストを実施し、どのようなテスト結果が報告されたかを記録することで、テスターが責任をもってテストを実施する環境を作ることも重要です。

ユニット・テストと問題の解決

本ステップでは、ユーザー・アクセプタンス・テストへの参加者が報告した問題を体系的に解決します。テスターからユーザー・アクセプタンス・テストの結果が戻された時点で、報告された問題を個別の開発者に割り当て、問題の検証と解決を行います。例えば、のテスト2.3を新規に確認された欠陥として記録します。

システム移行を繰り返し実施するにあたり、開発者はDB2内の移行された機能をMySQLの移行元のシステムの機能と比較し、特定の一連のジョブが予定どおり機能することを検証する必要があります。ユーザー・アクセプタンス・テストとともに、PHPUnitのようなユニット・テストのフレームワークを使用して本プロセスの自動化を行うことも有益です。 本事例のコードベースは簡単にプログラム化されたテストに対応するMVCコードに効果的に分割できなかったため、ユニット・テストや継続的なシステム統合を自動化するシステムは使用しませんでした。

イメージをキャプチャーのうえ、ベースライン情報として活用する

大量の使用シナリオのテストが完了した時点で、新規のテストを実施する前に、システムの現在の状態のバックアップ、および機能やパフォーマンス検証のベースライン資料としてとしてキャプチャーする必要があります。繰り返しの変更による安定した結果の提供のために、バックアップを保管しておくことが必要です。バックアップは従来のファイル形式やSQLバックアップの形式でもしくは完全なオペレーティング・システムのイメージの形式で保管できます。このようなバックアップは将来エラーが発生した際に利用でき、機能の比較を行うためのベースライン資料としても活用できます。

ステップ4: ボトルネックに対応し、ベースラインの機能との比較を行う

一連のユーザー・アクセプタンス・テストを全て実施し、ステークホルダーがサインオフした時点で、システムのパフォーマンス改善が必要です。本ステップには、以下のサブステップが含まれます。

ユーザー・アクセプタンス・テストによるベースライン情報を確認する

この準備ステップにおいては、検証済みシステムの機能を適切に把握して、パフォーマンスをはじめとするシステムの機能改善や、システム機能に直接関係しない改善によって新システムに求められる機能要件にマイナスの影響が発生したり、当該機能要件が損なわれたりしないことを確認する必要があります。本ステップは機能検証の後で実施する必要があり、その理由としては以下の2つが挙げられます。第1に、本ステップを実施することで、システムの機能に直接関係しない変更を行う前に、ステークホルダーがサインオフした機能をベースライン情報として把握する必要があります。第2に、本ステップによって、移行中に早計な最適化を行うリスクを回避できます。あるケースにおいては特定のクエリーや手法が処理を迅速にすることが判明しても、特にパフォーマンス上の問題がない場合は、コードベース全体への機械的な変更のバインドは避ける必要があります。

本事例においては、ユーザー・アクセプタンス・テストの履歴情報を保存することで、以前に実施した事象のテスト結果を確認することで、後に発生する回帰問題の検証に活用できるようにしています。

DB2のパフォーマンス上の問題を修正するツールを使用する

本移行プロセスではDB2の実装が最も重大な変更点であるため、このタイミングでは主にDB2の自動問題修正機能に基づいてテストを行います。コードを変更し、ユニット・テストとユーザー・アクセプタンス・テストを実施するプロセスにおいては、ボトルネックを検出し、必要に応じて変更を行う必要があります。ここで重要なことは、パフォーマンス上問題の潜在源について仮説を立てるのではなく、ユニット・テストやユーザー・アクセプタンス・テストを通じて確認済みの問題に対してのみ変更を行う必要があるということです。本プロセスの実施後、変更事項を文書化し、将来のアプリケーションに発生しうる問題に備えて情報を記録します。

早計な最適化の実施と一連のベストプラクティスに基づきシステム全体にフィックスを適用することは、微妙なバランスに基づいて実施すべきことです。本プロセスは、アプリケーションのシステムライフを通じて繰り返し実施するプロセスです。早計な最適化を行うリスクについては、右記の記載をご参照ください。

有益な手法としては、本事例のPTTアプリケーションに対して行ったように、60秒間以上起動するスクリプトをモニタリングしてエラー・レポートを送信する関数を実装することです。本機能の例については、本シリーズの第4部 (US)で説明しています。

当該エラー・レポートで提供される情報に基づいて、問題の原因がPHPの実行が遅いためなのか、それともクエリーの実行が遅いためなのかを確認できます。PHPで発生する問題については、Zend StudioやEclipse PDTで提供されるデバッガを使用することで、問題を見極めるが可能です。クエリーに関する問題については、IBM Data Studioを使用して問題を検出し、フィックスを適用できます。は、各クエリーに関するアクセスプラン・グラフの例を示しています。

図2. IBM Data Studioでクエリーのチューニングを行う
Flow chart showing departments flowing to fetches and scans, then to join and to return
Flow chart showing departments flowing to fetches and scans, then to join and to return

オペレーティング・システムのボトルネックを確認し、対応する

アプリケーションのパフォーマンス上の問題を発生させている原因としてDB2とPHPが当てはまらないことが判明した場合は、John Coggeshallによる"Zend Enterprise PHP Patterns"(参考文献を参照)に記載されている実績のあるベストプラクティスを活用して、繰り返しボトルネックの原因を見極める必要があります。つまり、以下の3つのコンポーネントに関するアプリケーション・ボトルネックの検証を開始して、単純なハードウェアのアップグレードや仮想マシンに割り当てられたリソースのチューニングによってパフォーマンス上の問題を解決できるかを確認できます。

  • CPU
  • メモリー
  • ディスク

上記の資料に記載されるボトルネックの見極めに使用されるツールの多くはLinuxのオペレーティング・システムに対応したものですが、手法の多くはWindows用のツールでも使用できます。これらの手法は、アプリケーションの開発中やアップデーしたアプリケーションをテスト・サーバーやステージング・サーバーに実装した後、さらには本番システムへの実装が完了した後も活用できます。

ステップ5: コード変換のベースライン情報を検証する

ステップ1からステップ4(各ステップには複数のサブステップが存在)までの繰り返しのプロセスを1度完了した時点では、Windowsワークステーション上に一連の機能を提供するデータベース・システムが完成し、変更や問題が発生した項目についていくつかの教訓が得られているはずです。仮想マシンを使用している場合、Winsowsシステムのイメージをスナップショットとしてキャプチャーすることができます。本情報は利用可能な機能をアーカイブした情報として活用することや、将来パフォーマンス変化を比較するためのベースライン資料としても使用できます。その他の方法としては、IBMやAmazonによるクラウド環境における仮想イメージを使用することで、同じ目的を達成できます。様々な方法でコードのアップデートを試してみることで、お客様の環境に最適な方法を確認できるようになります。

ステップ1からステップ4において使用するWindowsワークステーションのシステムの内容に満足できるようになった時点で、バージョン管理システムにおいて変換済みのコードのバージョンを保存し、本シリーズの第4部で説明する最終ステップの実装前に、インフラの準備ができます。

結論

本資料の目的は、PHPアプリケーションをMySQLからDB2に移行する際に通常必要となるプロセスを理解し、利用可能な参照資料を把握し、実際にスムーズに移行を行った事例について確認することです。

本シリーズの第3部は、以下の情報を提供します。

  • 変換の対象となる移行元のPHPコードについて理解する。
  • DB2用にアプリケーションをアップデートする方法について確認する。
  • 変換を行った後で、コードのテストとチューニングを行う方法について確認する。

第4部では、移行を行ったアプリケーションを実装し、継続的に保守を行う方法について説明します。

謝辞

本資料の記載内容を確認し、コメントを提供してくれたLeons PetrazickisとAmbrish Bhargavaに感謝します。


ダウンロード可能なリソース


関連トピック


コメント

コメントを登録するにはサインインあるいは登録してください。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Information Management, Open source
ArticleID=824326
ArticleTitle=PHPアプリケーションをMySQLからDB2に移行するプロセス: 第3部PHPコードの変換
publish-date=08272012