使用子查询的MySQL删除要花费数百秒,而选择查询的花费要少得多

时间:2019-09-12 15:32:44

标签: mysql sql performance innodb mysql-5.5

我有两个定义如下的表:

CREATE TABLE `a`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `cid` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
  `data` varchar(1024) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
  `update_time` timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP(0),
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 150 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

a中有10k行,b中有200k行,所有数据都是由随机函数生成的。下面是一个示例:

151 8VE6BU06    8VE6BU06    2019-09-12 23:07:39

这是三个测试,

1。案例1耗时2.889秒

SELECT cid FROM `a` WHERE a.cid not in (select b.cid from b);

execution plan
1   PRIMARY            a    ALL                 10094   Using where
2   SUBQUERY           b    ALL                 199826      

2。案例2花费628.699s

delete from `a` WHERE a.cid not in (select b.cid from b);

execution plan
1   PRIMARY             a   ALL                 10094   Using where
2   DEPENDENT SUBQUERY  b   ALL                 199826  Using where

3。案例3的费用为0.036秒

alter table b add index cid(cid);
delete from `a` WHERE a.cid not in (select b.cid from b);

execution plan                           
1   PRIMARY             a   ALL                             10094   Using where
2   DEPENDENT SUBQUERY  b   index_subquery  cid cid 302 func    1   Using index

问题

  1. 为什么test2需要这么长时间?
  2. 为什么test1使用SUBQUERY而test2使用DEPENDENT SUBQUERY?与test2相比,test1执行如此之快的原因是什么?
  3. 为什么test3与test2相比这么快?

MySQL版本: 5.6.20 innodb

表b中的行数据如下:

151 8VE6BU06    8VE6BU06      2019-09-12 23:07:39 

4 个答案:

答案 0 :(得分:4)

  • 5.5与def self.from_token_request(request) email = request.params["auth"] && request.params["auth"]["email"] confirmation_email = request.params["auth"] && request.params["auth"]["confirmation_email"] self.find_by(email: email, confirmation_email: confirmation_email) end 相比,解析和优化def auth_params params.require(:auth).permit(:email, :password, :confirmation_email) end DELETE的方式不同(不那么好)。更高的版本(5.7?或8.0)效果更好。
  • UPDATE必须创建撤消记录,以防SELECT中途崩溃;相对于选择而言,这是非常昂贵的。
  • 请注意前两个DELETE怎么说DELETEEXPLAINs。这意味着“对于一个表中的每一行,它将扫描另一表中的所有行”。第三个解释是由于ALL,速度提高了几个数量级。
  • 在较旧的版本中,ALL的性能通常很差-就像在完整扫描中一样。
  • 请考虑使用INDEX而非NOT IN ( SELECT ... )方法将其更改为“多表DELETE”。
  • 如果您正在计时“冷”系统,那么会有很多I / O。
  • 如果LEFT JOIN ... IS NULL太小而无法容纳IN的全部,这将导致大量的I / O,因此查询2的时间很长。
  • 不知道innodb_buffer_pool_sizeb的平均大小,进一步讨论I / O需求是不切实际的。

答案 1 :(得分:1)

这些真的以秒为单位进行测量吗?对于中等大小的表上的这种简单查询,这似乎非常

也就是说,对于相同的数据,通常DELETESELECT需要更长的时间。数据库引擎需要确保不破坏任何外键引用并更新索引等。此行为看起来很正常。

答案 2 :(得分:0)

  1. 在@shadow评论后,读取比写入/删除快。

  2. 请参阅https://dev.mysql.com/doc/refman/5.5/en/subquery-optimization.html

  3. 索引

索引将为您带来出色的性能。

为简单起见,索引就像文档的page0。

Page0 index
Mysql p1
Oracle p2
...
Postgresql p100

Page1 
Some looooooong Mysql material

Page2 
Some looooooong Oracle material
...

Page100 
Some looooooong Postgresql material

按索引阅读

在page0中找到密钥所需的时间相对较短。例如,在索引中找到postgresql并转到第100页之后,这种方法比读取数百页要快得多。

删除索引

您只能删除page0索引中的postgresql。 您不必删除page100中的所有内容。

有关更多信息,请参阅 How does database indexing work?

答案 3 :(得分:0)

我没有“声誉”级别来添加评论,因此添加了回复-除了上面@John所说的之外,Markus Winand的"Use The Index, Luke"可能对您有用。 / p>