优化代码以使用python(Django)填充数据库

时间:2015-10-03 22:00:47

标签: python django performance

我正在尝试使用Django将SQLite数据库填充到包含600万条记录的文件中。然而,即使有50000条记录,我写的代码也给了我很多时间问题。

这是我试图填充数据库的代码:

import os

def populate():   
    with open("filename") as f:
        for line in f:
            col = line.strip().split("|")
            duns=col[1]
            name=col[8]
            job=col[12]        

            dun_add = add_c_duns(duns)   
            add_contact(c_duns = dun_add, fn=name, job=job)

def add_contact(c_duns, fn, job):
    c = Contact.objects.get_or_create(duns=c_duns, fullName=fn, title=job)
    return c

def add_c_duns(duns):
    cd = Contact_DUNS.objects.get_or_create(duns=duns)[0]
    return cd  

if __name__ == '__main__':
    print "Populating Contact db...."
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings")
    from web.models import Contact, Contact_DUNS
    populate()
    print "Done!!"

代码工作正常,因为我用虚拟记录测试了它,并且它给出了期望的结果。我想知道是否有一种方法可以降低此代码的执行时间。谢谢。

3 个答案:

答案 0 :(得分:1)

我没有足够的声誉来评论,但这是一个推测性答案。

基本上,通过django的ORM执行此操作的唯一方法是使用bulk_create。所以首先要考虑的是使用get_or_create。如果您的数据库中存在可能在输入文件中有重复项的现有记录,那么您唯一的选择就是自己编写SQL。如果您使用它来避免重复输入文件 ,那么请对其进行预处理以删除重复的行。

所以如果你可以在没有get_or_create的部分的情况下生活,那么你可以遵循这个策略:

  1. 浏览输入文件的每一行,并为每个条目实例化一个Contact_DUNS实例(实际上不创建行,只需编写Contact_DUNS(duns=duns))并将所有实例保存到数组中。将数组传递给bulk_create以实际创建行。

  2. 使用value_list生成DUNS-id对列表,并将其转换为dict,其中DUNS编号为键,行ID为值。

    < / LI>
  3. 重复步骤1,但使用Contact实例。在创建每个实例之前,使用DUNS编号从步骤2的字典中获取Contact_DUNS id。以下列方式实例化每个Contact:Contact(duns_id=c_duns_id, fullName=fn, title=job)。同样,在收集Contact实例后,只需将它们传递给bulk_create即可创建行。

  4. 这应该从根本上改善性能,因为您将不再为每个输入行执行查询。但正如我上面所说,只有在您可以确定数据库或输入文件中没有重复项时,这才有效。

    编辑以下是代码:

    import os
    
    def populate_duns():
        # Will only work if there are no DUNS duplicates
        # (both in the DB and within the file)
        duns_instances = []   
        with open("filename") as f:
            for line in f:
                duns = line.strip().split("|")[1]        
                duns_instances.append(Contact_DUNS(duns=duns))
    
        # Run a single INSERT query for all DUNS instances
        # (actually it will be run in batches run but it's still quite fast)
        Contact_DUNS.objects.bulk_create(duns_instances)
    
    def get_duns_dict():
        # This is basically a SELECT query for these two fields
        duns_id_pairs = Contact_DUNS.objects.values_list('duns', 'id')
        return dict(duns_id_pairs)
    
    def populate_contacts():
        # Repeat the same process for Contacts
        contact_instances = []
        duns_dict = get_duns_dict()
    
        with open("filename") as f:
            for line in f:  
                col = line.strip().split("|")
                duns = col[1]
                name = col[8]
                job = col[12]
    
                ci = Contact(duns_id=duns_dict[duns],
                             fullName=name,
                             title=job)
                contact_instances.append(ci)
    
        # Again, run only a single INSERT query
        Contact.objects.bulk_create(contact_instances)
    
    if __name__ == '__main__':
        print "Populating Contact db...."
        os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings")
        from web.models import Contact, Contact_DUNS
        populate_duns()
        populate_contacts()
        print "Done!!"
    

答案 1 :(得分:1)

CSV导入

首先,600万条记录对于sql lite 来说是相当多的,更糟糕​​的是sqlite isn't very good并直接导入CSV数据。

  

对于CSV文件应该是什么样子,并没有标准   SQLite shell甚至没有尝试处理所有复杂的问题   解释CSV文件。如果您需要导入复杂的CSV文件和   SQLite shell没有处理它,你可能想尝试不同的   前端,例如SQLite数据库浏览器。

另一方面,Mysql和Postgresql更能处理CSV数据,而mysql的LOAD DATA IN FILE和Postgresql COPY都是在非常短的时间内导入大量数据的轻松方法。

Sqlite的适用性。

您正在使用django =&gt;你正在构建一个web app =&gt;多个用户将访问该数据库。这是关于concurrency的手册。

  

SQLite支持无限数量的同时读者,但它   只会在任何时刻允许一位作家。对于很多   情况,这不是问题。作家排队。每个申请   它的数据库是否能够快速运行并继续运行,并且没有锁定   超过几十毫秒。但是有一些应用程序   这需要更多的并发性,而这些应用程序可能需要寻求   一个不同的解决方案。

即使您的读取操作也可能相当慢,因为sqlite数据库只是一个文件。因此,对于这些数据,将涉及大量的搜索操作。使用适当的客户端服务器数据库,数据不能分布在多个文件甚至磁盘上。

对你而言,好消息是,只需更改你的settings.py,你就可以通过Django从Sqlite切换到Mysql到Postgresql。无需其他更改。 (反之并非总是如此)

所以我建议你在进入太深之前考虑切换到mysql或postgresl。它将帮助您解决当前的问题,并有助于避免迟早会遇到的问题。

答案 2 :(得分:0)

通过Python导入6,000,000是非常多的。如果Python不是一个硬性要求,您可以编写直接import the CSV data的SQLite脚本并使用SQL语句创建表。更快的是使用awk预处理文件,并输出与您的两个表对应的两个CSV文件。

我曾经使用sqlite3 CSV导入程序导入20,000,000条记录,只花了几分钟时间。