3

陈述

我们有 10 台机器 HBase 集群和内部数十亿行。每行由一个列族和约 20 列组成。我们需要执行包含起始行前缀和结束行前缀的频繁扫描请求。通常每次扫描都会返回大约 100 - 10000 行。

因为请求可以非常频繁地出现(每分钟最多几个请求),所以性能是优先考虑的。由于系统的架构,我们希望用 Python 而不是当前的 Java 代码来实现我们的解决方案。问题在于 Python 的性能比 Java 差 5 到 10 倍。

现在什么工作

我们有对 HBase 执行扫描请求的 Java 代码。它使用常用的 HBase Java API:

public List<String> getNumber(Number key) {
    List<String> res = new ArrayList<>();

    String start_key = key.getNumber();
    String next_key = key.getNumber() + "1";
    byte[] prefix_begin = Bytes.toBytes(start_key);
    byte[] prefix_end = Bytes.toBytes(next_key);
    Scan scan = new Scan(prefix_begin, prefix_end);
    ResultScanner scanner = table.getScanner(scan);
    for (Result result : scanner) {
        byte[] row = result.getRow();
        res.add(Bytes.toString(row));
    }
    
    return res;
}

这些查询在Callableinterface 和ScheduledThreadPoolExecutor. call()每个可调用的方法都只是运行getNumber(Number key)

public List<String> getNumbers(List<Number> keys) {
    List<String> res = new ArrayList<String>();

    List<Callables.CallingCallable> callables = new ArrayList();
    for (Number source : keys) {
        callables.add(new Callables.CallingCallable(this, source));
    }

    Object futures = new ArrayList();
    ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(24);

    try {
        futures = executor.invokeAll(callables);
    } catch (InterruptedException ex) {
    }

    executor.shutdown();
}

这工作得很好,并允许实现以下性能:

  • 每次扫描1.5 - 2.0 秒和
  • 每 100 次并行扫描5.0 - 8.0 秒

我们尝试什么

我们尝试借助Happybase库在 Python 中实现类似的解决方案:

@staticmethod
def execute_query(key, table_name, con_pool):
        items = []
        with con_pool.connection() as connection:
            table = happybase.Table(table_name, connection)
            [row_start, row_end] = get_start_and_end_row(key)
            selected_rows = table.scan(row_start=row_start, row_stop=row_end)
            for key, data in selected_rows:
                items.append(Item(data))
        return items

@staticmethod
def execute_in_parallel(table_name, hbase_host, hbase_port, keys):
        pool = ThreadPool(24)
        con_pool = happybase.ConnectionPool(size=24, host=hbase_host, port=hbase_port)
        execute_query_partial = partial(execute_query, table_name=table_name, con_pool=con_pool)
        result_info = pool.map_async(execute_query_partial, keys, chunksize=1)
        result = result_info.get()

取得的成绩:

  • 每次扫描2.0 - 3.0 秒,并且
  • 每 100 次并行扫描30 - 55 秒

正如我们所见,单次扫描的性能非常相似。但是 Python 中的并行任务要慢得多。

任何想法为什么会发生?也许我们的 Python/Happybase 代码有问题?还是 HBase Thrift 服务器(HappyBase 用来连接 HBase)的性能?

4

1 回答 1

1

有一种使用 Jython 的方法可以让您访问 java JVM 和 java 库。有了这个,你可以在同一个源文件中编写 python 和 java。然后将代码编译为 JVM 的 java 字节码。这应该提供与用 java 代码编写的 Jython 相同的性能,因此您不必用纯 java 编写。

Java 基准测试与 Python 相比要高得多。这是一个展示 java 和 python 之间性能的网站。

http://benchmarksgame.alioth.debian.org/u64q/python.html

这是 jython 的网站: http ://www.jython.org/

于 2016-01-13T01:17:31.423 回答