shimxmemo

メモをのこすよ!

CentOS 5.8にコンパイル済みのTokuDB入りMySQLをインストールするよ

これはすんなりいきましたよ。

参照したのは

Documentation, Collateral and White Papers | Tokutek

TokuDB for MySQL 5.5 Users Guide(pdf)

 

基本的にはデフォルトだと/usr/local/mysqlにインストールする作りになってました。

あと、あらかじめmysqlユーザーとか作っておいてね。

 

インストール環境

# cat /etc/redhat-release
CentOS release 5.8 (Final)
 
# uname -a
Linux hostname 2.6.18-308.13.1.el5 #1 SMP Tue Aug 21 17:10:18 EDT 2012 x86_64 x86_64 x86_64 GNU/Linux

手順

ダウンロードと解凍

TokuDB Community Edition Downloads | Tokutek

TokuDB 7.1.5 for MySQL 5.5.36, 64-bit Linux

をダウンロード

/usr/local/src

に設置

からの

# cd /usr/local/src
# tar zxvf ./mysql-5.5.36-tokudb-7.1.5-linux-x86_64.tar.gz
# mv ./mysql-5.5.36-tokudb-7.1.5-linux-x86_64 /usr/local/mysql

my.cnf設定

好きなので良いと思うんですが

# cd /usr/local/mysql
# cp ./support-files/my-innodb-heavy-4G.cnf ./my.cnf
# vi ./my.cnf
ここで文字コード設定や必要な設定を

MySQL初期設定

# cd /usr/local/mysql
# ./scripts/mysql_install_db \
--user=mysql
chown -R mysql:mysql .

mysqld立ち上げ

# /usr/local/mysql/support-files/mysql.server start

ここで

mysql error while loading shared libraries: libaio.so.1: cannot open shar ed object file: No such file or directory

というエラーが出たので

yum -y install libaio libaio-devel

足りないモジュールをyumでインストール。

で、再度スタート

# cd /usr/local/mysql
# ./support-files/mysql.server start

MySQL起動確認

# ps aux | grep mysql
root     31436  0.0  0.0  64016  1284 pts/0    S    17:02   0:00 /bin/sh /usr/local/mysql/bin/mysqld_safe --datadir=/usr/local/mysql/data --pid-file=/usr/local/mysql/data/hostname.pid
mysql    32090  1.4  2.3 3019076 288036 pts/0  Sl   17:02   0:01 /usr/local/mysql/bin/mysqld --basedir=/usr/local/mysql --datadir=/usr/local/mysql/data --plugin-dir=/usr/local/mysql/lib/plugin --user=mysql --log-error=/usr/local/mysql/data/hostname.err --open-files-limit=8192 --pid-file=/usr/local/mysql/data/hostname.pid --socket=/tmp/mysql.sock --port=3306

立ち上がってる!

TokuDB確認

# /usr/local/mysql/bin/mysql
mysql> show engines;
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
| Engine             | Support | Comment                                                        | Transactions | XA   | Savepoints |
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
| FEDERATED          | NO      | Federated MySQL storage engine                                 | NULL         | NULL | NULL       |
| MRG_MYISAM         | YES     | Collection of identical MyISAM tables                          | NO           | NO   | NO         |
| MyISAM             | DEFAULT | MyISAM storage engine                                          | NO           | NO   | NO         |
| BLACKHOLE          | YES     | /dev/null storage engine (anything you write to it disappears) | NO           | NO   | NO         |
| CSV                | YES     | CSV storage engine                                             | NO           | NO   | NO         |
| TokuDB             | YES     | Tokutek TokuDB Storage Engine with Fractal Tree(tm) Technology | YES          | YES  | YES        |
| MEMORY             | YES     | Hash based, stored in memory, useful for temporary tables      | NO           | NO   | NO         |
| ARCHIVE            | YES     | Archive storage engine                                         | NO           | NO   | NO         |
| InnoDB             | YES     | Supports transactions, row-level locking, and foreign keys     | YES          | YES  | YES        |
| PERFORMANCE_SCHEMA | YES     | Performance Schema                                             | NO           | NO   | NO         |
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
mysql> show plugins;
| TokuDB                        | ACTIVE   | STORAGE ENGINE     | ha_tokudb.so | GPL     |
| TokuDB_trx                    | ACTIVE   | INFORMATION SCHEMA | ha_tokudb.so | GPL     |
| TokuDB_locks                  | ACTIVE   | INFORMATION SCHEMA | ha_tokudb.so | GPL     |
| TokuDB_lock_waits             | ACTIVE   | INFORMATION SCHEMA | ha_tokudb.so | GPL     |
| TokuDB_file_map               | ACTIVE   | INFORMATION SCHEMA | ha_tokudb.so | GPL     |
| TokuDB_fractal_tree_info      | ACTIVE   | INFORMATION SCHEMA | ha_tokudb.so | GPL     |
| TokuDB_fractal_tree_block_map | ACTIVE   | INFORMATION SCHEMA | ha_tokudb.so | GPL     |

TokuDBあった!

show enginesでTokuDBが無かった場合
# /usr/local/mysql/bin/mysql
mysql> install plugin tokudb soname 'ha_tokudb.so';
mysql> install plugin tokudb_file_map soname 'ha_tokudb.so';
mysql> install plugin tokudb_fractal_tree_info soname 'ha_tokudb.so';
mysql> install plugin tokudb_fractal_tree_block_map soname 'ha_tokudb.so';
mysql> install plugin tokudb_trx soname 'ha_tokudb.so';
mysql> install plugin tokudb_locks soname 'ha_tokudb.so';
mysql> install plugin tokudb_lock_waits soname 'ha_tokudb.so';

TokuDB用に設定ファイル更新

デフォルトのストレージエンジンをTokuDBに

・my.cnf書き換え
# vi /usr/local/mysql/my.cnf
> default-storage-engine = MYISAM
< default-storage-engine = TokuDB

・再起動
# /usr/local/mysql/support-files/mysql.server restart
Shutting down MySQL... SUCCESS!
Starting MySQL.. SUCCESS!

・確認
# /usr/local/mysql/bin/mysql
mysql> use test;
mysql> CREATE TABLE `create_test` (
  `hoge` int(10) unsigned NOT NULL DEFAULT '0',
  `fuga` varchar(120) NOT NULL DEFAULT ''
);
*************************** 1. row ***************************
       Table: create_test
Create Table: CREATE TABLE `create_test` (
  `hoge` int(10) unsigned NOT NULL DEFAULT '0',
  `fuga` varchar(120) NOT NULL DEFAULT ''
) ENGINE=TokuDB DEFAULT CHARSET=latin1
mysql> INSERT INTO create_test(`hoge`, `fuga`) VALUES(1,'foo');
Query OK, 1 row affected (0.04 sec)
mysql> SELECT * FROM `create_test`;
+------+------+
| hoge | fuga |
+------+------+
|    1 | foo  |
+------+------+
mysql> show create table `create_test`\G;

できましたね!

複数のバージョンのMySQLを扱う為に

ダウンロードと解凍の時点で、違うディレクトリに設置

例)
/usr/local/mysqls/mysql_tokudb_built

my.cnf設定の前にファイル書き換え

/usr/local/mysql -> /usr/local/mysqls/mysql_tokudb_built
/tmp/mysql.sock -> /tmp/mysql_tokudb_built.sock

対象ファイルは/usr/local/mysqls/mysql_tokudb_built以下の

bin/mysqld_safe
bin/msql2mysql
bin/mysqlaccess
bin/mysql_config
bin/mysqld_multi
docs/INFO_BIN
include/my_config.h
include/mysql_version.h
support-files/my-large.cnf
support-files/my-huge.cnf
support-files/mysql.server
support-files/my-innodb-heavy-4G.cnf
support-files/my-medium.cnf
support-files/mysql-log-rotate
support-files/my-small.cnf

です。

まあこんな感じで書き換えて

cd /usr/local/mysqls/mysql_tokudb_built
perl -pi -e 's!/usr/local/mysql!/usr/local/mysqls/mysql_tokudb_built!g;s!/tmp/mysql.sock!/tmp/mysql_tokudb_built.sock!g' \
bin/mysqld_safe \
bin/msql2mysql \
bin/mysqlaccess \
bin/mysql_config \
bin/mysqld_multi \
docs/INFO_BIN \
include/my_config.h \
include/mysql_version.h \
support-files/my-large.cnf \
support-files/my-huge.cnf \
support-files/mysql.server \
support-files/my-innodb-heavy-4G.cnf \
support-files/my-medium.cnf \
support-files/mysql-log-rotate \
support-files/my-small.cnf

他には、my.cnfのserver-id変更やポート番号変更、

MySQL初期化作業の際に

/usr/local/mysqls/mysql_tokudb_built/scripts/mysql_install_db \
--basedir=/usr/local/mysqls/mysql_tokudb_built \
--user=mysql

とする位であとは同様の流れです。

なお、/usr/local/mysqls/mysql_tokudb_built/bin/mysqlで起動する場合、毎回socketファイルを指定してあげる必要があります。

/usr/local/mysqls/mysql_tokudb_built/bin/mysql --socket=/tmp/mysql_tokudb_built.sock

CentOS 5.8にcmakeインストール

はじめに

cmakeは設定しないからyumで入れようと思ったけど2.6.4と古いのでソースからインストール

 

  • バージョン切り替えてく上でもインストール先は/usr/local/cmake/<各バージョン>
  • /usr/local/cmake/default(主に使うバージョンのシンボリックリンク
  • /usr/local/cmake/default/binにPATHを通す

 

という方針。

 

参照したのは

仕事で使える魔法のLAMP(45):MySQLのビルドに欠かせないCMakeを準備する - @IT

 

インストール環境

# cat /etc/redhat-release
CentOS release 5.8 (Final)
 
# uname -a
Linux hogehoge 2.6.18-308.13.1.el5 #1 SMP Tue Aug 21 17:10:18 EDT 2012 x86_64 x86_64 x86_64 GNU/Linux

ソースダウンロード

# cd /usr/local/src/
# wget http://www.cmake.org/files/v2.8/cmake-2.8.12.2.tar.gz
# tar zxvf cmake-2.8.12.2.tar.gz
# cd cmake-2.8.12.2

make

# ./configure --prefix=/usr/local/cmake/2.8.12.2 |& tee configure_log.txt

# vi ./CMakeCache.txt
> CMAKE_BUILD_TYPE:STRING=
< CMAKE_BUILD_TYPE:STRING=Release

# make VERBOSE=1 |& tee make_log.txt

make test

make testするも

# make test |& tee make-test_log.txt
         25 - FindPackageTest (Failed)
        254 - RunCMake.include_directories (Failed)

こける

個別にテスト

# ./bin/ctest -VV -R FindPackageTest
# ./bin/ctest -VV -R RunCMake.include_directories

こけるこける

調べてみるものの

0013237: 25 - FindPackageTest (Failed) - MantisBT

Sending old, never assigned issues to the backlog.

あばば、解決してない…

あきらめてmake installしてみる

make install

# make install |& tee make-install_log.txt

切換しやすいように

# ln -s /usr/local/cmake/2.8.12.2 /usr/local/cmake/default

で、

/usr/local/cmake/default/bin

にPATHを通す

これで使えてます。

数字6桁パスワードのハッシュ値の総当たり、Perlでも約0.25秒で終わるよ

タイトルウソです。もっとかかりましたすみません。

 

徳丸浩の雑記帳: 数字6桁パスワードのハッシュ値の総当たり、PHPなら約0.25秒で終わるよ

を読んで、Perlでも実装してみた。

まあPHPとほとんど変わらないんですけれども。

今回はマルチプロセスの復習がてら実装。

環境

# OS
FreeBSD 9.1-RELEASE-p9 amd64
# CPU
Intel Xeon E312xx (Sandy Bridge)
# Perl
5.8.9

さくらのスタンダードプランです。はい。

コード

#!/usr/bin/perl

use common::sense;

use Getopt::Long;
use Digest::MD5 qw/md5_hex/;
use Parallel::ForkManager;
use Time::HiRes qw/time/;

GetOptions(
    's|salt=s'    => \my $salt,
    'hs|hash=s'   => \my $hash,
    'p|process=i' => \my $process,
    'verbose'     => \my $verbose,
);

my $max_num = 1000000;
$process ||= 2;

my $start_time = time;
main();
my $exec_time = time - $start_time;
print 'TOTAL_EXEC_TIME: ' . $exec_time . "\n";

exit;


sub main {
    my $current = 0;
    my $pm      = Parallel::ForkManager->new( $process );

    do {
        warn sprintf("parallel: %s process\n", $process);

        $pm->run_on_start(
            sub {
                my ($pid, $ident) = @_;
                warn sprintf("%s starts\n", $ident);
            }
        );

        $pm->run_on_finish(
            sub {
                my ($pid, $exit_code, $ident, $exit_signal, $core_dump, $data) = @_;
                $current++;
                warn sprintf("%s completes.(%d/%d)\n", $ident, $current, $process);
                warn sprintf("  sec : %s\n", $$data) if defined $data;# must Parallel::ForkManager over v0.7.6
            }
        );
    } if $verbose;

    for (my $start = 0; $start < $process; $start++) {
        $pm->start($start) and next;# parent go next

        my $child_start_time = time;
        _search_hash($start, $process);
        my $child_exec_time  = time - $child_start_time;

        $pm->finish(0, \$child_exec_time);
    }
    $pm->wait_all_children;

    return undef;
}

sub _search_hash {
    my ($start, $step) = @_;

    for ( my $i = $start; $i < $max_num; $i += $step) {
        my $pass = sprintf("%06d", $i);
        next if md5_hex($salt . '$' . $pass) ne $hash;

        # match
        my $time = time - $start_time;
        print sprintf("Solved: %s %s\n", $pass, $time);
    }

    return undef;
}

1;

Parallel::ForkManager 使って並行ダウンローダ作った - 理系学生日記

のコードを丸コピ…。

実行

[sv] $ perl ./jal.pl -p 4 -s hoge -hs 4b364677946ccf79f841114e73ccaf4f -v
parallel: 4 process
0 starts
1 starts
2 starts
3 starts
Solved: 567890 0.186630010604858
2 completes.(1/4)
  sec : 0.386913061141968
1 completes.(2/4)
  sec : 0.523470878601074
3 completes.(3/4)
  sec : 0.457498073577881
0 completes.(4/4)
  sec : 0.597269058227539
TOTAL_EXEC_TIME: 0.604266166687012

ぐはっ、思ったよりかかるな…共用サーバだから仕方ないか。

同じサーバで徳丸先生のコードを実行してみる。

f:id:shimx:20140210185838p:plain

おんなじぐらいか、Perlのがちょい遅いぐらいか。

同じようなコードの書き方でやったら同じぐらいになるかな?

という感じでした。

 

6桁の数字なら

パスワード忘れたよ!>キューに入れる>ワーカーが総当り>メールでパスワードを送信(この間わずか1秒)

が可能ですね!

Q4MにてSELECT COUNTで件数がとれるがSELECT *もdequeueもできない場合の強引な対処

某日Q4Mが動いているサーバがダウンしまして。

 

再起動後、Q4Mを立ち上げなおした所、Worker(Perl製。参照 http://perl-users.jp/articles/advent-calendar/2009/data-model/22.html)がQueueを受け取れなくなってました。

 

mysql> SELECT COUNT(*) FROM queue_table;
+----------+
| COUNT(*) |
+----------+
|   365474 |
+----------+
1 row in set (0.00 sec)

と件数がとれるんですが、

mysql> SELECT * FROM queue_table LIMIT 2;
Empty set (0.01 sec)

と中身がとれないという、おかしなことに。

もちろんqueue_wait()もできません。

ただ、enqueueはできています。

 

今回の環境は

CentOS 6.2 + MySQL 5.1.48 + Q4M 0.9.4
※Q4Mコンパイル時のconfigureはディレクトリ指定以外デフォルト

です。

 

今回は、消えても復旧できるログだったので力技で解決する事に。

 

同じ構成でテーブル作って、実データであるQ4Mファイルをコピーすれば同様に動くんですよ…

 

対応方法1

対象テーブルは

queue_table

 

  • Worker停止、enqueueも停止
  • 移行先テーブル作成
CREATE TABLE `queue_table_` (
  `id` char(255) DEFAULT NULL,
  `job_data` blob,
  `priority` tinyint(4) unsigned DEFAULT '10'
) ENGINE=QUEUE DEFAULT CHARSET=sjis;
  • ファイル確認
cd /usr/local/mysql_q4m/var/project
ls -la queue_table.Q4M queue_table_.Q4M
  • 実データコピー
cp -ap queue_table.Q4M queue_table_.Q4M
  • MySQLからのアクセス確認
mysql> SELECT * FROM queue_table_ LIMIT 2;
mysql> SELECT COUNT(*) from queue_table_;
mysql> SELECT queue_wait('queue_table_');
mysql> SELECT * FROM queue_table_;
mysql> SELECT queue_abort();
mysql> SELECT COUNT(*) FROM queue_table_;
※データが取得できればOK
  • テーブルリネーム
ALTER TABLE `queue_table` RENAME `queue_table_bak`;
ALTER TABLE `queue_table_` RENAME `queue_table`;
  • 確認できたら古いテーブルを消す
DROP TABLE `queue_table_bak`;
  • Worker開始、問題なければenqueueも開始

 

対応方法2

対象テーブルは

queue_table2

こちらは

  • 対応1やっても無理(表題の事象が起こる)
  • select count(*)は取れるがselect *は取れない
  • Workerを回している間はselect *投げて帰ってきたり返ってこなかったりする

どうもWorkerのqueue_runningで1件dequeueしている間は、select *でとれるようだ!?

そんなバカな。だが目の前にある現実。

 

  • enqueue停止

 

  • Workerを動かす

ただし、挙動的にはqueue_wait()queue_abort()を繰り返している

 

  • 移行先テーブル作成
CREATE TABLE `queue_table2_` (
  `id` char(255) DEFAULT NULL,
  `job_data` blob,
  `priority` tinyint(4) unsigned DEFAULT '10'
) ENGINE=QUEUE DEFAULT CHARSET=sjis;
  • 実データコピー
mysql> INSERT INTO `queue_table2_` (`id`,`job_data`,`priority`) SELECT * FROM `queue_table2`;
0件ならもう一度試す
  • MySQLからのアクセス確認
mysql> SELECT * FROM queue_table2_ LIMIT 2;
mysql> SELECT COUNT(*) from queue_table2_;
mysql> SELECT queue_wait('queue_table2_');
mysql> SELECT * FROM queue_table2_;
mysql> SELECT queue_abort();
mysql> SELECT COUNT(*) FROM queue_table2_;
※データが取得できればOK

ただし、

mysql> SELECT COUNT(*) FROM queue_table2;
+----------+
| COUNT(*) |
+----------+
|   365474 |
+----------+
1 row in set (0.00 sec)

mysql> SELECT COUNT(*) FROM queue_table2_;
+----------+
| COUNT(*) |
+----------+
|   365473 |
+----------+
1 row in set (0.00 sec)

多分Workerでdequeueしている分がないんだと思う。

データが1件欠損する。

 

  • Worker停止
  • テーブルリネーム
ALTER TABLE `queue_table2` RENAME `queue_table2_bak`;
ALTER TABLE `queue_table2_` RENAME `queue_table2`;

 

  • 確認できたら古いテーブルを消す
DROP TABLE `queue_table2_bak`;

 

  • Worker開始、問題なければenqueueも開始

 

とまあ、力技で解決しました。

テーブル構造が壊れてたのかなあ。

オススメできない対処方法です。

kumofsを利用する上での注意点

ぼくのかんがえたさいきょうの高ディスクI/Oマシン

http://shimx.hateblo.jp/entry/2013/09/11/194409 

 という記事の続き。

 

上記記事でセットアップした仮想マシンで何やってたかというと、今回障害が発生したプロダクトではkumofsというKVSを利用しておりまして

The Kumofs Project

http://kumofs.sourceforge.net/ 

 HAHAHA、見事に更新が止まってますね。

 

事象としては

kumo-serverが3台以上ダウンした

https://github.com/etolabo/kumofs/blob/master/doc/doc.ja.md

 に当たります。

 

この際、full-replaceサブコマンドを発行しろと書いてあるのですが、いかんせんサーバスペック的に時間がかかりすぎる、と。

 

なので、kumomergedbというコマンドを使って、複数サーバに分散させているデータベースファイルを1つにマージさせる手法を取りました。

 

full-replaceは分散されているデータの再配置を行っており(という理解をしている)、データを全部マージして1つのデータベースファイルにして、各サーバに配置すれば同様の結果になりますので(ファイルサイズはともかく)。

 

そもそも「バックアップとるだろJK」という声も聞こえますが、データベースファイルが肥大化しすぎて、バックアップするとサービス止まるという悲惨な状況に。

サーバを追加しようとしても、attachコマンド発行すると追加したサーバにデータを配置しようとしてトラフィックがえらいことになりパフォーマンスが低下、結果サービスが止まるという。

初期設計のミスでした。ほんとごめんなさいごめんなさいごめんなさい。

ここでkumomergedbコマンドを使う上での注意点があります。

 

kumofsを利用した事のある人には常識ですが、kumofsが利用しているTokyoCabinetには64GBの壁というものが存在します。 

TokyoCabinet 64GBの壁

http://ameblo.jp/cyberx-engineer/entry-10688375059.html

64bit環境で容量64GB以上のTokyoCabinetのデータファイルを扱うには、

HDBTLARGEオプションを指定する必要があるようです。 

この壁、実はkumomergedbする際に出力するファイルが64GB超えた場合にも存在します。

 公式ドキュメントには

$ kumomergedb backup.tch-20090101 \
              server1.tch-20090101 server2.tch-20090101 server3.tch-20090101

と書いてありますが、この

backup.tch-20090101

が64GBを超えると、データが書き込めなくなり、その先処理がいつまでたっても進まなくなります。

 

この場合、

$ tchmgr create -tl backup.tch-20090101 400000000
$ kumomergedb backup.tch-20090101 \
              server1.tch-20090101 server2.tch-20090101 server3.tch-20090101

と、事前にHDBTLARGEオプションをつけて空の出力ファイルを作っておくか、

 

$ cp -ap server1.tch-20090101 backup_tenuki.tch-20090101
# server1.tch-20090101があらかじめHDBTLARGEオプションを指定して作られたデータファイルならここでoptimizeの必要多分なし
$ tchmgr optimize -nl -tl backup_tenuki.tch-20090101 400000000
$ kumomergedb backup_tenuki.tch-20090101 \
              server2.tch-20090101 server3.tch-20090101
# deleteフラグ立ってるけど消えてないデータとか?余計なデータが混ざるようなのでもう一度optimizeすると前者と同じファイルになるよ
$ tchmgr optimize -nl -tl backup_tenuki.tch-20090101 400000000

とmergeする前にoptimizeかけて、そのファイルを出力先にするという、横着するのもありですね。

 

両方試して、できたファイルに対しsha512sumでハッシュを計算、チェックサムを比較したところ同じでした)

$ sha512sum backup.tch-20090101
d6328ab93e1638f74d5c8c61c3f16894e83c6fbe8b31f226e0aa7765a72f886c2c2764c7572a44543119320d3917a48b6671cf5733b29ca1d05e234e800af3a8
$ sha512sum backup_tenuki.tch-20090101
d6328ab93e1638f74d5c8c61c3f16894e83c6fbe8b31f226e0aa7765a72f886c2c2764c7572a44543119320d3917a48b6671cf5733b29ca1d05e234e800af3a8

 

 

他注意点。

 

kumofsはCentOS 6には普通にやってはインストールできません。

kumofs on CentOS6

http://blog.developerlabs.net/2013/02/kumofs-on-centos6.html

0.5.0 から msgpack の executeメソッドが使えず

/usr/local/bin/kumoctl:61:in `receive_message': undefined method `execute' for # (NoMethodError)

が出てくるので 0.4.7が必須です

どはまりした際、本当に助かりました…

 

こんな感じでインストールしました。

yum -y install libtool.x86_64 openssl.x86_64 openssl-devel.x86_64 zlib.x86_64 zlib-devel.x86_64 bzip2-devel.x86_64

cd /usr/local/src
tar zxvf ncurses-5.6.tar.gz
cd ncurses-5.6
./configure --with-shared --without-normal --without-debug
make
make install
ldconfig

cd /usr/local/src
tar zxfv ruby-1.9.3-p125.tar.gz
cd ruby-1.9.3-p125
./configure --prefix=/usr/local/ruby
make
make install

echo /usr/local/ruby/lib > /etc/ld.so.conf.d/ruby.conf
ldconfig

cd /usr/local/src
tar zxfv rubygems-1.8.24.tgz
/usr/local/src/rubygems-1.8.24
/usr/local/ruby/bin/ruby setup.rb

cd /usr/local/src
tar zxfv msgpack-0.5.0.tar.gz
cd /usr/local/src/msgpack-0.5.0
./configure --prefix=/usr/local/msgpack
make
make install
echo /usr/local/msgpack/lib > /etc/ld.so.conf.d/msgpack.conf
ldconfig

/usr/local/ruby/bin/gem install msgpack -v=0.4.7

cd /usr/local/src
tar zxfv tokyocabinet-1.4.45.tar.gz
cd /usr/local/src/tokyocabinet-1.4.45
./configure --prefix=/usr/local/tokyocabinet
make
make install
echo /usr/local/tokyocabinet/lib > /etc/ld.so.conf.d/tokyocabinet.conf
ldconfig

cd /usr/local/src
tar zxfv ragel-6.6.tar.gz
cd /usr/local/src/ragel-6.6
./configure --prefix=/usr/local/ragel
make
make install
ln -s /usr/local/ragel/bin/ragel /usr/bin/ragel

mkdir /usr/local/kumofs
cd /usr/local/src
tar zxvf kumofs-0.4.13.tar.gz
cd kumofs-0.4.13
./configure --prefix=/usr/local/kumofs/kumofs0413 --with-msgpack=/usr/local/msgpack --with-tokyocabinet=/usr/local/tokyocabinet
make
make install
ln -s /usr/local/kumofs/kumofs0413 /usr/local/kumofs/default

 

他に、サーバは生きているけどネットワークに障害が起こったケースも注意が必要です。

下記のようなkumo-managerが関わる障害が発生したと仮定

---------------
ネットワーク1
KMGR-1  KSV-1
        KSV-2
        KSV-3
        KSV-4
---------------
ネットワーク2
KMGR-2  KSV-5
        KSV-6
---------------

というようにネットワークが分かれていて、スイッチ等が故障してネットワーク1からネットワーク2に接続できなくなった場合

---------------
ネットワーク1
KMGR-1  KSV-1
        KSV-2
        KSV-3
        KSV-4
KMGR-1「あれ、KSV-5、KSV-6の2台死んだぞ?だがまだいける!」
---------------
ネットワーク2(死亡)
KMGR-2  KSV-5
        KSV-6
KMGR-2「あれ、KSV-1からKSV-4まで4台死んだぞ?full-replace必要だわー!」
---------------

という状態になります。

で、このまま復旧、ネットワーク1からネットワーク2につながるようになり、replaceサブコマンドを発行すると、

---------------
ネットワーク1
KMGR-1  KSV-1
        KSV-2
        KSV-3
        KSV-4
KMGR-1「あれ、KSV-1からKSV-4まで4台死んだぞ?full-replace必要だわー!」(<- KMGR-2から同期)
---------------
ネットワーク2
KMGR-2  KSV-5
        KSV-6
KMGR-2「あれ、KSV-1からKSV-4まで4台死んだぞ?full-replace必要だわー!」
---------------

という事も起きえます。

 

ドキュメントには「まずダウンしたkumo-managerを再起動してください」と書いてありますが、ネットワーク障害が復旧してからkumo-managerを再起動させるか、プロセスを殺しておいて復旧してから起動、replaceサブコマンドを発行しょう。

 

なかなかこんな事ないとは思いますが。

 

以下、要参照。

kumofs - がしまっくす

http://www.gashimax.com/wiki/index.php?kumofs

 kumofsを使うならここを熟読すべし。

@tmtms のメモ

http://tmtms.hatenablog.com/category/kumofs

 attach、detachの動作の解説など

 

多分もうkumofs使うことないだろうなあ。 


    
    
  

ぼくのかんがえたさいきょうの高ディスクI/Oマシン

ようやくトラウマから覚め、まとめる気になりました(その話はまた後日)。

数ヶ月前の事を当時のメモを参考にしつつ、思い出しながら書きます。

 

ここ数年、某所に旅行に行く度にサーバがぶっ飛んで(多分ヤンデレ)、復旧作業が発生します。

(最近は旅行先からsshで入って構ってあげるようにしてます。やれやれだぜ)

今年は、合計600GB強のデータ(今回のプロダクトで利用しているデータストア8台分)をマージしなければならないという事態になりました。

さすがに震えた。

 

今回のマージ作業の性質として、

  • データストア数台分のデータの中から、ユニークな部分を検索し、1つのファイルに書き出し
  • 過去同様の作業経験より、ボトルネックは書き出し部分(ディスクI/Oの書き込み性能)
  • マージ作業でメモリはほとんど使わない

という点があげられます。

 

どうやら勤め先のサーバのスペックだとマージだけで1週間以上かかりそうで、とにかく高速なディスクI/Oを持つマシンが必要に。

 

  • 最終的にマージ結果のファイルサイズは100GBぐらいに落ち着く
  • gzで圧縮すれば、元データは1ファイルあたり約15GB、合計120GB程度になる
  • 作業用ファイルだけ解凍すれば良いので、同時に必要となるストレージサイズは最低400GB程度(約120+約80*2+約100GB)

以上の点を頭におきつつ、amazon Web services(AWS)のインスタンス一覧を見ていた所、あるではないですか、素敵なインスタンスが!

 

最終的に利用したのはストレージに特化したインスタンスではなく、メモリに特化したインスタンスである

High Memory Cluster Eight Extra Large (cr1.8xlarge) instance

米国西海岸(オレゴン)リージョン(us-west-2)

(毎時$3.500かかる…!)

 です。

※当時東京リージョンで使えませんでした

 

これは

「単一ディスクならメモリをマウントしてRAMディスクとして使うのが一番速いんや!」

という思想だった為です。

※無検証

 

このcr1.8xlargeインスタンス、スペックとしては

Intel Xeon E5-2670 x 2

244GBメモリ

120GB*2 SSDインスタンスストレージ

となっております。うーんメモリお化け。

 

どうせインスタンスは揮発性、SSD2つはなりふり構わずRAID 0で組みます。

結果

Intel Xeon E5-2670 x 2

18GBメモリ

226GB RAMディスク

240GB SSDインスタンスストレージ(120GB SSDインスタンスストレージ*2 のRAID 0

という構成に。ひいっ化物!

※18GB残したのは適当です(なんで16GBにしなかったんだろう…記憶なし)。

 

このスペックなら、東京->西海岸、西海岸->東京と、ファイルの転送に多少時間がかかっても、十分お釣りが来る、と読みました。

 

トータル466GBあるので、うまくやれば(gzファイルとマージ先ファイルをRAMディスク上に、gzファイルを解凍した元データをSSDに)手間がかかりますがなんとかなりますし。

 

結果、1週間かかりそうだった作業が、作業自体は1日程度で終わりました…

(途中、高速転送ツールであるTsunami UDPを使用しようとしてうまく動かず、普通にrsyncで送ったり、OSのバージョン違いでデータストアのインストールに手間取ったりして、結局2日近くかかった)

2ファイルマージするのに40分かからなかったのを見て、ブヒィィィってなりましたもんね。

 

ちなみにTsunami UDPについては下記参照。使ってみたいんですよねーこれ。

リージョン間高速データ転送

http://ijin.github.io/blog/2013/04/03/accelerating-cross-region-data-transfer/

 

cr1.8xlargeを単一インスタンスでこんな使い方したのは珍しいんじゃないかなーと…

 

ありがとう、そしてありがとうAWS!

 

以下作業手順です。

 

今回一般的に配布されているイメージを利用しました。

AMIはCentOS 6.4の「ami-503bae60」を使用。

CentOS 6.4 AMI available

http://www.bashton.com/blog/2013/centos-6-4-ami-available/

  

インスタンスの立ちあげ、セットアップは省略。

Instance Store(Ephemeral Store)を全て付けるのを忘れずに。

 

まずは必要となるものをインストール

$ sudo yum update -y
$ sudo yum install mdadm xfsprogs -y

 

マウントを外す

$ umount /dev/xvdb
$ sudo blockdev --setra 65536 /dev/xvdb
$ sudo mkfs.xfs -f -l version=2 -l size=64m -l lazy-count=1 /dev/xvdb

$ umount /dev/xvdc
$ sudo blockdev --setra 65536 /dev/xvdc
$ sudo mkfs.xfs -f -l version=2 -l size=64m -l lazy-count=1 /dev/xvdc

 

RAID 0作成

$ yes | sudo mdadm --create --verbose /dev/md0 --level=0 -c256 --raid-devices=2 /dev/xvdb /dev/xvdc
$ sudo echo 'DEVICE /dev/xvdb /dev/xvdc' > /etc/mdadm.conf
$ sudo mdadm --detail --scan >> /etc/mdadm.conf

 

ファイルシステムを作成

$ sudo blockdev --setra 65536 /dev/md0
$ sudo mkfs.xfs -f -l version=2 -l size=64m -l lazy-count=1 /dev/md0
$ sudo mkdir -p /mnt/md0
$ sudo mount -t xfs -o discard,nobarrier,relatime /dev/md0 /mnt/md0
$ cd /mnt/md0

以上参照)

訳:エフェメラルディスクでRAID0

http://understeer.hatenablog.com/entry/2012/03/22/080938

Amazon EC2でEphemeral StoreをRAID0構成にしてディスクI/O性能を上げる

http://dev.classmethod.jp/cloud/amazon-ec2-ephemeral-store-raid0/

 

メモリマウント(/zなのは趣味)

umount /dev/shm
mount -t tmpfs -o size=226G tmpfs /z
df -ih /z

参照)

tmpfs は本当に容量が動的なのか

http://d.hatena.ne.jp/naoya/20060217/1140176470

 

これで環境完成!

PerlのmapをPerlで実装(車輪の再発明)

聞かれて答えられなかったので調べて書いた。

キモは関数で受け取る引数の制限(プロトタイプ)を利用。

&を第一引数として指定した場合には、ブロックごと受け取れるとのこと。

ブロックを受け取れるのは第一引数だけね。

プロトタイプ使うことないなーと思って読み飛ばしてたわ。 

参照

第140回 ブロックを渡せるのはRubyだけじゃない! Perlだって渡せるんだ! - bingo_nakanishiの他言語出身者のためのPerl入門

以下ソース 

#!/usr/bin/perl

use strict;
use warnings;

use Data::Dumper;
$Data::Dumper::Indent   = 1;
$Data::Dumper::Sortkeys = 1;
$Data::Dumper::Terse    = 1;

my @base_arr = (1 .. 5);

print 'all_incr ' , '-'x33 , "\n";
print Dumper all_incr(@base_arr);

print 'all_incr_own ' , '-'x33 , "\n";
print Dumper all_incr_own(@base_arr);

sub all_incr {
    my @arr = @_;
    map { my $num = $_; ++$num } @arr;
}

sub own_map (&@);
sub all_incr_own {
    my @arr = @_;
    own_map { my $num = $_; ++$num } @arr;
}

sub own_map (&@) {
    my $code = shift;
    my @return_arr;
    foreach (@_) {
      push @return_arr, $code->();
    }
    return @return_arr;
}

1;

結果 

all_incr ---------------------------------
2
3
4
5
6
all_incr_own ---------------------------------
2
3
4
5
6