任何人都可以使用的Cloud Bigtable版本支持Google的大多数服务

这是Google Cloud平台2015年第19篇文章。我也在我的博客上发布了。

Cloud Bigtable

我进入谷歌已经快一年了,感觉2015年真的过得飞快。我的工作有点像开发者倡导者,但又不完全是。我的一项工作是支持今年5月发布的“云大表”作为测试版。云大表(Cloud Bigtable)是谷歌著名的大规模分布式数据库“大表”(Bigtable)的服务,允许谷歌以外的开发者使用。这个大表是支撑谷歌服务,如搜索、地图、Gmail等的核心之一。

大众对于Bigtable这个分布式数据库的高可伸缩性很容易理解,但在Google之外,关于它能够扩展到什么程度并不为人所知。

谷歌的搜索索引有超过100,000,000 Gb的容量,相当于100Petabyte!而且实际的内部数字要比这个大得多(可能是几倍)。100Petabyte只是目前公开的保守估计。

Google自从大约10年前开始使用Bigtable,于2006年公开发表了一篇关于其设计的白皮书。这篇白皮书的作者是如此出色以至于让我感到他们与我所在的公司不同层次上的超级人物,其中包括Jeff Dean、Sanjay Ghemawat、Andrew Fikes等Google的传奇级开发者。正是从这篇白皮书中,开源的BigData生态系统诞生了。具体而言,实现了MapReduce的Hadoop以及受到GFS(Google文件系统)启发的HDFS正在改变IT产业。基于Bigtable设计的HBase也吸引了大量用户。

Bigtable的性能

在谷歌上,必须以惊人的速度向用户返回搜索结果(当然是理所当然的)。如果Bigtable太慢,谷歌会损失多少钱呢……如果搜索结果在一小时内变慢了一秒钟,我可以想象我的一年工资就会荡然无存。

因此,Bigtable经过相当程度的效率化。例如,从与Bigtable集群位于同一区域的GCE实例对其进行写入或读取,响应时间最多也就在10毫秒以内。这非常快。而且,p99(99%的请求在10毫秒以内返回)的响应时间。这是包括从客户端测量的网络通信延迟。从服务器端测量的延迟是6毫秒。

同时性价比也很高。从写入吞吐量的角度来看,每美元的 MB/s 超过了 HBase 和 Cassandra 的两倍以上。

Cloud Bigtable Performance

顺便提一下,以下的图表是实时性能。这是我做的演示应用程序创建的。上面的图表是请求数量,下面的图表是延迟(黑线是p50,蓝线是p99)。在每秒1.5万个查询的情况下,可以看到延迟的p99大约在10毫秒以内。

Cloud Bigtable Demo

Bigtable的设计

有人说Bigtable使得NoSQL概念非常受欢迎。然而,由于NoSQL的范围广泛,我们来解释一下Bigtable的设计。

Bigtable是一种所谓的键值数据库,但它具有一些普通键值数据库不具备的特殊功能。
首先,它具有一个键可以拥有多个值的功能。这类似于关系型数据库中的列,但与关系型数据库不同的是,它没有表模式,而是允许每行数据中的列可以根据行而变化。简而言之,可以将其表示为如下图所示:

因为行已经按键排序,所以可以通过指定行键进行扫描。但是无法针对每个列中所存储的数据进行查询或扫描。用关系型数据库来说就是有主键索引但没有次要索引。

每个行并没有事务,但对于包含在一行中的列,写入是具有一致性的。

将技术保密的陷阱

Google发布了MapReduce和Bigtable的技术白皮书,但并没有公开其代码。
在这种情况下,出现了开源软件的实现,但其中并没有使用Google的专有技术,因此性能不及Google的MapReduce和Bigtable。

最近的Google公司正在倾向于创建更多的开源软件和API,以便为开源社区和IT行业做出贡献。这一倾向具体体现在最近发布的Kubernetes和Tensorflow等项目中。

然而,对于Bigtable这一项技术来说,为时已晚,因此我们决定在技术上提供最接近HBase的API以兼容Bigtable。换句话说,开发人员可以在不改变代码的情况下直接使用Bigtable,同时也不会被供应商所束缚。

HBase 应用程序接口

HBase原生API做得非常不错,读写数据很简单。(只支持Java对我来说有点可惜哈哈)

例如,您可以通过以下方式建立连接并获取一行数据。

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 = 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目前处于beta阶段,明年将努力实现GA(一般公开)。首先我们将关注稳定性,GA之后将专注于新功能和改进。

由于Beta版本的特性,任何人都可以使用,所以请务必尝试一下。

广告
将在 10 秒后关闭
bannerAds