BigQuery转Pandas DataFrame速度极慢,如何优化查询效率?
问题描述
在BigQuery控制台执行查询返回近百万行数据仅需3-4秒,但在本地、Community Cloud、GCP环境的Streamlit Python应用中,相同查询(含写入Pandas DataFrame)耗时约180秒。性能分析显示大部分时间消耗在RowIterator上,当前环境RAM与CPU充足,寻求更高效的实现方式。
性能分析详情
├─ 163.313 RowIterator._items_iter google/api_core/page_iterator.py:206 │ [62 frames hidden] google, requests, urllib3, http, sock... │ 88.930 _SSLSocket.read <built-in> ├─ 8.598 QueryJob.__iter__ google/cloud/bigquery/job/query.py:2107 │ [27 frames hidden] google, requests, urllib3, http, sock... ├─ 3.988 Row.__getitem__ google/cloud/bigquery/table.py:1501 │ [2 frames hidden] google ├─ 3.569 Client.query google/cloud/bigquery/client.py:3362 │ [25 frames hidden] google, requests, urllib3, http, sock... └─ 2.815 [self] users.py
原实现代码
bq_client = st.session_state.bq_client sql_query = f""" SELECT * FROM `dataexploration-193817.user_data.all_users_progress` WHERE first_open BETWEEN PARSE_DATE('%Y/%m/%d','{start_date}') AND CURRENT_DATE() """ rows_raw = bq_client.query(sql_query) rows = [dict(row) for row in rows_raw] df_user_list = pd.DataFrame(rows)
高效实现方案
直接使用官方
to_dataframe()方法:避免手动遍历RowIterator转换字典再转DataFrame,客户端内置的批量转换逻辑经过优化,性能远高于手动循环:bq_client = st.session_state.bq_client sql_query = f""" SELECT * FROM `dataexploration-193817.user_data.all_users_progress` WHERE first_open BETWEEN PARSE_DATE('%Y/%m/%d','{start_date}') AND CURRENT_DATE() """ query_job = bq_client.query(sql_query) df_user_list = query_job.to_dataframe()启用BigQuery Storage API:这是专为大数据量导出优化的接口,比默认REST API快数倍。先安装依赖
google-cloud-bigquery-storage,再修改代码:from google.cloud.bigquery_storage import BigQueryReadClient query_job = bq_client.query(sql_query) df_user_list = query_job.to_dataframe(bqstorage_client=BigQueryReadClient())避免
SELECT *,只查询必要字段:冗余字段会增加数据传输量与解析耗时,明确指定所需列能大幅缩短处理时间。调大分页参数:默认每页获取1000行,增大
page_size可减少请求次数,提升迭代效率:rows_raw = bq_client.query(sql_query, page_size=10000)参数化查询替代字符串拼接:消除SQL注入风险,同时让BigQuery更好地缓存查询计划,提升后续执行效率:
from google.cloud.bigquery import ScalarQueryParameter sql_query = """ SELECT * FROM `dataexploration-193817.user_data.all_users_progress` WHERE first_open BETWEEN @start_date AND CURRENT_DATE() """ job_config = bigquery.QueryJobConfig( query_parameters=[ ScalarQueryParameter("start_date", "DATE", start_date) ] ) query_job = bq_client.query(sql_query, job_config=job_config) df_user_list = query_job.to_dataframe()
内容的提问来源于stack exchange,提问作者Jeff Oberlander
相关产品推荐
相关产品推荐

