0

我有一个大表(大约 1000 万行),我需要删除“早于”10 天的记录(根据 created_at 列)。我有一个 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

这里的一种方法可能是使用删除限制查询,以一定大小批量删除。假设批次 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 小时前

有你的问题。他们两个人。

它需要被索引才能有任何表现的希望。没有索引,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);

现在这created_at是一个索引 bigint,您的查询应该使用索引并且它应该非常快。


请注意,created_at应该datetime是以微秒精度存储时间的。然后就可以不用转换就可以使用MySQL 的日期函数了。

但这会弄乱你的代码,它需要一个毫秒的纪元数,所以你被困住了。请记住它以备将来使用。

对于此表,您可以添加一个生成的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 回答