4

概要
リレーショナル MySQL InnoDB データベースがあります。現在、Python 2.6.6 の peewee ORM を使用して、データベースにデータを追加するコードを用意しています。問題は、すべての外部キーが原因で、データ ポイントごとSELECTに 5 つ (最悪の場合) 5 つのステートメントを実行することになります。INSERTご想像のとおり、5,000,000 ほどのデータ ポイント (40 MiB 程度のデータ) を追加しようとすると、すべてのトランザクションが原因で非常に長い時間がかかります。


私がやろうとしていることを示すためのダミーの例を次に示します。

import csv
import peewee as pw
db = pw.MySQLDatabase(example, **{'passwd': 'example', 'host': 'example', 'port': 3306, 'user': 'example'})

class BaseModel(pw.Model):
    class Meta:
        database = db
class Users(BaseModel):
    User      = pw.PrimaryKeyField(db_column = 'User_ID')
    User_Name = pw.CharField(db_column = 'User_Name', max_length = 50)
    class Meta:
        db_table = 'users'
class Pets(BaseModel):
    Pets     = pw.PrimaryKeyField(db_column = 'Pet_ID')
    Pet_Name = pw.CharField(db_column = 'Pet_Name', max_length = 50)
    User     = pw.ForeignKeyField(db_column = 'User_ID', rel_model = Users)
    class Meta:
        db_table = 'pets'

def add_measurement(user_name, pet_name):
    # Add user
    try:
        self.dbo_users = Users.get(Users.User_Name == user_name)
    except Users.DoesNotExist:
        self.dbo_users = Users.create(User_Name = user_name)
    # Add pet and link to user
    try:
        self.dbo_pets = Pets.get(Pets.User == self.dbo_users.User,
                                 Pets.Pet_Name == pet_name)
    except Pets.DoesNotExist:
        self.dbo_pets = Pets.create(User = self.dbo_users.User,
                                    Pet_Name = pet_name)

db.connect()
example_data_file = r'C:\users_pets.csv'

# Add all data in CSV file to database, assume first row == header, all other
# rows are data, with the format of: user_name, pet_name
with open(example_data_file, 'rb') as f:
    reader = csv.reader(f)
    reader.next() # Skip header
    for row in reader:
        add_measurement(row[0], row[1])

問題
この基本的な例では、入力ファイルの各行で 2 つのSELECTステートメントと最大2 つのステートメントが使用されていますINSERT。非常に大きなファイルの場合、このすべてのデータをデータベースに入れるにはかなりの時間がかかります。

入力ファイルのチャンクを読み込み、1 つのINSERTステートメントを実行してすべてのデータをデータベースに大量にダンプする方法が必要です。私はすべての FK リレーションシップを知っているわけではないので、これらのSELECTステートメントをすべて実行する必要があります。ただし、FK はすべて自動インクリメントされるだけなので、手動で追跡できます。

これを peewee で行うのが理想的です。ただし、私は純粋な SQL ソリューションにもオープンです。メモリに挿入しようとしているすべてのデータを構築してから、一度にすべてダンプできると考えていました。このアプローチを使用すると、FK 関係についてデータベースに加えてメモリの内容もチェックする必要があります。

基本的に、大量のデータを取得してデータベースにダンプする「最速」の方法を探しています。どんな意見でも大歓迎です。

ソリューション
クライアントにとって、上記の実装よりも 2,000 倍以上高速に見えるソリューションを思いつくことができました。最終的に、すべてのデータを含む CSV ファイルを作成し、それをサーバーにコピーしてから、LOAD DATA INFILEそのデータを一時テーブルに格納するために使用しました。これは技術的にはデータベースにデータを持っていませんが、正しくは、ユーザーはデータが完了するのを待たずにすばやくデータをダンプできます。次に、実際のデータ挿入を処理するストアド プロシージャを作成します。このソリューションは少し複雑ですが、うまく機能します。

4

0 に答える 0