我们知道在mysql里, delete数据只会对其打上delete_flag, 不会真正的删除数据,该数据还在页内, 该页也还在Btree+中; 遍历树节点的时候发现有delete_flag的行就直接跳过.
那么一个页里的数据全部被delete
之后, 这一页是否还存在于Btree+中呢?
先说答案: 不在btree+中了,且数据可能也被mv一部分了
被无情的抛弃了
验证之前我们先来回顾一下ibd文件的基础结构. Btree+结构大概如下:
我们的数据就存储在leaf node中. 大概格式如下:
然后我们来准备点测试数据
-- create procedure
delimiter //
create procedure pro_insert_Nrows( IN rows1 int)
begin
declare n int;
set n=1;
set autocommit=off;
while n <= rows1
do
insert into db1.t20250612_2 values(n,md5(n));
set n=n+1;
end while;
commit;
end//
delimiter ;
-- create table
create table db1.t20250612_2 (id int primary key, c2 varchar(200));
-- call procedure
call pro_insert_Nrows(5000);
我们再使用ibd2sql查看下当前的页分布情况
python3 ibd2sql_web.py /data/mysql_3314/mysqldata/db1/t20250612_2.ibd
我这测试数据比较少(5000), 就只有1层(root&leaf page).
第1页的数据范围是1-134
第2页的数据范围是135-403
第3页的数据范围是404-672
....
当然我们还可以使用如下python代码来查看,更方便点
import struct
f = open('/data/mysql_3314/mysqldata/db1/t20250612_2.ibd','rb')
f.seek(4*16384,0)
data = f.read(16384)
offset = 99
for x in range(10): # only view 10 rows
offset += struct.unpack('>h',data[offset-2:offset])[0]
pk,pageid = struct.unpack('>LL',data[offset:offset+8])
print(x,'PK:',pk-2**31, "PAGE ID:",pageid)
假设我们没有看到开头的结论, 然后要删除某一个页的数据; 这时有4种可能(排列组合.):
其实从上图就能看出来, 只有猜想1,2的btree+是完整的, 3,4两种的btree+都不完整了.
我们就直接删除某个范围的数据, 比如: 135-->403的
delete from db1.t20250612_2 where id>=135 and id<404;
flush tables;
我们发现第二页(135-403)的page信息没了(父节点没有记录这个信息了), 那么这个信息就真的没了吗? 我们再瞅瞅brother是否记录这个PAGEID=6的信息.
f.seek(16384*5,0)
data = f.read(16384)
pre_pageno,next_pageno = struct.unpack('>LL',data[8:16])
print(pre_pageno,next_pageno)
f.seek(16384*7,0)
data = f.read(16384)
pre_pageno,next_pageno = struct.unpack('>LL',data[8:16])
print(pre_pageno,next_pageno)
f.seek(16384*6,0)
data = f.read(16384)
pre_pageno,next_pageno = struct.unpack('>LL',data[8:16])
PAGE_N_RECS = struct.unpack('>H',data[38:38+56][16:18])
print(pre_pageno,next_pageno,PAGE_N_RECS)
我们发现PAGE-5记录的下一页是7. PAGE-7记录的上1页是5. 也就是说brother也不在记录这个全部被delete的数据行了. 也就证明了是猜想1正确了.
而PAGE-6虽然还是指向5和7, 但是已经不算数了. 而且我们发现page6的数据范围本来应该是404 - 135 = 269
的, 但是现在只剩下146了(不应该是全部被删除了吗???), 那剩下的123条数据去哪了呢? 不在这个page-6里面了?
我们使用ibd2sql解析下被标记为delete的数据瞅瞅呢.
python3 main.py /data/mysql_3314/mysqldata/db1/t20250612_2.ibd --sql --delete | wc -l
发现被删除的数据为146条.
我们再看看这些delete的数据在那个page吧.
当page-start=5时, 被delete的数据有146条. page-5的下一页是page-7, 也就是说page-5和page-7记录的被删除的数据有146条
当page-start=6时, 被delete的数据有123条, page-6的下一页是page-7, 也就是说page-6和page-7记录的被删除的数据有123条
当page-start=7时, 被delete的数据有0条, page-7的下一页是page-8, 也就是说page-7和page-8记录的被删除的数据有0条.
综上也就是说PAGE-5记录了146条被删除的数据, PAGE-6记录了123条被删除的数据, PAGE-7记录了0条被删除的数据. 也就是当PAGE-6发现自己要被干掉的时候,赶忙把数据转移到了PAGE-5里面(虽然那部分数据还是被标记为delete了)
当某个页的数据要被全部删除时, 可能会把一页数据转移到其它page,并记录转移的数据量到PAGE_N_RECS; 然后被全部删除之后, brother和parent都会将那一页的信息移除.
那么我们大量删除数据时, 怎么使用ibd2sql去恢复呢? 毕竟btree+中都不记录那些PAGE_ID了啊, 虽然实际上那个PAGE里的信息还是有的.
很简单: 遍历所有page,不管是否是btree+的一部分, 只要是符合索引信息的page,我们全部遍历, 然后解析被标记为删除的数据即可, 正好最新版(> v1.10)--force
的逻辑就是这个. 所以我们只要加上--force即可.
参考:
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。