我需要删除旧的数据库条目有效负载,同时保留相同条目的其他数据(id和其他属性)。 有问题的表有 message_id 列(包含与其他信息连接的日期戳), content 列(BLOB,它占数据库总大小的90%以上)以及在这种情况下我们没有用的其他一些列。
我首先尝试使用条件运行简单更新:
UPDATE LOW_PRIORITY repository SET content="" WHERE SUBSTR( message_id, 6, 6 )<201601 AND message_box = "IN";
我从每个条目message_id中提取 YYYYMM ,如果它比选定的截止月份更早 - 我将 content 替换为空字符串。
数据库大小超过25GB,在我的表中保存了近2KK条目,并且运行在非常适中的硬件上,运行一段时间后我的查询失败并出现错误:
ERROR 2013 (HY000): Lost connection to MySQL server during query
通常我会尽量避免更改数据库变量,但是我知道当你尝试从大型转储文件恢复数据库时会弹出这个错误,因此我去更新设置以处理100MB数据包大小:
set global max_allowed_packet=104857600;
重新运行我的UPDATE查询导致了一个新错误:
ERROR 2013 (HY000): Lost connection to MySQL server during query
正如我之前提到的 - 我的MySQL服务器运行在一个非常适度的硬件上,我宁愿不修改可能使服务器超出可用资源的设置,因此而不是增加所有可用的超时数据库变量,我决定使用这样的查询在较小的块中运行我的查询:
UPDATE LOW_PRIORITY repository SET content="" WHERE message_id in (select message_id from(select message_id from repository where SUBSTR( message_id, 6, 6 )<201603 AND message_box = "IN" limit 0, 1000)as temp);
此查询失败并显示错误:
ERROR 1206 (HY000): The total number of locks exceeds the lock table size
当限制为“限制1”的单行时,它也会因同一查询而失败!
我是否错误地使用了分页,还是有其他更好的方式来执行此操作?
* DB运行的虚拟Ubuntu服务器具有双核Intel CPU,1GB RAM和100GB HDD。我不完全适合它的日常任务,我真的不想仅为这一个查询增加规格。
答案 0 :(得分:1)
你试图以复杂的方式欺骗mysql做一些它不想要的东西(在limit
语句中使用in
)(复杂=更多资源)。这没错,但你可以写
UPDATE LOW_PRIORITY repository SET content=""
WHERE content <> ""
and SUBSTR( message_id, 6, 6 ) < 201603 AND message_box = "IN"
limit 1000;
这将更新仍有内容的前1000行。
答案 1 :(得分:0)
我认为你的#1问题是你的WHERE条件无法在message_id字段上使用索引。
为什么不简单地做:
WHERE message_id < 20160100* ...
假设这是整数字段,201512 **将会少于201601 **,因此您的结果不会有任何变化。但是删除子字符串函数将允许您在该字段上使用索引。