分散的にsqliteを扱う場合のパフォーマンスに関するメモ

巨大なSQLliteのDBをいくつかのDBに分けた場合のパフォーマンスに関するメモ。

結論:2つ以上のDBに交互にSELECT文を発行すると、そうでない場合に比べて10〜100倍くらい遅くなった。


最初に複数のDBコネクションを配列に入れる。

my @dbhs = qw();
for(my $take=$TAKE_MIN; $take<=$TAKE_MAX; $take++){
    my $path = sprintf($DB_PATH2, $take);
    my $data_source2 = "dbi:SQLite:dbname=$path";
    my $dbh2 = DBI->connect($data_source2);
    push(@dbhs, $dbh2);
}

で、SELECT文を各DBに交互に発行。

    my $sql2 = "SELECT * FROM tb WHERE uid=? LIMIT 1";
    for(my $take=$TAKE_MIN; $take<=$TAKE_MAX; $take++){
        my $rows2 = $dbhs[$take-1]->selectall_arrayref($sql2, { Slice => {} }, $uid);
        for my $row2 (@$rows2){
            my $uid2 = $row2->{uid};
        }
    }

1つのDBだけに比べて2つ以上の場合は、HDDのガリガリ音も激しく、またdstatでdiskのread/write両方ほとんど数値がでていなかったので、HDDのヘッドが激しく動きまくってそこがボトルネックになっているのではないかと予想。

同じクラス名は避けるべし

重大: Mapping conflict. A Servlet registration exists with same mapping as the Jersey servlet application, named jp.unko.hoge1.resources.MyApplication, at the servlet mapping, /rest/*. The Jersey servlet is not deployed.
重大: Mapping conflict. A Servlet registration exists with same mapping as the Jersey servlet application, named jp.unko.hoge2.resources.MyApplication, at the servlet mapping, /rest/*. The Jersey servlet is not deployed.

hoge1アプリケーションからhoge2の.jarに依存を張っていて、その両方に同じクラス名のクラスがあるとまずいっぽい。
あ クラス名ってより@Pathか

JAVAでSQLiteを使う(glassfish)

WEBサービスで使う機械学習のモデルを実現するためにJAVAのヒープを使うっていう話もあったが、
メモリに頼ったやり方だとモデルが巨大化していった時にスケールしにくいし、ユーザ0のうちから金がかかるのは微妙。
なので、SQLite+ヒープのキャッシュで賄う方向性について模索中。


■ 環境構築
glassfishを使う場合、管理コンソールでの特別な設定は不要。
たぶんあれはプールしたいときに必要。でも、たしかどっかの記事でSQLiteではコネクションをPoolする意味ないみたいなのを読んだ気がする。

jarはここから最新のを落として、
xerial / sqlite-jdbc / Downloads — Bitbucket
以下の3箇所に配置。たぶん全部は必要ない。おいたらGlassfishのdomain再起動が必要。
・src/lib/以下
・略glassfish-4.1/glassfish/lib/
・略glassfish-4.1/glassfish/domains/domain1/lib/
古いjarバージョンだと、readonlyモードに切り替えられなくてハマった。
それと、複数の異なるバージョンを同じディレクトリに置かないほうがいい。


pom.xml

<dependency>
            <groupId>sqlite-jdbc-3.8.10.1.jar</groupId>
            <artifactId>sqlite-jdbc-3.8.10.1.jar</artifactId>
            <version>3.8.10.1</version>
            <scope>system</scope>
            <systemPath>${basedir}/src/lib/sqlite-jdbc-3.8.10.1.jar</systemPath>
        </dependency>

■ 実験
main.java

            Class.forName("org.sqlite.JDBC");
            SQLiteConfig config = new SQLiteConfig();
            config.setReadOnly(true);
            conn = DriverManager.getConnection("jdbc:SQLite:" + DB_PATH, config.toProperties());
            // check
            System.out.println("readonly:" + conn.isReadOnly());

            // test
            //...時間のかかるSQL文

同時に読み込みできるかtest

ab -n 2 -c 2 http://localhost:8080/TEST/rest/test

readOnlyモードが無効だった時はコンソールに「[SQLITE_BUSY] The database file is locked (database is locked) 」がでていたが、うまくいくと2つ以上のアクセスを同時にさばくことができた。



ーー
次に1つのスレッドがINSERTする中で、複数のREAD ONLYな処理を捌けるのかを確かめてみる.
非ReadOnlyモードで1秒間に2回INSERTを行い続ける中で、別のスレッドで数十秒レベルのSELECTをReadOnlyモードで行い続けた。
その結果、SELECT文の時間が1.5倍くらいになったものの、ロックエラーはでることはなくなった。

最大使用メモリの取得


参考:GNU timeでプロセスの最大メモリ使用量を取得するシェルスクリプトを書いてみた - N_Nao’s log


mac

/usr/bin/time -lp perl myScript.pl

maximum resident set sizeが最大使用メモリらしい

real 172.55
user 144.37
sys 20.92
12747128832 maximum resident set size
0 average shared memory size
0 average unshared data size
0 average unshared stack size
7352383 page reclaims
3 page faults
0 swaps
22 block input operations
2 block output operations
120 messages sent
121 messages received
0 signals received
215 voluntary context switches
113544 involuntary context switches

ディープラーニングフレームワークChainerをEC2のGPUインスタンスで動かす g2.2xlarge instance

GPUモードで動かしたことなかったのでEC2でやってみた。
GPUモードとCPUモードのスピードの差は圧倒的で、
GPU:CUP=2.49143505096:161.869711161だった

以下手順備忘録

1. g2.2xlargeを一番安いバージニアリージョンでたてる

ただしコマンドのレスポンスが遅いのでイライラする

2. ややこしいので以下全部root

sudo su -

3. いろいろインストール

yum -y update
yum -y install gcc gcc-c++ kmod perl python-devel
wget https://raw.github.com/pypa/pip/master/contrib/get-pip.py
python get-pip.py
pip install numpy

sudo reboot

参考:
chainer-test/Dockerfile.centos7_cuda at master · pfnet/chainer-test · GitHub

4. nvidiaドライバーをインストール

ここの「NVIDIA ドライバの手動インストール」のところ
Linux GPU のインスタンス - Amazon Elastic Compute Cloud
最後のrunファイル実行したらでてくる黒い画面での選択肢は全部YES系。
最初は違うバージョンをインストールしていたがうまくいかず、このバージョン(340.46)でうまくいった。

4.2. CUDAをインストール

wget http://developer.download.nvidia.com/compute/cuda/6_5/rel/installers/cuda_6.5.19_linux_64.run
chmod +x cuda_6.5.19_linux_64.run
mkdir installers
./cuda_6.5.19_linux_64.run -extract=`pwd`/installers
cd installers
./cuda-linux64-rel-6.5.19-18849900.run -noprompt

PATHとLD_LIBRARY_PATHの追加。
いくつかやり方があったけど、最終的にbashrcに書いてしまうのが確実だった

export CUDA_ROOT=/usr/local/cuda-6.5
LD_LIBRARY_PATH=/usr/local/cuda-6.5/lib64
export LD_LIBRARY_PATH
PATH=$PATH:$CUDA_ROOT/bin
export PATH

source ~/.bashrc

5. いろいろインストール

このあたりは勢いで入れたので必要かどうか怪しい

pip install --upgrade pip
yum install zlib zlib-devel tk-devel tcl-devel sqlite-devel ncurses-devel gdbm-devel readline-devel bzip2-devel db4-devel openssl-devel
yum install -y boost-devel

これはたしかchainerのソース見ていたら必要そうだったので入れたやつ

pip install scikits
pip install Mako
pip install six
pip install scikits.cuda==0.5.0b1 (このタイミングではエラーがでて入らなかったので一番最後に入れるとうまくいった)

pycudaのインストール

これが苦労した

wget https://pypi.python.org/packages/source/p/pycuda/pycuda-2015.1.2.tar.gz
tar zxvf pycuda-2015.1.2.tar.gz
cd pycuda-2015.1.2
./configure.py
make
make install

makeはいろいろwarningとかでるが最後がこんなので終わった。

src/wrapper/_pvt_struct_v2.cpp:1593:74: warning: deprecated conversion from string constant to ‘char*’ [-Wwrite-strings]
StructError = PyErr_NewException("pycuda._pvt_struct.error", NULL, NULL);
^
g++ -pthread -shared build/temp.linux-x86_64-2.7/src/wrapper/_pvt_struct_v2.o -L/usr/lib64 -lpython2.7 -o build/lib.linux-x86_64-2.7/pycuda/_pvt_struct.so

make installのあとはこんな感じ

Using /usr/lib/python2.7/dist-packages
Finished processing dependencies for pycuda==2015.1.2

pycudaのTEST

cd ~/pycuda-2015.1.2/test/
python test_driver.py
python test_gpuarray.py
python test_cumath.py

すべてグリーンになったらOK。
ここでのNGを放置するとchainerでもコケる。
最初はNGだったが、
NVIDIAドライバーのバージョン
環境変数(PATHとLD_LIBRARY_PATH)
を見直すことでうまくいくようになった。だったと思う。

今からもういちどこの手順でうまくいくか試してみよう...[OK]

chainer のインストール

最新版(2015/07/04時点)だとcomputational_graph.py周りのimportが新たに加わっていてエラーがでたので、gitから1.0.1を落としてきてソースからインストールした。
あと、sklearn.datasetsを入れるのはすごく大変そうだった。
あ、でも今最新版みたら逆にその部分はimport dataとなっていてsklearn.datasets不要になっているっぽい?

気付き

GPUインスタンスのように高額なインスタンスだと、すごく作業に集中できることがわかった。

TypeError: amax() got an unexpected keyword argument 'keepdims'

というエラーはnumpyのversion updateによって解決

sudo easy_install -U numpy

serviceはかならずreturn {}を含めなければならないっぽい

さもなければコンソールにinjection関係のエラーがでる。