7

Django アダプターを使用して単純な CSV をアップロードしています。100 または 200 の連絡先をインポートする場合は、完全に機能するようです。しかし、5000 件の連絡先を含む 165kb のファイルをアップロードしようとすると、完了しません。私はそれを試行し続けさせました.1時間後に戻ったとき、それはまだ試行中でした.

これの何が問題なのですか?Django アダプタを使用すると、165kb ファイルのインポートに 1 時間以上かかることはありません。コードに何か問題がありますか?

 def process(self):
        self.date_start_processing = timezone.now()
        try:


            # Try and import CSV
            ContactCSVModel.import_data(data=self.filepath, extra_fields=[
                {'value': self.group_id, 'position': 5},
                {'value': self.uploaded_by.id, 'position': 6}])

            self._mark_processed(self.num_records)
        except Exception as e:
            self._mark_failed(unicode(e))

Csvモデル

class ContactCSVModel(CsvModel):

    first_name = CharField()
    last_name = CharField()
    company = CharField()
    mobile = CharField()
    group = DjangoModelField(Group)
    contact_owner = DjangoModelField(User)


    class Meta:
        delimiter = "^"
        dbModel = Contact
        update = {'keys': ["mobile", "group"]}
4

4 に答える 4

7

大きなタスクを小さなピースに分割します。

ステップ 1 - CSV ファイルを読み取るだけ

ContactCSVModel.import_from_filename() と ContactCSVModel.import_from_file() の両方が csv 行を返します。データベースとの対話をスキップするには、django モデルとの対話を無効にします。これにより、タスクが大幅に高速化され、インポートされたデータが印刷されます。これは間違いなくうまくいくはずです!

CSVモデル

class ContactCSVModel(CsvModel):

    first_name = CharField()
    last_name = CharField()
    company = CharField()
    mobile = CharField()
    group = DjangoModelField(Group)
    contact_owner = DjangoModelField(User)


    class Meta:
        delimiter = "^"

あなたのコード

 def process(self):
        self.date_start_processing = timezone.now()
        try:


            # Try and import CSV
            lines = ContactCSVModel.import_data(data=self.filepath, extra_fields=[
                {'value': self.group_id, 'position': 5},
                {'value': self.uploaded_by.id, 'position': 6}])
            print lines # or use logging

            self._mark_processed(self.num_records)
        except Exception as e:
            self._mark_failed(unicode(e))

ステップ 2 - django モデルの相互作用を有効にしますが、DB 内の既存のアイテムをチェックするために無効にします。

この機能を有効にすると、CSV のすべての行について DB にクエリを実行し、自然キーの仕様に従って既存のアイテムをチェックするため、無効にします (ソース コードを読みました)。おそらく、CSV のすべての行が固有の連絡先であることをご存知でしょう。

これは、インポート全体で DB クエリが遅いという問題がある場合に役立ちますが、インポートが大量のメモリを消費する場合には実際には役に立ちません。

class ContactCSVModel(CsvModel):

    first_name = CharField()
    last_name = CharField()
    company = CharField()
    mobile = CharField()
    group = DjangoModelField(Group)
    contact_owner = DjangoModelField(User)


    class Meta:
        delimiter = "^"
        dbModel = Contact

ステップ 3 - CSV の同じサイズのチャンクをインポートする

CSVModel を使用し、Contact モデルとの対話を有効にしますが、ContactCSVModel.import_data() に小さなイテラブルを提供します。私は 500 に設定しました。必要に応じて変更してください。以下のコード サンプル (リンク) は、アイデアを得るためのものです。これを既存のコードに入れるには、少し変更する必要があります。これは、メモリ消費が問題である場合に役立ちます。

import csv
reader = csv.reader(open(self.filepath, 'rb'))

def gen_chunks(reader, chunksize=100):
    """ 
    Chunk generator. Take a CSV `reader` and yield
    `chunksize` sized slices. 
    """
    chunk = []
    for i, line in enumerate(reader):
        if (i % chunksize == 0 and i > 0):
            yield chunk
            del chunk[:]
        chunk.append(line)
    yield chunk

for chunk in gen_chunks(reader, chunksize=500):
    ContactCSVModel.import_data(data=chunk, extra_fields=[
                {'value': self.group_id, 'position': 5},
                {'value': self.uploaded_by.id, 'position': 6}])

ステップ 4 - 大量のメモリ消費と遅い操作をターゲットにする

django-adaptors はインポート中にすべての Contact モデル インスタンスをメモリに保持し、一括挿入操作ではなく複数の単一コミットが原因で操作が遅くなるため、大きなファイルにはあま​​り適していません。

あなたは多少ジャンゴアダプターに縛られています。この django パッケージに依存している場合、一括挿入に切り替えることはできません。タスクマネージャーを使用して、Windows で top または htop を使用して Linux でのメモリ消費を確認します。プロセスが大量に消費され、OS がスワッピングを開始する場合は、より効率的なメモリ消費と一括挿入をオプションとして備えた別の django アドオンに切り替えてください - csv インポート用にそれらがたくさんあります。

もう 1 つのヒントは、読み取りには csv モジュールを使用し、データベースとの対話には django モデルの知識を使用することです。これは実際には難しいことではありません。全体像の個別のタスクで試してみて、それらが機能する場合はそれらをまとめてください。頑張ってください。

于 2013-04-18T22:39:51.987 に答える
2

まず、csv にデータ エラーがないことを確認します。たとえば、列に誤ったエスケープ文字または不適切なデータ型が含まれている場合、おそらく DB は一部の列で null 値を受け入れることができません。

ハングしている間、DB が読み込まれているかどうかを手動で確認できますか? コマンドライン MySQL プロンプトまたはワークベンチのいずれかを使用しますか? そうである場合、自動コミットがオンになり、ハングしている行を確認できるはずです。次に、CSV でそのレコードを確認します。

ただし、自動コミットがオフになっている場合 (Django がデフォルトで何をするのか、DB がどのように構成されているのかわかりません)、トランザクション バッファがオーバーフローしている可能性があります。これを回避するには、トランザクションを段階的に手動でフラッシュ/コミットする方法が必要です。

于 2013-04-18T12:40:38.940 に答える
2

import_data最初に試すことは、反復可能オブジェクトを関数に渡すことです。

ContactCSVModel.import_data(open(self.filepath), extra_fields=[
                {'value': self.group_id, 'position': 5},
                {'value': self.uploaded_by.id, 'position': 6}])

2番目に試すことは、次を使用することimport_from_filenameです:

ContactCSVModel.import_from_filename(self.filepath, extra_fields=[
                {'value': self.group_id, 'position': 5},
                {'value': self.uploaded_by.id, 'position': 6}])

これで解決しない場合は、どこにぶら下がっているかを調べてみてください。csvファイルのサイズを小さくして手動で行うか、にモックを置くか、CsvImporter.process_linecsv.readerをモックして、行を処理する代わりにそれらを印刷して、どこで停止するかを確認できます。嘲笑の助けが必要な場合はお知らせください。

また、この問題は関連している可能性があります。

于 2013-04-18T10:41:53.893 に答える
2

I don't know much about django-adaptors, but some things that have helped me when database imports are slow are to use the @transaction.commit_manually() decorator on a method, or using the Model.objects.bulk_create() method. For you, it looks like that commit_manually method might help, but the bulk_create method wouldn't, because you aren't actually controlling the creation process.

于 2013-04-18T17:55:49.870 に答える