在 python 中读取巨大 MySQL 表的最快方法

Posted

技术标签:

【中文标题】在 python 中读取巨大 MySQL 表的最快方法【英文标题】:Fastest way to read huge MySQL table in python 【发布时间】:2018-12-11 15:02:21 【问题描述】:

我试图读取由数百万行组成的非常巨大的 mysql 表。我使用过Pandas 库和chunks。请看下面的代码:

import pandas as pd
import numpy as np
import pymysql.cursors

connection = pymysql.connect(user='xxx', password='xxx', database='xxx', host='xxx')

try:
    with connection.cursor() as cursor:
        query = "SELECT * FROM example_table;"

        chunks=[]

        for chunk in pd.read_sql(query, connection, chunksize = 1000):
            chunks.append(chunk)
        #print(len(chunks))    
        result = pd.concat(chunks, ignore_index=True)
        #print(type(result))
        #print(result)

finally:
    print("Done!")

    connection.close()

如果我限制要选择的行数,实际上执行时间是可以接受的。但如果还想选择最少的数据(例如 100 万行),则执行时间会显着增加。

也许有更好/更快的方法从 python 中的关系数据库中选择数据?

【问题讨论】:

***.com/questions/34180448/…这看起来和你的问题很相似 【参考方案1】:

您可以尝试使用不同的 mysql 连接器。我建议尝试mysqlclient,这是最快的 mysql 连接器(我相信有相当大的优势)。

pymysql 是一个纯 python mysql 客户端,而mysqlclient 是(更快的)C 库的包装器。

用法和pymsql基本一样:

import MySQLdb

connection = MySQLdb.connect(user='xxx', password='xxx', database='xxx', host='xxx')

在此处阅读有关不同连接器的更多信息:What's the difference between MySQLdb, mysqlclient and MySQL connector/Python?

【讨论】:

感谢@djmac 的建议。我会试一试。我可以考虑什么来加快阅读过程的总体速度?也许这个块解决方案没有那么好优化? 老实说,我认为这主要取决于数据库结构(和数据)很多。如果您真的在整个表上执行SELECT *,那么不同的数据结构(即不是 MySQL)可能会更好?否则,确保您拥有正确的索引会产生很大的不同(EXPLAIN 声明可以帮助您并提供有关良好调整的指导) 好的。我对pandas 的内部结构不太熟悉——但是 MySQL-python 连接器(mysqlclient 所基于)比 pymysql 快 1000%(根据这个wiki.openstack.org/wiki/PyMySQL_evaluation)。 另一种选择是考虑并行化(例如,使用 multiprocessing 模块和工作人员池)。例如,您可以让多个工作人员选择较小的块并将它们与pd.concat 拼接在一起。您甚至可以对数据库进行分片(例如,垂直分区)并获得更好的性能 - 尽管如果瓶颈是 pandas,那可能就不那么划算了。 当然 - 将其作为另一个答案【参考方案2】:

另一种选择可能是使用multiprocessing 模块,将查询拆分并将其发送到多个并行进程,然后将结果连接起来。

如果不了解 pandas 分块 - 我认为您将不得不手动进行分块(这取决于数据)...不要使用 LIMIT / OFFSET - 性能会很糟糕。

这可能不是一个好主意,具体取决于数据。如果有一种有用的方法来拆分查询(例如,如果它是一个时间序列,或者有某种适当的索引列可以使用,它可能是有意义的)。我在下面放了两个例子来展示不同的情况。

示例 1

import pandas as pd
import MySQLdb

def worker(y):
    #where y is value in an indexed column, e.g. a category
    connection = MySQLdb.connect(user='xxx', password='xxx', database='xxx', host='xxx')
    query = "SELECT * FROM example_table WHERE col_x = 0".format(y)
    return pd.read_sql(query, connection)

p = multiprocessing.Pool(processes=10) 
#(or however many process you want to allocate)

data = p.map(worker, [y for y in col_x_categories])
#assuming there is a reasonable number of categories in an indexed col_x

p.close()
results = pd.concat(data) 

示例 2

import pandas as pd
import MySQLdb
import datetime

def worker(a,b):
    #where a and b are timestamps
    connection = MySQLdb.connect(user='xxx', password='xxx', database='xxx', host='xxx')
    query = "SELECT * FROM example_table WHERE x >= 0 AND x < 1".format(a,b)
    return pd.read_sql(query, connection)

p = multiprocessing.Pool(processes=10) 
#(or however many process you want to allocate)

date_range = pd.date_range(start=d1, end=d2, freq="A-JAN")
# this arbitrary here, and will depend on your data /knowing your data before hand (ie. d1, d2 and an appropriate freq to use)

date_pairs = list(zip(date_range, date_range[1:]))
data = p.map(worker, date_pairs)

p.close()
results = pd.concat(data)

这可能是更好的方法(并且没有经过适当的测试等)。如果您尝试一下,有兴趣知道它是怎么回事。

【讨论】:

我想我还没有完全理解这些例子是如何工作的......让我们说第一个。您为列表 col_x_categories 的每个项目映射并调用函数工作者。但是我们在哪里定义 y 和 col_x_categories?而且我认为这个映射操作对于 n 个进程是并行的。【参考方案3】:

对于那些使用 Windows 并且无法安装 MySQLdb 的用户。我正在使用这种方式从大表中获取数据。

import mysql.connector

i = 1
limit = 1000

while True:
    sql = "SELECT * FROM super_table LIMIT , ".format(i, limit)
    cursor.execute(sql)
    rows = self.cursor.fetchall()

    if not len(rows):  # break the loop when no more rows
        print("Done!")
        break

    for row in rows:   # do something with results
        print(row)

    i += limit

【讨论】:

以后会很慢。因为“限制”。如果使用“限制”,则将进行全表扫描。 “加入”将是一个适当的解决方案。像这样 ''' "SELECT id, xValue, yValue, zValue, createdAt FROM (SELECT id FROM Position LIMIT 2000000, 1000) q JOIN Position p ON p.id = q.id" '''

以上是关于在 python 中读取巨大 MySQL 表的最快方法的主要内容,如果未能解决你的问题,请参考以下文章

MySQL数据读取速度问题

MySQL系列- MySQL执行计划

MySQL系列- MySQL执行计划

从 ORACLE 读取的最快 OLEDB

从多个文件中读取大数据并在python中聚合数据的最快方法是啥?

读取 .csv 文件时在 Python 中解析日期的最快方法是啥?