我正在使用Laravel 5.7从API服务器获取大量数据(约50万行),并将其插入表的频率很高(称为表A)(至少每6个小时一次,24/7)-但是,下次插入时仅插入更改就足够了(但是至少有60-70%的项目会更改)。因此,该表将很快具有数千万行。
我想到了制作一个辅助表(称为表B)以将所有新数据存储到其中的想法。在将所有内容插入表A之前,我想将其与表B中的先前数据(使用Laravel,PHP)进行比较-因此,我将仅插入需要更新的记录。同样,通常它将占记录的60-70%。
我的第一个问题是,在这种情况下(显然,我想使其尽快完成),是否上述方法是首选方法?更新表中的记录将花费更多时间,并且会使表忙/锁定它。是否有更好的方法可以实现相同目的(意味着更新数据库中的记录)。
我面临的第二个问题是插入速度慢。现在,我正在使用本地环境(16GB RAM,I7-6920HQ CPU),MySQL插入行的速度非常慢(一次大约30-40条记录)。一行的大小约为50个字节。
我知道通过修改InnoDB的设置可以使其更快。但是,我也想以为我可以在Laravel方面做些事情来提高性能。
现在,我的Laravel代码如下所示(一次只能插入1条记录):
foreach ($response as $key => $value)
{
DB::table('table_a')
->insert(
[
'test1' => $value['test1'],
'test2' => $value['test2'],
'test3' => $value['test3'],
'test4' => $value['test4'],
'test5' => $value['test5'],
]);
}
$response
是一种数组。
所以我的第二个问题:有什么方法可以将记录的插入时间增加到大约50k /秒-在Laravel应用程序层(通过批量插入)和MySQL InnoDB级别(更改配置)。
当前的InnoDB设置:
innodb_buffer_pool_size = 256M
innodb_log_file_size = 256M
innodb_thread_concurrency = 16
innodb_flush_log_at_trx_commit = 2
innodb_flush_method = normal
innodb_use_native_aio = true
MySQL版本是5.7.21。
如果我忘记告诉/添加任何内容,请在评论中让我知道,我会尽快进行。
修改1: 我打算使用的服务器上将装有SSD-如果有任何区别。我认为MySQL插入仍将算作I / O。
答案 0 :(得分:0)
请勿在{{1}}内部调用insert()
,因为当您拥有foreach()
时它将对数据库执行n number of queries
。
首先创建一个与数据库列名匹配的数据对象数组。然后将创建的数组传递给n number of data
函数。
这将仅对数据库执行insert()
查询,无论您拥有多少数据。
这快得多,太快了。
one
答案 1 :(得分:0)
感谢@Namoshek,我也遇到了同样的问题。解决方法是这样的。
$users= array_chunk($data, 500, true);
foreach ($users as $key => $user) {
Model::insert($user);
}
根据数据,您还可以使用array_push()然后插入。
答案 2 :(得分:0)
1,"Nicole",71,29,"Tusk"
2,"Bob",49,66,"Schiffer"
3,"Susan",61,17,"Tusk"
4,"Bob",24,59,"Trump"
5,"Nicole",25,46,"Goldberg"
6,"Bob",16,71,"Goldberg"
7,"Mark",43,43,"Schiffer"
并在插入结束时手动提交根据MySQL 8.0文档。 (8.5.5 Bulk Data Loading for InnoDB Tables)
您可以通过关闭自动提交来提高INSERT速度:
- 将数据导入InnoDB时,请关闭自动提交模式,因为它会为每个插入操作将日志刷新到磁盘。要在导入操作期间禁用自动提交,请在其周围加上SET自动提交和COMMIT语句:
autocommit
在Laravel中执行此操作的其他方法是使用Database Transactions:
SET autocommit=0;
... SQL import statements ...
COMMIT;
DB::beginTransaction()
// Your inserts here
DB::commit()
与多个INSERT
一起使用根据MySQL 8.0文档(8.2.5.1 Optimizing INSERT Statements),您还可以通过在单个插入语句上使用多个VALUES
来优化INSERT速度。
要使用Laravel做到这一点,您只需将值数组传递给VALUES
方法即可:
insert()
根据文档,它可以快很多倍。
我在这篇文章中发布的两个MySQL文档链接都包含大量有关提高INSERT速度的提示。
如果您的数据源是(或可以是)CSV文件,则可以使用mysqlimport
导入数据来更快地运行它。
使用PHP和Laravel从CSV文件导入数据是一项开销,除非您需要在插入之前进行一些数据处理。
答案 3 :(得分:-1)
您需要进行多行插入,但还需要对插入进行分块以不超过数据库限制
您可以通过对数组进行分块来实现
foreach (array_chunk($response, 1000) as $responseChunk)
{
$insertableArray = [];
foreach($responseChunk as $value) {
$insertableArray[] = [
'test1' => $value['test1'],
'test2' => $value['test2'],
'test3' => $value['test3'],
'test4' => $value['test4'],
'test5' => $value['test5'],
];
}
DB::table('table_a')->insert($insertableArray);
}
您可以增加块1000
的大小,直到达到数据库配置限制为止。确保保留一些安全裕度(数据库限制的0.6倍)。
使用laravel不能比这更快。