0

(created_at 列によると) 10 日よりも「古い」レコードを削除する必要がある大きなテーブル (約 1,000 万行) があります。これを行うために実行するpythonスクリプトがあります。created_at は varchar(255) であり、次のような値を持ちます1594267202000

import mysql.connector
import sys
from mysql.connector import Error

table = sys.argv[1]
deleteDays = sys.argv[2]

sql_select_query = """SELECT COUNT(*) FROM {} WHERE created_at / 1000 < UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL %s DAY))""".format(table)
sql_delete_query = """DELETE FROM {} WHERE created_at / 1000 < UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL %s DAY)) LIMIT 100""".format(table)

try:
    connection = mysql.connector.connect(host=localhost,
                                         database=myDatabase,
                                         user=admin123,
                                         password=password123)
    cursor = connection.cursor()

        
    #initial count of rows before deletion
    cursor.execute(sql_select_query, (deleteDays,))
    records = cursor.fetchone()[0]


    while records >= 1:
        # stuck at following below line and time out happens....
        cursor.execute(sql_delete_query, (deleteDays,))
        connection.commit()
        cursor.execute(sql_select_query, (deleteDays,))
        records = cursor.fetchone()[0]

    #final count of rows after deletion
    cursor.execute(sql_select_query, (deleteDays,))
    records = cursor.fetchone()[0]

    if records == 0:
        print("\nRows deleted")
    else:
        print("\nRows NOT deleted")

except mysql.connector.Error as error:
    print("Failed to delete: {}".format(error))
finally:
    if (connection.is_connected()):
        cursor.close()
        connection.close()
        print("MySQL connection is closed")

このスクリプトを実行すると、DELETE QUERY が実行されますが、次の理由で失敗します。

削除に失敗しました: 1205 (HY000): ロック待機タイムアウトを超えました。トランザクションを再開してみてください

innodb_lock_wait_timeoutが現在 50 秒に設定されていることはわかっていますが、この問題を解決するためにそれを増やすことができますが、タイムアウトには触れたくないので...基本的にチャンクで削除したいですか? 私のコードを例として使用して、ここでそれを行う方法を知っている人はいますか?

4

2 に答える 2

0

ここでのアプローチの 1 つは、削除制限クエリを使用して、特定のサイズで削除をバッチ処理することです。100 レコードのバッチを想定すると、次のようになります。

DELETE
FROM yourTable
WHERE created_at / 1000 < UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL %s DAY))
LIMIT 100;

厳密に言えば、 を使用するときは常にORDER BY句が必要であることに注意してくださいLIMIT。上に書いたことは、削除の基準に一致する 100 件のレコードを削除する可能性があります。

于 2020-07-31T05:22:02.303 に答える
0

created_at にはインデックスがなく、varchar(255)です– Saffik 11 時間前

問題があります。それらの2つ。

パフォーマンスを期待するには、インデックスを作成する必要があります。インデックスがない場合、MySQL はテーブル内のすべてのレコードをチェックする必要があります。インデックスを使用すると、一致するものに直接スキップできます。

整数を varchar として格納することはできますが、MySQL が変換します。これは悪い習慣です。ストレージを浪費し、不良データを許可し、低速です。

数値として格納されるようにcreated_at をbigintに変更してから、インデックスを作成します。

alter table your_table modify column created_at bigint;
create index created_at_idx on your_table(created_at);

インデックス付きのbigintcreated_atになったので、クエリはインデックスを使用する必要があり、非常に高速になるはずです。


マイクロ秒の精度で時刻を格納する であることに注意してcreated_atください。その後、変換せずにMySQL の日付関数datetimeを使用できます。

しかし、それはミリ秒のエポック番号を期待するコードを台無しにするので、あなたはそれで立ち往生しています. 将来のテーブルのためにそれを覚えておいてください。

このテーブルでは、生成されたcreated_at_datetime列を追加して、日付の操作を簡単にすることができます。そしてもちろん、それをインデックス化します。

alter table your_table add column created_at_datetime datetime generated always as (from_unixtime(created_at/1000));
create index created_at_datetime on your_table(created_at_datetime);

その後、where句ははるかに単純になります。

WHERE created_at_datetime < DATE_SUB(NOW(), INTERVAL %s DAY)
于 2020-07-31T17:52:52.347 に答える