Help us understand the problem. What is going on with this article?

Googleのほとんどのサービスを支えるBigtableの誰でも使える版 Cloud Bigtable

More than 3 years have passed since last update.

これは Google Cloud Platform Advent Calendar 2015の19日目の記事です。私のブログにもポストしています。

Cloud Bigtable

そろそろ僕がGoogleに入って1年になります。ほんとにあっという間に2015年が終わった感じです。「Developer Advocate」という肩書のエバンジェリストのような、エバンジェリストじゃないような仕事をしています。
僕の仕事の一つが、今年の5月にbetaとして公開した「Cloud Bigtable」のリリース支援です。Cloud Bigtable というのは、Googleの有名な大規模分散データベースの「Bigtable」をGoogleの開発者以外でも使えるようにしたサービス。このBigtableはGoogleのサービス、検索、Maps、Gmailなど、ほとんどのサービスを支えています。

Bigtableは分散データベースでスケーラービリティが高い...というところまではわかりやすいのですが、実際にどれだけスケールできるのかはGoogleの外ではあまり知られていないと思います。

Googleの検索インデクスは 100,000,000 Gb 以上の容量があります。つまり 100 Petabyte ! しかも実際の内部的な数字はこれよりかなり大きく(おそらく何倍か)。100Petabyteは現在公開されているひかえめな数字に過ぎません。

GoogleではBigtableを約10年前から使っていますが、2006年にその設計について書かれたホワイトペーパーを公開しました。このホワイトペーパーの著者はGoogleのレジェンド級開発者 Jeff Dean, Sanjay Ghemawat, Andrew Fikes など、自分と同じ会社に勤めていると思えない超人たち。このホワイトペーパーからオープンソースのBigDataエコシステムが生まれました。具体的には、MapReduceを実装するHadoopや、GFS(Google File System)をインスパイアしたHDFSがIT産業を変えています。Bigtableの設計を元にしたHBaseも利用者を集めました。

Bigtableのパフォーマンス

Googleでは(当然のことながら)検索結果をものすごい速いスピードでユーザーに返さないといけません。Bigtableが遅いとGoogleはお金いくら損しちゃうのかな……1時間で検索結果が1秒遅くなったら、私の1年分の給料がぶっ飛んでしまうのが想像できます。

そのために、Bigtableはかなり効率化されています。例えば、Bigtableクラスターと同じリージョンにあるGCEのインスタンスから書き込みも、読み込みも、せいぜい10ms以内に返ってくる。これは速い。しかも、p99 (99%のリクエストは10ms以内に返ってくる)です。とんでもない速い。これはクライアントから計測したネットワーク通信を含んだレイテンシー。サーバーから計測すると6msになります。

そしてコストパフォマンスも高い。書き込みスループットで考えると MB/s per $ はHBaseやCassandraの倍以上です。

Cloud Bigtable Performance

ちなみに、以下のグラフはリアルタイムのパフォーマンスです。僕が作ったデモアプリで作成しています。上のグラフはリクエスト数、下のグラフはレイテンシー(黒い線がp50 、青い線がp99)です。1.5万 QPS (query per second)の状態で、レイテンシーのp99が大体10ms以内 なのがわかります。

Cloud Bigtable Demo

Bigtableの設計

BigtableはNoSQLの概念を人気にさせたと言われています。でもNoSQLはその幅が広いので、Bigtableがどんな設計になっているのかを説明しましょう。

BigtableはいわゆるKey/Valueデータベースですが、単純なKey/Valueデータベースにはないいくつか特殊な機能を持っています。
まずは、一つのキーに複数の値を持つ機能があります。これはRDBMSと同じようなコラムとして表現されるけど、RDBMSと違ってテーブルスキーマがなく、rowデータに含まれているコラムがrowごとに変わってもよいのです。図にすると、

rowもキーによってソートされているので、rowキーを指定してスキャンができます。しかしそれぞれのコラムに入っているデータでのクエリーやスキャンができません。RDBMS的に言うとprimary key indexがあるけど、secondary indexがない、ということですね。

row間のトランザクションはないけど、一つのrowに含まれているコラムに対して、書き込みの整合性はあります。

技術を秘密にする罠

GoogleはMapReduceやBigtableの技術をホワイトペーパーとして公開しましたが、そのコードは公開しませんでした。
その状況でOSSの実装が出てきたけれど、そこには当然Googleのノウハウが使われておらず、GoogleがMapReduceやBigtableほどの性能がありません。

最近のGoogleは、OSSやIT産業にノウハウやその技術を貢献できるように、もっとOSSやAPIを作ろうという傾向があります。これは最近公開した Kubernetesや、Tensorflowで具体的に実現しています。
ただ、Bigtableに関しては時すでに遅しなので、技術的に一番近いHBase 互換のAPIをBigtagleで提供することになりました。つまり、HBaseを使っていた開発者はコードを変えずにそのままBigtableを使えるし、Bigtableにベンダーロックされることもありません。

HBase API

HBaseネィティブAPIは割とよく出来てきて、データの読み書きが簡単です。 (Javaしかないのは私的には悲しいけどw)

たとえばこんな感じでConnectionを作って、一つのRowを取得できます。

try {
    Connection connection  = ConnectionFactory.createConnection();
    try {
        Table table = connection.getTable(TableName.valueOf(tableName));

        // rowIdをもとにGetリクエストを作成して、テーブルにリクエストを実行
        Result result = table.get(new Get("rowId".getBytes()));

        // 結果を回して、それぞれのコラムと値をstdoutに出力
        for (Cell cell : result.listCells()) {
            String row = new String(CellUtil.cloneRow(cell));
            String family = new String(CellUtil.cloneFamily(cell));
            String column = new String(CellUtil.cloneQualifier(cell));
            String value = new String(CellUtil.cloneValue(cell));
            long timestamp = cell.getTimestamp();
            System.out.printf("%-20s column=%s:%s, timestamp=%s, value=%s\n", row, family, column, timestamp, value);
        }
    } finally {
        // 最後にコネクションを閉じる
        connection.close();
    }
} catch (IOException e) {
    e.printStackTrace();
}

Putはこんな感じ

// Putリクエストを作成
Put put = new Put(Bytes.toBytes(rowId));

put.addColumn(Bytes.toBytes(columnFamily), Bytes.toBytes(column), Bytes.toBytes(value));
// 複数のコラムを一発でputできる
// put.addColumn(Bytes.toBytes(columnFamily), Bytes.toBytes(column), Bytes.toBytes(value));

// テーブルにリクエストを実行
table.put(put);

Scanはこういうふうにできます。

// Create a new Scan instance.
Scan scan = new Scan();

// スキャンのフィルターがいくつかあります。
scan.setFilter(new SingleColumnValueFilter(columnFamily, columnName, CompareFilter.CompareOp.EQUAL, "mycolumn"));

ResultScanner resultScanner = table.getScanner(scan);

// スキャンのrowを回す
for (Result result : resultScanner) {
    // rowの中のコラムと値を回す
    for (Cell cell : result.listCells()) {
        // 結果を出力する
        String row = new String(CellUtil.cloneRow(cell));
        String family = new String(CellUtil.cloneFamily(cell));
        String column = new String(CellUtil.cloneQualifier(cell));
        String value = new String(CellUtil.cloneValue(cell));
        long timestamp = cell.getTimestamp();
        System.out.printf("%-20s column=%s:%s, timestamp=%s, value=%s\n", row, family, column, timestamp, value);
    }
}

Bigtableのこれから

Cloud Bigtableは現在betaで、来年GA (一般公開)するように頑張っています。まず安定性にフォーカスして、GAのあとに新しい機能や改善にフォーカスすることになるでしょう。

Betaの間は誰でも使えるので、ぜひ試してみてください

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした