Dash调用Clickhouse二次查询报WinError 10038及属性错误求助
问题原因
- 你使用了全局单例的Clickhouse客户端实例,主流Clickhouse Python驱动的默认客户端不是线程安全的,且默认配置下单次查询完成后会自动关闭底层socket连接
- Dash的回调默认是多线程执行,第二个回调触发时,第一个回调已经使用完全局客户端并销毁了对应的socket连接,第二次复用已销毁的连接实例执行查询就会触发对应报错
解决方案
方案1:每次查询新建独立客户端实例(适合查询量小的场景)
将全局客户端改为每次查询时新建、查询结束主动销毁,避免连接复用冲突:
# 封装客户端初始化逻辑 def get_ch_client(): # 替换为你自己的Clickhouse连接配置 from clickhouse_driver import Client return Client( host='你的Clickhouse地址', port=9000, user='你的用户名', password='你的密码', database='默认数据库' ) def get_alldata(timestart): client = get_ch_client() try: statement = f""" SELECT * FROM "xxx"."xxx" WHERE timestart>=toDateTime('{timestart}')""" df = client.query_dataframe(statement) return df finally: # 查询完成主动断开连接 client.disconnect() def second_request(id, time, line): client = get_ch_client() try: # 注:如果id、time、line是列表类型,需要先转为逗号分隔的字符串 # 示例:id_str = ','.join(map(str, id)) statement = f'''SELECT student,class FROM "xxx"."xxx" WHERE id IN ({id}) AND time IN ({time}) AND line IN ({line})''' df = client.query_dataframe(statement) return df finally: client.disconnect()
方案2:使用线程安全的连接池(适合查询量大的场景)
如果查询频次高的场景下使用连接池复用连接,降低每次新建连接的性能开销:
from clickhouse_driver import Pool # 全局初始化连接池,max_connections设置支持的最大连接数 ch_pool = Pool( host='你的Clickhouse地址', port=9000, user='你的用户名', password='你的密码', database='默认数据库', max_connections=10 ) def get_alldata(timestart): with ch_pool.get_client() as client: statement = f""" SELECT * FROM "xxx"."xxx" WHERE timestart>=toDateTime('{timestart}')""" df = client.query_dataframe(statement) return df def second_request(id, time, line): with ch_pool.get_client() as client: statement = f'''SELECT student,class FROM "xxx"."xxx" WHERE id IN ({id}) AND time IN ({time}) AND line IN ({line})''' df = client.query_dataframe(statement) return df
额外注意事项
- 你当前的SQL写法是直接拼接用户输入的参数,存在SQL注入风险,推荐使用驱动自带的参数化查询能力传入参数,示例如下:
# 参数化查询写法示例 statement = '''SELECT student,class FROM "xxx"."xxx" WHERE id IN %(id)s AND time IN %(time)s AND line IN %(line)s''' df = client.query_dataframe(statement, parameters={'id': id, 'time': time, 'line': line})
- 如果Dash服务开启了多进程部署模式,不要在父进程初始化客户端或连接池,需要在每个子进程中独立初始化。
内容的提问来源于stack exchange,提问作者Minecraft_Json
相关产品推荐
相关产品推荐

