开源技术 * IBM 微讲堂:Kubeflow 系列(观看回放 | 下载讲义) 了解详情

Db2 11.5 新特性:高级事务日志空间管理

2019 年 6月 27日 IBM 正式发布 Db2 11.5 基于 AI 的新一代数据库。作为曾经的 Db2 开发人员及三线支持人员,并且现在还在从事数据库运维工作的 DBA,笔者第一时间去关注和了解 Db2 11.5 的新特性,然后感叹一声,这还是曾经熟悉的 Db2 吗 ,自然语言查询(natural language querying)使数据查询拥有了像搜索引擎一样的体验;数据库能够像数据科学家一样工作即支持流行的数据科学语言如 Go、Ruby 等助力数据科学家快速的构建 AI 应用;使用了机器学习算法的优化器能够显著提高查询速度……

由于对 AI 还处于懵懂状态,也就无法切身体会以上这些内容所带来的震撼和颠覆,然后继续浏览 Db2 知识中心 “What’s new…” 里的新功能,支持外部表(External Table )即可以把数据保存在 Db2 之外但仍可以通过 Db2 查询这些数据;更丰富的监控接口和监控元素例如可以监控 SQL 失败率;性能方面的多个提升包括支持 4K 扇 区设备;表压缩和空间释放方面的改进;SQL兼 容性提升;值得一提的是对 Db2 pureScale 的改进,包括重用空闲 page、简化的 GDPC 部署过程、缩短了 CF 切换时间、多个 XI(cross invalidation)连接以提高 GDPC 集群的 TPS(在以前的版本只有一个 XI 连接,现在终于改进了)、GPFS 的变化、改进 castout 性能等。看完了以上这些新特性,终于找回了熟悉的感觉。

浏览到最后,发现了这样一个新特性,高级事务日志空间管理(advanced log space management),有了它 DBA 们再也不用担心”数据库的事务日志已满”的问题了。说的夸张了一点儿,毕竟目前这个功能还是 tech preview,也就是不建议用在生产环境。但无论如何,对 DBA 来说这都是一个振奋人心的消息,笔者还是想和大家一起来探索和验证一下这个新特性能给我们带来什么样的惊喜。

<h2 id=”重现-” 数据库的事务日志已满”-问题>重现 “数据库的事务日志已满” 问题

对于 Db2 DBA 以及 Db2 应用开发者来说,这个报错肯定不陌生,”SQL0964C 数据库的事务日志已满。 SQLSTATE=57011″,或者英文版本的 “SQL0964C The transaction log for the database is full.”,再或者应用程序返回的 “DB2 SQL Error: SQLCODE=-964, SQLSTATE=57011″,如果去看 db2diag.log,还会发现这样的信息,”Log File has reached its saturation point” 以及 “DIA8309C Log file was full.”,不同的信息,说的都是同一个问题。我们先用简单的场景来重现一下。

准备环境

本文所有的操作是在 Linux平台的 Db2 11.5 GA 版本上进行,从 IBM 官方网站可以下载,安装过程和之前版本没什么变化,装完之后发现是 “IBM DB2 Developer-C Edition”,不像以前都是 “Trial” 版本而且 30 天过期。Db2 版本信息和 license 信息如清单 1 所示。

清单 1. Db2 11.5 版 本信息和 license 信息

> db2level
DB21085I  This instance or install (instance name, where applicable:
"db2inst1") uses "64" bits and DB2 code release "SQL11050" with level
identifier "0601010F".
Informational tokens are "DB2 v11.5.0.0", "s1906101300", "DYN1906101300AMD64",
and Fix Pack "0".
Product is installed at "/db2/software/11.5".

> db2licm -l
Product name:                     "IBM DB2 Developer-C Edition"
License type:                     "Community"
Expiry date:                      "Permanent"
Product identifier:               "db2dec"
Version information:              "11.5"
Max amount of memory (GB):        "16"
Max number of cores:              "4"
Max amount of table space (GB):   "100"

创建一个新的数据库 MYDB。生产环境通常都是使用归档日志模式,因此本文的环境也采用归档日志模式,不会面面俱到的涵盖循环日志的情况。建库以及调整归档日志模式命令如清单 2 所 示。

清单 2. 创建 MYDB 数 据库,调整为归档日志模式

     > > db2 create db mydb
DB20000I  The CREATE DATABASE command completed successfully.

> db2 get db cfg for mydb |grep LOGARCHMETH1
First log archive method                 (LOGARCHMETH1) = OFF

> mkdir -p /DATA/arc_log

> db2 update db cfg for mydb using LOGARCHMETH1 disk:/DATA/arc_log
DB20000I  The UPDATE DATABASE CONFIGURATION command completed successfully.

> db2 backup db mydb to /dev/null
Backup successful. The timestamp for this backup image is : 20190914070415

> db2 get db cfg for mydb |grep LOGARCHMETH1
 First log archive method                 (LOGARCHMETH1) = DISK:/DATA/arc_log/

活动日志空间相对较小的场景

引起数据库的事务日志已满的问题,一种比较常见也比较容易理解的场景是,Db2活 动日志空间较小,但数据库有大事务或者高并发的短事务。Db2 活 动日志空间大小是由事务日志相关参数 LOGPRIMARY、LOGSECOND 和 LOGFILSIZ 决定的,即 Db2 活动日志空间大小=(LOGPRIMARY + LOGSECOND)* LOGFILSIZ * 4KB

为了比较容易重现问题,首先调小 Db2 活动日志空间,命令如清单 3 所 示。

清单 3. 调小活动日志空间

> > db2 get db cfg for mydb |egrep 'LOGFILSIZ|LOGPRIMARY|LOGSECOND'
 Log file size (4KB)                         (LOGFILSIZ) = 1024
 Number of primary log files                (LOGPRIMARY) = 13
 Number of secondary log files               (LOGSECOND) = 12

> db2 update db cfg for mydb using LOGPRIMARY 3 LOGSECOND 2
> db2 disconnect all; db2 terminate;
> db2 deactivate db mydb; db2 activate db mydb

> db2 get db cfg for mydb |egrep 'LOGFILSIZ|LOGPRIMARY|LOGSECOND'
 Log file size (4KB)                         (LOGFILSIZ) = 1024
 Number of primary log files                (LOGPRIMARY) = 3
 Number of secondary log files               (LOGSECOND) = 2

在清单 3 中可以看到,Db2 活动日志空间由默认的(13 + 12)* 1024 * 4KB = 100MB,缩小为(3 + 2)* 1024 * 4KB = 20MB

然后分别模拟大事务和高并发的短事务情况下数据库的事务日志已满的问题,命令和结果如清单 4 和清单 5 所示。

清单 4. 模拟大事务

> db2 connect to mydb
> db2 "create table t1 like syscat.tables"
> db2 "insert into t1 select * from syscat.tables"
> db2 "insert into t1 select * from t1"
> db2 "insert into t1 select * from t1"
...... //继续重复执行insert
> db2 "insert into t1 select * from t1"
DB21034E  The command was processed as an SQL statement because it was not a
valid Command Line Processor command.  During SQL processing it returned:
SQL0964C  The transaction log for the database is full.  SQLSTATE=57011

> less ~/sqllib/db2dump/DIAG0000/db2diag.log
MESSAGE : ZRC=0x85100009=-2062548983=SQLP_NOSPACE
          "Log File has reached its saturation point"
          DIA8309C Log file was full.

清单 5. 模拟高并发短事务

> cat insert.sh
#!/bin/sh
db2 connect to mydb
while :; do db2 "insert into t1 select * from syscat.tables" >> res.out;done

<  for ((i=1;i< =40;i++)); do  nohup sh insert.sh &  done

> less res.out
DB21034E  The command was processed as an SQL statement because it was not a
valid Command Line Processor command.  During SQL processing it returned:
SQL0964C  The transaction log for the database is full.  SQLSTATE=57011

在清单 4 中 ,随着每个 insert 插入的数据行数的增加,事务也越来越大,最终单个 insert 需要的日志空间超过了活动日志空间,于是 insert 失败并报错 SQL0964C 即数据库日志空间已满,同时 db2diag.log 也有相应的错误信息。 在清单5中 ,每个 insert 插入的数据行数是固定的,但因为是并发插入,在并发数比较高时也会发生数据库日志空间已满的错误,当并发数减少之后,问题可以缓解或者消失。

长事务的场景

还有一种比较特殊而且危害很大的情况,事务很小,但由于应用程序代码缺陷或者人为操作遗漏导致事务长时间不提交甚至永远不提交(简称长事务),进而导致发生数据库事务日志已满问题,并且此后数据库上所有的交易(非查询类)都会失败,直到长事务结束或者被force掉 。模拟长事务场景的命令和结果如清单6所 示。

清单 6. 模拟长事务

//命令行终端1,模拟长事务
> db2 +c "insert into t1 select * from syscat.tables fetch first 1 row only"
DB20000I  The SQL command completed successfully.

//命令行终端2,模拟正常事务
> while :; do db2 "insert into t1 select * from syscat.tables"; done
//等待一会儿之后开始报错
SQL0964C  The transaction log for the database is full.  SQLSTATE=57011

//命令行终端1,长事务提交
> db2 commit
DB20000I  The SQL command completed successfully.

//命令行终端2,其他事务不再报错
DB20000I  The SQL command completed successfully.
......

在清单 6 中 ,一个事务没有提交,在一段时间内其他的事务可以正常提交,从某一时间开始所有的 insert 都持续报错 SQL0964C 即数据库日志空间已满,直到长事务提交才恢复正常。另外说明一下,当前环境的 Db2 活动日志空间很小,但即使把活动日志空间调的很大,所能起到的效果也不过是晚点儿开始报错而已。

问题的分析及如何应对

引起数据库的事务日志已满的问题的场景有多种,总结起来主要是以下几种情况:

  1. 数据库事务日志相关参数(LOGPRIMARY, LOGSECOND, LOGFILSIZ)配置不合理,活动日志空间偏小,当数据库的 TPS 增 大时部分应用请求报错,交易成功率低,并且可能反复发生。
  2. 对于正常运行的数据库系统,也可能由于突发的业务高峰导致短时间内占满数据库活动日志空间,这期间部分应用请求会报错,交易成功率低,而当业务高峰过去之后不再出现该错误,系统能够自动的恢复正常。
  3. 对于活动日志空间足够大的数据库系统,还可能由于计划外的大事务例如一个事务里修改了大量的数据,导致该大事务以及部分其他的应用请求报错事务日志已满,这种情况在大事务失败回滚之后,活动日志空间可以被释放。
  4. 危害最大的情况是由于长事务导致发生日志已满问题,必须及时定位和结束长事务,否则数据库彻底无法提供服务。

对于前两种情况,都可以认为是高并发事务而活动日志空间相对较小引起的,应急处理方案是临时调大 LOGSECOND 参数,在事后调整数据库事务日志相关参数,增加活动日志空间,对于突发业务高峰还可以考虑从应用系统的角度增加限流机制。对于第三种情况,可以设置数据库参数 MAX_LOG,该参数限制单个应用所占用的日志空间占主日志空间的最大百分比,当大事务的日志达到该百分比时,Db2 会 杀掉该应用,应用的事务回滚并且报错 ERRORCODE=-4499, SQLSTATE=08001,从而可以有效避免因为大事务导致的事务日志已满的问题。

而对于最后一种情况,应急调大 LOGSECOND 参数可以临时解决问题,恢复交易成功率,但最终还是需要尽快找出并解决”罪魁祸首”的长事务,这就意味着无论半夜几点,DBA 都 需要起床来分析处理。

为什么个别的长事务,即使其所占的活动日志空间很小并且后续的其他事务都及时提交了,最终还会导致数据库日志空间满并且所有的交易都会失败的严重后果呢?这是由于Db2事 务日志的机制决定的。Db2 的活动日志空间由这三个参数 LOGPRIMARY(主日志文件数),LOGSECOND(辅助日志文件数)和 LOGFILSIZ(日志文件大小)控制,其中 LOGPRIMARY和 LOGSECOND 总 数不能超过 256,而且只有 LOGSECOND 可以在线配置。Db2 按顺序写日志文件,当日志文件中的活动事务日志全部提交或者回滚之后,这个日志文件可以被归档然后 Db2 会 生成新的日志文件。如果一个事务长时间没有提交或者回滚,其日志所在的日志文件无法被归档,而同时其他的应用请求逐渐用完其他所有的日志文件,此时就会发生日志空间已满的问题。

简单的说,长事务就像一颗钉子一样钉住一个日志文件,而日志文件又是总数固定并且顺序使用,一段时间之后,Db2 就无法归档也无法生成新的日志文件,于是问题就发生了。

Db2 11.5 高级事务日志空间管理带来的变化

根据介绍,Db2 11.5 高级事务日志空间管理功能,能够主动发现可能引起数据库事务日志已满问题的长事务,并且把该长事务的日志数据从活动日志文件中抽取出来,单独的放在一个为该事务专用的日志文件中,这样一来原来所在的活动日志文件可以被归档,Db2 可以继续创建新的日志文件。日志的抽取是由新的 EDU db2loggx 负责的,当日志抽取发生的时候,新的文件会出现在活动日志所在的目录,包括X\<log file number\>_TID\<transactionid\>.LOG,X\<log file number\>.TMP和 X\<log file number\>.META,分别是长事务的日志内容,抽取日志时的临时元数据文件和抽取日志之后元数据文件。IBM 提供的文档中对这部分的解释比较简略,接下来实际验证一下效果。

启用高级事务日志空间管理

在 Db2 11.5 中引入了新的注册表变量 DB2_ADVANCED_LOG_SPACE_MGMT,默认为 OFF,要使用高级事务日志空间管理功能,数据库必须使用归档日志模式,设置该变量为 ON,然后重新激活数据库使该参数生效。命令如清单 7 所 示。

清单 7. 启用高级事务日志空间管理

> db2set DB2_ADVANCED_LOG_SPACE_MGMT=ON
> db2 force applications all; db2 terminate
> db2 deactivate db mydb; db2 activate db mydb
> db2set -all
[i] DB2_ADVANCED_LOG_SPACE_MGMT=ON
[i] DB2COMM=TCPIP

> less ~/sqllib/db2dump/DIAG0000/db2diag.log
......
EDUID   : 22                   EDUNAME: db2loggx (MYDB) 0
......
Log extraction under advanced log space management has been enabled for database.
 Primary extraction path = /home/db2inst1/db2inst1/NODE0000/SQL00001/LOGSTREAM0000/
  Mirror extraction path = Not set

在清单 7 中可以看到,设置参数并重新激活数据库之后,在 db2diag.log 中有信息显示,数据库已经启用了高级事务日志空间管理功能,日志抽取的存放目录即当前的活动日志目录。同时注意到,这条信息是叫做 db2loggx 的 EDU 写的,这是 11.5 版本中新引入的 EDU。

重试长事务场景

在启用了高级事务日志空间管理功能之后,再次尝试长事务场景,验证该功能所带来的变化。步骤和清单 4 类似,不过稍微有些变化,把活动日志空间调回到初始的 13 和 12,每次 insert 操作的行数也相对较少,还要多开几个终端进行观察。

清单 8. 启用高级事务日志空间管理后的长事务场景

//命令行终端1,模拟长事务
> db2 +c "insert into t1 select * from syscat.tables fetch first 100 row only"
DB20000I  The SQL command completed successfully.

//命令行终端2,模拟正常的事务
> while :; do db2 "insert into t1 select * from syscat.tables fetch first 100 row only"; sleep 1; done

//命令行终端3,观察活动日志目录
> cd /home/db2inst1/db2inst1/NODE0000/SQL00001/LOGSTREAM0000/
>  while :; do ls -lrt ;sleep 3; done

//命令行终端4,用db2pd观 察log情 况
> while :; do db2pd -d mydb -logs; sleep 3; done

如果没有启用Db2高 级事务日志空间管理功能,一段时间之后在命令行终端 2 会出现 SQL0964C 的报错。但这次没有报错,而是在命令行终端 3 看到活动日志目录中除了以 S 开头的 .LOG 之外出现新的文件,包括 X0000001_TID0000000000000185.LOG 文件,多个以 X 开头的 .META 文件,以及一个以 X 开头的 .TMP 文件。这些文件应该是上文介绍的长事务的日志内容,抽取日志时的临时元数据文件和抽取日志之后元数据文件,如果用 strings 命令查看新生成 .LOG 文件,可以看到文件开头有 “EXTRALOG” 标记,后面跟着的应该是 syscat.tables 里的数据行即 insert 的内容。如清单 9 所示。

清单 9. 抽取的长事务的日志文件

> cd /home/db2inst1/db2inst1/NODE0000/SQL00001/LOGSTREAM0000/
> ls |grep X
X0000001.META
X0000001_TID0000000000000185.LOG
X0000002.META
X0000003.META
......
X0000022.TMP

> strings X0000001_TID0000000000000185.LOG
EXTRALOG
NYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY
 NRN
SYSIBM  SYSTABLESSYSIBM  SYSCATSPACE                                SYSIBMIDENTITYSYSIBMIDENTITYSYSIBM

监控日志抽取行为

在 Db2 高级事务日志空间管理功能的介绍中提到,可以用多种方式监控对长事务的活动日志抽取行为,包括:

  • MON_GET_TRANSACTION_LOG
  • MON_GET_UNIT_OF_WORK
  • MON_GET_UNIT_OF_WORK_DETAILS
  • db2pd –logs

再去 Db2 知识中心查看以上 MONGET 函数,可以发现在 Db2 11.5 中增加了多个监控元素,例如在 MON_GET_TRANSACTION_LOG 中增加了 13 个 LOG_EXTRACTION* 监控元素,在 MON_GET_UNIT_OF_WORK和 MON_GET_UNIT_OF_WORK_DETAILS 中增加了 LOG_EXTRACTION_DISK_SPACE_USED 监控元素。db2pd 的介绍中没有提到新的输出内容,但从清单 8 的 db2pd 结果中可以看到增加了 “Current Log to Extract”,其值随着时间的变化也在不断的增加。清单 10 是 MON_GET_UNIT_OF_WORK 的结果输出,可以看到对该事务的日志抽取占用了一定的存储空间。

清单 10. 监控日志抽取占用的空间

>
     > db2 "SELECT application_handle, uow_id, LOG_EXTRACTION_DISK_SPACE_USED
FROM TABLE(MON_GET_UNIT_OF_WORK(NULL,-1)) AS t "

APPLICATION_HANDLE   UOW_ID      LOG_EXTRACTION_DISK_SPACE_USED
-------------------- ----------- ------------------------------
                  76           1                         210503

关于该新特性的更多说明

其实在上述验证的过程中也并不是一次就成功的发生对长事务的活动日志抽取,而是尝试了多次依然遇到了数据库事务日志已满的报错,例如在清单 8 中命令行终端2模 拟正常的事务时单个insert的 数据量较大、频率较快,就会很快的遇到 SQL0964C 日志满报错。

再去看 Db2 高级事务日志空间管理功能的介绍,在 “Troubleshooting” 部分也提到了,即使启用了该新特性,也还是会发生数据库事务日志已满的问题。在这段时间的摸索中,感觉该新特性毕竟还是处于 “tech preview” 阶段,并没有像第一眼看到的时候那么惊喜。即便如此,该新特性对于解决长事务引起数据库日志空间已满问题是一个很好的思路。

可能有人会问,为什么不用 NUM_LOG_SPAN 参数来解决长事务导致事务日志已满问题?关于这个问题不做展开,可以参考文后的参考资料。

总结

如果说 IBM 将 AI、机器学习算法等引入 Db2 数据库是颠覆性创新,那用创新的方法解决传统场景中用户的痛点可以称作”微创新”,而这种 “微创新” 在产品的发展历程中也是不可或缺的。本文简要介绍了 Db2 11.5 基于 AI 的 新一代数据库,并探索了 Db2 11.5 的高级事务日志空间管理新特性。在探索的过程中,较为全面的分析和总结了 “数据库事务日志已满” 问题,然后验证了高级事务日志空间管理对解决长事务场景带来的新思路。尽管该新特性还是 “tech preview” 阶段,功能有一定的限制,但还是非常值得期待的。

最后,本文仅代表笔者观点,不代表 IBM Db2 官方观点。

参考资料

获得产品和技术