tiancheng_2011
作者tiancheng_2011·2012-03-31 21:32
数据库管理员·北京华胜天成

几个重要概念的解释

字数 9180阅读 636评论 0赞 0
问题如下:我们现在碰到一个问题,日志空间经常提示占满,现在如何查看某个 AppHandl占用日志空间的大小。或着查看到某个TranHdl交易占用日志空间大小.????
思路一:
如果你版本是9.X以上的话,直接用系统视图查看:
select * from sysibmadm.snapappl order by UOW_LOG_SPACE_USED desc fetch first 10 rows;
监视最消耗日志的前10条记录。
思路二:

db2 get snapshot for application applid  applid | grep -i "UOW log space used (Bytes)"

思路三:
select agent_id, uow_log_space_used/1024/1024 from sysibmadm.snapappl where agent_id=xxx

#监控日志使用情况
select int(total_log_used/1024/1024) as "Log Used (Mb)",int(total_log_available/1024/1024) as "Log Space Free(Mb)",
int((float(total_log_used)/float(total_log_used+total_log_available))*100) as "Pct Used",int(tot_log_used_top/1024/1024) as "Max Log Used (Mb)",
int(sec_log_used_top/1024/1024) as "Max Sec. Used (Mb)",int(sec_logs_allocated) as "Secondaries" from sysibmadm.snapdb;

#监控占有日志空间最旧的交易
select substr(ai.appl_status,1,20) as "Status",substr(ai.primary_auth_id,1,10) as "Authid",substr(ai.appl_name,1,15) as "Appl Name",
int(ap.UOW_LOG_SPACE_USED/1024/1024) as "Log Used (M)",int(ap.appl_idle_time/60) as "Idle for(min)",ap.appl_con_time as "Connected Since"
from sysibmadm.snapdb db,sysibmadm.snapappl ap,sysibmadm.snapappl_info ai where ai.agent_id=db.APPL_ID_OLDEST_XACT and ap.agent_id=ai.agent_id;
思路四:
500G的空间都不够!?大事务的情况下,需要经常COMMIT,否则无论多大的空间都不够。

问题二:

前几天 数据库 运行程序时候 报 SQL0964C  The transaction log for the data
base is full.  SQLSTATE=57011 的错误。后来 给加了几个 (LOGPRIMARY) 和(LOGSECOND)
现在 再次报错误。之前一直运行正常,应用没有改变过,我觉得近期不会说因为数据量突然增大如此之多,会造成数据日志充满。
不知道 是不是哪里的设置被人改动 出了问题,哪位大侠给分析下?

解释一:
db2 的活动日志满通常是因为有大量未提交的事务的数据,让日志的空间不能及时释放,使得新的事物无法申请到可用的日志空间;建议一是加到logprimary和logsecond  二是force applications 停止造成此问题的应用,那么它所占用的日志空间会因应用程序的回滚而释放
解释二:

事务的锁比较多,所以虽然是循环日志,前面锁定的事务占用了日志文件,没有(或不能)提交,导致后面的logsecondary日志即使分配了,也没有办法再循环利用前面的日志。
看看infocenter:
       在循环日志记录期间,不会生成新日志文件(辅助日志除外)且不删除旧日志文件。日志文件以循环的方式来处理。即,当最后一个日志文件已满时,DB2® 开始写入第一个日志文件。
如果所有日志文件都是活动的且循环日志记录进程不能回绕至第一个日志文件,则日志已满情况就可能发生。
      另外,日志记录的是对数据库对象和数据所作的所有更改,而数据量不必然影响日志大小。
      感觉问题的关键在sql。

解释三:
一种可能是插入数据的表空间不够,引发虚报SQL0964C 错误。
二种可能是parimary log 和secondary log 的设置不合理。
三种是所执行的应用内持有最旧的事物号,阻止了lsn 的分配,需要杀死 。
你是在循环日志的方式下 ,把BLK_LOG_DSK_FUL=yes .就是在日志已满的时候,只有读操作可行的。Lock timeout (sec) (LOCKTIMEOUT) = -1  是说让无限等待需要的锁,建议根据你的应用olnp还是ontp 进行设置。

当 BLK_LOG_DSK_FUL 数据配置参数设置为 NO 的时候,当活动日志所在目录不能创建新的日志文件时,接收日志磁盘已满错误的事务将会失败并回滚。

问题五:db2 中fence(防御) 用户的作用????

解释一
如果指定了fence用户,则udf和存储过程是在自己的地址空间中运行的,不和数据库的引擎地址空间在一起,如果出现了问题,不会影响到引擎运行,对系统的安全稳定运行起到一定的防护作用,不过运行速度则有所影响

在操作系统上可以看到很多db2fmp进程,这些都是受防护方式进程,对,就是db2fmp来干这个事情的.
db2fmp
The fenced mode process is responsible for executing fenced stored procedures and
user-defined functions outside of the firewall.
The db2fmp process is always a
separate process, but might be multithreaded, depending on the types of routines
that it executes.
有两个参数可以控制受防护方式进程,KEEPFENCED、FENCED_POOL
一般建议不要FENCED_POOL太大,太大了可以看到很多db2fmp进程处于idle状态,浪费资源

解释二:
俺来简单地概括fmp
首先呢,当用户调用一个存储过程的时候,实际上就是一个进程调用一个用户写的程序。
抛开db2不谈,一般来说编程时应该怎么实现呢?
两个选择
1)如果程序是C写的,那么我们可以编译成一个.lib然后封装成.a,里面包含可执行的代码。然后呢在主程序中通过dlopen()系统调用把这个.a给load到进程里面,其中一块内存地址会被映射到这个lib里面。然后呢,通过我们事先规定的入口 (也就是存储过程的名字)我们可以用dlsym()从这个刚刚load的库里面找到我们需要的函数入口,然后调用该函数执行。
2)另一种方法,我们可以fork出一个新的进程,然后通过message queue告诉这个新的进程我们需要哪个库哪个函数,然后用这个新的进程去load,然后把结果通过message queue告诉主进程。这个新的进程也就是所谓的db2fmp :-)
两者有什么区别呢?没啥 :)除了一个用新的进程打开库,一个用主进程。
为什么要用新的进程呢?fork()会有较大的overhead的哦。

假设用户的代码写的很烂,到处都是那种
void* p;
p = NULL;
free(p);
这种狗屁咚咚,那么程序里到处都有可能出现sigsegv,也就是非法内存访问错误。
当进程碰到这种问题的时候呢,操作系统会给进程扔一个signal 11。一个写的好的程序一般都会定义signal handler,也就是当碰到这些signal 的时候怎么处理。在db2里面,每当signal 11发生的时候,它会让所有的线程写一个叫做trap file的文件,一般格式是<pid>.<tid>.stack.txt,同时每个线程还会写一个dump file,里面包括很多内部数据结构以便于日后分析。
当这部分操作完成的时候,也就是signal handler结束后,假如我们出现问题的汇编语句是在地址0x123,那么操作系统会跑回来再一次执行0x123里面的冬冬。不过我们知道signal handler里面除了写trap/dump文件什么也没有干,那么这一次会再次产生signal 11。
当第二次产生signal 11的时候,第一次调用signal handler以后我们已经把signal handler卸载了。这样第二次的signal 11会调用系统默认signal handler,也就是结束进程,产生一个core file。
这就是非法内存访问crash instance的原理。

这样,如果用户的程序写的不好,极有可能造成signal 11。当这种情况发生,并且程序是在主进程执行的时候,那么db2sysc就挂拉,也就是用户UDP造成instance crash。
我们一般不希望发生这种情况。
因此,在第二中执行方式中,如果db2fmp死了,他的父亲 (也就是db2wdog)会受到sigchild,也就是说“你的儿子挂啦,你自己看着办“。然后db2wdog一看是db2fmp死了,也就想:没事,反正是用户的UDP,死道友莫死贫道,不干俺的事,俺的db2sysc继续执行。
这样就直接在db2diag.log里面发一个类似“fmp死拉死拉地“这种消息,然后继续执行

而为什么fmp要用fenced user id呢?这是一种保护措施。
比如俺是一个传说中的黑客,入侵了一个系统,把一个开发员的.C文件替换成我自己的恶意代码(或者本来这个开发员就是一个商业间谍)。然后开发员不知道,直接prep/bind了。当他执行这个udp的时候,如果我的代码里面包含attach shared memory,并且用户与instance owner一样的话,我就有权利直接看到database shared memory里面所有的冬冬,包括机密的用户数据。这是不应该发生的(或者更直接的,直接在我的udp里面执行/usr/bin/ksh,我就直接拿到拥有instance owner的shell了!!)。
所以如果fenced user和instance owner不是一个人的话,我就只能通过db2 SQL访问数据了,这样所有的操作都是在db2的验证机制下进行,也就是安全性得到保证。。。
问题六:崩溃恢复的原理????????????
crash recovery的起始点是
min ( minbuflsn, lowtranlsn )
其中minbuflsn是存在bufferpool中没有被刷到磁盘上最早的脏页所对应的lsn
lowtranlsn是最早的一个未提交交易所对应的lsn
min ( minbuflsn, lowtranlsn ) 的意思是取两者间最低的一个值,也就是不一致状态的起始点
从这里开始,对transaction log里面的每一条记录replay。如果其所对应的数据页lsn大于等于日志lsn,也就是说数据页比日志的那条记录要新,也就是不用replay那一条,否则按log里面的冬冬原封不动replay。
当replay到日志的最后一条,也就是forward phase完成,当前数据库的状态是不一致的,因为crash的时候未提交的数据也被replay了。因此现在要做backward phase。
backward phase也就是对现存的所有未提交交易,根据每一条日志记录的previous lsn pointer找到那个交易里面的上一条lsn,一直逆推到该交易的开始。当所有的未提交交易都被回滚,数据库状态一致
对于LZ问的“数据库的一致状态(consistent state)”,简单概括就是
1)所有交易已经提交
2)bufferpool里面所有数据都已经刷到磁盘上
也就是minbuflsn和lowtranlsn都是当前最新的

发一些学习崩溃恢复时总结的基本概念和相关知识,自己理解,有可能表达不是很准确,
希望发现问题时及时指出:

1、脏页是什么:

1、首先解释一下脏页:脏页是已输入缓存区且已修改但尚未写入磁盘的数据页。

如下的示例中缓冲池中的页面便是脏页(和日志的状态没有关系)。


物理的页和缓存的页不一致,则缓冲区的页就是脏页。

缓冲池中页面的样子

Id Name
1 A
2 T

数据库物理存储上的样子

Id Name
1 A
2 B

相对于物理中的数据,缓冲池中的页中数据发生了改变,所以是脏页。

脏页被写入硬盘后,数据达到了一致,便成为了干净页。如下说示:

缓冲池中页面的样子


Id Name
1 A
2 T

数据库物理存储上的样子


Id Name
1 A
2 T

2、log日志的结构
所有的UDB的log 记录都有一个日志管理器头信息。这个头信息包括总的日志记录的大小,日志类型,以及关于事务的信息。日志中不会包含账户,统计,优化统计等信息。
每个日志记录都用一个特定的log序列号(lsn)唯一的标识起来。这个LSN是该日志在整个数据库日志中相对于第一个log的记录的第一个字节的偏移字节地址
日志中的每个事务都有一个日志记录头信息唯一标识。这个事务标识有6个字节,每个新的事务开始时,事务标识就递增1。所有在一个事务中的log记录都包含同一个事务标识。

3、提前写日志规则(write-ahead logging, WAL)
 预写式日志(WAL) 是一种实现事务日志的标准方法,很多主流的数据库都是用的这种方法。
db2数据库提前写日志规则是:对数据文件的修改(脏页)刷入磁盘之前,必须保证相应修改的日志记录已经保存到硬盘上。
(即:对硬盘上的数据文件(表和索引)的修改必须只能在这些修改已经记录了日志之后,即在这些新的日志记录到硬盘之后。 这样的话我们就不需要在每次事务提交的时候都把数据页记录到磁盘。)
注:
日志文件保存在硬盘上的时间为:
1、发生了commit语句。
2、日志缓冲区满了。
(在第二种情况下,可能发生事务中的一条单独的sql引起日志缓冲区满,导致日志写硬盘,随后可能软检查点发生,脏页被写到硬盘。即出现事务还没有提交,数据更改已经写到硬盘上的情况。)。
提前写日志的优点:事务提交的时候,只需要提交相应的日志记录到磁盘即可,不需要刷数据页。
4、日志控制文件、minbuflsn 与lowtranlsn
      数据库管理器使用日志控制文件。当日志文件已满时以及在软检查点期间,将此日志控制文件写入磁盘。
日志控制文件记录了最旧的有效日志记录。最旧的有效日志记录有两个状态,磁盘上的控制文件记录了“记录的状态”,而内存中变化的那一份记录了当前状态,软检查点的定时基于最旧的有效日志记录的“当前状态”和“记录的状态”之间的差别,该差别以softmax与logfilsiz 的百分比给出。
      另外,日志控制文件中记录了两个与崩溃恢复相关的重要的值minbuflsn 与lowtranlsn。
      minbuflsn是存在bufferpool中没有被刷到磁盘上最老的脏页所对应的LSN,是redo动作的第一个日志记录,前滚操作时就是从这个log开始往前进行的。这个值保存在日志控制文件中,
       lowtranlsn是最老的一个未提交交易所对应的LSN,是回滚动作的起点。
6、softmax 恢复范围和软检查点时间间隔配置参数
     softmax是一个数据库级别配置参数,缺省值 100,范围100 [ 1 - 100 * logprimary ],计量单位是一个主日志文件的大小百分比,例如,如果使用缺省值100,则数据库管理器将尝试把需要恢复的日志数保持为1。如果您指定 300 作为此参数的值,则数据库管理器将尝试把需要恢复的日志数保持为 3。
softmax参数用来:
1、确定崩溃恢复需要的日志数量:
使用缺省值,则数据库管理器将尝试把需要恢复的日志数保持为1。并且,数据库管理器使用softmax参数来触发页清除程序,以确保比指定的恢复窗口旧的页都已写入磁盘。
2、确定软检查点的频率。
软检查点期间,总是将日志控制文件写入磁盘(当日志文件已满时也会写),保证崩溃恢复时可以使用最新的minbuflsn和lowtranlsn。您可能想增大或减小此参数的值,这取决于您的可接受的恢复窗口是大于还是小于一个日志文件。降低此参数的值将导致数据库管理器不但更经常地触发页清除程序而且还更频繁地采用软检查点。这些操作可减少需要处理的日志记录数和在崩溃恢复期间处理的冗余日志记录数。
      但是注意:更多的页清除程序触发器和更频繁的软检查点增加了与数据库日志记录相关联的开销,这可能会影响数据库管理器的性能。而且,如果您遇到下列情况,更频繁的软检查点可能不会缩短重新启动一个数据库所需的时间:
落实点很少的很长的事务。
很大的缓冲池并且未将包含落实事务的页很频繁地写回至磁盘。(注意,使用异步页清除程序可能有助于避免此情况。)
在这两种情况中,保留在内存中的日志控制信息不频繁更改,因而在将日志控制信息写入磁盘时不存在优势,除非日志控制信息已更改。

7、写数据和日志的关系
1、事务已经提交,数据也已经写到硬盘上。
2、事务没有提交,数据也没有写到硬盘上。
3、事务已经提交,数据没有写到硬盘上。
4、事务没有提交,数据已经写到硬盘上。

前面的两种情况在崩溃恢复的过程中,不需要考虑,因为数据已经一致了(提交的已物理写了,没有提交的没有物理变化)。
后面的两种情况在崩溃恢复时需要考虑:
对于3)事务已经提交,数据没有写到硬盘上这种情况。需要redo日志。
对于4)事务没有提交,数据已经写到硬盘上这种情况。需要undo日志。

8、崩溃恢复

当重新启动某个数据库时,将使用日志文件来执行该数据库的崩溃恢复。

crash recovery的起始点是min ( minbuflsn, lowtranlsn ),其中minbuflsn是存在bufferpool中没有被刷到磁盘上最早的脏页所对应的lsnlowtranlsn是最早的一个未提交交易所对应的lsnmin ( minbuflsn, lowtranlsn ) 的意思是取两者间最低的一个值这就是不一致状态的起始点。恢复中使用日志的最大尺寸将不会超过logfilsiz*softmax/100  (in  4-KB  pages)

min ( minbuflsn, lowtranlsn )这里开始,对transaction log里面的每一条记录replay。如果其所对应的数据页lsn大于等于日志lsn,也就是说数据页比日志的那条记录要新,也就是不用replay那一条,否则按log里面的记录内容原封不动replay

replay到日志的最后一条,也就是forward phase完成,当前数据库的状态是不一致的,因为crash的时候未提交的数据也被replay了。因此现在要做backward phase

backward phase也就是对现存的所有未提交交易,根据每一条日志记录的previous lsn pointer找到那个交易里面的上一条lsn,一直逆推到该交易的开始。当所有的未提交交易都被回滚,数据库状态一致。

这样做了以后可以确保该数据库处于一致状态(即,所有已落实的事务都应用于该数据库而所有未落实的事务都不应用于该数据库)。

你是说类似oracle回滚段的db2实现方法?那个叫做currently committed,也就是当前提交。
原理比oracle的那个简单方便多了。对于每一条update语句,在过去的版本里面,transaction log要写用户新update的数据和原先的数据,用来做rollback用。
现在呢,当transaction第一次更改一行的时候(原来里面的数据已经是提交的)会把这一行的内容写在transaction log里面,然后对于每一个该行的update都往日志里面新加一个数据结构指向那个已经提交的版本。这样当db2想要看已经提交的版本的时候,简单地读出那个日志里面保存的版本就好了。
这个功能不需要什么回滚段的设置,基本上在参数上不需要进行改动(不过可能用户需要增加log buffer来保留更多的日志在内存,减少log I/O的开销)。

DB2 9.7 里实现了类似ORALCE的多版本回滚段,又有pureScale,终于能和ORACLE
颠疯对决了,pureScale 是9.8的新特性,号称RAC Killer。




如果觉得我的文章对您有用,请点赞。您的支持将鼓励我继续创作!

0

添加新评论0 条评论

Ctrl+Enter 发表

作者其他文章

相关文章

相关问题

X社区推广