4

約 350,000 行を含むテーブルがあり、最近 MyISAM ストレージ エンジンから InnoDB に変更しました。

クエリを実行します

UPDATE `users` SET `online` = 0

サーバーが起動するたびに、MyISAM を使用しても問題はまったくありませんでした。通常、クエリは数百行のみに影響します。クエリの実行時間は遅く、平均で約 1.5 秒でしたが、我慢できる程度でした。

InnoDB に変更したので、クエリが完了するまでに数十秒かかることがあります。

これは mysql-slow.log の一部です

# Query_time: 29.431546  Lock_time: 0.000091 Rows_sent: 0  Rows_examined: 348617
SET timestamp=1372505574;
UPDATE users SET online = 0;

この特定のクエリは、200 行のオンライン値を変更し、他の行は既に 0 でした。

クエリを次のように変更することで問題を解決できました

UPDATE `users` SET `online` = 0 WHERE `online` != 0

このクエリには約 0.1 秒かかりました

では、私の質問です。MyISAM から InnoDB に変更すると時間が大幅に増加するのはなぜですか?

また、WHERE 部分がないとクエリが遅くなるのはなぜでしょうか? 私が理解している限り、MySQL のクエリ オプティマイザは非常に強力ですが、これは逆のことを示唆しています。この非常に遅いクエリ実行時間の原因は何ですか?

MySQL サーバーのバージョンは 5.5.31-0 です。

4

3 に答える 3

3

InnoDB では、updateステートメントはスキャンするすべての行をロックします。つまり、200 行を更新するには、350 000 行レベルのロックを作成する必要がありますが、同時にロールバック ロックを維持し、既に変更された値を読み取っているすべてのトランザクションに以前の値を提供します (トランザクションが変更されていないため)。コミットされ、変更は最終的ではありません)

一方、MyISAM はテーブル全体をロックします。

したがって、すべての行を更新する必要がある場合は、テーブル全体をロックすると、パフォーマンスが大幅に向上します (行レベルのロックは必要ありません)。

しかし、さらに良いことに、あなたが行ったように WHERE 句を指定すると、InnoDB は一致する行に対してのみロックを取得します (インデックス ツリー内のいくつかのギャップ ロックも同様ですが、これは問題の範囲外です)。

于 2013-06-30T15:24:52.470 に答える
2

InnoDB はトランザクション セマンティクスを実装します。つまり、テーブルの他の読者に、online列の値がすべてまったく同じ瞬間にゼロに変化するという錯覚を投影するために、広範な作業を行います。また、クライアントまたはサーバーがクラッシュした場合に、値を現状に自動的にロールバックすることもできます。 MyISAM はこれを気にしません。数十万行のテーブルの場合、これは大変な作業です。

値がすでにゼロであることは気にしません。とにかくそれらを変更します。

この句を使用すると、WHERE変更する行が大幅に少なくなるため、トランザクション ロジックで行う作業が大幅に少なくなります。

このトランザクション ロジックは重要な機能です。完璧ではないエッジケースの方法で使用していました。

于 2013-06-29T11:51:25.667 に答える
1

クエリを from に書き直したほうがよいでしょう

UPDATE `users` SET `online` = 0 WHERE `online` != 0

UPDATE `users` SET `online` = 0 WHERE `online` = 1

= vs != を使用すると、インデックスをより適切に検索できます

http://sqlfiddle.com/#!2/31088/4を確認 し、そこにある「実行計画の表示」をオンにして、ここで何が起こっているかを確認します

!= triggers a range 
= triggers a ref which should perform better

補足として、通常、選択性の悪い列にインデックスを付けるべきではありません

于 2013-08-16T15:23:05.440 に答える