聲明
我們有10臺機器HBase集羣和數十億行內部。每行由一個列族和〜20列組成。我們需要執行頻繁的掃描請求,其中包含起始行前綴和結束行前綴。通常每次掃描都會返回100到10000行。在Java和Python中對HBase的並行掃描請求具有不同的性能
因爲請求可能會經常發生(每分鐘最多幾個請求),所以性能會被預先設置。由於系統的體系結構,我們希望用Python代替當前的Java代碼來實現我們的解決方案。問題出在Python上,我們得到的性能比Java差5至10倍。
什麼現在工作
我們有執行掃描請求到HBase的Java代碼。它採用ususal的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秒和
- 30 - 55 sec每100次並行掃描
正如我們所看到的,單次掃描的性能非常相似。但Python中的並行任務要慢得多。
任何想法爲什麼會發生?也許我們的Python/Happybase代碼存在一些問題?還是HBase Thrift服務器(HappyBase用來連接HBase)的性能?