How can I improve my INSERT statement performance?
在这里,Josh的回答让我对如何将256x64x250值数组插入MySQL数据库有了一个很好的开端。当我在我的数据上尝试他的insert语句时,速度非常慢(比如对于16MB文件,只需6分钟)。
1 2 3 4 5 6 | ny, nx, nz = np.shape(data) query ="""INSERT INTO `data` (frame, sensor_row, sensor_col, value) VALUES (%s, %s, %s, %s)""" for frames in range(nz): for rows in range(ny): for cols in range(nx): cursor.execute(query, (frames, rows, cols, data[rows,cols,frames])) |
我在读mysql for python,它解释说这不是正确的方法,因为执行400万个独立的插入非常低效。
现在,我的数据由很多零组成(实际上超过90%),所以我抛出了一个if语句,所以我只插入大于零的值,而使用executeMany():
1 2 3 4 5 6 7 8 | query ="""INSERT INTO `data` (frame, sensor_row, sensor_col, value) VALUES (%s, %s, %s, %s )""" values = [] for frames in range(nz): for rows in range(ny): for cols in range(nx): if data[rows,cols,frames] > 0.0: values.append((frames, rows, cols, data[rows,cols,frames])) cur.executemany(query, values) |
号
这奇迹般地将我的处理时间缩短到了20秒左右,其中14秒用于创建值列表(37k行),4秒用于实际插入数据库。
所以现在我想知道,我该如何进一步加快这个过程?因为我有一种感觉,我的循环非常低效,必须有更好的方法。如果我需要为每只狗插入30个测量值,这仍然需要10分钟,这对于这个数量的数据来说似乎太长了。
我的原始文件有两个版本:有头文件还是没有头文件。我很想尝试加载数据,但我不知道如何正确解析数据。
插入400万行(16MB数据)的最快方法是使用加载数据内嵌-http://dev.mysql.com/doc/refman/5.0/en/load-data.html
因此,如果可能,请生成一个csv文件,然后使用加载数据内嵌。
希望这有帮助:)
编辑
所以我取了你的一个原始数据文件rolloff.dat,写了一个快速而脏的程序,把它转换成下面的csv格式。
从以下位置下载frames.dat:http://rapidshare.com/files/454896698/frames.dat
帧.dat
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | patient_name, sample_date dd/mm/yyyy, frame_time (ms), frame 0..248, row 0..255, col 0..62, value "Krulle (opnieuw) Krupp",04/03/2010,0.00,0,5,39,0.4 "Krulle (opnieuw) Krupp",04/03/2010,0.00,0,5,40,0.4 ... "Krulle (opnieuw) Krupp",04/03/2010,0.00,0,10,42,0.4 "Krulle (opnieuw) Krupp",04/03/2010,0.00,0,10,43,0.4 "Krulle (opnieuw) Krupp",04/03/2010,7.94,1,4,40,0.4 "Krulle (opnieuw) Krupp",04/03/2010,7.94,1,5,39,0.4 "Krulle (opnieuw) Krupp",04/03/2010,7.94,1,5,40,0.7 "Krulle (opnieuw) Krupp",04/03/2010,7.94,1,6,44,0.7 "Krulle (opnieuw) Krupp",04/03/2010,7.94,1,6,45,0.4 ... "Krulle (opnieuw) Krupp",04/03/2010,1968.25,248,241,10,0.4 "Krulle (opnieuw) Krupp",04/03/2010,1968.25,248,241,11,0.4 "Krulle (opnieuw) Krupp",04/03/2010,1968.25,248,241,12,1.1 "Krulle (opnieuw) Krupp",04/03/2010,1968.25,248,241,13,1.4 "Krulle (opnieuw) Krupp",04/03/2010,1968.25,248,241,14,0.4 |
该文件只包含每行和每列都有值的帧的数据,因此不包括零。24799个数据行是从原始文件生成的。
接下来,我创建了一个临时加载(临时)表,将frames.dat文件加载到该表中。这是一个临时表,允许您在加载到适当的生产/报告表之前操作/转换数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 | drop table if exists sample_temp; create table sample_temp ( patient_name varchar(255) not null, sample_date date, frame_time decimal(6,2) not null default 0, frame_id tinyint unsigned not null, row_id tinyint unsigned not null, col_id tinyint unsigned not null, value decimal(4,1) not null default 0, primary key (frame_id, row_id, col_id) ) engine=innodb; |
。
剩下的只是加载数据(注意:我使用的是Windows,因此您必须编辑此脚本使其与Linux兼容-检查路径名并将'
'更改为'')
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | truncate table sample_temp; start transaction; load data infile 'c:\\import\\frames.dat' into table sample_temp fields terminated by ',' optionally enclosed by '"' lines terminated by ' ' ignore 1 lines ( patient_name, @sample_date, frame_time, frame_id, row_id, col_id, value ) set sample_date = str_to_date(@sample_date,'%d/%m/%Y'); commit; Query OK, 24799 rows affected (1.87 sec) Records: 24799 Deleted: 0 Skipped: 0 Warnings: 0 |
。
24K行在1.87秒内加载。
希望这有帮助:)
如果数据是numpy数组,则可以尝试以下操作:
1 2 3 4 5 6 7 | query ="""INSERT INTO `data` (frame, sensor_row, sensor_col, value) VALUES (%s, %s, %s, %s )""" values = [] rows, cols, frames = numpy.nonzero(data) for row, col, frame in zip(rows, cols, frames): values.append((frame, row, col, data[row,col,frame])) cur.executemany(query, values) |
或
1 2 3 4 | query ="""INSERT INTO `data` (frame, sensor_row, sensor_col, value) VALUES (%s, %s, %s, %s )""" rows, cols, frames = numpy.nonzero(data) values = [(row, col, frame, val) for row, col, frame, val in zip(rows, cols, frames, data[rows,cols,frames])] cur.executemany(query, values) |
。
希望有帮助
我不使用python或mysql,但是批量插入性能通常可以通过事务来提高。
在每个语句中插入多行是一种优化方法。但是,为什么需要3个循环呢?也许某种数据转换会有用。
另一个选项是在插入期间禁用索引,前提是您确定不会有任何重复的数据(假设您在表中实际有索引)。必须为每个语句更新索引,并检查索引以防止重复。
在开始插入之前调用
从手册中:
更改表…禁用键告诉MySQL停止更新非唯一索引。更改表…然后应使用启用键重新创建缺少的索引。MySQL使用一种比逐个插入密钥快得多的特殊算法来实现这一点,因此在执行大容量插入操作之前禁用密钥会大大加快速度。正在使用alter table…除前面提到的权限外,禁用密钥还需要索引权限。
如果我理解正确,executeMany()将为要插入的每一行执行insert-into查询。可以通过创建一个包含所有值的插入查询来改进这一点,该查询应如下所示:
1 2 3 4 5 6 7 | INSERT INTO data (frame, sensor_row, sensor_col, value) VALUES (1, 1, 1, 1), (2, 2, 2, 2), (3, 3, 3, 3), ... |
python代码应该在括号中生成行值,并从中创建一个查询字符串,以最终执行一次查询。
您可以使用列表理解而不是
1 2 3 | values = [(frames, rows, cols, data[rows,cols,frames]) \ for frames in range(nz) for rows in range(ny) \ for cols in range(nx) if data[rows,cols,frames] > 0.0] |
号
我估计这会让你稍微加快速度,比如10-20%。