[MYSQL] python扫描磁盘恢复数据的可行性验证与速度测试


导读

有些场景(比如drop/truncate table)可能需要扫描磁盘才能恢复数据, undrop-for-innodb就很好用, 但我的ibd2sql还不支持啊, 于是就准备给它加这么个功能. 当然得先验证下是否可行以及速度怎么样, 速度不行的话.

原理

表的数据是一页页的放在磁盘(文件系统)上的. 只要磁盘上的数据没有删除,即使逻辑上删除了文件也是能恢复的, 如果时间短的话, 可以从文件系统级别根据inode恢复; 时间长了, 文件就不再完整了, 只能全盘扫的方式恢复了.

☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 免费无限量使用 DeepSeek R1 模型☜☜☜

[MYSQL] python扫描磁盘恢复数据的可行性验证与速度测试

那么扫描磁盘的时候我们怎么知道哪部分数据是我们要的数据呢? 这就得先看看数据文件的结构了.

innodb的数据是放在索引上的, 即INDEX_PAGE, 我们只需要扫描到我们需要的INDEX_PAGE即可. 怎么判断是否是我们需要的PAGE呢? 请看:

[MYSQL] python扫描磁盘恢复数据的可行性验证与速度测试

有个2字节的PAGE_LEVEL表示这是叶子节点,即方数据的; 还有个8字节的INDEX_ID表示这个页是对应的某个索引的. 而我们根据表可以找到其对应的索引, 并获取到对应的INDEXID; 既然要恢复, 那么我们肯定就知道要恢复的表了哦. 不知道也没关系, ibdata1/mysql.ibd里面是有记录哪些表是被删除的, 并且有相关的indexid.

那么我们的恢复思路就是: 扫描ibdata1/mysql.ibd获取要恢复表的index; 扫描磁盘寻找对应的PAGE; 然后使用ibd2sql等工具将PAGE中的数据提取出来.

由于linux上一切皆文件, 磁盘也是文件, 所以我们就把磁盘当作普通文件读取即可. 然后将读取的结果进行校验.这种工作一个进程肯定是不够的, 所以支持并发是必须的. 本来还应该校验page是否完整的, 但算了.

[MYSQL] python扫描磁盘恢复数据的可行性验证与速度测试

演示

理论已经有了,就可以试试效果了. 为了方便查看进度, 我做了个动态的进度条(每个进程指定自己在屏幕上的位置,并输出进度).

准备数据

-- 准备测试表和数据create table db1.t20251128_for_drop(id int primary key auto_increment, name varchar(200));insert into db1.t20251128_for_drop(name) values('ddcw');insert into db1.t20251128_for_drop(name) select name from db1.t20251128_for_drop;insert into db1.t20251128_for_drop(name) select name from db1.t20251128_for_drop;-- ....-- 然后干掉它(你就可以跑路了)drop table db1.t20251128_for_drop;
[MYSQL] python扫描磁盘恢复数据的可行性验证与速度测试

扫描需要恢复表的indexid

python3 main.py /data/mysql_3306/mysqldata/mysql.ibd --delete --set table=tables  | grep t20251128_for_droppython3 main.py /data/mysql_3306/mysqldata/mysql.ibd --delete --set table=indexes | grep 463
[MYSQL] python扫描磁盘恢复数据的可行性验证与速度测试

我们这里扫描出2条是因为第一次建测试表的时候忘记加主键了, 其实不影响的, 但我还是删除了重建. 经过上面的步骤我们得到indexid为254

扫描磁盘获取数据

然后我们就可以根据上面拿到的indexid去扫盘了.

python3 scan_drop_table_demo.py --device /dev/vda1 --indexid 254 --parallel 8
[MYSQL] python扫描磁盘恢复数据的可行性验证与速度测试
[MYSQL] python扫描磁盘恢复数据的可行性验证与速度测试
[MYSQL] python扫描磁盘恢复数据的可行性验证与速度测试

看起来还是比较绚的(艹,忘记加点色了).

第一列是 进程逻辑ID,

第二列是 进度条

第三列是 进度百分比

Ghiblio Ghiblio

专业AI吉卜力风格转换平台,将生活照变身吉卜力风格照

Ghiblio 157 查看详情 Ghiblio

第四列是 速度

第五列是 这个进程扫描磁盘的起止位置

第六列是 这个进程扫描到多少个匹配的page了.

[MYSQL] python扫描磁盘恢复数据的可行性验证与速度测试

花了151秒扫描了40GB的磁盘, 速度大概是271MB/s, 还行, 反正支持并发,上限还是很高的.

解析扫描的page

最后我们就可以解析扫描出来的结果了, 我这里忘记显示输出文件了. 没事, 反正是个demo

python3 main.py 0000000000000254.page.ibd --sdi /data/mysql_3306/mysqldata/db1/t20251128_for_drop_new.ibd --sql --limit 10 --set leafno=0 --set rootno=0
[MYSQL] python扫描磁盘恢复数据的可行性验证与速度测试

看起来没得问题, 但数据应该不全, 毕竟我这个测试环境比较闲,那文件系统肯定老早就给我回收一部分了,可恶!

源码见文末

总结

所以,python扫描磁盘效果还是不错的, 速度也不错.

在能扫描磁盘后, 我们能恢复mysql的范围就更广了, 基本上数据物理上存在我们就能恢复, 感觉自己棒棒哒!

这个脚本起始很早就写好了的, 但之前测试的时候始终未成功, 后来发现我测试的那个环境的innodb-page-size是4K, 而我这个demo的pagesize是写死了的16K..... 就TM离谱!

由于只是测试demo脚本, 不建议用于生产, 可等我后续给它丫集成到ibd2sql后再考虑生产

附源码:

#!/usr/bin/env python3# write by ddcw @https://github.com/ddcw# 测试扫描磁获取相关Indexid的page的测试例子,验证可行性和效率import osimport sysimport statimport timeimport structimport shutilimport argparsefrom multiprocessing import ProcessPAGE_SIZE = 16384def print_error_and_exit(msg,exit_code=1):msg += ""sys.stdout.write(msg)sys.exit(exit_code)def print_info(msg):msg += ""sys.stdout.write(msg)def format_size(n):if n < 1024:return f'{n} B'elif n < 1024*1024:return f'{round(n/1024,2)} KB'elif n < 1024*1024*1024:return f'{round(n/1024/1024,2)} MB'elif n < 1024*1024*1024*1024:return f'{round(n/1024/1024/1024,2)} GB'elif n < 1024*1024*1024*1024*1024:return f'{round(n/1024/1024/1024/1024,2)} TB'else:return f'{round(n/1024/1024/1024/1024/1024,2)} PB'def _argparse():parser = argparse.ArgumentParser(add_help=True,description="测试扫描磁获取相关Indexid的page的测试例子,验证可行性和效率")parser.add_argument('--device',dest="DEVICE_NAME",required=True,help='磁盘设备/文件')parser.add_argument('--start',dest="OFFSET_START",type=int,default=0,help='要扫描的磁盘设备/文件的起始地址,默认0')parser.add_argument('--end',dest="OFFSET_END",type=int,default=-1,help='要磁盘设备/文件的结束地址,默认全部')parser.add_argument('--step',dest="OFFSET_STEP",type=int,default=512,help='扫描步长,默认512字节')parser.add_argument('--buffering',dest="BUFFERING",type=int,default=16*1024*1024,help='缓存大小(非open缓存),默认16MB')parser.add_argument('--indexid',dest="INDEXID",type=int,required=True,help='要扫描的表的Indexid')#parser.add_argument('--tablespaceid',dest="TABLESPACE_ID",help='要扫描的表的tablespace id')parser.add_argument('--parallel',dest="PARALLEL",type=int,default=1,help='并发度')parser.add_argument('--output',dest="OUTPUT_FILENAME",type=int,default=1,help='输出文件名,默认为indexid.page')parser = parser.parse_args()return parser# 初始化屏幕def init_screen():x = os.system('clear')columns,lines = shutil.get_terminal_size()return columns# 获取磁盘设备大小(可能是lv,可能是磁盘,也可能是文件)def get_size_from_dev(filename):f_stat = os.stat(filename)file_size = 0status = Trueif stat.S_ISREG(f_stat.st_mode): # filefile_size = f_stat.st_sizeelif stat.S_ISBLK(f_stat.st_mode):real_dev = ''try:real_dev = os.readlink(filename).split('/')[-1] # lvexcept:real_dev = os.path.basename(filename) # devwith open(f'/sys/class/block/{real_dev}/size') as f:sectors = int(f.read().strip())try:with open(f'/sys/class/block/{real_dev}/queue/hw_sector_size') as f:sector_size = int(f.read().strip())except:sector_size = 512file_size = sectors * sector_sizeelse:status = Falsereturn status,file_size# 监控进程,只展示效果,不干活的. (算逑, 不要它了,直接worker输出)def monitor(q):pass# worker进程,打工仔. (我将给你一个展示的机会!)def worker(p,filename,start,end,step,indexid,buffering,output_filename,screen_size=0):import timef = open(filename,'rb')fo = open(output_filename,'wb')f.seek(start,0)buff = b''readed_size = 0indexid = b'\x00\x00'+struct.pack('>Q',indexid)hc = 0while end > readed_size:start_time = time.time()readsize = buffering-len(buff)buff += f.read(readsize)if len(buff) < 16384:breakreaded_size += readsizeoffset = 0while True:data = buff[offset:offset+16384]if len(data) < 16384:breakif data[:4] == data[-8:-4] and data[24:26] == b'E\xbf' and data[64:74] == indexid:offset += 16384hc += 1fo.write(data)else:offset += stepbuff = buff[offset:]end_time = time.time()progress = min(100,round(readed_size/end*100,2))rate = format_size(readsize/(end_time-start_time)) + '/s'progress_bar = "#"*(int(progress)//2)progress_bar_wsp = " "*(50-len(progress_bar))content = f"[P{str(p+1).zfill(2)}] [{progress_bar}{progress_bar_wsp}] {progress}% {rate} {start}:{start+readed_size} {hc} {' '*5}"sys.stdout.write(f"\033[{p+2};0H{content}")sys.stdout.flush()fo.close()#import random#for i in range(20):#line_num = p+2#column = 1#content = ''.join([ '#' for _ in range(i) ])#sys.stdout.write(f"\033[{line_num};{column}HP[{p}]{content}")#sys.stdout.flush()#time.sleep(random.random())def main():starttime = time.time()parser = _argparse()filename = parser.DEVICE_NAMEparallel = parser.PARALLELif not os.path.exists(filename):print_error_and_exit(f'{filename} is not exists')screen_size = init_screen()status,file_size = get_size_from_dev(filename)if not status:print_error_and_exit(f'{filename} only support dev/file')msg = f"SCAN DEVICE {filename}({format_size(file_size)})"msg = " "*((screen_size-len(msg))//2) + msgpd = {}sys.stdout.write(f'{msg}')sys.stdout.flush()step = parser.OFFSET_STEPstart = parser.OFFSET_STARTend = parser.OFFSET_END if parser.OFFSET_END > start else file_sizeper_size = (end-start)//parallel//step*step+stepindexid = parser.INDEXIDoutput_filename_pre = '/tmp/' + str(indexid).zfill(16)+'.page'for x in range(parallel):output_filename = f"{output_filename_pre}{'.'+str(x) if parallel > 1 else ''}"pd[x] = Process(target=worker,args=(x,filename,start+x*per_size,per_size,step,indexid,parser.BUFFERING,output_filename,screen_size))for x in range(parallel):pd[x].start()for x in range(parallel):pd[x].join()stoptime = time.time()sys.stdout.write(f"\033[{parallel + 3};{0}HFinish! cost:{round(stoptime-starttime,2)} sec.")sys.stdout.flush()if __name__ == '__main__':main()

以上就是[MYSQL] python扫描磁盘恢复数据的可行性验证与速度测试的详细内容,更多请关注其它相关文章!


# mysql  # 芝罘上市公司网站优化  # 萧山区网络推广网站价格  # 从化网络营销推广方案  # 技术网站推广参考价  # 网站建设工作室服装  # 常山本地推广营销  # 珠海网站优化多少钱  # 进度条  # 这是  # 收官  # 开源  # 给它  # 对价  # 就可以  # 放在  # 数据恢复  # linux  # python  # git  # node  # github  # 字节  # 工具  # ai  # cos  # red  # 202  # 文件系统  # 有个  # 忻州网站优化推广  # 网站上做推广怎么做的呢  # 整合营销推广找谁 


相关栏目: 【 Google疑问12 】 【 Facebook疑问10 】 【 优化推广96088 】 【 技术知识133117 】 【 IDC资讯59369 】 【 网络运营7196 】 【 IT资讯61894


相关推荐: 看了天美对AI的布局,我感觉它想得是真明白  猿辅导推出Motiff,整合三大AI功能,助力UI设计生产力革新  李开复官宣新公司「零一万物」,进军 AI 2.0  大脚攀爬者车主福利!无人机、运动相机大奖等你来挑战  本届人工智能大会上的这个“镇馆之宝”,来自长宁企业西井科技!  科普:什么是AI大模型  Valve 将拒绝采用 AI 生成未知版权内容的游戏上架 Steam  OpenAI 已全面开放 GPT-3.5 Turbo、DALL-E 及 Whisper API  Dubbo负载均衡策略之 一致性哈希  企业软件行业更将被AI全面重构!Moka李国兴:未来优秀组织和个人将一定是善于使用AI生产力的  脑虎科技:奔跑在“脑机接口”最前沿 跨界融合取得阶段性成果  聚焦WAIC|AI技术支撑大模型探索未来  警惕!AI或致虚假信息泛滥  有 ARM 和 X86 两个版本,香橙派游戏掌机细节曝光  磐镭发布全新 GeForce RTX 4080 ARMOUR 显卡,售价为 9499 元  值得买科技入选“北京市通用人工智能产业创新伙伴计划”应用伙伴  尼康尼克尔Z 180-600mm f/5.6-6.3 VR镜头发布:12499元 拍鸟神器  江永:精准施训提升通信无人机应急救援能力  在这里见未来!杭州未来科技城全球AI盛会邀您共探最前沿  丰田汽车研究院推出生成式人工智能汽车设计工具  热点 | 人工智能黄金时代开启  国内首款大尺寸仿鸵双足机器人“大圣”亮相,穿戴红色战袍  OpenAI 引入个性化指令功能,消除对话中的重复偏好与信息  从谷歌到亚马逊,科技巨头们的AI痴迷  人形机器人概念集体爆发,能买吗?  AI人工智能软件,婚纱设计师的必备利器  Snap宣布研发出新技术 可大幅提升AI生成图像速度  赋能选题探索:AI助手在经济学专业中的应用指南  厂商陆续公布AI进展 完美世界游戏展示复合应用AI in GamePlay  GPT-4是如何工作的?哈佛教授亲自讲授  可按用户语气自动回复消息,Zoom 推出基于生成式 AI 的新功能  “技术+实践+生态”三箭齐发,京东方抢占物联网高地  特斯拉首发人形机器人“擎天柱”亮相世界人工智能大会  AI数字人业务频频获点赞,谦寻积极引领示范作用  J*a与人工智能结合:构建智能云服务  人工智能产业协同创新中心:全产业链资源在这里汇聚  阿里达摩院向公众免费开放100项AI专利许可  奥比中光子公司和斯坦德机器人深度合作,共同推进新一代激光雷达的研发  科技数码圈的新物种 乐天派桌面机器人 AI +安卓+机器人 首发价1799元  亚马逊确认今年不举办re:MARS人工智能大会  6月14日《星空下的对话》 张朝阳陆川将畅聊人生、电影、心理学与AI  「电子果蝇」惊动马斯克!背后是13万神经元全脑图谱,可在电脑上运行  消息称 ChatGPT 未来有望增加更多功能:上传文件分析信息,还能记住用户画像  OpenOOD更新v1.5:全面、精确的分布外检测代码库及测试平台,支持在线排行榜、一键测试  AI赋能艺术 超现实达利奇幻之旅在沪开启  Meta将VR头显最低年龄限制从13岁降至10岁  史玉柱谈AI:国内最缺是计算数学人才,曾给浙大数学系捐五千万  RoboNeo安装教程  “世界上最像人的机器人”接入 Stable Diffusion ,现场完成作画  清华系面壁智能开源中文多模态大模型VisCPM :支持对话文图双向生成,吟诗作画能力惊艳 

 2025-12-01

了解您产品搜索量及市场趋势,制定营销计划

同行竞争及网站分析保障您的广告效果

点击免费数据支持

提交您的需求,1小时内享受我们的专业解答。

运城市盐湖区信雨科技有限公司


运城市盐湖区信雨科技有限公司

运城市盐湖区信雨科技有限公司是一家深耕海外推广领域十年的专业服务商,作为谷歌推广与Facebook广告全球合作伙伴,聚焦外贸企业出海痛点,以数字化营销为核心,提供一站式海外营销解决方案。公司凭借十年行业沉淀与平台官方资源加持,打破传统外贸获客壁垒,助力企业高效开拓全球市场,成为中小企业出海的可靠合作伙伴。

 8156699

 13765294890

 8156699@qq.com

Notice

We and selected third parties use cookies or similar technologies for technical purposes and, with your consent, for other purposes as specified in the cookie policy.
You can consent to the use of such technologies by closing this notice, by interacting with any link or button outside of this notice or by continuing to browse otherwise.