我有一个Laravel应用程序,该应用程序具有一个常见的操作,用户可以从名为3列的Sign的数据库表中获取约300,000行。表列的描述如下:id(int-10), sign(varchar-16), status (int-10)
该表有大约3亿个条目。用户输入某些条目时,这些行的状态列将更改为用户的ID。请注意,该用户每次总是输入大约300,000个条目。
我将innodb_buffer_pool_size和innodb_log_file_size分别增加到2GB和1GB。系统具有3.75GB的RAM。
这是代码-
$collection = Sign::select('sign')
->where('status', 0)
->where(DB::raw('CHAR_LENGTH(sign)'), '=', 7)
->take(300000);
//write the the signs in $collection in a file here
$collection->update(['status' => $user->id]);
在我的情况下,表数据可以在不到1秒的时间内轻松获取。该更新语句以前大约需要100-200s,但最近我已将操作系统从Ubuntu 14升级到16,此更新语句之后大约需要500-600s。 有什么办法可以使这个过程更快?我应该增加RAM吗?
答案 0 :(得分:0)
您不必像选择和更新那样直接选择更新查询,就像laravel雄辩一样,
$collection = Sign::where('status', 0)
->whereRaw('CHAR_LENGTH(sign) = 7')
->update(['status' => $user->id]);
在任何数据库选择操作中,总是很快,因为它只涉及表的扫描,您可能会获得更多详细信息,例如EXPLAIN SELECT * FROM sign;
答案 1 :(得分:0)
要克服CHAR_LENGTH(sign)
不使用索引的缓慢性,generated columns提供了解决方案。
在这里,我们创建一个sign_length,计算得出的符号长度为一列:
ALTER TABLE sign ADD sign_length INT UNSIGNED AS (CHAR_LENGTH(sign))
, ADD INDEX status_sign_length(status,sign_length)
然后使用:
$collection = Sign::where('status', 0)
->where('sign_length', 7)
->update(['status' => $user->id])
->take(300000);
注意:larvel不是我的强项,欢迎更正。
答案 2 :(得分:0)
2G太高了。系统在交换吗?如果是这样,请降低buffer_pool或增加RAM。交换对于MySQL来说是可怕的。
由于新的操作系统可能会占用更多内存,因此上述声明可能可以解释这种情况。
请提供由$collection->update(['status' => $user->id]);
生成的SQL; 不是显而易见的。据我所知,$collection
将保留所有行的300K id的列表,并为IN
创建一个UPDATE
子句。
UPDATEing
行比SELECTing
行昂贵得多。前者必须保留行的副本,以防万一发生崩溃而需要ROLLBACK
。
什么版本的MySQL? UPDATE
的优化程序中最近进行了更改。
如果SELECT
和UPDATE
之间有东西,您可能需要SELECT ... FOR UPDATE
,否则另一个连接可能会抢走相同的行,并弄乱数据! / p>