
1 、由于业务需要,需要对接第三方的数据,如某个用户访问时会执行同步接口,之后按顺序对数据执行删除、修改和新增。目前增改都没有问题,但是删除会很慢,导致每分钟都会有好几条慢日志生成。
2 、该表数据目前是三百多 W 。
3 、目前的删除 sql 是 DELETE FROM order WHERE order_id IN () AND status IN (),order_id 有几个到几十个不等,status 有几个。
阿里云 RDS 的慢日志(截取某一分钟): https://imgur.com/S5N6w2v EXPLAIN 结果: https://imgur.com/dhCSC3a
4 、所作尝试 1 )改用 select 所有主键 id 再 in 删除主键 id,发现执行时间更长 2 )改用 delete where userid = 用户 id,发现执行时间更长 3 )改用循环删除操作,CPU 顶不住
1 duanxianze 2021-06-16 10:29:37 +08:00 互联网公司,从不真正删除任何数据,打个标记也就行了,mysql 的数据结构删除本来就是慢,非要删除就每晚空闲的时候对打了标记的统一删除 |
2 AngryPanda 2021-06-16 10:30:03 +08:00 4.3 + 异步删除? |
3 sunnyday123 2021-06-16 10:40:24 +08:00 order_id IN () AND status IN (): IN 会让索引失效,应该是走了全表扫描.尝试去掉 in,status 和 order_id 建一个联合索引 |
4 SmartKeyerror 2021-06-16 10:40:26 +08:00 InnoDB 存储引擎的话,删除数据可能会造成 B+Tree 中存在数据空洞,如果不进行表整理的话,随着频繁的插入和删除,.ibd 文件可能会越来越大,导致 delete from 执行时扫描了许多原本已经删除的。最好方法当然是使用 is_deleted 标志位,如果一定要进行物理删除的话,可以在删除之前将可能被删除的数据 select 到 buffer pool 中,然后在进行删除,相当于做了一层缓存 |
5 lostSoul 2021-06-16 10:42:26 +08:00 还真有 delete 的? 我们都是 update 的 如果数据太多 更新也会慢 上队列异步慢慢慢处理吧 |
6 DoraemonWOO OP @sunnyday123 先查询出主键 id 的集合再 in 主键删除都不行。。 |
7 DoraemonWOO OP @SmartKeyerror 请问你指的是表的碎片率? |
8 sunnyday123 2021-06-16 11:02:35 +08:00 @aeo 只要有 in 索引就失效,orde_id =id and status in() 还是全表扫描,explain 看下扫描行数吧. |
9 DoraemonWOO OP @sunnyday123 但是我在 4.2 也尝试使用过 where userid=用户 id 的条件,userid 是有设置索引,同样也很慢 |
10 SmartKeyerror 2021-06-16 11:04:42 +08:00 @aeo13 对,从 EXPLAIN 的结果上来看,应该不至于这么慢才对,而且数据只有 300 万 |
11 DoraemonWOO OP @SmartKeyerror 查看了一下阿里云的表分析,表空间 3.04 GB,索引空间 943.88 MB,碎片率是 0.19% |
12 MoYi123 2021-06-16 11:20:33 +08:00 看看触发器? 我之前有个 nt 同事,会在表 delete 的时候,把整个表备份一次。 |
13 DoraemonWOO OP @MoYi123 没有创建任何触发器哦 |
14 CEBBCAT 2021-06-16 11:26:02 +08:00 你这个 EXPLAIN 是 EXPLAIN 的按主键删除吧?其他几种删除方法呢?比如你说的 STATUS IN () 的那个。 另外,建表语句可以给一下吗? 如果表上的索引不少,我怀疑删除数据的时候也会被重建索引拖慢速度 |
15 DoraemonWOO OP @CEBBCAT EXPLAIN 的是 DELETE FROM order WHERE order_id IN () AND status IN ()这个的 建表语句: https://imgur.com/undefined 是的,索引不少,数据空间 2.11 GB,索引空间是 943.88 MB |
16 DoraemonWOO OP @CEBBCAT 建表语句重新贴一下: https://imgur.com/2Nw4acp |
17 MoGeek 2021-06-16 15:22:49 +08:00 推荐一楼的方法,直接打标识就好。可以定期删除 |
18 westoy 2021-06-16 15:32:03 +08:00 打 flag 然后半夜里定时清啊, 哪有生产环境在线硬删数据的...... |
19 keepeye 2021-06-16 15:35:17 +08:00 实在不行呢,可以按 status 来遍历删除,这样 order_id 索引大概能生效 DELETE FROM order WHERE order_id IN (?) AND status = 1; DELETE FROM order WHERE order_id IN (?) AND status = 2; ... DELETE FROM order WHERE order_id IN (?) AND status = N; |
20 wolfie 2021-06-16 15:39:55 +08:00 @sunnyday123 只要有 in 索引就失效?上面不是贴了 explain 吗。 |
21 beitayongguo 2021-06-16 16:47:14 +08:00 用 in 为啥会索引失效? 从 explain 看没啥问题 让 dba 帮忙抓包看看呢 |
22 vindac 2021-06-16 16:48:01 +08:00 是不是在用多线程操作 |
23 myd 2021-06-16 16:53:35 +08:00 创建联合索引 order_id, status |
24 DoraemonWOO OP @myd 应该不是索引问题啊,查询后用 where 主键 in 删除都巨慢。。 |
25 DoraemonWOO OP @vindac 没有哦 |
26 billccn 2021-06-16 17:04:56 +08:00 有其他表外键到这个表吗?如果其他表相应的列不做索引,删除的时候只有查全表才能确定外键是不是还有效。 |
27 DoraemonWOO OP @billccn 没有外键 |
28 offswitch 2021-06-16 17:19:47 +08:00 @sunnyday123 有 in 索引就失效?这是哪门子的说法? 4.0 以下才有这种情况。从上面抛出的执行计划上看是 range,走了 orderid,用了主键。 |
29 offswitch 2021-06-16 17:27:35 +08:00 阿里云的 RDS,是不是配置比较低呢?事务的问题,用定时任务一条条删除不行吗? |
30 rockyliang 2021-06-16 19:00:30 +08:00 感觉两个图片对不上,EXPLAIN 显示扫描行数是 1,但 RDS 慢日志显示扫描行数几千行。建议用 SHOW PROFILE 命令查看语句的详细执行情况,看到底慢在哪里 |
31 huazaige 2021-06-16 19:46:06 +08:00 大致思路: 可以创建一个结构一样的新表,把需要留下的数据 copy 到这张新表里,然后直接删除老表,重命名新表 1 、基于老表创建一张新表:create table order_new like order; 2 、把需要留下的数据 插入到新表里:insert into order_new select * from order where id>1 and id<=3000000; 3 、删除老表:drop table order; 4 、重命名新表:alter table order_new rename to order; |
32 littlewing 2021-06-16 23:09:54 +08:00 300 多万数据就算扫全表也不可能 6s 多,除非你单行数据特别大(几十 MB 这种) |
33 rekulas 2021-06-16 23:10:53 +08:00 1 检查是否索引导致删除慢 2 分析下表试试 不清楚你 tps 多少 |
34 pgfourwell2020 2021-06-16 23:14:42 +08:00 @billccn 666 |
35 DoraemonWOO OP @offswitch 是的,配置较低。请问事务的问题指的是? |
36 Leigg 2021-06-17 09:41:56 +08:00 via Android @aeo13 直接执行 delete from where order_id in order_ids...不可能慢吧,order_id 是有索引的,你创建组合索引来试试。 |
37 raaaaaar 2021-06-17 10:19:11 +08:00 额,不都是软删除么 |