Question个
有没有一种非内存密集型的方法可以从MariaDB数据库中提取大型数据集并将其直接导入Python(通过python-mariadb连接器)?
Edit: Solution个
关注@Georg Richter的solution below,我们可以这样做:
connection = mariadb.connect(user='<username>', host='localhost', database='my_database')
cursor = connection.cursor(buffered=False)
query = 'SELECT column_1,column_2 FROM my_table'
cursor.execute(query)
df = pd.DataFrame({col:[] for col in range(number_of_columns_you_extract)})
size = 2**23
while rows := cursor.fetchmany(size):
df = pd.concat([df, pd.DataFrame(rows)], copy=False)
出于某种原因,这会将我的int值强制转换为Float.但内存使用量永远不会超过10 GB.
Context个
我在Linux上的MariaDB中有一个大型数据库.我经常不得不将它的一部分导入到Python中进行进一步的计算.就内存使用而言,MariaDB相当经济,但Python并非如此.虽然在Python中保存数据的内存消耗较少(例如,作为具有适用于所有列的适当数据类型的Pandas DataFrame),但通常的工作流程涉及多次复制数据,并且在某些阶段内存需求是巨大的.
Simple Example个
import pandas as pd
import mariadb
import gc
connection = mariadb.connect(user='<username>', host='localhost', database='my_database')
cursor = connection.cursor(dictionary=False)
query = 'SELECT column_1,column_2 FROM my_table'
cursor.execute(query)
rows = cursor.fetchall()
del cursor
gc.collect()
df = pd.DataFrame(rows)
del rows
gc.collect()
该示例提取5亿行和两列,所有值都是整数.
超过cursor.execute(query)
后,内存使用量约为35 GB.在rows = cursor.fetchall()
之后,它会上升到90 GB左右.在将数据转换为Pandas DataFrame并删除其他对象后,内存需求降至约8 GB.我使用top监视内存使用情况.
我不确定数据是如何保存在Cursor对象中的.I rows
,它是一个元组列表.(如果您使用cursor = connection.cursor(dictionary=True)
来保留列名,那么它将是一个字典列表,每个字典都将列名作为字符串.不用说,在这种情况下,内存需求远远超过上面报告的90 GB.)
Ideas for solutions个
-
go 掉保存数据的两个中间阶段(
cursor.execute()
之后和cursor.fetchall()
之后),直接保存到Pandas DataFrame中.Python-MariaDB似乎不提供这一功能.在我看来,这并不是一件容易实现的事情. -
以块为单位导入数据.在这种情况下,我担心(1)可能的数据类型不一致,(2)最终将数据帧连接成一个数据帧时的内存需求,(3)在更复杂的MariaDB查询(特别是将查询结果拆分成块的特殊实现)的情况下可能引入的不一致,以及(4)计算时间,如果在最坏的可能情况下,查询可能不得不多次运行,因为有Ara块.
-
将查询结果保存到硬盘中,并将其读回Pandas 中.这基本上意味着要解决python-MariaDB连接器已经实现的所有问题,并用手动修改来代替它.此外,这可能会导致硬盘的写入权限出现问题.(我的MariaDB配置中有
ProtectHome=true
,这将防止MariaDB写入/home
等中的任何位置.出于安全原因,这是大多数Linux的标准.可以通过绑定挂载或允许用户处理/home
以外的目录来解决这个问题,但我认为这两个都不是特别好的主意.) -
在@yothegiitou的 comments 之后,您可以通过将MariaDB连接直接插入
pd.read_sql
并使用chunksize
参数来稍微提高内存使用率.内存使用量仍然很大(生成器对象的内存使用量为30 GB,在创建DataFrame的过程中内存使用量增加了10 GB),但只有我最初方法的一半左右.另外一个警告是,pd.read_sql
会抛出UserWarning: pandas only supports SQLAlchemy connectable...
个警告.这可能意味着,也可能不意味着在某些情况下存在任何真正的问题--对于我审理的那个 case ,它看起来很好.代码将为:chunks = pd.read_sql(statement, connection, chunksize=50000000) dfx = pd.concat(list(chunks))
个
Additional Information个
MariaDB版本是11.2.2,Python版本是3.11.6,Python-MariaDB连接器版本是1.1.8,Linux是带内核6.7.4的Arch Linux.