与从 CSV 文件导出和导入相比,Python MySQLdb SScursor 速度较慢。加速可能吗?

标签 python mysql csv file-io mysql-python

作为构建数据仓库的一部分,我必须查询大约 7500 万行的源数据库表。

我想对 7500 万行做一些处理,然后将结果添加到另一个数据库中。现在,这是相当多的数据,我主要通过两种方法取得了成功:

1) 使用 MySQL 的“SELECT ... INTO”功能将查询导出为 CSV 文件,并使用 python 的 fileinput 模块读取它,以及

2) 使用 MySQLdb 的 SScursor 连接到 MySQL 数据库(默认游标将查询放在内存中,杀死 python 脚本)并以大约 10k 行的 block (这是我发现的 block 大小)获取结果是最快的)。

第一种方法是“手动”执行 SQL 查询(大约需要 6 分钟),然后使用 python 脚本读取 csv 文件并进行处理。我使用 fileinput 读取文件的原因是 fileinput 不会从一开始就将整个文件加载到内存中,并且适用于较大的文件。仅遍历文件(读取文件中的每一行并调用 pass)大约需要 80 秒,即 1M 行/秒。

第二种方法是一个 python 脚本执行相同的查询(也需要大约 6 分钟,或稍长),然后一个 while 循环获取行 block ,只要 SScursor 中还有任何剩余的行。在这里,仅仅读取行(一个接一个地获取 block 而不做任何其他事情)需要大约 15 分钟,或者大约 85k 行/秒。

上面的两个数字(行/秒)可能并没有真正的可比性,但是在我的应用程序中对这两种方法进行基准测试时,第一个大约需要 20 分钟(其中大约 5 分钟是 MySQL 转储到 CSV 文件中),并且第二个大约需要 35 分钟(其中大约五分钟是正在执行的查询)。这意味着转储和读取 CSV 文件的速度大约是直接使用 SScursor 的两倍。

这没有问题,如果它不限制我的系统的可移植性:“SELECT ... INTO”语句要求 MySQL 具有写入权限,我怀疑这不如使用游标安全。另一方面,15 分钟(并且随着源数据库的增长而增加)并不是我每次构建都能抽出的时间。

那么,我是不是漏掉了什么? SScursor 是否有任何已知原因比转储/读取 CSV 文件慢得多,这样文件输入是 C 优化的,而 SScursor 不是?关于如何处理这个问题的任何想法?有什么要测试的吗?我相信 SScursor 可能和第一种方法一样快,但在阅读了所有我能找到的关于此事的信息后,我被难住了。

现在,代码:

并不是说我认为查询有任何问题(它是我可以要求的最快速度并且在两种方法中花费的时间相似),但这里是为了完整性:

    SELECT LT.SomeID, LT.weekID, W.monday, GREATEST(LT.attr1, LT.attr2)
    FROM LargeTable LT JOIN Week W ON LT.weekID = W.ID
    ORDER BY LT.someID ASC, LT.weekID ASC;

第一种方法的主要代码是这样的

    import fileinput
    INPUT_PATH = 'path/to/csv/dump/dump.csv'
    event_list = []
    ID = -1

    for line in fileinput.input([INPUT_PATH]):
            split_line = line.split(';')
            if split_line[0] == ID:
                event_list.append(split_line[1:])
            else:
                process_function(ID,event_list)
                event_list = [ split_line[1:] ]
                ID = split_line[0]

    process_function(ID,event_list)

第二种方法的主要代码是:

    import MySQLdb
    ...opening connection, defining SScursor called ssc...
    CHUNK_SIZE = 100000

    query_stmt = """SELECT LT.SomeID, LT.weekID, W.monday,
                    GREATEST(LT.attr1, LT.attr2)
                    FROM LargeTable LT JOIN Week W ON LT.weekID = W.ID
                    ORDER BY LT.someID ASC, LT.weekID ASC"""
    ssc.execute(query_stmt)

    event_list = []
    ID = -1

    data_chunk = ssc.fetchmany(CHUNK_SIZE)
    while data_chunk:
        for row in data_chunk:
            if row[0] == ID:
                event_list.append([ row[1], row[2], row[3] ])
            else:
                process_function(ID,event_list)
                event_list = [[ row[1], row[2], row[3] ]]
                ID = row[0]
        data_chunk = ssc.fetchmany(CHUNK_SIZE)

    process_function(ID,event_list)

最后,我在 Ubuntu 13.04 上安装了 MySQL 服务器 5.5.31。我使用 Python 2.7.4 和 MySQLdb 1.2.3。谢谢你陪我这么久!

最佳答案

使用后cProfile我发现很多时间都花在隐式构造 Decimal 对象上,因为这是从 SQL 查询返回到我的 Python 脚本中的数字类型。在第一种方法中,十进制值作为整数写入 CSV 文件,然后由 Python 脚本读取。 CSV 文件 I/O 使数据“扁平化”,使脚本更快。这两个脚本现在的速度大致相同(第二种方法仍然稍微慢一点)。

我还对MySQL数据库中的日期做了一些整数类型的转换。我现在的查询是:

SELECT LT.SomeID,
       LT.weekID,
       CAST(DATE_FORMAT(W.monday,'%Y%m%d') AS UNSIGNED),
       CAST(GREATEST(LT.attr1, LT.attr2) AS UNSIGNED)
FROM LargeTable LT JOIN Week W ON LT.weekID = W.ID
ORDER BY LT.someID ASC, LT.weekID ASC;

这几乎消除了两种方法之间处理时间的差异。

这里的教训是,在进行大型查询时,数据类型的后处理很重要!重写查询以减少 Python 中的函数调用可以显着提高整体处理速度。

关于与从 CSV 文件导出和导入相比,Python MySQLdb SScursor 速度较慢。加速可能吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17553707/

相关文章:

vba - 您能找到导致我的 Excel VB 脚本速度变慢的原因吗?

php - 如果 xampp 在我妈妈的笔记本电脑上运行,它是否会构成安全威胁?

mysql - 无法在 Windows 中通过批处理文件获取 MYSQL 数据库的备份

MySql 查询从给定类别中选择文章?

unix - 使用 Awk 从分隔文件中提取特定列

python - 使用 pandas 从 csv 读取日期时间时出错

javascript - 在 scrapy 中获取 document.write 的输出

python - 合并 Pandas Dataframe 中的列

python - Panda3d 中基础对象如何控制相机

python - 使用 Python 在数据文件中的 JSON 对象之间添加逗号?