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
aeo13 OP @sunnyday123 先查询出主键 id 的集合再 in 主键删除都不行。。
|
7
aeo13 OP @SmartKeyerror 请问你指的是表的碎片率?
|
8
sunnyday123 2021-06-16 11:02:35 +08:00
@aeo 只要有 in 索引就失效,orde_id =id and status in() 还是全表扫描,explain 看下扫描行数吧.
|
9
aeo13 OP @sunnyday123 但是我在 4.2 也尝试使用过 where userid=用户 id 的条件,userid 是有设置索引,同样也很慢
|
10
SmartKeyerror 2021-06-16 11:04:42 +08:00
@aeo13 对,从 EXPLAIN 的结果上来看,应该不至于这么慢才对,而且数据只有 300 万
|
11
aeo13 OP @SmartKeyerror 查看了一下阿里云的表分析,表空间 3.04 GB,索引空间 943.88 MB,碎片率是 0.19%
|
12
MoYi123 2021-06-16 11:20:33 +08:00
看看触发器? 我之前有个 nt 同事,会在表 delete 的时候,把整个表备份一次。
|
14
CEBBCAT 2021-06-16 11:26:02 +08:00
你这个 EXPLAIN 是 EXPLAIN 的按主键删除吧?其他几种删除方法呢?比如你说的 STATUS IN () 的那个。
另外,建表语句可以给一下吗? 如果表上的索引不少,我怀疑删除数据的时候也会被重建索引拖慢速度 |
15
aeo13 OP @CEBBCAT
EXPLAIN 的是 DELETE FROM order WHERE order_id IN () AND status IN ()这个的 建表语句: https://imgur.com/undefined 是的,索引不少,数据空间 2.11 GB,索引空间是 943.88 MB |
16
aeo13 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
|
26
billccn 2021-06-16 17:04:56 +08:00
有其他表外键到这个表吗?如果其他表相应的列不做索引,删除的时候只有查全表才能确定外键是不是还有效。
|
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
|
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
额,不都是软删除么
|