Java 和 Python 中对 HBase 的并行扫描请求具有不同的性能
Parallel scan requests to HBase in Java and Python have different performance
声明
我们有 10 台机器的 HBase 集群和里面的数十亿行。每行由一个列族和约 20 列组成。我们需要执行频繁的扫描请求,其中包含开始行前缀和结束行前缀。通常每次扫描 returns 大约 100 - 10000 行。
因为请求可能会非常频繁(每分钟最多几个请求),所以性能优先。由于系统的架构,我们希望在 Python 而不是当前的 Java 代码中实现我们的解决方案。问题是 Python 我们获得的性能比 Java.
差 5 到 10 倍
现在有效
我们有 Java 代码执行对 HBase 的扫描请求。它使用常用的 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;
}
这些查询在 Callable
接口和 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 秒 每次扫描和
- 5.0 - 8.0 秒 每 100 次并行扫描
我们尝试什么
我们尝试在 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)?
有一种使用 Jython 的方法,它允许您访问 java JVM 和 java 库。这样你就可以在同一个源文件中写 python 和 java 了。然后代码被编译为 JVM 的 java 字节码。这应该提供与用 java 代码编写的 Jython 相同的性能,因此您不必用纯 java.
编写
Java 基准 vs Python 高得多。这是一个显示 java 和 python 之间性能的网站。
http://benchmarksgame.alioth.debian.org/u64q/python.html
这里是 jython 的网站:
http://www.jython.org/
声明
我们有 10 台机器的 HBase 集群和里面的数十亿行。每行由一个列族和约 20 列组成。我们需要执行频繁的扫描请求,其中包含开始行前缀和结束行前缀。通常每次扫描 returns 大约 100 - 10000 行。
因为请求可能会非常频繁(每分钟最多几个请求),所以性能优先。由于系统的架构,我们希望在 Python 而不是当前的 Java 代码中实现我们的解决方案。问题是 Python 我们获得的性能比 Java.
差 5 到 10 倍现在有效
我们有 Java 代码执行对 HBase 的扫描请求。它使用常用的 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;
}
这些查询在 Callable
接口和 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 秒 每次扫描和
- 5.0 - 8.0 秒 每 100 次并行扫描
我们尝试什么
我们尝试在 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)?
有一种使用 Jython 的方法,它允许您访问 java JVM 和 java 库。这样你就可以在同一个源文件中写 python 和 java 了。然后代码被编译为 JVM 的 java 字节码。这应该提供与用 java 代码编写的 Jython 相同的性能,因此您不必用纯 java.
编写Java 基准 vs Python 高得多。这是一个显示 java 和 python 之间性能的网站。
http://benchmarksgame.alioth.debian.org/u64q/python.html
这里是 jython 的网站: http://www.jython.org/