【问题标题】:Slow MySQL database query time in a Python for loopPython for 循环中的 MySQL 数据库查询时间慢
【发布时间】:2020-06-25 06:33:32
【问题描述】:

我的任务是运行 8 个相等的查询(每个国家/地区 1 个查询),这样做会从 MySQL 数据库返回数据。我无法在一个国家/地区运行 1 个查询的原因是每个国家/地区都需要具有不同的列名。此外,需要使用动态日期范围(过去 7 天)每天更新结果。是的,我可以运行所有国家并使用 Pandas 进行列命名和所有操作,但我认为以下解决方案会更有效。因此,我的解决方案是创建一个 for 循环,该循环使用预定义的列表,其中包含所有国家/地区各自的维度和日期范围变量,这些变量会根据当前日期而变化。我遇到的问题是,在循环中运行的 MySQL 查询比直接在我们的数据仓库中运行相同的查询花费的时间要多得多(~140-500 秒对 30 秒)。该解决方案适用于 DWH 的较小表。问题是我不知道究竟是哪个部分导致了问题以及如何解决它。

这是我的代码示例,其中实现了一些较小的“测试”:

#Import libraries:
from google.cloud import storage
from google.oauth2 import service_account
import mysql.connector
import pandas as pd
import time
from datetime import timedelta, date

#Create a connection to new DWH:
coon = mysql.connector.connect(
  host="the host goes here",
  user="the user goes here",
  passwd="the password goes here"
)

#Create Google Cloud Service credential references:
credentials = service_account.Credentials.from_service_account_file(r'C:\Users\ivo.vancans\OneDrive\Documents\Python Workspace\my credential json goes here.json')
project_id='my project id goes here'

cursor = coon.cursor()

#Create lists of countries and dimensions
countries = ['EE','FI','LV','LT']
appp_id_dim = ['ga:dimension5','ga:dimension5','ga:dimension5','ga:dimension5']
status_dim = ['ga:dimension21','ga:dimension12','ga:dimension20','ga:dimension15']
score_dim = ['ga:dimension11','ga:dimension11','ga:dimension19','ga:dimension14']

#Define the current date and date that was 7 days before current date:
date_now = date.today() - timedelta(days=1)
date_7d_prev = date_now - timedelta(days=7)

#Create a loop
for c,s in zip(countries, score_dim):
    start_time = time.time()
    #Create the query using string formating:
    query = f"""select ca.ID, sv.subType, SUM(svl.score) as '{s}'
    from aio.CreditApplication ca
    join aio.ScoringResult sr 
    on sr.creditApplication_ID = ca.ID 
    join aio.ScorecardVariableLine svl 
    on svl.id = sr.scorecardVariableLine_ID
    join aio.ScorecardVariable sv 
    on sv.ID = svl.scorecardVariable_ID
    where sv.country='{c}'  
    #and sv.subType ="asc"
    and sv.subType != 'fsc'
    and sr.created >= '2020-01-01'
    and sr.created between '{date_7d_prev} 00:00:00' and '{date_now} 23:59:59'
    group by ca.id,sv.subType"""

    #Check of sql query
    print('query is done', time.time()-start_time)

    start_time = time.time()
    sql = pd.read_sql_query(query, coon)
    #check of assigning sql:
    print ('sql is assigned',time.time()-start_time)

    start_time = time.time()
    df = pd.DataFrame(sql
                      #, columns = ['created','ID','state']
                      )
    #Check the df assignment:
    print ('df has been assigned', time.time()-start_time)

    #Create a .csv file from the final dataframe:
    start_time = time.time()
    df.to_csv(fr"C:\Users\ivo.vancans\OneDrive\Documents\Python Workspace\Testing Ground\{c}_sql_loop_test.csv", index = False, header=True, encoding='utf-8', sep=';')
    #Check csv file creation:
    print ('csv has been created',time.time()-start_time)

    #Close the session
    start_time = time.time()
    cursor.close()

    #Check the session closing:
    print('The cursor is closed',time.time()-start_time)

此示例包含 4 个国家/地区,因为我尝试将数量减半,但这也无济于事。那是我认为我对 DWH 端有某种查询限制,因为主要减速总是从第 5 个国家开始。分别运行它们几乎需要相同的时间,但仍然需要很长时间。 所以,我的测试表明循环总是滞后于查询数据的步骤。每隔一步只需不到一秒,但查询时间会上升到 140-500 秒,有时甚至更多,如前所述。那么,您认为问题出在哪里?

【问题讨论】:

  • 如果你改变请求的顺序,它仍然是占用大部分时间的最后一个查询吗? EXPLAIN <query> 是您通常在 MySQL 中使用的,用于向您提供有关查询运行方式的详细信息,因此您可以查看任何丢失的索引等。我还建议 using an actual profiler 获取有关您的应用程序的反馈下次做;它消除了很多猜测(它还能够告诉你在不同的 python 模块中花费的时间)。
  • 感谢您的评论。改变顺序是什么意思?哪个国家先出?如果是这样,是的..一般来说这并不重要。一些国家/地区拥有更多的数据行,但使用 Python 解决方案的速度与简单地在 dwh 上使用 sql 客户端运行查询相比要慢。
  • 因为根据您的帖子,它是占用大部分时间的最后一个,无论查询的顺序如何,似乎都表明服务器端的东西;但是分析器应该能够为您提供更多详细信息 - 至少可以查看是否是实际的 read_sql_query 需要时间。我还会尝试打印查询本身并确保它与您期望的查询相同(即您手动运行的查询)。但是,是的,DWH 可能存在限制因素。
  • 我做了这件事,打印出查询并在 dwh 上复制粘贴运行它,工作得很好。我现在将研究您建议的分析器。

标签: python mysql pandas eclipse pydev


【解决方案1】:

找到了解决方案!在与我公司的一位对 SQL 和我们特定的 DWH 引擎有更多经验的人交谈后,他同意提供帮助并重写了 SQL 部分。我不得不重写它,而不是离开子查询,这样就不会有子查询。为什么?因为我们的特定引擎不会为子查询创建索引,所以单独连接的表将具有索引。这极大地缩短了整个脚本的运行时间,从大约 40 分钟的运行时间缩短到大约不到 1 分钟。

【讨论】:

    猜你喜欢
    • 2021-02-08
    • 2011-10-31
    • 1970-01-01
    • 1970-01-01
    • 2014-07-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多