Python 带有HappyBase连接池的PySpark dataframe.foreach()返回';类型错误:can';t pickle thread.lock对象';
我有一个PySpark作业,它更新HBase中的一些对象(Spark v1.6.0;happybase v0.9) 如果我为每行打开/关闭一个HBase连接,它就可以正常工作:Python 带有HappyBase连接池的PySpark dataframe.foreach()返回';类型错误:can';t pickle thread.lock对象';,python,apache-spark,pyspark,happybase,Python,Apache Spark,Pyspark,Happybase,我有一个PySpark作业,它更新HBase中的一些对象(Spark v1.6.0;happybase v0.9) 如果我为每行打开/关闭一个HBase连接,它就可以正常工作: def process_row(row): conn = happybase.Connection(host=[hbase_master]) # update HBase record with data from row conn.close() my_dataframe.foreach(pr
def process_row(row):
conn = happybase.Connection(host=[hbase_master])
# update HBase record with data from row
conn.close()
my_dataframe.foreach(process_row)
经过数千次升级后,我们开始看到如下错误:
显然,为每个upsert打开/关闭连接效率很低。这个函数实际上只是一个适当解决方案的占位符
然后,我尝试创建一个版本的process\u row
函数,该函数使用连接池:
pool = happybase.ConnectionPool(size=20, host=[hbase_master])
def process_row(row):
with pool.connection() as conn:
# update HBase record with data from row
由于某些原因,此函数的连接池版本返回错误(请参阅):
你能看出我做错了什么吗
更新
我看到并怀疑我遇到了相同的问题:Spark试图序列化池
对象并将其分发给每个执行器,但此连接池对象无法在多个执行器之间共享
听起来我需要将数据集拆分为多个分区,并在每个分区中使用一个连接(请参阅)。根据文档中的一个示例,我尝试了以下方法:
def persist_to_hbase(dataframe_partition):
hbase_connection = happybase.Connection(host=[hbase_master])
for row in dataframe_partition:
# persist data
hbase_connection.close()
my_dataframe.foreachPartition(lambda dataframe_partition: persist_to_hbase(dataframe_partition))
不幸的是,它仍然返回“不能pickle thread.lock objects”错误。说到底,happybase连接只是tcp连接,因此它们不能在进程之间共享。连接池主要适用于多线程应用程序,也适用于单线程应用程序,这些应用程序可以将连接池用作全局“连接工厂”,并进行连接重用,这可能会简化代码,因为不需要传递“连接”对象。它还使错误恢复变得更容易一些 在任何情况下,进程之间都不能共享池(它只是一组连接)。由于这个原因,试图将其序列化是没有意义的。(池使用的锁会导致序列化失败,但这只是一个症状。) 也许您可以使用一个助手,有条件地创建一个池(或连接)并将其存储为一个模块局部变量,而不是在导入时实例化它,例如
_pool = None
def get_pool():
global _pool
if _pool is None:
_pool = happybase.ConnectionPool(size=1, host=[hbase_master])
return pool
def process(...)
with get_pool().connection() as connection:
connection.table(...).put(...)
这将在首次使用时而不是在导入时实例化池/连接。我无法重现此问题。您确定没有使用
持久化\u将其他内容拖动到\u hbase
?如果是的话,你能创建一个?我的意思是用最少的数据,完成Spark的初始化。乍一看,代码中有很多内容。一些数据帧?您可以提供如何创建数据帧吗?抛出can't pickle错误的主要原因是您试图通过序列化跨进程共享锁对象。另外,您可以使用my_dataframe.foreachPartition(persist_to_hbase)而不是创建额外的lamda。也看看它是否有用。谢谢@wouter。我有几分钟的时间就试试这个。我最终使用spark hbase连接器用Java重新编写了Python脚本,效果很好。
def persist_to_hbase(dataframe_partition):
hbase_connection = happybase.Connection(host=[hbase_master])
for row in dataframe_partition:
# persist data
hbase_connection.close()
my_dataframe.foreachPartition(lambda dataframe_partition: persist_to_hbase(dataframe_partition))
_pool = None
def get_pool():
global _pool
if _pool is None:
_pool = happybase.ConnectionPool(size=1, host=[hbase_master])
return pool
def process(...)
with get_pool().connection() as connection:
connection.table(...).put(...)