概述
我有一个关系 MySQL InnoDB 数据库。我目前有代码可以使用 Python 2.6.6 中的 peewee ORM 将数据添加到我的数据库中。问题是,由于我所有的外键,我最终每个数据点做了五个SELECT
和(最坏的情况)五个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])
问题
在该基本示例中,输入文件中的每一行使用两条SELECT
语句,最多使用两条INSERT
语句。对于非常大的文件,将所有这些数据放入数据库需要大量时间。
我想要某种方法来读取输入文件的块,然后执行一条INSERT
语句将所有数据大量转储到数据库中。因为我不会知道所有的 FK 关系,所以我仍然需要做所有这些SELECT
陈述;但是,因为我所有的 FK 都是自动递增的,所以我可以手动跟踪它们。
理想情况下,我想用 peewee 来做这件事;但是,我也对纯 SQL 解决方案持开放态度。我在想我可以建立我试图插入内存的所有数据,然后一次全部转储。使用这种方法,除了数据库之外,我还需要检查内存内容是否存在任何 FK 关系。
基本上,我正在寻找“最快”的方式来获取大量数据并将其转储到数据库中。非常感谢任何输入,我现在几乎被卡住了。
解决方案
我能够为客户提出一个比上述实现快 2,000 倍以上的解决方案。我最终构建了一个包含所有数据的 CSV 文件,将其复制到服务器,然后使用LOAD DATA INFILE
将这些数据存储到临时表中。虽然这在技术上没有数据库中的数据,但它允许用户快速转储数据,而无需等待它完成。然后我有一个存储过程来处理实际的数据插入。这个解决方案有点复杂,但效果很好。