假设我有一个名为 的池tank
,其中有一个名为 的数据集data
。还有至少一个data
命名的快照last_snapshot
:
tank
tank/data
tank/data@last_snapshot
diff
据我所知,确定数据集是否已更改的“慢”方法是检查 的输出:
zfs diff tank/data@last_snapshot
它将显示自上次快照以来每个单独文件/文件夹的数据集的所有更改。如果有很多更改,此命令会产生大量输出并运行几秒钟甚至几分钟。
一种更快但(据我所知)不太可靠的方法是查看written
数据集的属性:
tank/data written 72K -
tank/data@last_snapshot written 1,83G -
如果数据集(不是任何快照)的值written
介于 56 到 128k 之间,则数据集通常自上次/最近快照以来尚未修改。这要快得多,但在不理解为什么这个数字会变化如此之大的情况下,我不想依赖这种方法。
我如何安全快速地提出是/否问题:自上次快照以来数据集是否已更改/修改?
设计理念(背景):我确实有相当数量的数据集,它们很少会收到“重大”更改,比如每几周一次。每天制作一次快照并保留最后一个(例如 7 个)很快就会导致失去最后一个“重大”更改之前的状态。这就是我的想法:我们只在发生“重大”更改时才制作快照,并保留其中的最后 7 个。
答案1
这是一个好问题,我现在能做的就是投赞成票。
在思考这个问题时,我尝试过的一些猜测途径是:
再次对数据集进行快照,并比较
createtxg
两个快照的(“出生”)属性。在一次测试中,我很幸运并且具有连续的值(这意味着在创建 snapA 和创建 snapB 之间的过渡期间没有发生任何事务)。这个事实后来被证明毫无价值,因为进一步的研究告诉我,事务组在整个池中全局编号,而不是在每个文件系统中唯一编号。createtxg
将快照的值与文件系统的最新事务组进行比较。我不确定,但如果快照createtxg
等于或高于文件系统的最新事务组,则可以推断快照创建本身是已提交到该文件系统的最新事务。我不确定这是否属实,但如果可以将快照createtxg
与文件系统的最新快照进行比较txg
以精确确定快照是否“原始”,那将是偶然的。
此时,我能提供的最佳答案是信任written
文件系统的属性,但仅限于严格的参数范围内:
1)确保所有待处理的事务已提交到文件系统
sync; sleep 1; sync # maybe I'm just being superstitious?
2) 确保文件系统处于非活动状态(未安装)
zfs umount tank/data
written
3)使用-p
以下标志查询属性的确切机器可解析值zfs list
zfs list -Hpo written tank/data
在我推断快照是原始的之前,该数字必须恰好为 0。 “小”还不够好,如果我们把zfs
手册页按面值计算:
以下本机属性包含有关数据集的只读统计信息。这些属性既不能设置,也不能继承。除非另有说明,本机属性适用于所有数据集类型。
...
createtxg
在其中创建数据集的事务组 (txg)。书签
createtxg
与它们最初绑定的快照具有相同的内容。此属性适合对快照列表进行排序,例如增量发送和接收。
...
written
自上一个快照以来写入此数据集的引用空间量。
进一步的承诺是:
written@snapshot
自指定快照以来写入此数据集的引用空间量。这是此数据集引用但指定快照未引用的空间。
需要卸载文件系统,以便当您注意到值为 0 并开始对该状态采取行动时不会出现竞争条件,但与此同时另一个进程正要写入文件系统并破坏您的聚会。
如果有人知道如何查询文件系统最近提交的事务组号,我有义务知道该方法。一些基本的谷歌搜索没有找到任何我认为有用的东西。
另一个有趣的属性是referenced
快照的属性与其父文件系统的属性。
referenced
此数据集可访问的数据量,可能会也可能不会与池中的其他数据集共享。创建快照或克隆时,它最初引用与创建它的文件系统或快照相同的空间量,因为其内容是相同的。
然而,我的预感是这个属性也会导致错误的希望,因为文件系统和快照可能都引用 10G 数据,但它与磁盘上实际块中的 10G 不同,正如短语“可能或可能”所暗示的那样不被共享”。匹配referenced
值似乎更像是必要条件而不是充分条件。
总而言之,带着很大的谦虚和不确定性,我认为人们必须坚持这个zfs list
输出,然后才能假设快照是原始的:
# zfs create -o mountpoint=/root/test w541/test
# zfs snap w541/test@snap1
# zfs list -po written,written@snap1 w541/test
WRITTEN WRITTEN@SNAP1
0 0
弄脏了快照,它就不再原始了:
# touch test/foo
# zfs list -po written,written@snap1 w541/test
WRITTEN WRITTEN@SNAP1
57344 57344
将其滚回去,它又变得原始了:
# zfs rollback w541/test@snap1
# zfs list -po written,written@snap1 w541/test
WRITTEN WRITTEN@SNAP1
0 0
但为了获得最高的完整性,在查询属性之前,应该卸载文件系统,也许需要卸载一段时间written
。
我将非常感谢任何人对任何错误的假设、疏忽或任何好心人提供的额外见解的纠正。
答案2
为了将来的参考,我采用了以下逻辑。经过密集测试,它似乎运行良好。不过,我们非常感谢更好的答案和想法。作为参考,另请参阅zfs-discuss 邮件列表上的讨论。
for dataset in tree:
# If there is no snapshot for this dataset, make one ...
if len(dataset['SNAPSHOTS']) == 0:
make_snapshot(dataset)
continue
# If nothing has been written, no snapshot is required.
if dataset['written'] == 0:
continue
# If written is north of 1 MByte, make a snapshot.
if dataset['written'] > (1024 ** 2):
make_snapshot(dataset)
continue
# If the dataset is not a filesystem and
# we got this far, make a snapshot.
if dataset['type'] == 'volume':
make_snapshot(dataset)
continue
# Only filesystems with less than 1 MByte written left.
# Let's look at the diff of the last snapshot.
diff_out = run_command([
'zfs', 'diff',
dataset['NAME'] + '@' + dataset['SNAPSHOTS'][-1]['NAME']
])
# If the diff is not empty, make a snapshot.
if len(diff_out.strip(' \t\n')) > 0:
make_snapshot(dataset)
答案3
这是一个bash
为递归池处理此问题的脚本,可以根据您的喜好进行更改。
function processChanges() {
POOL=$1
ROWS=$(zfs get -Hr written ${POOL} |grep -v @ |awk '{print $1 "|" $3}' |grep -v "${POOL}|")
for ROW in ${ROWS}; do
CHANGED=0
V=${ROW##*|}
P=${ROW%%|*}
echo -n "Checking ${P}... "
if [ "${V: -1}" = "K" ]; then
BYTES_CHANGED=$(echo "${V:0:-1} * 1024" |bc )
elif [ "${V: -1}" = "M" ]; then
BYTES_CHANGED=$(echo "${V:0:-1} * 1024 * 1024" |bc )
elif [ "${V: -1}" = "G" ]; then
BYTES_CHANGED=$(echo "${V:0:-1} * 1024 * 1024 * 1024" |bc )
else
BYTES_CHANGED=${V}
fi
BYTES_CHANGED=${BYTES_CHANGED%%.*}
MEG=$(( 1 * 1024 * 1024 ))
if [ ${BYTES_CHANGED} -gt ${MEG} ]; then
CHANGED=1;
elif [ ${BYTES_CHANGED} -ne 0 ]; then
LASTSNAP=$(zfs list -t snap ${P} |grep ${P} |sort |tail -n1)
LASTSNAP=$(echo ${LASTSNAP##*@}); LASTSNAP=$(echo ${LASTSNAP%% *});
DIFF=$(zfs diff ${P}@${LASTSNAP})
if [ "${DIFF}" != "" ]; then
CHANGED=2;
fi
fi
if [ ${CHANGED} -gt 0 ]; then
echo "Changed"
else
echo "No changes"
fi
done
}
processChanges s/storage