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

时间:2018-07-03 10:22:40

标签: python mysql pandas numpy

我试图读取一个由几百万行组成的非常巨大的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()

实际上,如果我限制要选择的行数,则执行时间是可以接受的。但是,如果只选择最少的数据(例如 1百万行),则执行时间将大大增加。

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

3 个答案:

答案 0 :(得分:0)

您可以尝试使用其他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?

答案 1 :(得分:0)

另一种选择可能是使用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)

执行此操作的方法可能更好(并且没有经过适当的测试等)。有兴趣知道如何尝试。

答案 2 :(得分:0)

对于使用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)