Python Django加载数据-内存不足

Python Django加载数据-内存不足,python,django,database-restore,Python,Django,Database Restore,我使用创建了一个500mb json文件来转储我的db 现在我正试图使用恢复数据库,但Django似乎试图在应用它之前将整个文件加载到内存中,结果出现内存不足错误,进程被终止 没有办法绕过这个问题吗?loaddata通常用于固定装置,即少量数据库对象用于启动系统和进行测试,而不是用于大块数据。如果您达到了内存限制,那么您可能没有将其用于正确的目的 如果您仍然拥有原始数据库,那么应该使用更适合此目的的数据库,如PostgreSQL的pg_dump或MySQL的mysqldump,正如Joe指出的,

我使用创建了一个500mb json文件来转储我的db

现在我正试图使用恢复数据库,但Django似乎试图在应用它之前将整个文件加载到内存中,结果出现内存不足错误,进程被终止


没有办法绕过这个问题吗?

loaddata
通常用于固定装置,即少量数据库对象用于启动系统和进行测试,而不是用于大块数据。如果您达到了内存限制,那么您可能没有将其用于正确的目的


如果您仍然拥有原始数据库,那么应该使用更适合此目的的数据库,如PostgreSQL的
pg_dump
或MySQL的
mysqldump

,正如Joe指出的,PostgreSQL的pg_dump或MySQL的mysqldump更适合您的情况

如果您丢失了原始数据库,有两种方法可以尝试恢复数据:

第一:找到另一台有更多内存并且可以访问数据库的机器。在该计算机上构建项目,并在该计算机上运行loaddata命令

我知道这听起来很傻。但是,如果您可以在笔记本电脑上运行django,并且可以远程连接到db,那么这是最快的方法

第二:破解Django源代码

检查django.core.erializers.json.py中的代码:

def Deserializer(stream_or_string, **options):
    """
    Deserialize a stream or string of JSON data.
    """
    if not isinstance(stream_or_string, (bytes, six.string_types)):
        stream_or_string = stream_or_string.read()
    if isinstance(stream_or_string, bytes):
        stream_or_string = stream_or_string.decode('utf-8')
    try:
        objects = json.loads(stream_or_string)
        for obj in PythonDeserializer(objects, **options):
            yield obj
    except GeneratorExit:
        raise
    except Exception as e:
        # Map to deserializer error
        six.reraise(DeserializationError, DeserializationError(e), sys.exc_info()[2])
下面的代码就是问题所在。stdlib中的
json
模块只接受字符串,不能延迟处理流。因此django将json文件的所有内容加载到内存中

stream_or_string = stream_or_string.read()
objects = json.loads(stream_or_string)

您可以使用。py yajl使用yajl创建内置json.load和json.dump的替代方案

我在将数据从Microsoft SQL Server迁移到PostgreSQL时遇到了这个问题,因此
sqldump
pg_dump
不是我的选择。我将json装置分割成适合内存的块(对于宽表和64GB ram,大约有1M行)


然后,您可以使用
manage.py loaddata加载它们。这是一个FOSS python包,包含类似的黑客工具,用于处理django中的大型迁移和数据挖掘任务。

我想补充一点,我在ijson的类似用例中非常成功:

为了从django dumpdata获得json文件中对象的迭代器,我修改了json反序列化程序,如下所示(省略导入):


按原样使用py-yajl的问题是,您仍然可以在一个大数组中获取所有对象,这会占用大量内存。此循环仅使用与单个序列化Django对象相同的内存。此外,ijson仍然可以使用yajl作为后端。

您可以使用XML格式进行序列化/反序列化。它是通过文件流在内部实现的,与JSON相比不需要太多内存。不幸的是,Django JSON反序列化不使用流

所以,试试看:

./manage.py dumpdata file.xml
然后

./manage.py loaddata file.xml

由于对某些字段应用了限制,因此在
pg_dump
/
pg_restore
方面也存在问题

在我的例子中,我通过zappa在aws lambda上运行django,并希望迁移到aurora serverless(postgres)。我从bastion t2.micro实例生成了dumpdata文件,但是当我尝试加载数据时,该微实例没有足够的内存,并且该进程被os on loaddata尝试杀死

因此,我需要对数据进行分块,以便可以在实例的内存中对其进行处理,并且由于字段限制,需要按特定顺序加载记录。(如果没有,我会得到链接记录不存在的错误)

下面是我的脚本,用于对dumpdata进行分块,并按顺序将其分块,以便在没有约束相关错误的情况下成功加载:

注意:这是在具有足够内存的机器上准备的,以便在内存中保存所有数据。因此,该脚本的结果在创建后被传输到
t2.micro
实例,其中loaddata按结果顺序运行

导入json
从输入导入列表开始
从集合导入计数器,defaultdict
从pathlib导入路径
工作目录=Path.home()/“migratedb”
dumpdata\u filepath=working\u directory/'db\u backup\u 20190830.json'
def chunk_dumpdata_json(dumpdata_filepath:Path,app_model_order:List):
文件\创建\顺序=[]
最大块记录数=25000
使用dumpdata\u filepath。在以下位置以数据\u的形式打开('r'):
all_data=json.load(data_in.read())
打印(f'记录计数:{len(所有数据)}')
model_records=defaultdict(列表)
对于总计数,在枚举中记录(所有数据):
app_model_name=记录['model']
在app_model_order中断言app_model_name,f'{app_model_name}不在:{app_model_order}
模型记录[应用程序模型名称].追加(记录)
#按模型顺序分组
总记录计数=0
chunks=defaultdict(列表)
对于应用程序模型订单中的应用程序模型:
对于模型\记录中的记录[应用\模型]:
记录块=总记录块计数-(总记录块计数%max\u块记录)
块[record_chunk]。追加(记录)
总记录计数+=1
对于chunk,以chunk.items()为单位记录:
chunk_filename=f'dumpdata_v1_chunk{chunk}.json'
chunk\u filepath=工作目录/chunk\u文件名
打印(chunk\u filepath.name)
文件\u创建\u顺序.append(chunk\u filepath.name)
将chunk_filepath.open('w',encoding='utf8')作为输出:
out.write(json.dumps(记录))
返回文件\u创建\u顺序
应用程序\型号\订单=(
'应用程序模型1',
'app.model2',
)                
结果\文件\创建\顺序=区块\转储数据\ json(转储数据\文件路径、应用\模型\顺序)
然后我获取下面脚本的输出,将其保存到
loaddata.sh
并运行它:

for item in result_file_creation_order:
    if item:
        print(f'echo "Loading {item} ..."')
        print(f'python manage.py loaddata ~/migrationdata/{item}')
你的dj是什么
./manage.py dumpdata file.xml
./manage.py loaddata file.xml
for item in result_file_creation_order:
    if item:
        print(f'echo "Loading {item} ..."')
        print(f'python manage.py loaddata ~/migrationdata/{item}')