转载

DB2 Advanced Copy Services: DB2 Advanced Copy Services 的脚本化接口,第 2 部分

DB2 10.5 为 DB2 Advanced Copy Services (DB2 ACS) 引入了一个名为脚本化接口 (scripted interface) 的新功能。该功能使得客户能够实现 shell 脚本,而不是 C 库。这些脚本可使用存储供应商提供的工具来运行快照操作 (operation)。脚本化接口的使用可独立于您的存储硬件。此外,在存储硬件上市后,DB2 会迅速提供这方面的支持。

该功能支持 DB2 的所有 3 种架构:企业服务器、使用数据库分区功能 (DPF) 的多分区数据库,以及使用 pureScale 的数据库。在所有通过了 DB2 认证的 UNIX 和 Linux 平台上都支持它。

本系列文章介绍了这项功能,后续文章将会提供一些真实的示例。这是本系列的第二篇文章,将会使用 Linux LVM 演示脚本化接口。

LVM 是用于 Linux 操作系统的逻辑卷管理器(Logical Volume Manager)。除了这些功能之外,它还支持逻辑卷的快照。本文将介绍如何借助 DB2 ACS 脚本化接口捕获 DB2 数据库备份的快照。

Linux LVM 简介

Linux LVM 是一个 Linux 内核扩展,它在存储硬件之上引入了一个抽象层。这提高了存储管理的灵活性;例如,它简化了分区的调整。此外,LVM 允许捕获 LVM 卷组中的分区的快照。

本文使用了图 1 中所示的布局:

图 1. 逻辑卷组

DB2 Advanced Copy Services: DB2 Advanced Copy Services 的脚本化接口,第 2 部分

在图 1中,您可以看到物理卷 /dev/sdb 。LVM 卷组 data 用完了整个物理卷。卷组 data 包含逻辑卷 joerndata1data2logs 。这个卷组中仍然有空闲空间,将被用于捕获这些逻辑卷的快照。逻辑卷使用 ext4 文件系统格式化。

逻辑卷按表 1 中所示进行挂载:

表 1. 挂载的逻辑卷

逻辑卷 挂载点 用途
/dev/data/joern /db2/joern 数据库目录
/dev/data/data1 /db2/data1 自动存储路径 1
/dev/data/data2 /db2/data2 自动存储路径 2
/dev/data/logs /db2/logs 数据库日志

表 2 简短概述了本文中提及的用于 LVM 实用程序的命令,其中还指出了这些命令的用途:

表 2. 命令概述

命令 用途
lvdisplay 显示逻辑卷的信息。具体来讲,选项 –c 用于获取冒号分隔的信息列表,包括大小。关于所包含的列的概述,请查看手册页。
lvcreate 在一个卷组中创建逻辑卷。该命令还用于为包含存储路径和日志路径的逻辑卷创建快照卷。
lvconvert 将快照回禀会包含存储和日志路径的逻辑卷中;也就是说,可将它用于还原快照。
lvchange 更改一个逻辑卷的属性。在某些情况下,有必要取消激活并再次激活逻辑卷,以便成功完成合并。
lvremove 删除逻辑卷,在删除镜像期间使用。
vgdisplay 查询卷组的信息。具体来讲,选项 –c 用于获取冒号分隔的信息列表,包括大小。关于所包含的列的概述,请查看手册页。

LVM 快照使用 lvcreate 命令和 –s 选项创建。LVM 可处理写入时复制;也就是说,在首次更改原始卷上的一个数据块时,该数据块的原始内容的镜像会写入到快照中。最后,该快照仅包含原始卷中更改的数据块,但包含的是在创建该快照时这些数据块所拥有的内容。这可能是很少量的数据。但是因为我们还希望能够还原丢弃的数据库,所以我们建议让快照逻辑卷的大小等于原始卷。如果某个 LVM 快照卷空间不足,那么 Linux 会停止对其执行写入操作,这进而会损坏快照备份镜像。

获取正确的特权

该脚本化接口调用的客户脚本需要借助实例所有者的特权来运行。通常,此用户不允许运行更改 LVM 配置的命令。

在本文中,运行这些命令的特权是使用 sudo 授予的。这些命令可使用命令 visudo 添加到 sudo 配置中,为使用的每个命令添加以下行:

jklauke ALL=NOPASSWD: /sbin/lvdisplay

这样,用户无需在使用该命令时输入根密码。

对于其他平台,可能还有其他解决方案;例如,将实例所有者添加到允许使用合适的命令的分组中。我们不鼓励使用设置了 setuid 位的脚本。

使用 LVM,使用脚本化接口处理快照备份

本节提供一段客户脚本的示例实现,该脚本使用了 Linux LVM 的创建、使用和删除快照的能力。

在这个客户脚本中,需要频繁地使用以下循环:

110    for i in `grep "^DATAPATH" $config | / 111              awk -F= '{print $2}' | / 112              xargs -I/{/} df /{/} | / 113              grep '^/dev' | / 114              awk '{print $1;}' | / 115              uniq ` 116    do

这为您提供了需要在执行快照期间备份的一组逻辑卷。该命令执行以下步骤:

  1. 从协议文件中获取路径列表。在此示例中,作为第 110 行的结果返回的数据路径为:
    DATAPATH=/db2/joern
  2. 将路径名称与选项名称分开(仅获取 DATAPATH=path 中等号后的部分(第 111 行):
    /db2/joern
  3. 将该路径转换为该路径所在的逻辑卷(第 112 行):
    Filesystem 1K-blocks Used Available
    Use% Mounted on/dev/mapper/data-joern 2064208 268980 1690372 14% /db2/joern
  4. 仅获取包含逻辑卷名称的行(第 113 行):
    /dev/mapper/data-joern 2064208 268980 1690372
    14% /db2/joern
  5. 仅获取逻辑卷的名称(第 114 行):
    /dev/mapper/data-joern
  6. 仅获取每个逻辑卷一次(第 115 行):
    /dev/mapper/data-joern
    (在这种情况下,这无关紧要,因为只有一个逻辑卷。)

备份

本文剩余内容将会详细介绍 备份还原删除 操作。这些操作的动作 (action) 已在本文章系列的第 1 部分 中介绍。对于备份,这些动作包括:准备、快照、验证、依赖于验证调用的结果,以及存储元数据或回滚。

再一次声明,我们建议将快照卷的大小设置为与原始卷相同。

准备

LVM 脚本中的 准备 动作的目的是确定每个卷组中是否拥有足够的空闲空间,以便能够将每个逻辑卷都备份到它的卷组中。为此,我们实现了 doPrepare() 函数,如清单 1 所示:

清单 1. doPrepare() 函数代码

65 doPrepare() { 66 # 67 # P R E P A R E 68 # 69 # -------------------------------------- 70    getSetting "OPERATION" 71    operation=$_setting 72    if [ $operation = "SNAPSHOT" ] 73    then 74       for group in `sudo vgdisplay -c | awk -F: '{print $1}'` 75       do 76          freespace=`sudo vgdisplay -c $group | / 77                     awk -F":" '{print $16}'` 78          cmd="egrep '^DATAPATH|^LOGPATH' $config | / 79               awk -F= '{print /$2}' | / 80               xargs -I/{/} df /{/} | / 81               grep '^/dev' | / 82               awk '{print /$1}' | / 83               uniq | / 84               xargs -I/{/} sudo lvdisplay -c /{/} | / 85               awk -F: -v c=$group '/$2==c { SUM += /$8 } END {print SUM}'" 86 87          echo "# cmd: "$cmd 88          neededspace=`eval $cmd` 89 90          if [ $neededspace -gt $freespace ] 91          then 92             echo "# Goup: " $group 93             echo "# Freespace: " $freespace 94             echo "# Needed Space: " $neededspace 95             RC=$RC_NOT_ENOUGH_SPACE 96             break 97          fi 98       done 99    fi 100 # -------------------------------------- 101 }

函数 doPrepare() 将执行以下步骤:

  1. 确定 快照 操作当前是否在运行。因此,从协议文件读取选项 OPERATION,检查它,以确定当前操作是否为 快照 操作(第 70 – 73 行)。
  2. 检查每个卷组;对于 vgdisplay 中的每个卷组,获取该卷组的名称并将该名称与给定字符串分开(第 74 行)。
  3. 确定当前卷组的空闲空间:获取 vgdisplay 输出并仅分析第 16 列(第 76 和 77 行)。
  4. 查找该数据库的位于此卷组中的所有路径。首先,按上一节中所述来确定逻辑卷(第 78 – 83 行)。
  5. 对于每个逻辑卷,获取 lvdisplay 的信息(第 84 行)。
  6. 统计位于当前卷组中的所有逻辑卷所需的空间(第 85 行)。
  7. 将该命令写入协议文件,以便将它用于调试之目的,然后运行该命令(第 87 和 88 行)。
  8. 检查此卷组中的快照所需的空间是否小于空闲空间。如果不是,则返回一个错误(第 90 到 97 行)。

快照

现在我们已确定拥有足够的空间,我们将使用函数 doSnapshot() 获取一个快照,如清单 2 所示:

清单 2. doSnapshot() 函数代码

103 doSnapshot() { 104 # 105 # S N A P S H O T 106 # 107 # -------------------------------------- 108    getSetting "TIMESTAMP" 109    timestamp=$_setting 110    for i in `grep "^DATAPATH" $config | / 111              awk -F= '{print $2}' | / 112              xargs -I/{/} df /{/} | / 113              grep '^/dev' | / 114              awk '{print $1;}' | / 115              uniq ` 116    do 117       vol=`sudo lvdisplay -c $i | awk -F: '{print $1;}'| tr -d ' '` 118       echo "USER_VOLUME_DATA="$vol 119       snapName=`basename $vol`"_snap_"$timestamp 120       sudo lvcreate -s -n $snapName -l100%ORIGIN $vol 121    done 122    getSetting "DB2BACKUP_LOGS" 123    includeLogs=$_setting 124    if [ $includeLogs = "INCLUDE" -a $RC -eq 0 ] 125    then 126       for i in `grep "^LOGPATH" $config | / 127                 awk -F= '{print $2}' | / 128                 xargs -I/{/} df /{/} | / 129                 grep '^/dev' | / 130                 awk '{print $1;}' | / 131                 uniq ` 132       do 133          vol=`sudo lvdisplay -c $i | awk -F: '{print $1;}'| tr -d ' '` 134          echo "USER_VOLUME_LOG="$vol 135          snapName=`basename $vol`"_snap_"$timestamp 136          sudo lvcreate -s -n $snapName -l100%ORIGIN $vol 137       done 138    fi 139 # -------------------------------------- 140 }
  1. 确定镜像的时间戳后(第 108 和 109 行),在标准命令后的第一个循环中获取存储数据的每个逻辑卷(第 110 到 116 行)。
  2. 将设备映射器转换为逻辑卷的真实名称(第 117 行)。
  3. 为了更容易还原,可将用作快照的逻辑卷写入协议文件中(第 118 行)。
  4. 通过将时间戳附加到原始逻辑卷的名称后,生成新快照的名称(第 119 行)。
  5. 运行快照(第 120 行)。

此命令的选项如下所示:

  • -s :创建逻辑卷 $vol 的快照
  • -n :提供新逻辑卷的名称
  • -l100%ORIGIN :将新逻辑卷的大小设置为与原始卷相同。

在前几步中,获取了 data 逻辑卷的快照。现在我们需要获取日志卷的快照。首先确定这是否有必要这样做(第 122 到 125 行)。

以下行对日志卷执行了与数据卷相同的操作:确定逻辑卷,使用前缀 USER_VOLUME_LOG 存储逻辑卷,以便将它用于还原之用途,并运行快照(第 126 到 137 行)。

验证

清单 3. doVerify() 函数代码

238 doVerify() { 239 # 240 # V E R I F Y 241 # 242 # -------------------------------------- 243    mkdir /tmp/verify 244    getSetting "TIMESTAMP" "" $oldConfig 245    timestamp=$_setting 246    for i in `grep "^USER_VOLUME_DATA" $config | / 247              awk -F= '{print $2}'` 248    do 249       vol=$i"_snap_"$timestamp 250       sudo mount $vol /tmp/verify 251       $RC=$? 252       sudo umount /tmp/verify 253       if [ $RC -neq 0 ] 254       then 255          echo "# Mounting of $vol failed" 256          break 257       fi 258       echo "# Volume $i checked" 259    done 260    getSetting "DB2BACKUP_LOGS" 261    includeLogs=$_setting 262    if [ $includeLogs = "INCLUDE" -a $RC -eq 0 ] 263    then 264       for i in `grep "^USER_VOLUME_LOG" $config | / 265                 awk -F= '{print $2}'` 266       do 267          vol=$i"_snap_"$timestamp 268          sudo mount $vol /tmp/verify 269          $RC=$? 270          sudo umount /tmp/verify 271          if [ $RC -neq 0 ] 272          then 273             echo "# Mounting of $vol failed" 274             break 275          fi 276          echo "# Volume $i checked" 277       done 278    fi 279    rmdir /tmp/verify 280 # -------------------------------------- 281 }

为了确定快照是否已成功捕获,该脚本会尝试挂载每个快照的逻辑卷,检查此操作是否成功,然后再次卸载它。具体而言,该脚本将运行以下步骤:

  1. 在系统的目录结构中创建一个用作挂载点的临时目录(第 243 行)。
  2. 读取从协议文件中生成快照名称所需的时间戳(第 244 到 254 行)。
  3. 对于协议文件中的每个数据源(第 246 到 248 行),生成快照卷(第 249 行)的名称,并尝试将它挂载到临时目录(第 250 行)。
  4. 检查挂载是否成功(第 251 行)。
  5. 卸载该卷(第 252 行)。
  6. 如果挂载该卷失败,则会返回一条错误消息(第 253 到 257 行)。

如果需要包含日志文件,该脚本必须检查这些卷的快照是否成功:

  1. 查看包含来自协议文件的日志的选项(第 260 和 261 行)。
  2. 检查是否必须包含日志,以及数据卷的挂载是否成功(第 262 行)。
  3. 对于协议文件中的每个日志卷(第 264 和 265 行),生成快照卷的名称(第 267 行)
  4. 尝试挂载该卷(第 268 卷)
  5. 跟踪卷挂载成功与否(第 269 行)并卸载它(第 270 行)。
  6. 检查挂载的结果,如果失败,则返回一条错误消息(第 271 到 275 行)。

最后,删除临时目录(第 280 行)。

回滚

如果在 验证 动作期间出现错误,也就是说,如果至少一个快照卷未能挂载,那么下一个要调用的动作将是 回滚 。此过程的实现如清单 4 所示:

清单 4. doRollback() 函数代码

295 doRollback() { 296 # 297 #  R O L L B A C K 298 # 299 # -------------------------------------- 300    getSetting "OPERATION" 301    operation=$_setting 302    if [ $operation = "SNAPSHOT" ] 303    then 304       for i in `grep "^VOLUME_DATA" $config | / 305                 awk -F= '{print $2}'` 306       do 307          sudo lvremove $i_snap_$timestamp 308       done 309       getSetting "DB2BACKUP_LOGS" 310       includeLogs=$_setting 311       if [ $includeLogs = "INCLUDE" -a $RC -eq 0 ] 312       then 313          for i in `grep "^VOLUME_LOG" $config | / 314                    awk -F= '{print $2}'` 315          do 316             sudo lvremove $i_snap_$timestamp 317          done 318       fi 319    fi 320 # -------------------------------------- 321 }

基本来讲,回滚会运行以下步骤来删除已存在的所有快照:

  1. 检查这是否是一个 快照 操作的回滚(第 300 到 303 行)。
  2. 对协议文件中包含的所有数据卷执行循环(第 304 到 306 行)。
  3. 使用 lvremove 删除该逻辑卷(第 307 行)。这个 lvremove 命令仅获取卷名称作为参数。
  4. 检查是否拥有日志的快照卷(第 309 到 312 行)。
  5. 循环日志卷并删除它们(第 316 行)。

Storemetadata

Storemetadata 不执行任何命令。Storemetadata 可将协议文件保存到任何备份基础架构中。如第 1 篇文章中所述,协议文件对还原镜像必不可少。在调用 storemetadata 时,不再需要向协议文件添加更多的信息;也就是说,可按原样使用它。

还原

还原的用途显而易见:它将快照卷合并到原始卷中;换句话说,在原始卷中,它将每个更改的数据块替换为来自快照的原始数据块。清单 5 显示了该实现:

清单 5. doRestore() 函数代码

142 doRestore() { 143 # 144 # R E S T O R E 145 # 146 # -------------------------------------- 147 148    getSetting "OBJ_ID" 149    id=$_setting 150    # Construct key to search for in currenct protocol file 151    key="RESULT_"$id"_FILE" 152    getSetting $key 153    oldConfig=$_setting 154 155    getSetting "TIMESTAMP" "" $oldConfig 156    timestamp=$_setting 157    for i in `grep "^USER_VOLUME_DATA" $oldConfig | / 158              awk -F= '{print $2}'` 159    do 160       vol=$i"_snap_"$timestamp 161       echo "# Unmounting volume $vol" 162       sudo umount -f $i 163       echo "# Merging volume $vol" 164       sudo lvconvert --merge --background $vol 165       if [ $? -neq 0 ] 166       then 167          echo "# Deactivating volume $vol" 168          sudo lvchange -an $i 169          echo "# Activating volume $vol" 170          sudo lvchange -ay $i 171       fi 172       echo "# Mounting volume $vol" 173       sudo mount $i 174       echo "# Take the backup of volume $vol again" 175       sudo lvcreate -s -n $vol -l100%ORIGIN $i 176    done 177    # if logs included 178    getSetting "ACTION" 179    readAction=$_setting 180    if [ $readAction = "DB2ACS_ACTION_READ_BY_OBJECT" ] 181    then 182       for i in `grep "^USER_VOLUME_LOG" $oldConfig | / 183                 awk -F= '{print $2}'` 184       do 185          vol=$i"_snap_"${timestamp} 186          echo "# Umounting volume $vol" 187          sudo umount -f $i 188          echo "# Merging volume $vol" 189          sudo lvconvert --merge --background $vol 190          if [ $? -neq 0 ] 191          then 192             echo "# Deactivating volume $vol" 193             sudo lvchange -an $i 194             echo "# Activating volume $vol" 195             sudo lvchange -ay $i 196          fi 197          echo "# Mounting volume $vol" 198          sudo mount $i 199          echo "# Take the backup of volume $vol again" 200          sudo lvcreate -s -n $vol -l100%ORIGIN $i 201       done 202    fi 203 # -------------------------------------- 204 }

在还原期间,打开了两个协议文件:已更改的当前还原操作的协议文件,以及以只读方式打开的快照备份操作的协议文件。第二个协议文件必须先打开。为此,将会从还原协议文件中读取对象 ID(第 148 和 149 行),生成选项名称(第 151 行),然后读取备份协议文件的名称(第 152 和 153 行)。现在,从协议文件还原并执行相应的镜像,如下所示:

  1. 读取从协议文件生成逻辑卷名称所需的时间戳(第 155 和 156 行)。
  2. 循环可在协议文件中找到的每个数据卷(第 157 到 176 行)。
  3. 生成快照卷名称(第 160 行)。
  4. 卸载原始的逻辑卷(第 162 行)。
  5. 合并快照卷(第 164 行)。
  6. 如果此操作未成功,则取消激活并再次激活该卷(第 165 到 171 行)。
  7. 挂载合并的原始卷(第 173 行)。
  8. 因为合并会删除快照卷,所以有必要立即重新创建快照卷(第 175 行)。

按以下方式处理逻辑卷:

  1. 从还原协议文件中读取动作选项(第 178 和 179 行)。依赖于此选项,可能必须还原日志卷。如果它的值为 DB2ACS_ACTION_READ_BY_OBJECT,则需要还原。如果它的值为 DB2ACS_ACTION_READ_BY_GROUP,则不需要还原。
  2. 在以下步骤中,使用与还原数据源相同的步骤还原日志卷(第 182 到 201 行)。

删除

删除 动作(如清单 6 所示)运行以下步骤从相应的卷组中删除快照卷:

清单 6. doDelete() 函数代码

206 doDelete() { 207 # 208 # D E L E T E 209 # 210 # -------------------------------------- 211    getSetting "RESULT_"${objectId}"_FILE" 212    oldConfig=$_setting 213    getSetting "TIMESTAMP" "" $oldConfig 214    timestamp=$_setting 215    for i in `grep "^USER_VOLUME_DATA" $oldConfig | / 216              awk -F= '{print $2}'` 217    do 218       vol=$i"_snap_"${timestamp} 219       echo "# Volume $vol" 220       echo "# "`sudo lvremove -f $vol` 221    done 222    getSetting "DB2BACKUP_LOGS" "" $oldConfig 223    includeLogs=$_setting 224    if [ $includeLogs = "INCLUDE" ] 225    then 226       for i in `grep "^USER_VOLUME_LOG" $oldConfig | / 227                 awk -F= '{print $2}'` 228       do 229          vol=$i"_snap_"${timestamp} 230          echo "# Volume $vol" 231          echo "# "`sudo lvremove -f $vol` 232       done 233    fi 235 # -------------------------------------- 236 }
  1. 使用该命令和 –o 选项检索对象 ID。
  2. 使用此对象 ID 生成选项名称,然后检索备份协议文件的名称(第 211 行)。
  3. 读取此文件(第 212 行)。
  4. 现在,可根据需要读取时间戳,以便生成快照的逻辑卷的名称(第 213 到 214 行)。
  5. 循环所有数据卷(第 215 到 221 行),生成快照卷的名称(第 218 行),然后删除该卷(第 220 行)。
  6. 检查备份镜像是否包含日志文件;也就是说,检查备份协议文件中 DB2BACKUP_LOGS 的值是否被设置为 INCLUDE。
  7. 如果是,则通过生成这些快照卷的名称(第 229 行),并在循环中删除它们(第 231 行),从而删除这些卷(第 226 到 232 行)。

回页首

结束语

本文演示了如何使用 LVM 来创建 DB2 数据库的快照镜像。文中详细解释了所有操作的所有动作。您应该很容易看出,使用脚本化接口实现客户脚本非常简单。

回页首

下载

描述 名字 大小
脚本下载 dm-1310acs_lvm.sh ---
正文到此结束
Loading...