【问题标题】:Very slow delete on mysql base with subquery带有子查询的mysql基础上的删除速度非常慢
【发布时间】:2011-11-13 17:46:35
【问题描述】:

这个 mysql 查询运行了大约 10 个小时,还没有完成。出事了。

这里有两个表格(文本和垃圾邮件)。 Spam 将垃圾邮件条目的 ID 存储在我要删除的文本中。

DELETE FROM tname.text WHERE old_id IN (SELECT textid FROM spam);

spam 只有 2 列,都是整数。 800K 条目的文件大小为几 Mbs。两个整数都是主键。

文本有 3 列。 id(主键)、文本、标志。大约 1200K 条目和大约 2.1 GB 大小(大多数垃圾邮件)。

服务器是一个至强四核,2 GB 内存(不要问我为什么)。只有 apache(为什么?)和 mysqld 正在运行。它是一个旧的免费 bsd 和 mysql 4.1.2(不要问我为什么)

线程:6 个问题:188805 慢查询:318 打开:810 刷新表:1 打开表:157 每秒查询平均:7.532

Mysql my.cnf:

[mysqld]
datadir=/usr/local/mysql
log-error=/usr/local/mysql/mysqld.err
pid-file=/usr/local/mysql/mysqld.pid
tmpdir=/var/tmp
innodb_data_home_dir =
innodb_log_files_in_group = 2
join_buffer_size=2M
key_buffer_size=32M
max_allowed_packet=1M
max_connections=800
myisam_sort_buffer_size=32M
query_cache_size=8M
read_buffer_size=2M
sort_buffer_size=2M
table_cache=256
skip-bdb
log-slow-queries = slow.log
long_query_time = 1

#skip-innodb
#default-table-type=innodb
innodb_data_file_path = /usr/local/mysql/ibdata1:10M:autoextend
innodb_log_group_home_dir = /usr/local/mysql/
innodb_buffer_pool_size = 128M
innodb_log_file_size = 16M
innodb_log_buffer_size = 8M
#innodb_flush_log_at_trx_commit=1
#innodb_additional_mem_pool_size=1M
#innodb_lock_wait_timeout=50

log-bin
server-id=201

[isamchk]
key_buffer_size=128M
read_buffer_size=128M
write_buffer_size=128M
sort_buffer_size=128M

[myisamchk]
key_buffer_size=128M[server:~] dmesg | grep memory
real memory  = 2146828288 (2047 MB)
avail memory = 2095534080 (1998 MB)

read_buffer_size=128M
write_buffer_size=128M
sort_buffer_size=128M
tmpdir=/var/tmp

查询只使用一个 cpu,top 表示 25% cpu 时间(所以 1 of 4)。

real memory  = 2146828288 (2047 MB)
avail memory = 2095534080 (1998 MB)

62 processes:  2 running, 60 sleeping
CPU states: 25.2% user,  0.0% nice,  1.6% system,  0.0% interrupt, 73.2% idle
Mem: 244M Active, 1430M Inact, 221M Wired, 75M Cache, 112M Buf, 31M Free
Swap: 4096M Total, 1996K Used, 4094M Free

  PID USERNAME     THR PRI NICE   SIZE    RES STATE  C   TIME   WCPU COMMAND
11536 mysql         27  20    0   239M   224M kserel 3 441:16 94.29% mysqld

知道怎么解决吗?

【问题讨论】:

  • 表上的存储引擎是什么?
  • 您的查询包含一个 old_id 列,但您对表 text 的描述没有 - 您真的描述了整个表吗?总的来说,我怀疑这个问题会随着更新的 MySQL 版本神奇地消失。
  • 确保在text.old_idspam.textid 上有索引。
  • 是的,抱歉,它的 old_id 只是和 int 值(主键)。嗯...它是一个有很多用户的生产系统(由一个免费程序使用,如果服务器停止工作,它将停止工作)...菜鸟刚刚放弃了它,我被选中修复它...跨度>
  • 我可以建议您将 MySQL 4.1.2 修补到更新的 4.1.21 版本,同样适用于 apache。有一些反向移植可能会对您有所帮助,尤其是针对 mysql_real_escape_string() 的安全修复程序

标签: mysql subquery config sql-delete


【解决方案1】:

根据我的经验,子查询通常是导致 SQL 语句执行时间变慢的原因,因此我尽量避免它们。试试这个:

DELETE tname FROM tname INNER JOIN spam ON (tname.old_id = spam.textid);

免责声明:此查询未经测试,请先备份! :-)

【讨论】:

  • 他的断言对于那个年代的 MySQL 版本非常正确。当他们第一次引入子查询时,以及在那之后的一段时间内,它们存在大量的性能问题。
  • +1 back - 这是一个很好的查询/声明。测试执行计划,看看是否有区别...
  • 服务器配置人员怎么样?
  • 这是什么魔法
【解决方案2】:

您选择的where id in (select ...) 总是表现不佳。

改为使用非常有效的普通连接:

DELETE `text` 
FROM spam
join `text` on `text`.old_id = spam.textid;

注意先从垃圾邮件中选择,然后加入文本,这将提供最佳性能。

【讨论】:

    【解决方案3】:

    当然,这将花费很多时间,因为它为每条记录执行子查询,但是通过直接使用 INNER JOIN,此查询仅执行一次 让我们认为查询将采取

    10 ms for 50000 rec  full time = 50000 * 10 ms ---> 8.333 minutes !! at least don't forget the condition and deleting time .....
    

    但是使用 join 查询只会执行一次:

    DELETE t FROM tname.text t INNER JOIN (SELECT textid FROM spam) sq on t.old_id = sq.textid ;
    

    【讨论】:

      【解决方案4】:

      将不在spam 表单text 中的行复制到新表。然后删除text 表并重命名创建的表。 好主意是不要向创建的表添加任何键。重命名后添加键。

      【讨论】:

      • 是的,认真的!为什么我没有想到这一点,这是迄今为止大多数实际应用中的最佳解决方案!
      【解决方案5】:

      我认为您可能希望使用 LIMIT 将删除分块,并且您可能希望在 JOIN 中执行该删除。我在这篇文章中写了更多关于这方面的内容,专门帮助归档数据和删除不再需要的行。

      https://shatteredsilicon.net/blog/2021/07/12/mariadb-mysql-performance-tuning-optimization-how-to-delete-faster-on-mysql/

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-11-21
        • 1970-01-01
        • 2015-12-03
        • 1970-01-01
        • 2019-09-29
        • 1970-01-01
        相关资源
        最近更新 更多