我的片子放两个目录:一个给 Jellyfin 扫,一个按年份归档。开头图省事,两边各放一份,反正盘大。上个月盘告急,一格一格往下掉,我才发现同一批文件在这块盘上待了两遍。
删掉一份当然最快,但下次整理库又得复制回来。后来换了个办法:文件只留一份数据,两个目录各挂一个名字。微软管这叫硬链接,NTFS 上一直有,只是平时没人提。
复制 1.2GB 用了 1.6 秒,硬链接 45 毫秒,占盘 0
先把条件说清楚,免得数字对不上。Ryzen 5 3550H 的小主机,16GB 内存,单块 512GB 的 NVMe(ASint AS806),Windows 11 26200,盘是 NTFS。样本是四个 300MB 的文件,合计 1.2GB,模拟一季剧的四集。
看剩余空间的差值比看文件属性靠谱,我全程盯着 D 盘可用量:
- 复制一份到第二个目录:1650 毫秒,可用空间从 156377.1MB 掉到 155177MB,正好少了 1200MB
- 删掉复制品:回到 156377MB
- 改成硬链接:45 毫秒,可用空间 156377MB 一动不动
三次里复制是 1400、1477、1650 毫秒,硬链接是 38、45 毫秒。也就是说这 1.2GB 的活,用链接 45 毫秒就干完了,而且一个字节都没多占。
但有个地方特别容易看错:这时候用资源管理器看,或者在 PowerShell 里 Get-ChildItem 统计两个目录,总大小还是 2.4GB。硬链接不会让文件变小,它只是不让第二份数据落地。判断到底省没省,只能看可用空间,或者用 du ——du -s 数硬链接时只算一次,我这边量 A 目录是 1258291200 字节,B 目录直接报 0。
它不是快捷方式,是同一份数据的第二个名字
这点必须掰开说,因为搞错了会丢东西。
我往 A 目录里那个 314572800 字节的文件末尾追加了三个字节,再去看 B 目录:也是 314572803。两个路径指向同一份数据,改哪边都是改同一个东西。fsutil hardlink list 能看到它们:
\work\...\A\ep3.mkv
\work\...\B\ep3.mkv
快捷方式(.lnk)是个几十字节的小文件,双击才跳过去;符号链接是另一个文件系统对象,删掉目标就断了。硬链接不是——它是同一份数据在目录里的另一个条目,没有"谁是原件"这回事。我把 A 目录里的文件全删了,B 目录那个照样能读,字节数一点没变,可用空间也没涨。
反过来说,这也意味着它不是备份。你手滑删了一个名字,另一个名字还在;但数据本身被覆盖、被误改、盘坏了,两边一起没。
只有同一个分区能用,跨盘符直接报错
跨盘试了一次,报错很直白:「系统无法将文件移到不同的磁盘驱动器」。硬链接是分区内的东西,跨不过卷。想把 C 盘的素材链到 D 盘的片库,做不到。
文件系统也有限制:NTFS 可以,exFAT 和 FAT32 没有这个概念,移动硬盘基本别想。
还有一条是我没料到的。我把 A 目录开成 SMB 共享,从 \\localhost\cfhl 这个 UNC 路径去建链接,居然成功了,文件也正常出现在共享里。但这只是因为服务端就是 Windows 自己。换成群晖、威联通这种 Linux 内核加 Samba 的机器,创建硬链接这一步通常直接被拒——Samba 默认不放开这个操作,客户端拿到的就是一句权限不足。所以这条办法的现实适用范围是:挂在 Windows 上的 NTFS 盘,或者你自己能改 Samba 配置的机器。
备份的时候它原形毕露
省下来的空间,一备份就还回去了。
我对这个硬链接目录跑了一遍 robocopy /E,1609 毫秒,可用空间掉了 1200MB——备份过去的是两份独立的数据,不是链接。查备份里那个文件的链接数,是 1,不是 2。顺手加了 /SL 想让它保留链接,没用,那个参数只针对符号链接,对硬链接无效,退出码 1。
所以心里要有数:盘上省一半,备份里翻倍。用按文件复制的工具(robocopy、资源管理器拖拽、大部分同步盘客户端)都是这个结果。按内容去重的备份工具不会,但那是另一套逻辑,我这次只量了 robocopy,没去碰别的。
碎文件上它反而更慢
这条是反直觉的,我量了两遍才信。
2000 个 4KB 的字幕文件,复制一份:PowerShell 的 Copy-Item 用了 2860 毫秒;改成逐个 New-Item -ItemType HardLink 建链接,11541 毫秒,慢了四倍。我以为是 PowerShell 每个命令都有开销,换成 node 的原生接口再测:fs.copyFileSync 2129 毫秒,fs.linkSync 3367 毫秒——还是慢。
道理不难想:4KB 的文件复制本来就便宜,就是读一次写一次;建链接反倒要走一次元数据事务,还要更新目录项。文件小到这份上,省下的那点数据量抵不过多出来的操作。
所以我现在只用它对付大文件:一集几百 MB 的那种,一个季度几十集,省下来的量很实在。字幕、nfo、海报这类碎文件,复制就复制了。
删的时候有个坑:进了回收站空间不释放
最后这个是踩出来的。
我删掉一个硬链接文件,注意是普通删除,进回收站那种。删完去看可用空间,一点没变。再查链接列表,那个文件多出来第三条路径:
\work\...\A\ep3.mkv
\work\...\B\ep3.mkv
\$RECYCLE.BIN\S-1-5-21-...\$RAEDKA6.mkv
回收站里的"删除"其实是把文件挪进 $RECYCLE.BIN 改个名,链接关系还留着,数据自然还在盘上。想要空间回来,得清空回收站,或者删的时候按 Shift 直接删。挂 NAS 的共享文件夹删到 #recycle 也是一个道理。
我现在的用法很朴素:同一个分区内的片库和归档目录之间用硬链接,跨盘老老实实复制;备份前先算一遍空间够不够翻倍;删东西一律 Shift。库里那两个名字,播放器和刮削器按路径认条目,看到的是两条记录,这点得接受。
你们是留一个库,还是分两个库各放一份?