SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

Google App Engine for JavaでMySQLを使ってみる

Google Cloud SQLとBigtableのコード記述と処理スピードを比較・検討してみる

Google App Engine for JavaでMySQLを使ってみる(2)

  • X ポスト
  • このエントリーをはてなブックマークに追加

3. Java Beans

コンストラクタ

リスト3 ビーンズ(bomBean.java)
package  com.business;
import java.sql.*;
import java.util.List;
import com.google.appengine.api.rdbms.AppEngineDriver;
import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.EntityNotFoundException;
import com.google.appengine.api.datastore.FetchOptions;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.Query;

public class bomBean{    
    StringBuffer respBuf = new StringBuffer("");
    StringBuffer rv1 = new StringBuffer("");
    String rv = "";
    // For rdb
    String db       = "business";
    String user     = "user1";
    String pass     = "pass1";
    Connection con  = null;
    // For kvs
    DatastoreService ds = null;
    public bomBean(){                                        //(1)
        try{
            DriverManager.registerDriver(new AppEngineDriver());
            con = DriverManager.getConnection("jdbc:google:rdbms://branecosmology:business/business", user, pass);
                                                             //(2)
        } catch (SQLException e) {    
            respBuf.append("jdbc Driver load error<r>");            
        }
        // For kvs
        ds = DatastoreServiceFactory.getDatastoreService();  //(3)
}    
// beans
:
}

 ビーンズからはMySQLとBigtableの両方にアクセスするので、コンストラクタ(1)では、どちらのアクセスにも対応できるようにしています。

  1. (2)ではCloud SQLで提供される専用ドライバを使用してMySQLデータベースへの接続を行い、
  2. (3)ではBigtableアクセス用にDatastoreServiceインスタンス(ここではds)を生成しています。

 以上の準備でこのビーンズからは、Cloud SQLのMySQLとBigtableの両方にアクセスできるようになります。

 ここで(2)のデータベースへの接続では、ユーザ名とパスワードでuser1とpass1を指定していますが、これは事前にGoogle APIs Consoleから登録してあるもので、図3のようにuser1にはSELECT, INSERT, UPDATE, DELETEの権限が付与されています。

図7 Google APIs Consoleからユーザ権限を確認
図7 Google APIs Consoleからユーザ権限を確認

マスタ登録

Cloud SQL

リスト4 Cloud SQLでマスタ登録(addItemInfo)
    public String addItemInfo(String item_no,
                              String item_name,
                              String final_flag,
                              String vendor_no,
                              String vendor_name){
        // Start Timer
        long start = System.currentTimeMillis();                          //(1)
        // Start Timer
        try{
            String sql = "insert into item_info values(?, ?, ?, ?, ? )";  //(2)
            PreparedStatement ps = con.prepareStatement(sql);
            ps.setString(1, item_no);
            ps.setString(2, item_name);
            ps.setString(3, final_flag);
            ps.setString(4, vendor_no);
            ps.setString(5, vendor_name);
            ps.executeUpdate();                                           //(3)
            // End Timer
            long stop = System.currentTimeMillis();                       //(4)
            // End Timer
            long time = stop - start;    
            rv = " 実行時間:  " + time +"ms";                             //(5)
        }catch(SQLException e){
            rv = "接続失敗\n理由:" + e.toString();
        }catch(Exception e){
            e.printStackTrace();
            rv = "登録失敗\n理由:" + e.toString();
        }finally{
            try{
                con.close();
            }catch(Exception e){}
        }
        return rv;
    }

 リスト4はCloud SQLでのマスタ登録メソッドですが、Cloud SQL独自の処理パターンはなく、一般的なJavaからのMySQLアクセスそのものです。処理の流れとしては、メソッドが呼ばれた直後の(1)で処理開始時刻(単位ミリ秒)を測定して、その後登録処理を行います。

 ここでは動的SQLを使用しているので、SQL文では(2)のように変数を?で指定し、変数値セット後(3)で実行しています。SQLが正常に実行された場合は処理が次の行に移り、そこで処理終了時刻を測定します(4)。あとは、実行時間(終了時刻と開始時刻の差)を計算して、(5)で戻り値rvにセットしています。

Bigtable

 Bigtableにエンティティを登録する場合、同一キーのエンティティがすでに存在する場合でも登録できてしまいます。この場合は前のエンティティを上書きする形で結果的に更新処理になります。

 従って、新規登録を保証するためには、登録エンティティのキー値と同じ値を持つエンティティが存在しているかどうかをプログラム内でチェックし、存在しない場合のみ登録するようにする必要があります。

リスト5 パターン2:Bigtableでマスタ登録2(addIteminfo_kv2)
    public String addItemInfo_kv2(String item_no,
                                String item_name,
                                String final_flag,
                                String vendor_no,
                                String vendor_name){
        // Start Timer
        long start = System.currentTimeMillis();
        // Start Timer
        Key key = null;
        Entity itemf = null;
        try{
            key = KeyFactory.createKey("item_info", item_no);    //(1)
            Entity itemt = ds.get(key);                          //(2)
        }catch(EntityNotFoundException e1){                      //(3)
            try{
                itemf = new Entity(key);                         //(4)
                itemf.setProperty("item_no", item_no);           //(5)
                itemf.setProperty("item_name", item_name);
                itemf.setProperty("final_flag", final_flag);
                itemf.setProperty("vendor_no", vendor_no);
                itemf.setProperty("vendor_name", vendor_name);
                ds.put(itemf);                                   //(6)
                // End Timer
                long stop = System.currentTimeMillis();    
                // End Timer
                long time = stop - start;    
                rv = " 実行時間: " + time + " ms";
            } catch(Exception e3){
                rv = "登録不成功 " +e3;
            }
        }catch(Exception e2){
            rv = "キー生成エラー " +e2;
        }
        return rv;
    }

 リスト5では(1)でキーを生成した後、(2)ではそのキーでエンティティを検索しています。もしそのキーを持つエンティティがまだ存在していない場合、つまり新規登録の場合は(3)の例外(EntityNotFoundException)が発生するので、その場合のみ書き込みを行うようにしています。書き込み処理では(4)で生成されたキーからエンティティを作成し、(5)以下でそのエンティティに対するプロパティ値をセットした後、(6)のputメソッドで書き込みを実行しています。

 createKeyでのキー生成では、親kindを指定するものなど4種類ありますが、ここでは、kind名と文字列のキー名からキーを生成する方式を使用しています。

 キーの生成からエンティティの作成では、下記のように分けて記述することもできますが、(1)のように引数内でのキー生成に変えるだけで、書き込み速度は明らかに速くなっています。

Key key = KeyFactory.createKey("item_info", item_no);
Entity itemf = new Entity(key);

登録処理時間の測定結果

表1 登録処理時間の測定結果(単位ミリ秒)
  1 2 3 4 5
Cloud SQL 登録(クラウド) 125 110 123 112 119
Cloud SQL 登録(ローカルCI) 595 439 617 427 439
Cloud SQL 登録(ローカルMI) 257 31 28 25 31
Bigtable 登録(クラウド) 47 19 18 22 21

 ※CIはGoogle Cloud SQLインスタンス、MIはMySQLインスタンスを意味しています。

 登録時間測定は、クラウド環境でCloud SQLとBigtableで、ローカル環境でCloud SQLインスタンスを使用した場合(CI)とMySQL インスタンス(MI)の2種類で、それぞれ5回測定しています。

 表1を見ると、クラウド環境ではBigtableアクセスの方がCloud SQLよりかなりパフォーマンスが良くまっています。また、ローカルインスタンスではMySQL(MI)の方がCloud SQL(MI)より圧倒的に良いパフォーマンス結果になっていますが、これは前にも見たように、アクセスするMySQLデータベースがローカルかクラウド上かの差異も影響しているはずです。

 全体としては、クラウド環境でのBigtableアクセスが最も高速で、その次に速いのがMySQLインスタンスを使用したローカルでのCloud SQL登録となっています。

 他に登録以外の処理を含めたパフォーマンス全体としては、次の傾向が見られます。

  • 一般に1回目の処理で2回目以降よりも時間がかかる場合があり、表1の登録ではローカルMIのCloud SQLとクラウドのBigtableでその傾向がでています。この傾向は他の測定ではもっと極端に出る場合もあります。
  • 登録処理ではありませんでしたが、初回のアクセスで時間が掛からない場合でも、連続アクセスの途中で時間がかかる場合も出てきます。
  • 計測は2日間に数回行い、最後に行った結果を掲載していますが、実行日および時間によっても測定結果がやや異なってくる傾向があります。
表2 登録処理時間平均(単位ミリ秒)
  全平均 1-4平均
Cloud SQL 登録(クラウド) 117.80 116.00
Cloud SQL 登録(ローカルCI) 503.40 475.00
Cloud SQL 登録(ローカルMI) 74.40 28.75
Bigtable 登録(クラウド) 25.40 20.00

 表2は、表1の測定時間の平均値ですが、全平均の他に1-4平均の項目があります。ここで1-4平均とは、最も時間の掛かったアクセスを除いた平均で、リスト1では、Cloud SQLおよびBigtable2の1回目、Bigtable1の3回目を除外した平均値です。これは次の参照などで顕著ですが、1回目の測定で極端に時間が掛かる場合があり、平均値がその後のアクセス実感と異なってしまうことから掲載しています。

グラフ1 Cloud SQLとBigtableの登録処理の測定(Y軸ミリ秒)
グラフ1 Cloud SQLとBigtableの登録処理の測定(Y軸ミリ秒)

 表1をグラフ表示するとグラフ1のようになります。グラフ表示により、クラウド環境ではCloud SQLよりもBigtableの方が登録処理速度で勝っているのがはっきり見て取れます。また、業務系・基幹系でCloud SQLを使用する場合は、既存オン・プレミスシステムからの移行になるケースがほとんどなので、グラフ1ではCloud SQL(ローカルMI)と、Cloud SQL(クラウド)の対比に近い形になると考えられます。

コラム

 Google App Engineのクラウドを構成するサーバでは、一般的なパーツが使用されていると言われており、スケールアウトによって巨大な分散システムを構成しています。従って、登録データが書き込まれるハードディスクなども特別なものではなく、PCなどにも使用されている一般的なのもが使われているはずです。

 この場合、ハードディスクアクセスの平均的な時間は、例えば1ブロック4kバイトのデータでは25~40ミリ秒程度です。

次のページ
マスタ参照

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
Google App Engine for JavaでMySQLを使ってみる連載記事一覧
この記事の著者

清野 克行(セイノ カツユキ)

慶應義塾大学工学部電子物理専攻卒。日本IBM、日本HPで、製造装置業を中心とした業務系/基幹業務系システムのSE/マーケティングや、分散アプリケーションによる社内業務システム開発などに携わる。現在は、クラウドやAjax関連の/ソフト開発/書籍執筆/セミナー講師/コンサルティング、などを行っている。情...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/6499 2012/04/13 10:30

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング