我安装了 BareOS,对默认配置文件的修改很少。执行了完整、增量和差异备份。大多数客户端似乎都按预期进行了备份。
然而,我的一个客户似乎在每个增量周期中都重复备份超过 10% 的整个文件系统。
如何找到重复备份的最大文件和文件夹?
BAT 在这里似乎没什么用,因为它只列出了文件节点本身的大小,而不是整个文件夹的大小。我实际上是在寻找一个du
在 BareOS 框架内适用于特定备份尝试的命令。
答案1
虽然我很欣赏 @damiano-verzulli 的努力,但在 FreeNode 上的 BareOS IRC 频道中的讨论却没有得到这样的回应:
事实证明,Kjetil Torgrim Homme 已经编写了一个脚本来执行此操作,名为bacula-du
。(以及许多其他有用的脚本!)
它们都已列出并可从这里获取:
http://heim.ifi.uio.no/kjetilho/hacks/
具体bacula-du
解释如下:
Usage: bacula-du [OPTIONS] -j JOBID Summarize disk usage of directories included in the backup JOBID Main options are: -a, --all write counts for all files, not just directories -S, --separate-dirs do not include size of subdirectories -t, --threshold=SIZE skip output for files or directories with usage below SIZE. default is 1 octet. -L, --largest=NUM only print NUM largest directories/files There is also an alternate mode which can be useful as a faster alternative to a verify job. Usage: bacula-du --md5sum -j JOBID --md5sum output list of all files in job in md5sum format
巴库拉杜(版本 1.4)
这里我要补充一点。要使此功能正常工作,它必须能够访问数据库(显然)。在默认配置中,它使用基于用户的安全机制,因此您必须以 bareos 用户身份运行该命令才能使其正常工作,例如
$ sudo -u bareos ./bacula-du -j 1429
done reading database.
807160 /log/
6372 /var/openldap-data/
6372 /var/
813532 /admin/
...
119983392 /
答案2
请注意重要的第一部分末尾添加了更新
不幸的是,即使可以很容易地检查特定备份的具体情况,但获取备份文件的文件大小却不那么容易。
让我们通过一些例子来更深入地了解。
就我而言,bconsole
我可以通过以下方式获取最后一份工作的列表:
*list jobs
Automatically selected Catalog: CatalogoBareos
Using Catalog "CatalogoBareos"
+-------+------------------------+---------------------+------+-------+------------+-------------------+-----------+
| JobId | Name | StartTime | Type | Level | JobFiles | JobBytes | JobStatus |
+-------+------------------------+---------------------+------+-------+------------+-------------------+-----------+
[...]
| 7,060 | backup-XXXXXXXX | 2016-01-02 16:00:50 | B | I | 2 | 74,225,116 | T |
| 7,062 | backup-YYY | 2016-01-02 16:04:47 | B | F | 890,482 | 181,800,839,481 | T |
[...]
+-------+------------------------+---------------------+------+-------+------------+-------------------+-----------+
从上面可以看出有两个工作:
- 作业 7060:增量备份,涉及 2 个文件,总计 74MB 数据;
- 作业 7062:一次“完整”备份,有趣的是 890492 个文件,总共 181GB 的数据;
让我们关注 Job 7060,因为它是一个增量作业。让我们检查一下哪个文件已备份:
*list files jobid=7060
c:/allXXX_Backup/PLONE/Backup_Plone.bkf
c:/allXXX_Backup/PLONE/
+-------+-----------------+---------------------+------+-------+----------+------------+-----------+
| JobId | Name | StartTime | Type | Level | JobFiles | JobBytes | JobStatus |
+-------+-----------------+---------------------+------+-------+----------+------------+-----------+
| 7,060 | backup-XXXXXXXX | 2016-01-02 16:00:50 | B | I | 2 | 74,225,116 | T |
+-------+-----------------+---------------------+------+-------+----------+------------+-----------+
*
因此作业 7060 涉及一个文件(Backup_Plone.bkf)和一个目录(包含文件夹)。
不幸的是,正如你所看到的,list files jobid=7060
输出不是提供您需要的文件大小,所以.....希望它有用,但是不是解决你的问题。
让我们向前迈进一步。
我去过bareos 官方文档无法找到从 bconsole 中获取“文件大小”的正确方法。因此我决定采用更复杂的方法:直接通过 SQL 访问目录。
注意:直接访问目录时请务必小心,因为每一个不当操作都可能导致严重损坏和相关的数据丢失!
一旦连接到数据库引擎(在我的情况下是 MySQL......但这是一个细节,与 PostgreSQL 一样),我看到备份文件元数据存储在(...以及其他)中:
File
表:它存储了大部分元数据,但以下情况除外……Filename
表:它存储备份文件的文件名Path
表:存储备份文件的完整路径
令我大吃一惊的是,我发现File
桌子确实不是包含一个size
字段。因此,通过简单的查询不可能获得我们需要的内容。无论如何,我发现了一个有趣的LStat
字段(稍后会详细介绍)。
因此我启动了以下 SQL 查询:
select
f.JobId,f.LStat,f.MD5, fn.Name, p.Path
from
Filename fn,
File f,
Path p
where
f.JobId=7060 and
fn.FilenameId = f.FilenameId and
p.PathId = f.PathId
并得到以下结果:
mysql> select f.JobId,f.LStat,f.MD5, fn.Name, p.Path
-> from
-> Filename fn,
-> File f,
-> Path p
-> where
-> f.JobId=7060 and
-> fn.FilenameId = f.FilenameId and
-> p.PathId = f.PathId
-> ;
+-------+------------------------------------------------------+------------------------+------------------+-------------------------+
| JobId | LStat | MD5 | Name | Path |
+-------+------------------------------------------------------+------------------------+------------------+-------------------------+
| 7060 | A A IH/ B A A A EbJQA A A BWheFw BWheFw BTD/En A A L | 8ZuPGwdo9JYJileo+sVlfg | Backup_Plone.bkf | c:/all***_Backup/PLONE/ |
| 7060 | A A EH/ B A A A A A A BWhRjY BWgz4o BTD/En A A L | 0 | | c:/all***_Backup/PLONE/ |
+-------+------------------------------------------------------+------------------------+------------------+-------------------------+
2 rows in set (0.00 sec)
至于该LStat
领域,在官方 BareOS 开发者指南我看见:
> Column Name Data Type Remark
> [...]
> LStat tinyblob File attributes in base64 encoding
所以,现在的问题是:
- LStat 是否包含文件大小?
并且,我敢打赌,答案是“是的!绝对!”:
- 如何从 LStat 字符串中检索 FileSize?
快速搜索“BareOS LStat”让我得到了几个结果。几秒钟后,我得到了这线程,包括一些关于 LStat 字段的评论,包括一个用于正确解码它的小型 PERL 脚本。它在这里(*由 Brian McDonald 提供,2005 年*),稍加修改以更好地满足您的需要:
#!/usr/bin/perl
my $base64_digits =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
my $base64_values = { };
my $val = 0;
foreach my $letter ( split(//, $base64_digits ) )
{ $base64_values->{ $letter } = $val++; }
print "Please, enter your LSTAT string:";
my $lstat = <STDIN>;
my $vals = decode_stats($lstat);
print "Here are the results:\n";
foreach my $key (keys %{$vals}) {
printf("%15s: %s\n",$key, $$vals{$key}) ;
}
exit;
sub decode_stats
{
my $stats = shift;
my $hash = { };
# The stats data is in base64 format. Yuck! - I mean Yay!
# Each value is base64 encoded incorrectly, a deficiency we have
# to correct here. In particular, some values are encoded with a single
# base64 character. This results in a 6 bit value, and you have to
# understand how bacula padded and manipulated those values before storing
# them in the DB.
# the fields are, in order:
my @fields = qw(
st_dev st_ino st_mode st_nlink st_uid st_gid st_rdev st_size
st_blksize st_blocks st_atime st_mtime st_ctime LinkFI st_flags data
);
# Decoding this mess is based on reading src/lib/base64.c in bacula, in
# particular the to_base64 function, which is how these got in the DB in
# the first place.
my $field_idx = 0;
foreach my $element ( split( /\s+/, $stats ) )
{
my $result = 0;
for ( my $i = 0; $i<length($element); $i++ )
{
if ($i) { $result <<= 6; }
my $r = substr($element, $i, 1 );
$result += $base64_values->{$r};
}
$hash->{ $fields[$field_idx] } = $result;
$field_idx++;
}
return $hash;
}
当启动并输入 LSTAT 字符串时,它会报告大量数据,包括文件大小(st_size,输出的最后一个字段):
verzulli@iMac-Chiara:/tmp$ perl pp.pl
Please, enter your LSTAT string:A A IH/ B A A A EbJQA A A BWheFw BWheFw BTD/En A A L
Here are the results:
LinkFI: 0
st_atime: 1451614576
st_ino: 0
st_flags: 0
st_mtime: 1451614576
st_dev: 0
st_nlink: 1
st_blksize: 0
st_blocks: 0
data: 11
st_gid: 0
st_mode: 33279
st_uid: 0
st_rdev: 0
st_ctime: 1393553703
st_size: 74224640
现在,我们有了文件大小,但不幸的是,它不是通过单个查询即可轻松找到单个备份作业的最大文件。
有几种解决方案:
如果您运行的是 MySQL 5.6.1 或更高版本,或者支持 BASE_64 编码/解码的 DBMS 引擎,则可以查询 LSTAT 的 SUBSTR,然后要求 DB 引擎将其值解码为 Base64 值。例如,请参阅这里
你可以写一个存储过程。实际上它应该已经存在于 PostgreSQL 中,至于这(他们说:“...为 lstat 字段添加了示例 postgresql 存储过程....");
你可以写一个小的 PERL 脚本,查询目录并进行解码
...
希望这就足够了 ;-)
更新 1
我刚刚发现了虚拟文件系统 API,明确地“...主要面向希望为 Bareos 开发新 GUI 界面的开发人员...“。
这些 API 提供了一组新命令(所谓的“点命令”),其中包括一个有趣的功能.bvfs_lsfiles
,它在控制台上显示一些元数据,包括 LSTAT字段。因此:
- 无需直接访问底层 DB/Catalog 即可获取 LSTAT 字段。
此外,BareOS 15.2 引入了新的“API 模式 2”,增加了对 JSON 输出的支持。我刚刚测试了:
- 启用 V.2 API 后,返回的 JSON 对象
.bvfs_lsfiles
,包含文件大小字段,已正确解码。
下面是一个例子:
*.bvfs_update
Using Catalog "MyCatalog"
*.bvfs_lsfiles path=/var/log/2016/01/06/ jobid=79
107131 34080 3614785 79 P0A CCMR IGA B A A A H1V BAA BI BWjIkK BWjJAx BWjJAx A A C shorewall.log
107131 34081 3614786 79 P0A CCMQ IGA B A A A BT1 BAA Q BWjIkK BWjI7p BWjI7p A A C system.log
*.api 2
{
"jsonrpc": "2.0",
"id": null,
"result": {
"api": 2
}
}*
*.bvfs_lsfiles path=/var/log/2016/01/06/ jobid=79
{
"jsonrpc": "2.0",
"id": null,
"result": {
"files": [
{
"type": "F",
"stat": {
"dev": 64768,
"nlink": 1,
"mode": 33152,
"ino": 533265,
"rdev": 0,
"user": "root",
"group": "root",
"atime": 1452050698,
"size": 32085,
"mtime": 1452052529,
"ctime": 1452052529
},
"pathid": 107131,
"name": "shorewall.log",
"fileid": 3614785,
"filenameid": 34080,
"jobid": 79,
"lstat": "P0A CCMR IGA B A A A H1V BAA BI BWjIkK BWjJAx BWjJAx A A C",
"linkfileindex": 0
},
{
"type": "F",
"stat": {
"dev": 64768,
"nlink": 1,
"mode": 33152,
"ino": 533264,
"rdev": 0,
"user": "root",
"group": "root",
"atime": 1452050698,
"size": 5365,
"mtime": 1452052201,
"ctime": 1452052201
},
"pathid": 107131,
"name": "system.log",
"fileid": 3614786,
"filenameid": 34081,
"jobid": 79,
"lstat": "P0A CCMQ IGA B A A A BT1 BAA Q BWjIkK BWjI7p BWjI7p A A C",
"linkfileindex": 0
}
]
}
}*
因此,最终,使用最新版本的 BareOS,原始问题似乎可以解决没有直接访问目录。
答案3
添加到迪亚马诺·韦尔祖利的有用答案,这是一个像他的 perl 代码一样解码 LSTAT 字段的 SQL 存储过程;
它不是非常高效,因为每次运行时它都会重新定义变量。我预计对于大量文件来说,它的速度会比本机代码慢一个数量级。
USE bacula;
-- Function to add. (Only once)
-- OUTPUTS:
-- 1: Device
-- 2: Inode
-- 3: Mode (unix permissions)
-- 4: Hard link count
-- 5: UID
-- 6: GID
-- 7: DeviceType
-- 8: Size in bytes
-- 9: Block size (4k?)
-- 10: Last access
-- 11: Last modified
-- 12: Last changed
-- 13/14/15 Link/flags/misc.
DELIMITER $$
CREATE OR REPLACE FUNCTION base64_decode_lstat (lsIndex INT ,lstat VARCHAR(128)) RETURNS BIGINT
BEGIN
DECLARE i,j,partlen INT;
DECLARE alphabet BINARY(64);
DECLARE part VARCHAR(64);
DECLARE iOut BIGINT;
SET part = TRIM(SUBSTRING_INDEX(SUBSTRING_INDEX(lstat, ' ', lsIndex), ' ', -1));
SET alphabet = BINARY 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
SET i = 0;
SET iOut = 0;
SET partlen = LENGTH(part);
WHILE i < partlen DO
SET j = partlen - i - 1;
-- Note: SUBSTRING is not zero-indexed, so +1 as otherwise off-by-1.
-- Note 2: LOCATE is also not zero-indexed, so subtract 1 to fix off-by-1.
-- Note 3: j is made zero-indexed so it runs from n-1 to 0.
-- This does addition in base-64.
SET iOut = iOut + ((LOCATE(SUBSTRING(part, i + 1, 1), alphabet) - 1) << (6 * j));
SET i = i + 1;
END WHILE;
RETURN iOut;
END $$
DELIMITER ;
使用示例(获取数据库中第一个文件的修改时间的 unix 时间戳);
SELECT base64_decode_lstat(11, (SELECT File.LStat FROM File LIMIT 1));