12

約4000万件中267件のレコードを削除しようとしています。クエリは次のようになります。

delete from pricedata
where
pricedate > '20120413'

pricedateはchar(8)フィールドです。

調整については知ってinnodb_buffer_pool_sizeいますが、できれば

select from pricedata
where
pricedate > '20120413'

267レコードを取得し(これですべてです)、エラーは発生しません。なぜ削除時にチョークするのでしょうか。

調整innodb_buffer_pool_sizeがうまくいかない場合はどうすればよいですか?

4

3 に答える 3

10

インデックスがないようですpricedate(またはMySQL何らかの理由でこのインデックスを使用していません)。

REPEATABLE READ(デフォルトのトランザクション分離レベル)を使用InnoDBすると、クエリによって読み取られて除外されたレコードに共有ロックが設定され、ロック用の十分なスペースがないようです40M

この問題を回避するには、次のいずれかの解決策を使用します。

  1. pricedateインデックスがない場合はインデックスを作成します(時間がかかる場合があります)

  2. クエリを小さなチャンクに分割します。

    DELETE
    FROM    pricedata
    WHERE   pricedate > '20120413'
            AND id BETWEEN 1 AND 1000000
    
    DELETE
    FROM    pricedata
    WHERE   pricedate > '20120413'
            AND id BETWEEN 1000001 AND 2000000
    

    など(必要に応じてid範囲を変更します)。各ステートメントは独自のトランザクションで実行する必要があることに注意してください(AUTOCOMMITオフの場合は、各ステートメントの後にコミットすることを忘れないでください)。

  3. トランザクション分離レベルでDELETEクエリを実行します。レコードが読み取られるとすぐに、レコードからリフトロックREAD COMMITTEDが作成されます。InnoDBステートメントモードでバイナリログを使用していて、binlogで安全でないクエリを許可しない場合、これは機能しません(これはデフォルト設定です)。

于 2012-04-20T21:24:55.983 に答える
7

(遅い答えですが、人々がグーグルでこの問題を見つけたときにそれを持っているのは常に良いことです)

innodb_buffer_pool_sizeを変更したり、インデックスを作成したりせずに解決できるのは、削除する行の数を制限することです。

たとえば、あなたの場合DELETE from pricedata where pricedata > '20120413' limit 100;です。これにより、100行が削除され、167行が残ります。したがって、同じクエリを再度実行して、別の100を削除できます。最後の67では、注意が必要です...データベースに残っている行の数が指定された制限より少ない場合、次の数に関するエラーが発生します。ロックします。おそらく、サーバーが100まで満たすためにさらに一致する行を検索するためです。この場合、を使用limit 67して最後の部分を削除します。limit 267(もちろん、最初から使用することもできます)

そして、スクリプトを書くのが好きな人のために...古いデータをクリーンアップするためにbashスクリプトで使用した良い例:

   # Count the number of rows left to be deleted
   QUERY="select count(*) from pricedata where pricedata > '20120413';"
   AMOUNT=`${MYSQL} -u ${MYSQL_USER} -p${MYSQL_PWD} -e "${QUERY}" ${DB} | tail -1`
   ERROR=0
   while [ ${AMOUNT} -gt 0 -a ${ERROR} -eq 0 ]
   do
      ${LOGGER} "   ${AMOUNT} rows left to delete"
      if [ ${AMOUNT} -lt 1000 ]
      then
         LIMIT=${AMOUNT}
      else
         LIMIT=1000
      fi
      QUERY="delete low_priority from pricedata where pricedata > '20120413' limit ${LIMIT};"
      ${MYSQL} -u ${MYSQL_USER} -p${MYSQL_PWD} -e "${QUERY}" ${DB}
      STATUS=$?
      if [ ${STATUS} -ne 0 ]
      then
         ${LOGGER} "Cleanup failed for ${TABLE}"
         ERROR=1
      fi
      QUERY="select count(*) from pricedata where pricedata > '20120413';"
      AMOUNT=`${MYSQL} -u ${MYSQL_USER} -p${MYSQL_PWD} -e "${QUERY}" ${DB} | tail -1`
   done
于 2014-05-20T05:54:14.303 に答える
6

何が機能したか:innodb_buffer_pool_sizeを256Mに変更しました(Quassnoiの元のコメントの下のコメントを参照してください)。

于 2012-04-21T21:24:28.327 に答える