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

Ugo*_*goL 6 python mysql numpy pandas

我试图读取一个由数百万行组成的非常大的 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()
Run Code Online (Sandbox Code Playgroud)

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

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

djm*_*mac 8

另一种选择可能是使用该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) 
Run Code Online (Sandbox Code Playgroud)

实施例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)
Run Code Online (Sandbox Code Playgroud)

可能有更好的方法来做到这一点(并且尚未经过适当的测试等)。如果你尝试一下,就有兴趣知道它是如何进行的。


djm*_*mac 3

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

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

用法基本相同pymsql

import MySQLdb

connection = MySQLdb.connect(user='xxx', password='xxx', database='xxx', host='xxx')
Run Code Online (Sandbox Code Playgroud)

在此处阅读有关不同连接器的更多信息:MySQLdb、mysqlclient 和 MySQL 连接器/Python 有何区别?