如何使包含300,000行的更新语句更快?

时间:2019-04-03 04:32:49

标签: php mysql sql ubuntu optimization

我有一个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吗?

3 个答案:

答案 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)

对于只有3.75GB的RAM,buffer_pool的

2G太高了。系统在交换吗?如果是这样,请降低buffer_pool或增加RAM。交换对于MySQL来说是可怕的。

由于新的操作系统可能会占用更多内存,因此上述声明可能可以解释这种情况。

请提供由$collection->update(['status' => $user->id]);生成的SQL; 是显而易见的。据我所知,$collection将保留所有行的300K id的列表,并为IN创建一个UPDATE子句。

UPDATEing行比SELECTing行昂贵得多。前者必须保留行的副本,以防万一发生崩溃而需要ROLLBACK

什么版本的MySQL? UPDATE的优化程序中最近进行了更改。

如果SELECTUPDATE之间有东西,您可能需要SELECT ... FOR UPDATE,否则另一个连接可能会抢走相同的行,并弄乱数据! / p>