TTからCassandraへのインポート

Cassandra-0.6にもsstable2json, json2sstableというimport/exportするコマンドがあるが、
これは内部用のものなので、他のDBからのインポート用にはそのまま使えない。

しかも実際使ってみたけど1G程度のJSONインポートでヒープが足らんと言われてしまった。。。

仕方ないのでThrift経由でデータを入れるPerlスクリプトを書く。
参考にしてください。

tchmgr list -pv > tt_data.tsv # TTからのTSVエクスポート
#!/usr/bin/perl

use strict;
use warnings;
use Net::Cassandra;
use Coro;
use Coro::Semaphore;   

$|=1;
open IN, $ARGV[0] or die "cant open" . $ARGV[0]; # tt_data.tsv
my @hosts = qw/127.0.0.1/; # hostを記述
my $keyspace = 'keyspace1';
my @clients;
push @clients, Net::Cassandra->new(hostname => $_)->client for @hosts;

my $coro_num = 2000;
my $count = 0;
my @coros;
my $semaphore = Coro::Semaphore->new($coro_num);
while(<IN>) {
  $count++;
  my $line = $_;
  push(@coros, async {
    chomp $line;
    my ($ttkey, $value) = split /\t/ , $line;
    my ($tt, $key, $column) = split /:/ , $ttkey;
    my $client = $clients[$count % scalar @clients];
    $semaphore->up;
    eval {
      $client->insert(
        $keyspace,
        $key,
        Net::Cassandra::Backend::ColumnPath->new(
          { column_family => 'COLUMNFAMILY', column => $column}
        ),
        $value,
        time,
        Net::Cassandra::Backend::ConsistencyLevel::ALL
      );
    };
    # print $key . "\t" . $column  .  "\t" . $value . "\n";
  });
  $semaphore->down;
} 

close(IN);

MySQL6.0アップデートのやりかた

fedora13で動作中のMySQL5.1からMySQL6.0にアップデートしたのでメモ。

事前準備

yumで入れたmysql,mysql-server(MySQL5.0)が入っていて既に動作しているものとする。

以下をyumで入れておく

作業

  • 最新版のmysqlを取得
su - root
cd /usr/local/src/
wget http://dev.mysql.com/get/Downloads/MySQL-6.0/mysql-6.0.11-alpha.tar.gz/from/http://ftp.jaist.ac.jp/pub/mysql
tar xvzf mysql-6.0.11-alpha.tar.gz
cd mysql-6.0.11-alpha
  • configure -helpでオプションをcheckして適切なオプションを指定。
    • prefixは/usr/local/mysqlに。
    • innodb,falcon,maria,memory,myisamを使用可に
    • partitionを使用可に
./configure --with-charset=utf8 --with-extra-charsets=all --with-mysqld-user=root --with-innodb \
 --with-falcon --with-maria --with-heap --with-myisam --enable-local-infile --prefix=/usr/local/mysql \
 --with-plugins=innobase,partition --with-unix-socket-path=/tmp/mysql.sock
  • make,make install
    • makeは少し時間かかった
make
make install
  • 動いているmysqlを停止.mysql-serverのuninstall
/etc/init.d/mysql stop
yum -y remove mysql-server
  • mysqld(MySQL6.0)の起動
    • my.cnfは5.1のままでいけたの楽ちん。
/usr/local/mysql/bin/mysqld_safe --user=root &

カンタンでしたね

mysql -u root -p
mysql> select version();
+--------------+
| version()    |
+--------------+
| 6.0.11-alpha |
+--------------+

MySQLメモ

はじめに

参考にした本

  • 実践ハイパフォーマンスMySQL

サーバーアーキテクチャ

  • レイヤー1
    • 接続処理、認証、セキュリティなど
    • その他ネットワークベースの一般的なクライアント/サーバーツールで必要となるサービスを含む
  • レイヤー2
    • クエリ解析、分析、最適化、キャッシュ、組み込み関数(日付、時間、算術演算、暗号化など)
    • Stored Procedure, Trigger, View
  • レイヤー3
    • ストレージエンジン
    • ストレージエンジンAPI
      • ストレージエンジン間の違いを吸収→クエリレイヤで大部分を透過的に
      • トランザクション開始」「この主キーを持つ行の取得」といった操作を実行する低レベル関数
    • SQL解析や相互通信はしない
    • あくまでサーバーからのリクエストに応えるだけ

最適化と実行

  • MySQLはクエリを解析して内部構造(解析ツリー)を作成後、さまざまな最適化を適用する
    • クエリの書き換え
    • テーブル読み取り順序
    • 使用インデックスの選択
  • オプティマイザ
    • ストレージエンジンにその能力と特定操作のコスト、テーブルデータを統計的に問い合わせ
      • ストレージエンジンは特定クエリに役立つインデックスをもつ

並行性の制御

  • サーバレベルとストレージエンジンレベルの2つのレベルで行う必要あり
Read/Write Lock
  • Read Lock: 共有ロックで相互にノンブロッキング
  • Write Lock: 排他ロックで、Read Lockと他のWrite Lockをブロック
Lockの粒度
  • ロック操作のオーバーヘッド
    • ロックの取得/解除
    • ロックが空いてるかどうかのチェック
  • ほとんどのRDBは行レベルロック(クエリに対応するレコードのみロック)
  • MySQLは幾つか選択肢がある
    • テーブルロック
      • オーバヘッドが少ない
      • 基本は上で述べたRead/Write Lockをテーブル全体に適応させる
      • Write LockはRead Lockより優先度が高いので、Read Lockをキュー内で飛び越えて前方に配置される
      • READ LOCALテーブルロックを使うと、同時書き込み操作を許可することができる
      • 例: ISAM, MyISAM
    • 行ロック
      • 並行性が最も高い
      • オーバヘッドが最も高い
      • 行ロックはサーバーではなくストレージエンジンで実装される
      • 例: InnoDB, Falcon

トランザクション

分離レベル(ACIDのI)
  • SQL規格では4つの分離レベルを定義
  • 分離レベルが低いほど並行性が高くなり、オーバヘッドが低くなる
  • 4つの分離レベルまとめ
分離レベル Dirty Read Non-Repeatable Read Phantom Read Read Lock
READ UNCOMMITED ×
READ COMMITED × ×
REPEATABLE READ × × ×
SERIALIZABLE × × ×
  • READ UNCOMMITED
  • READ COMMITED
    • 分離レベルの単純定義を満たす
    • Non-Repeatable Read発生
      • 同じ文を2回実行すると異なるデータが返される可能性
    • MySQLを除くほとんどのRDBのデフォルト分離レベル
  • REPEATABLE READ
    • ファントムリード発生
      • ある範囲の行を選択後、別のトランザクションがその範囲に新しい行を挿入し、その後に同じ範囲を選択したときに新しいファントム(幻の)行が現れるという問題
      • InnoDBやFalconではMVCCに基づいてこれを解決できる
      • MySQLのデフォルト分離レベル
  • SERIALIZABLE
    • 競合しないようトランザクションを強制的に順序付けする
    • 読み取る全ての行にロックを設定

デッドロック

トランザクションログ

  • ログ先行書き込み
    1. データのメモリ上のコピーを変更
    2. トランザクションログへの書き込み
      • ログイベントの追加はシーケンシャルI/Oなので比較的高速
    3. テーブルを更新
  • ほとんどのストレージエンジンが採用
  • リカバリ方法はストレージエンジンごとによって異なる

MySQLトランザクション

AUTOCOMMIT
  • AUTOCOMMITをONにすると、各クエリを別々のトランザクションで実行
  • OFFの場合はCOMMIT/ROLLBACKを発行するまでは常にトランザクションの中にいて、
  • COMMIT/ROLLBACKが発行された時点で新しいトランザクションを開始する
  • 「SET TRANSACTION ISOLATION LEVEL」により分離レベルを設定可能
トランザクションでの複数ストレージエンジンの併用
暗黙的/明示的ロック
  • InnoDB
    • 暗黙的
      • トランザクション中はいつでもロック取得可能だが、COMMIT/ROLLBACKまで開放されない
      • すべてのロックを同時に開放する
      • 分離レベルに基づいて自動的にロック処理を行う
    • 明示的
      • SELECT ... LOCK IN SHARE MODE
      • SELECT ... FOR UPDATE

マルチバージョンの並行性制御(MVCC)

  • InnoDB,Falcon,PBXT
    • ×:単純な行ロックメカニズム
    • ◯:行レベルロック+MVCC
  • MVCC
    • ある時点で存在していたデータのスナップショットを作成という仕組みで動作
    • トランザクション長に関わらず、参照可能データを一貫させる
    • 複数トランザクションが同じテーブル内の異なるデータを同時参照可能
  • MVCCのメリット
    • 多くの場合でロックの必要性を完全排除し、オーバヘッドの多くを解消
    • ロックを使用しない読み取りを許可し、かつ書き込み操作では必要なレコードのみをロックすることができる
  • MVCCの実装
    • ストレージエンジンごとに違う
    • Optimistic/Pessimisticな実装がある
InnoDBのMVCC
  • 行ごとに2つの隠れた値を格納
    • 行が作成されたタイミング
    • 行が期限切れ(削除)されたタイミング
  • イベントが発生した実時間ではなく、システムバージョン番号を記録
  • トランザクションは開始時点でのシステムバージョン番号を記録
  • クエリは、それぞれ、各行のバージョン番号とトランザクションのバージョンとを照合しなければならない。
  • 照合条件は分離レベルで異なる
    • MVCCはREPEATABLE READ/READ COMMITEDのみ対応
  • 本にはREPEATABLE READの時のCRUDクエリそれぞれにおいて条件が書かれている
ロック方式 並行性 オーバーヘッド エンジン
テーブルレベル 最低 最低 MyISAM,Merge,Memory
行レベル NDB Cluster
MVCCを備えた行レベル 最高 最高 InnoDB,Falcon,PBXT,solidDB

ストレージエンジン

  • MySQL6.xの時点で13種類ある
    • MyISAM,MyISAM Merge,Memory,InnoDB,Falcon,Archive,CSV,Blackhole,Federated,NDB Cluster,PBXT,solidDB,Maria
    • 主なものについて下にまとめる
Engine MySQL Version Transaction Lock粒度 usage 使用すべきでない状況
MyISAM ALL × 同時挿入可能なテーブル SELECT,INSERT,一括READ Read/Writeの混在
Memory ALL × テーブル 中間計算、静的lookup 大きなデータセット、永続化
InnoDB ALL MVCC+行レベル Transaction なし
Falcon 6.0 MVCC+行レベル Transaction なし
Archive 4.1 MVCC+行レベル Log,集計 ランダムアクセス,更新,削除
NDB Cluster 5.0 行レベル 高可用性 典型的な用途
PBXT 5.0 MVCC+行レベル Transaction,Log クラスタ化インデックスの必要性
solidDB 5.0 MVCC+行レベル Transaction なし

Hatena 2010 サマーインターンに採用されました。

去年の夏から「来年は絶対にどこかしらのインターンの参加しよう!!」と思い、どこがいいかなぁと探していて一番面白そう、というか一番情報源が豊富だなーと思って目をつけていたのが、このHatenaのインターン

既に別のインターンの面接に落ちていて自信を無くしてたのですが、無事受かって良かったです。

僕のHatenaインターンへの志望動機は「大規模なリソースを使ってWebアプリケーションを書きたい!」というもの。

既にWebのベンチャーでアルバイトを4年間やっていて、小規模のWeb開発の経験はまぁあるといえばあるのですが、どうせなら、今専攻している「分散システム」の知識を生かしたWeb開発がやりたいなぁと前々から思ってたわけです。

Hatenaといえば、Perl。なんで中途半端にPerlなんだろうといつも思うのですが、きっと昔からのノウハウが豊富にあるからでしょう。
そういう僕はPerlについてはほぼ初心者同然なのですが(以前にMovalble Typeのソースを少しいじった程度)、これまでに幾つか言語を触った経験があるので、まぁ事前にある程度勉強すればたぶん大丈夫でしょう。(←甘い?)

夏が楽しみになってきました。ちょっと不安でもあるけど。。

MyCassandraにRedisを追加

MyCassandraでRedisを使えるようにしました。前述の通りJDBCを使うJDBC-Redisは実装がいまいちでしたので、独自api実装のJRedisを利用するようにしました。

これでMyCassadraは物理ストレージとしてSSTable,MySQL,Redisを使うことが出来ます。(Dynamo的な部分やインタフェース、データ構造などはそのまま)

今週はMyCassandra(Redis)についてYCSBを使ってベンチマークをとってみようと思います。

MySQLのバルク処理は書かなきゃ駄目。

前回のYCSBのload phaseがMyCassandra(Cassandra+MySQL)ではめちゃくちゃ遅いという件でしたが、タイトル通りです。

さて大量のインサートをするときに、以下の3つの手法によってどれくらい差が出るのだろうか?

  1. singleインサートを何度もやる
  2. マルチプルインサート(複数レコードのインサートを1クエリで)
  3. バルクインサート(csvファイルでインサート)

答えは以下のサイトを参照あれ。

http://www.inter-office.co.jp/contents/194/

実行時間が1>>2>3というふうになっています。バルクインサートは単純インサートの800倍も早くなるという結果です。

バルク処理は書かなきゃ駄目。以上。

遅いのはSQLパーサー?

前述したYCSBを使ってCassandraとMyCassandraのベンチマークを取っているところ。

まだ途中なのでちゃんとした結果は公表出来ないのですが、とりあえず1ノードで1KBのレコードを100万件ロードして、ベンチをとったところ、基本的に予想通りな結果になってます。

具体的に言うと、

  • 書き込み性能はCassandraの方が6.2倍速い
  • 読み込み性能はMyCassandraの方が6.3倍速い

というようにちょうど性能が逆転した形になっています。(CassandraのMemTableサイズは64MB)
MyCassandraの方は読み込み性能が優れているわけですが、ストレージにMySQLを使っていることからレンジクエリについてもCassandraよりも最適化できそうな気がします。(今後実装してベンチマークEで測定してみる予定)

ただ本ベンチマークの実行とは関係ないのですが、
MyCassandraのデータロードが数十倍遅いという問題が...。
更新では問題ありません。あくまで新規データの追加(INSERT)の問題があるようです。

Cassandra/InnoDBの書き込み手順は以下のようになってます。

  • Cassandra
    • CommiLog(HDD)=>MemTable(Memory)=>SSTable(HDD)
    • MemTable=>SSTableへの書き出しは非同期
  • InnoDB
    • log_buffer(HDD)=>buffer_pool(Memory)=>HDD
    • innodb_flush_log_at_trx_commitが2ならbuffer_poolからHDDへの書き出しは非同期(0,1は同期)

と、見た目は両方とも同じ構造になっていることが分かります。
なのに、性能がこんなに遅いのはどこに問題があるのでしょう。

考えられるのはSQLのパース処理とコミット処理が遅いこと。またはMySQLの非同期の実装方法が良くないのではないかと。1秒周期でシステムコール`fdatesync()`を実行するんだとか。