1つの更新ステートメントを使用するMySQLトランザクションは、4000行のテーブルで> 4秒を要します

Most of the time this transaction takes <1s, average of 200ms or so. But occasionally, it takes >4s! This sproc gets run 5-6 times per second or so.

私のsprocはとても簡単です(innoDB - REPEATABLE READ):

START TRANSACTION;
SELECT end_time INTO currentEndTime FROM auctions WHERE id=var_auction_id;

IF (ADDTIME(currentEndTime  , var_time_increment) < NOW()) THEN
  UPDATE auctions SET end_time = ADDTIME(NOW(), var_time_increment), price = price+var_price_increment, leader_id = var_leader_id, modified = NOW() WHERE id = var_auction_id AND closed = 0;
ELSE
  UPDATE auctions SET end_time = ADDTIME(end_time, var_time_increment), price = price+var_price_increment, leader_id = var_leader_id, modified = NOW() WHERE id = var_auction_id AND closed = 0;
END IF;
SELECT ROW_COUNT() INTO myRowCount;

IF (_error) THEN
  ROLLBACK;
ELSE
  SET var_return = myRowCount;
  COMMIT;
END IF;

私は4sのスパイクを引き起こしていること、私が試したことを理解したい:

  1. I thought it might be concurrent, but I've seen this sproc called 5 times in 1 second, and those transactions take <100ms. And for the 4s ones, it's not that many concurrent transactions

  2. Index is set properly on id

  3. Table is small... 4000 rows or so.

  4. Can't really run slow query log since it's MySQL 5.0, want to avoid rebooting the server to turn on the slow query flag unless it's the last resort.

私はその原因について何か提案するか、それ以外に何を調査する必要があります。

3
MySQLの設定がInnoDB用に最適化されているかどうかを確認することもできます(5.0のデフォルト設定はMyISAMには良いですが、通常はInnoDBでの使用には適していません)。ここから始められます: mysqlperformanceblog.com/2007/11/ 01 /&hellip;
追加された 著者 ypercubeᵀᴹ,
テーブルのプライマリキーとは何ですか? innodbバッファプールの大きさはどれくらいですか?あなたはチェックポイントの間に無駄な屋台に走っているかもしれません。問題が起きたときに show innodb status の出力を監視してください。
追加された 著者 The Scrum Meister,
そのクエリを実行するサーバーの負荷が重い? uptime コマンドまたは top コマンドで表示されるのは何ですか?またはタスクマネージャー? (OSによって異なります)
追加された 著者 Frosty Z,
オーバーロードされていない、奇妙な...おそらく UPDATE id + closed code> WHERE の基準は?さらに、 free -m を与えるものは何ですか?
追加された 著者 Frosty Z,
少なくともボトルネックはここのハードウェアではないことがわかっています...
追加された 著者 Frosty Z,
これはトップ情報です:トップロード平均:0.97,0.89,0 0.91タスク:201合計、3ランニング、198スリープ、0停止、0ゾンビCPU:21.7%us、1.5%sy、0.0%ni、76.4 %id、0.0%wa、0.2%hi、0.2%si、0.0%st
追加された 著者 Kyra,
このボックスは8GのRAMを搭載したクアッドコアAMD Opteron 1354
追加された 著者 Kyra,
私はそれを監視しようとします。主キーはIDです。 innoDBのサイズは67108864です。私はshow innodbのステータスを監視しようとしますが、問題を追跡するのにはまだ問題があります。
追加された 著者 Kyra,
@Frosty-free -mはmemを表示します:7874、空き116、空きスワップ0、空き2047
追加された 著者 Kyra,

1 答え

これが役立つかどうかは分かりませんが、

SELECT end_time INTO currentEndTime FROM auctions WHERE id = var_auction_id;

IF (ADDTIME(currentEndTime, var_time_increment) < NOW()) THEN
    UPDATE auctions 
    SET end_time = ADDTIME(NOW(), var_time_increment)
      , price = price + var_price_increment
      , leader_id = var_leader_id
      , modified = NOW() 
    WHERE id = var_auction_id 
    AND closed = 0;
ELSE
    UPDATE auctions 
    SET end_time = ADDTIME(end_time, var_time_increment) 
      , price = price+var_price_increment
      , leader_id = var_leader_id
      , modified = NOW() 
    WHERE id = var_auction_id 
      AND closed = 0;
END IF;

次のように書き直すことができます。

    UPDATE auctions 
    SET end_time 
        = ADDTIME( CASE WHEN ADDTIME(end_time, var_time_increment) < NOW()
                        THEN NOW()
                        ELSE end_time
                   END
                 , var_time_increment
                 ) 
      , price = price + var_price_increment
      , leader_id = var_leader_id
      , modified = NOW() 
    WHERE id = var_auction_id 
      AND closed = 0;

または:

    UPDATE auctions 
    SET end_time 
        = ADDTIME( CASE WHEN end_time < ADDTIME(NOW(), - var_time_increment)
                        THEN NOW()
                        ELSE end_time
                   END
                 , var_time_increment
                 ) 
      , price = price + var_price_increment
      , leader_id = var_leader_id
      , modified = NOW() 
    WHERE id = var_auction_id 
      AND closed = 0;

A compound index on (closed, id) should also help the UPDATE statement to avoid reading from the table when closed <> 0.

1
追加された
@TheScrumMeister:なぜ( id がPKの場合に)助けにならないのですか?条件 id = var_auction_id AND closed = 0 は、複合インデックスがない場合にクエリがテーブルを読み取らなければならないことを意味します。
追加された 著者 ypercubeᵀᴹ,
私の(書き換え)提案は、 closed の使用方法や変更方法とは関係ありません。これは、 end_time だけを取り出す最初の SELECT を避けることを意味します。
追加された 著者 ypercubeᵀᴹ,
closed = 0 を設定しているクライアントに関しては、それを行うトランザクションはありません。
追加された 著者 ypercubeᵀᴹ,
id が主キーの場合、複合インデックスは役に立ちません。主キーでない場合は、表を再設計する必要があります。
追加された 著者 The Scrum Meister,
@ypercube a)テーブル全体を読む必要はなく、 id = auction_id で単一行を取り出し、 closed = 0 かどうかを確認します。 b)Innodbは、 id、closed に複合インデックスがあっても、インデックスが実際の行を指していなくても、プライマリキーでデータをクラスタ化します。行をフェッチするために使用されます。索引を使用する利点はありません(主キーで行をフェッチする必要があるため)、オプティマイザは複合索引を使用しません。 dev.mysql.com/doc/refman/5.0を参照してください。 /en/innodb-index-types.html
追加された 著者 The Scrum Meister,
idが実際にプライマリキーです。私は(selectを使って)更新ステートメントの説明を走らせ、それはインデックスから読んでいるようです。
追加された 著者 Kyra,
@ypercube、あなたの提案の潜在的な利点を説明してもらえますか?私はそれを理解しているとは思わない。複数のクライアントがこのトランザクションを実行すると、そのうちの1人だけがclose = 0を設定できるだけでなく、他のクライアントはクローズを書き込むことができないため、無駄な読み込みはありませんか?何か不足していますか?ありがとう!!
追加された 著者 Kyra,