DB2 重点及内部实现细节

–0.名词解释
行溢出:varchar的长度被update了,这个使得原来的数据页存不下该长度,因此在原来的存数据的地方存了个rid,指向新的一页。

–1.db2diag & XXX.nfy的区别

诊断 db2diag 日志文件主要供 IBM软件支持机构用于进行故障诊断。管理通知日志主要由数据库和系统管理员用于进行故障诊断。管理通知日志消息也以标准化消息格式记录到 db2diag 日志文件。

–2.PC/IXF格式是否包含DDL
该格式包含DDL(其实不是DDL,只是它包含了各col的定义),但是在import或db2move的时候不推荐用它来建表。原因在于其建立的表往往包含的约束不全。

–3.表空间概念(Ext就是extent。db2只有在一个container中写满一个ext,才会去写下一个)
0 1
———————-
0 – Ext0 – Ext1 –
———————- 0
1 – Ext2 – Ext3 –
———————-
2 – Ext4 ———–
———————- 1
3 – Ext5 ———–
———————-

Range Stripe Stripe Max Max Start End Adj. Containers
Number Set Offset Extent Page Stripe Stripe
[0] [0] 0 3 99 0 1 0 2 (0, 1)
[1] [0] 0 5 149 2 3 0 1 (0)

Range Number:范围号。就是最右边的0和1。如果新起了一个条带集,那么范围号也会的改变
Stripe Set:条带集。用户定义定义的(注意,这个和条带不是一回事)。
Max Extent:最大的Ext号。从0开始计数
Max Page:最大的Page号。从0开始计数
Start Stripe:某个range中开始的条带号。就是最左边的数字
End Stripe:某个range中结束的条带号。就是最左边的数字。
Adj:是重平衡时候的偏移量
Containers:改range包含的容器情况

!!!要点:写的时候在同一个range里轮流写,每个container写一个extent后写下一个。如果某一时刻这个range无法成立了,那么具体情况具体分析。比如对于AutoResize会报错,而开启了Auto Resize的AUTOMATIC Storage会的生成新的条带集。

测试下AutoResize在某容器文件系统系统满后的行为:
在没有满的时候,会的自动的扩容。
当其中的某个容器的文件系统慢了以后,就会的报错:
DB21034E The command was processed as an SQL statement because it was not a valid Command Line Processor command. During SQL processing it returned:SQL0968C The file system is full. SQLSTATE=57011
这说明AutoResize不会的重新在还有空间的文件系统里建立新的条带集。
解决方法一般有三个:
1.增加没有空间的文件系统的大小
2.建立新的条带集(实际情况下请保证容器数目和老的一致)
[db2inst1@localhost ~]$ db2 “alter tablespace asTBS begin new stripe set (file ‘/home/db2inst1/asasa’ 100)”
3.手工alter还有空间的文件系统的容器,生成新的range(不推荐,因为虽然省了空间,但是IO上慢了)
[db2inst1@localhost ~]$ db2 “alter tablespace asTBS extend (file ‘/fs50/arcont’ 100)”

测试下Automatic Storage在某容器文件系统系统满后的行为:
首先,需要开启Auto Resize。否则管起来非常麻烦,因为Automatic Storage是不能直接修改容器大小的,所以一般遇到这种情况如果表空间空间不够而Auto Resize又没开的话就蛋疼了。
在开启Auto Resize后,如果某个容器的文件系统满了,系统不会报错,而是会的产生一个新的条带集。不过这个是不推荐的,因为这个会降低IO。如果之后的某个时候有能力给该文件系统扩容了,那么如果要提高IO速度,推荐使用REBALANCE。

DMS表空间结构
逻辑结构(数据库管理 > 管理概念 > 数据库 > 表空间 > 系统数据、用户数据和临时数据的表空间 > 表空间和存储器管理 > 数据库管理的空间):
——————-

——————-
SMP
——————-
EMP
——————-
Table01XXX
——————-
Table01XXX
——————-
SMP
——————-

名词解释
01.对象表:一个内部关系表,它将对象标识映射至该表的第一个 EMP 扩展数据块的位置。这个 EMP 扩展数据块直接或间接地映射出该对象中的所有扩展数据块。
对象表的格式:
———————————————————————-
对象标识(比如表) | 第一个EMP(数字代表物理上的第几个Extent)|
———————————————————————-
Table01 | 12 |
Table02 | 24 |
———————————————————————-

02.扩展数据块映像页(EMP): EMP 扩展数据块直接或间接地映射出该对象中的所有扩展数据块。每个 EMP 都包含一连串的条目。每一条目都将对象相对扩展数据块号映射至该对象扩展数据块所在的相对表空间页号。直接 EMP 条目直接将对象相对地址映射至相对表空间地址。第一个 EMP 扩展数据块中的最后一个 EMP 页包含间接条目。间接 EMP 条目映射至 EMP 页,EMP 页然后映射至对象页。第一个 EMP 扩展数据块中最后一个 EMP 页中的最后 16 条目包含双重间接条目。

03.空间映射页(SMP):SMP用来管理扩展数据页EMP,表空间的第二个扩展数据块就是表空间的第一个SMP。SMP以固定的间隔方式分布在表空间中。每个SMP保存的是当前SMP到下一个SMP中的那些extent的分配情况。

–4.DB2内部组件
EDU就是线程。EDU ID是一个代号,agent是edu的一种。
agentid和app handle其实一样,是一个session的id。
也就是说,一个app连过来的时候,有一个agent去响应,这个时候即相当于建立了一个session。这个session的id就是agentid,这个agent所在的线程就是一个edu,有一个eduid。至于app id那个就是20130912XXX或localXXX的那种表示名字的东东。

部分缩写:
SQLAC C程序接口
SQLF 配置接口。对于数据库配置的修改需要通过这个组件
SQLA 应用程序服务
SQLU 数据库实用工具。
SQLN SQL编译器组件
SQLKD 缓冲池分布式服务
SQLKT 表队列服务
SQLKQ 缓冲池队列服务
SQLKF FCM-快速通信管理器组件

部分组件说明:
b BPS buffer pool management

c CCI communications between clients
处理通信协议。主要有三类:C运行时(ODBC、CLI和OLE)、TYPE4、TYPE2
对于第一类,db2diag里会的是sqlccXXX的形式。对于第二类,db2diag里会的是db2jcXXX的形式

d DMS data management
用于处理数据并返回数据。

e BSU database engine processes
为实例和数据库分配内存,拦截和处理信号,发用异常。该组件其实就是底层引擎。

o OSS operating system calls
主要负责内存管理、IO、线程的创建管理、Semaphore信号量的处理、安全认证和错误日志的输出。如果db2diag中包含该组件的错误信息,切错误信息里有errno,那么这个errno可以在/usr/include/sys/errno.h中找到

p DPS data protection(such as locking and logging)
包括logging/locks/transaction management

r RDS relational database services
用于优化和处理SQL,构造访问计划

s sorting operations

x、I IXM indexing operations

SQMO 内存优化组件。stmm就是它的一部分。

–5.Bufferpool
bufferpool的定义保存在BPCB中,这个是存在数据库目录下的那两个互备的BP文件中的。在dbheap中,对每个bufferpool中的每个页都保存了一个bufferpool的页描述符BPD,大概100字节。其中有各种flags以及fixcount技术,用于反映该页被逻辑应用的次数。BPD包含三对指针分别指向hash链表(hashnext/hashprev)、hate链表(hatenext/hateprev)和dirty链表(dirtynext/dirtprev)。除此之外还有一个真正指向页面的指针BPP。还包括缓冲池ID:poolID、对应页面的对象ID:objectID、对象的类型objectType、池中的页面数poolPageNum和对象页面数objectPageNum等。

hash链表:链接hash值相同的页。用于对页面的迅速定位。hash桶与该链表关联。
hate链表:链接允许被置换出bufferpool的页。用于快速找到可以牺牲的页(不是hash,就是普通的双向链表)
dirty链表:用于将脏页链接起来。当放生检查点或者其它写情况的时候,可以快速找到这些脏页。(不是hash,就是普通的双向链表)

Hash:
DB2使用hash算法对页面进行管理。查找的时候使用池ID、对象ID、对象类型、池页面号计算hash。注意,这些在查找的时候都是是已知的。查找的过程是通过前面那些生成的hash值跳到hash桶中去查找是否在该桶上。然后通过hashnext和hashprev查找所有的页面。如果没有找到,那么需要选择牺牲一个页面用于存放该页。

hash示意图(注意,这些都保存在db heap中。BPD有指向数据页的指针,数据页保存在bufferpool中):
—–
|桶1|-hash pointer->|BPD| -hash pointer->|BPD|
—– | |
hate pointer dirty pointer
V |
—– |
|桶2|-hash pointer->|BPD| |
—– | |
hate pointer |
V |
—– V
|桶3|-hash pointer->|BPD| -hash pointer->|BPD| -hash pointer->|BPD|
—–

—–
|桶4|-hash pointer-> BPD|

基于块的缓冲池:
预取进程虽然一次读取很多页,但是这些页在bufferpool中不一定是连续的。大部分平台上这不是问题,但是SUN平台就不是了。为了在SUN上提高IO,需要将数据读入到连续的bufferpool中。因此我们把bufferpool分为两个部分,一个是以页为单位,一个以块为单位。一个块代表连续的几个页。这个区域只用来存放顺序预取读取的数据。
—–

–6.预取与页面清除
是否进行预取以及预取数量由bufferpool大小、预取大小和执行计划共同控制。DB2顺序检索也会导致预取。预取可以是并行的,也就是说可以由多个进程为一个SQL查询进行IO操作。

几个名词:
预取队列:预取请求被放置到的队列。当放的页面数达到预取触发点时,就会发生预取。
预取触发点:prefetch的倍数的点,这个时候会的开始预取。

是否进行页面清除由下面的情况决定:
1.bufferpool中的脏页超过了限定。限定由数据库参数chngpgs_thresh决定。这个值是一个百分比。
2.softmax的值规定了内存中最大的LSN和写入日志文件的最老的事务LSN的间隔。如果softmax的值为100,表示有100%个日志文件对应的数据页可以保存在内存中(也就是一个日志文件对应的数据页)。
3.被选中的牺牲页为脏页面。

–7.代理程序
空闲代理:没有任何任务的代理
活动的协调代理:处于工作状态的代理
子代理:接受协调代理分发出来的工作的下一级代理
代理池:在实例启动的时候生成一些代理放在这里,数量取决于实例参数num_initagents。当有应用连接的时候,就给出一个代理去工作。如果所有的代理都用完了,那么久新建立一个代理。当请求都没有的时候,这个代理可能返回代理池,也可能自动销毁。是否自动销毁取决于num_poolagents。

代理的参数说明:
MAXAGENTS:9.5后不再使用。表示实例中的全部代理的最大数量。
NUM_POOLAGENTS:用于控制代理池中的空闲代理的数量。不过如果存在连接集中器,那么可能空闲的数量会的大于这个参数。
NUM_INITAGENTS:实例启动的时候生成的代理数量。
MAX_CORRDAGENTS:决定了实例中同一时刻最大的协调代理的数目(如果是多分区,那么指的是一个节点上的最大协调代理数目)
MAX_CONNECTIONS:决定了允许连接到一个实例的最大的连接数(在多分区环境指的是一个节点上的最大的连接数)
MAXCAGENT:9.5后取消。决定了实例中的令牌的数量。一个协调代理只有得到了令牌才能去服务应用程序。

连接集中器:
注意,使用的时候有很多限制。建议参考Infocenter。
如果MAX_CONNECTIONS大于MAX_COORDAGENTS,那么连接集中器就被激活。它允许多个连接对应同一个代理。注意,即便这样,连接数也不能大于MAX_CONNECTIONS。当连接数大于MAX_COORDAGENTS但小于MAX_CONNECTIONS的时候,那些连接会的先等待代理。当某个代理处理完某个连接时候(比如应用变成了UOW Waitting,执行程序代码啥的),会的来处理某个等待着的连接。

注意,不是所有的应用连接都算作外部连接,有些db2自己的应用不算。所以用db2pd -agents看到的会比db2pd -applications多一些

–8.内存
那些heap配置的参数很多都是软限制。所以一般用db2mtrk看到的会比配置的参数用的内存多。在db2diag里也会看到类似的警告。一般可以忽略。

排序:
SHEAPTHRES实例级别总的可用的私有排序堆大小。如果一个实例中的排序消耗超过了这个软限制,那么对于额外的私有排序所分配的内存将大大减少。私有排序发生在私有代理内存内。

SHEAPTHRES_SHR
V8的时候这个是数据库级别总的共享排序堆的硬限制。如果超过了这个限制,任何请求排序的应用将收到错误,直到共享内存消耗降低到限制以下。V9后成了软限制。如果内存不够了,就从database overflow buffer中区数据。

SORTHEAP
数据库配置参数。是每个排序所能用的内存的最大大小的软限制。实际使用由优化器决定,因此建议多用用runstats。

数据库共享内存=(主缓冲池+4个隐藏缓冲池+数据库堆+实用程序堆+locklist+程序包缓存+编目缓存)+(estore的页数*100字节)+大约10%的开销 –没有算入压缩字典等内容。大部分内存在激活数据库是申请,但是诸如使用程序堆一般会在使用backup、load等工具的时候申请全部。

OSS组件向其它组件提供了三个级别的抽象内存管理:
memory block内存块:是一片连续的内存空间,是DB2内部申请内存分配的基本单位。主要用于被其它组件申请。
memory pools内存池:由内存块组成,其地址空间不连续。这个概念的区分由内存的使用范围、用途、行为来组织分配。其一般叫得出名字,比如package cache。
memory sets内存集:每个内存池都属于一个内存集。这个概念是从操作系统角度来看的。其可以是私有的,也可以是共享的。如果是共享的说明它在操作系统角度表现为共享内存段。

相关参数
instance_memory:整个实例可以使用的最大的内存。不会的在实例启动的时候全部分配,只不过是个上限。
appl_memory:应用需要的内存,如果设置为非自动,那么数据库启动的那个的时候会的分配指定大小。
database_memory:数据库需要的内存,如果设置为非自动,那么数据库启动的那个的时候会的分配指定大小。但好像windows上不是这么回事。。。

db2pd -dbptnmem看到的current usage是按照database_memory最大值+appl_memory最大值+XXX啥的来的。个人感觉,如果database_memory这类东西被设置的过大,比如远大于物理内存,那么应该会的在下次激活DB的时候才会生效,尽管如此,db2pd的输出却是生效了,current usage已经可以看到大的无比的输出了(应该是个bug,因为10.5的时候就不行了)。从这个角度上讲,db2pd -dbptnmem看到的好像也不能全信。。。干脆就db2mtrk看看或是db2pd -memsets看下commit的内存好了。

db2pd -memsets -db sample的输出:
[db2inst1@DB2_105 ~]$ db2pd -db sample -memsets

Database Member 0 — Database SAMPLE — Active — Up 0 days 00:06:27 — Date 2013-10-21-00.21.29.812237

Memory Sets:
Name Address Id Size(Kb) Key DBP Type Unrsv(Kb) Used(Kb) HWM(Kb) Cmt(Kb) Uncmt(Kb) CmtRt(Kb) DcmtRt(Kb) HoldRt(Kb) Sngl
SAMPLE 0x00007F722C6A0000 53706764 160832 0x0 0 1 2816 110208 110336 110336 50496 0 50496 0 2
AppCtl 0x00007F72363B0000 53673994 160064 0x0 0 12 0 1280 2496 2496 157568 192 157568 0 16
App10 n/a 53641225 128 0x0 0 4 0 128 0 128 0 0 0 0 0
其中:
Size(Kb)的大小为database_memory和appl_memory配置的大小。这个SIZE感觉类似于windows里的reserve memory,就是在虚拟地址空间里分配了的那些内存,占用了虚拟地址的空间,但未映射到任何磁盘数据页或物理内存。某些平台上,这里包含 Unrsv(Kb)的内存。
Unrsv(Kb):不懂。。。好像是说这里的内存可以被任何pool使用的,而Used里边的内存其实已经是各个pool用的内存的总和,已经定了,除非stmm,否则只能pool自己用。其大小貌似等于Size(Kb)-各个Pool用的内存总和。如果在数据库运行的时候把database_memory这类参数设置的非常大,那么多余的内存就会都涌入到Unrsv(Kb)这里,其实呢,这些内存并没有被commit,所以应该是不占内存的。
Used(Kb):实际被使用的,这个和db2mtrk -d -v看到的比较像
Cmt(Kb):已经被commit的内存,这个依据OS的不同而有不同的含义。对于Windows来说,就是已经在reserve memory中映射到磁盘或主存的那些内存
Uncmt(Kb):未被commit的内存。
一般看Used比较好。

做个测试,在linux下动态修改bufferpool的大小,并用db2pd -db sample -memsets和free命令,会发现free的内存和Used无关,只和Cmt有关。

–9.排序
名词解释:
溢出:排序堆不够大,数据放入到临时表中
piped:最终的结果不需要临时表,可以直接返回
non-piped:最终的结果需要用到临时表,从临时表中读取数据

有四种情况:
非溢出且管道化:缓冲区够用
溢出且非管道化:排序的时候内存就不够用了,最后的结果内存也不够用
溢出且管道化的:排序的时候内存不够用了,最后的结果内存够用
非溢出且非管道化的:出现在排序被多次打开读的情况(因为管道化的排序只能在第一次读取的时候发生)

排序原理(阶段):
1.创建阶段:排序需要的一些结构(如排序缓冲区)被分配和初始化
2.插入阶段:数据插入到缓冲区中(插的时候同时排序)。如果缓冲区不够了,那么写到缓冲区中(overflow一次)。每一次overflow称为一轮,一个排序的所有轮都会的放入到一个临时表中。(注意,如果临时表的缓冲区够大,那么不会的写入临时表的表空间,否则的话会的写入到表空间中)
3.如果是non-piped排序,那么临时表中被排序的每个轮会的在这个阶段合并,并插入到一个新的临时表中。对于piped排序(并且溢出了),如果缓冲区不能一次性完成所有的合并,那么除了最后一次合并外的所有合并将会在本阶段完成,最后一次合并在数据提取阶段完成。
4.数据提取阶段:客户端从排序结果中一行一行读取数据。对于piped,可能会的进行最后一次的合并。对于non-piped,本阶段就是打开临时表读取数据。
5.关闭阶段:释放资源。

–10.监控函数、快照
SYSIBMADM.XXX大部分都是快照,快照的定义来自于对应的表函数。表函数应该来自于API。(db2 “select viewname from syscat.views where viewschema=’SYSIBMADM'”)
SYSIBMADM下的快照大概分成下面几类:
1.SNAP开头的:提供了许多不同的快照监视器 SQL 管理视图。

2.一些计算过的,名字一看就知道含义的:DB2 V9.5后提供了一组管理视图,它们不仅返回个别监视元素的值,而且还返回监视任务中通常需要的计算值(比如,先从SNAP开头的视图里得到数据,然后计算,返回视图)。

3.MON开头的:DB2 for Linux, UNIX, and Windows Version 9.7 引入了许多新的监控 SQL 表函数,它们以前缀 MON_ 开头(例如,MON_GET_CONNECTION 和 MON_GET_TABLE,它们分别报告连接和表的监控数据)。这些新的表函数可以替代 9.7 版之前的快照监控表函数,它们效率更高、更轻量。另外,它们报告无法通过系统监控接口获取的许多新的监控元素,包括一整套耗时元素,数据库服务器在这些元素上花费时间处理请求。这些新的监控接口报告系统总体数据(聚合为事务、连接、服务类和工作负载)以及特定对象的数据(例如索引和表)。

快照的时间问题:
现有的监控表函数和视图(SNAPXXX、MONXXX)获取的数据是不会按照会话重置的。使用的时候,需要打开快照开关(个人理解,快照开关就是传给最底层API的一个参数)。

!!!重要说明!!!
新的监控 SQL 表函数的缺点是没有每会话重置功能,现有的系统监控快照 SQL 接口也有这个缺点。它们报告的监控数据总是相对于激活数据库时。也就是说,在每个数据库分区上激活数据库时,从 0 开始计数,然后一直增加,直到无效为止。生成数据快照的命令行和 C API 接口可以对监控数据执行每会话重置。每会话意味着重置只对于执行重置的应用程序有效。应用程序执行重置之后,它会看到相对于重置时刻的监控数据(重新从 0 开始计数)。执行监控的其他应用程序不受每会话重置影响,它们仍然访问相对于激活数据库时的值。每会话重置让不同的应用程序可以得到不同的监控数据视图,因为所有监控应用程序不必在同一时间重置数据。
监控数据重置功能在许多场景中都有用。例如,假设希望查明在下午 1 点到 2 点之间在 TEST.EMPLOYEE 表上执行了多少次表扫描。如果使用快照监控命令行接口,可以像下面这样做:
在下午 1 点,连接数据库服务器并重置监控数据:
RESET MONITOR FOR DATABASE
在下午 2 点,在同一连接上,执行以下命令查询所有表的监控数据。因为使用同一连接而且在下午 1 点执行了重置,所以监控数据覆盖从下午 1 点到 2 点的时间段。
GET SNAPSHOT FOR TABLES ON
搜索返回的监控数据(数据以文本报告的形式返回)并寻找 TEST.EMPLOYEE 表的数据。报告的表扫描次数表示在下午 1 点到 2 点之间在这个表上执行的扫描。

–10.MDC
MDC就是把多个纬度(列)在物理上以块的方式放在一起,然后每个块都由块索引(BID)来索引,比如:
table t1(month int, location char(30), cost int)
插如下面的几条记录:
1,a,100
1,b,100
1,b,300
2,a,192
我在month和location上建立MDC,物理上慢慢的会变成这样:
————————
|块1|1,a,100 —–BID(Key为1,a)
————————
|块2|1,b,100 1,b,300 —–BID(Key为1,b)
————————
|块3|2,a,192 —–BID(Key为2,a)
————————
如果我这个时候where条件是month=1 and location=’b’,那么根据BID我就能找到所有的数据,并且这些数据在物理上是连续了,我可以直接按页或块获取,速度提高了很多(类似于聚集索引)
注意,每个块的大小为extent的大小。也就是说,哪怕插入了一条记录,也会申请extent的数据,因此如果设计不当,会的使用大量的存储空间。如果一个块不够了,会自动申请下一个块。

对于快索引,查找的时候其实是这样的:数据库管理 > 管理概念 > 数据库对象 > 表 > 表分区和数据组织方案 > 多维集群表 > 块索引
简单的说,每个纬度(列)都会建立一个索引, 并且是块索引BID,然后查找的时候,用and或or来查找。

–11.MQT
MQT 的知识已集成到 SQL 和 XQuery 编译器中。在编译器中,查询重写阶段和优化器将查询与 MQT 匹配,并确定是否要用 MQT 取代访问基本表的查询。

MQT关键在于匹配,设计的MQT表如果能符合查询的要求,那么就会使用MQT,否则如果不用的话,MQT就没啥用。

MQT有用户维护和系统维护两种。前者需要用户手动插入数据,后者则只能由系统通过refresh维护。一般用后者。

MQT可能会出现数据不一致的情况。比如数据过期了。比如:
db2 “create table t1(count int)”
db2 “insert into t1 values(1)”
db2 “create table mqt_tq(sum) as (select sum(count) from t1) data initially deferred refresh deferred”
db2 set current refresh age any
db2 set integrity for mqt_tq immediate checked not incremental –这一步会的自动refresh
db2 runstats on table DB2INST1.MQT_TQ with distribution and detailed index all
db2 “insert into t1 values(2)”
db2 “select sum(count) from t1” –这里得到和为1,而不是3
db2 refresh table mqt_tq
db2 “select sum(count) from t1” –这里得到和为3

使用MQT的时候有两个前提条件:current fresh age变量是否设置、是否runstats。

–12.Windows的DB2的权限
sysadm_group -“系统管理权限组名”配置参数

此参数定义具有数据库管理器实例的 SYSADM 权限的组名。

配置类型
数据库管理器
适用于
带有本地客户机和远程客户机的数据库服务器
客户机
带有本地客户机的数据库服务器
带有本地客户机和远程客户机的分区数据库服务器
参数类型
可配置
缺省值
NULL
SYSADM 权限级别是实例级的最高级别管理权限。具有 SYSADM 权限的用户能够在实例中运行某些实用程序以及发出某些数据库和数据库管理器命令。

SYSADM 权限是由用于特定的操作环境中的安全性工具确定的。
对于 Windows 操作系统而言,可以将此参数设置为本地组或域组。组名的长度必须符合在 SQL 和 XML 限制中指定的长度限制。如果对 sysadm_group 数据库管理器配置参数指定“NULL”,那么下列用户具有 SYSADM 权限:
本地 Administrators 组的成员
域控制器上 Administrators 组的成员(如果未设置 DB2_GRP_LOOKUP 或者将其设置为 DOMAIN)
DB2ADMNS 组的成员(如果已启用“扩展安全性”功能)。DB2ADMNS 组的位置在安装期间确定
LocalSystem 帐户
对于 Linux 和 UNIX 系统,如果指定 NULL 作为此参数的值,那么 SYSADM 组缺省为实例所有者的主组。
如果该值不是 NULL,那么 SYSADM 组可以是任何有效的 UNIX 组名。

要将此参数复原为缺省值 (NULL),请使用 UPDATE DBM CFG USING SYSADM_GROUP NULL。必须用大写字母指定关键字 NULL。

!!!!!另外,用户如果不是Administrator,打开cmd的时候不能用db2cmd的那个,要用开始菜单DB2目录下的含有Administrator的那个。

如果修改了SYSADM_GROUP,那么除了需要把那个用户加到新的SYSADM_GROUP里外,该用户还需要加到DB2ADMNS组。同时为了赋予DBADM权限,需要:
—————————————————
9.7里面 DBADM需要SECADM来授权的。
1,find SECADM user ID in sysibm.sysdbauth
2,create SECADM id,再用SECADM ID连接授权即可。 –一般不用,一般SECADM为数据库的创建者。
3,grant SECADM XXX
然后GRANT DBAD on database XXX to group XXX
—————————————————

–13.存储
单个预取请求大小为一个EXTENT。
对于顺序读,读的单位可能是几个PAGE(甚至总的大小超过EXTENT),所以为了增加并行,要加大磁盘条带深度。

对于OLAP:
RIAD的条带宽度 X 数据盘个数 = EXTENT
DB2_PARALLEL_IO不用设置
PREFETCH_SIZE=EXTENT X 容器个数

对于OLTP:
RIAD的条带宽度 = EXTENT
DB2_PARALLEL_IO设置为RAID的数据盘个数
PREFETCH_SIZE=EXTENT X 容器个数 X DB2_PARALLEL_IO

由于PREFETCH_SIZE现在不是automatic了,所以每次增加、删除容器的时候请务必手工调整,否则影响性能。

–14.日志
检查点:用于限制恢复的时候需要的日志个数。如果没有这个,那么就需要扫描所有的日志文件,看看哪些日志文件的没有来做结尾。

有三个地址空间:
1.磁盘
2.主存(一般就是缓冲池)
3.事务局部地址空间,假设为t
也就是说,对域A=A*2这样的操作,数据需要先从磁盘读到主存中,然后事务管理器再从主存中取出A,放到局部地址空间t中(类似于寄存器,t=A),然后执行t=t*2,然后再把t写回到主存中,然后再刷入磁盘。
这里有四个操作:
INPUT(X) -> 从磁盘读取X到主存
READ(X) -> 从主存读取X到局部地址空间,这里可能会的间接调用INPUT
WRITE(X) -> 从局部地址空间写回到主存
OUTPUT(X) -> 从主存写回到磁盘

日志记录的几种形式:


undo日志:
只记录原先的值,如表示事务T修改了元素X,其原先的值为v。
undo日志需要遵循两个原则:
1.如果事务T改变了数据库元素X,那么形如这样的日志记录必须在X的新值写到磁盘之前写到磁盘中。
2.如果事务提交,则其COMMIT日志记录必须在事务改变的所有数据库元素写到磁盘之后写到磁盘中,但应尽快。
简单的说就是这个顺序(1、2是对各个数据库元素的):
1.把所有修改数据库元素的日志记录写入磁盘
2.改变数据库元素自身
3.写入commit日志记录
如:

READ(A,t)
t = t*2
WRITE(A,t)
READ(B,t)
t = t*2
WRITE(B,t)
FLUSH LOG
OUTPUT(A)
OUTPUT(B)

FLUSH LOG
恢复的时候从后往前重做。如果发现了,且有对应的,那么就不用恢复了,否则就得undo那些日志,undo完了后在最后写一个,这样下次恢复的时候就知道这个事务的日志已经undo过了。

undo检查点:
系统会周期性的对日志做检查点,在一个简单的检查点(静止检查点)中,系统会:
1.停止接收新的事务
2.等到所有当前活跃的事务提交或终止并且在日志记录中写入COMMIT或ABORT
3.将日志刷新到磁盘
4.写入日志记录,并再次刷新日志
5.重新开始接收新的事务
上面的这种方法有时候会的让系统看上去像是hang住了,所以一般用一种非静止检查点的方法来进行检查点的写入,其步骤为:
1.写入日志记录,并刷新日志入磁盘。其中T1,T2…Tk是所有活跃的事务(尚未提交(那肯定也就没有将修改写入磁盘啦))的名字或标识符
2.等待事务T1,T2…Tk的所有事务提交或终止,但允许其它事务开始
3.当T1,T2…Tk都已经完成时,写入日志记录并刷新日志入磁盘
在恢复的时候,有两种情况:
1.如果先是遇到了,那么我们知道所有未完成的事务在前一记录后开始。因此我们可以从后向前扫直到扫到一个记录,然后就可以停止了。以前的日志没有用处。
2.如果我们先是遇到了,那么奔溃发生在检查点的过程中。但是,未完成的事务只有从后往前扫描的时候达到START CKPT前遇到的那些以及T1,T2…Tk中的那些。因此,只要我们扫描到这些事务中最早的那个事务开始,就不用再扫描了。
换句话说,一旦记录写到了磁盘,那么我们就可以将上一个START CKPT之前的日志删除了。

redo日志:
的含义是事务T写入数据库元素X的新值v。
redo的原则为:
在修改磁盘上的任何数据元素X以前,要保证与X的这一修改相关的所有的日志记录,包括更新记录记录,都必须出现在磁盘上。
写磁盘的顺序为:
1.指出被修改元素的日志记录
2.commit日志记录
3.改变的数据库元素自身
如:

READ(A,t)
t = t*2
WRITE(A,t)
READ(B,t)
t = t*2
WRITE(B,t)

FLUSH LOG
OUTPUT(A)
OUTPUT(B)
恢复的时候,会的:
1.确定已提交的事务
2.从头开始扫描日志。对于遇到的每一个记录:
2.1.如果T是未提交的事务,则什么也不做
2.2.如果T是提交的事务,则重做该日志
3.对每个未完成的事务T,在日志中写入一个记录并刷新日志

redo检查点:
非静止检查点:
1.写入日志记录,其中T1,…Tk是所有活跃(即未提交的)事务,并刷入日志。
2.将START CKPT记录写入日志时所有已提交的事务已经写到缓冲区但还没有写到磁盘的数据库元素写入磁盘
3.写入日志记录并刷新日志

undo/redo日志
表示事务T改变了数据元素X的值,其原先的值为v,新值为w。
其必须满足下面的规则:
在由于某个事务T所做改变而修改发生在磁盘上的数据元素X前,更新记录必须出现在磁盘上。换句话说,undo/redo值仅仅需要undo和redo都有的约束,对于这个记录可以在磁盘的任何数据库元素的修改之前或之后写入磁盘。
但是,这个规则虽然能满足一致性的要求,但可能会的丢失事务。比如客户commit了一个事务,但这个commit没有刷入磁盘,造成恢复的时候给undo了,那么客户就会不开心,哪怕这个数据库还是一致的。所以一般还会有一个规则:
记录一旦出现在日志中,就必须被刷新到磁盘上。

一般DB2的话COMMIT了以后,才会把logbuf中的日志写入日志文件。在这之前必须通过锁机制防止脏页写入磁盘。

其恢复策略为:
1.按照从前往后的顺序,重做所有已提交的事务。
2.按照从后往前做的顺序,撤销所有未提交的事务。

undo/redo日志的非静止检查点:
1.写入日志记录,其中T1,…Tk是所有的活跃的事务,并刷新日志入磁盘。
2.将所有脏缓冲区(块)写到磁盘,脏缓冲区即包含一个或多个修改过的数据元素的缓冲区。和redo不同的是,我们刷新所有的脏缓冲区,而不仅仅刷新那些被提交事务写过的缓冲区。
3.写入日志记录并刷新日志。
比如:





(这一步会把所有的脏页写入磁盘,那么T1其实就ok了(当然得看到下面的END CKPT才真的OK),T2的话磁盘上有脏数据)





如果崩溃发生在末尾,也就是说T2、T3的COMMIT已经能看到了,那么因为有END CKPT,所以T1就OK了。我们只需要从开始重做T2、T3,甚至不需要去读之前的日志,因为把之前所有的数据都写如磁盘了。
如果崩溃发生在记录写如磁盘之前。那么我们通过检查点可以知道T1是OK了,T2的话需要把C redo为15。至于这种看都不用看了,因为这些已经写入到磁盘了。同时,我们需要undo T3,如果T3在里(这个例子里不在),那么就不得不看之前的日志了。

对于在线备份的恢复:
1.写入日志记录
2.执行一个适当的检查点
3.更具需要执行数据库备份
4.确定足够的日志被拷贝了下来
5.写入日志
这个时候,在第2项之前的日志可以抛弃了(实际上需要包含所有的活动日志)。

LSN:Log Sequence Number。代表着物理流,其含义就是数据在磁盘上的真实的位置。比如一个日志文件大小是10MB,也就是10485760字节,那么在第5个日志中的第10个page的lsn就是5*10485760+10*4096=0x320A000。每个日志也中第一个LSN就是代表该日志页中第一个日志记录的相对字节数。说白了就是在日志流的第一个字节处,可以把所有的日志想象成一条直线,然后每个LSN就是一条日志记录的便宜。
LSO:Logical Stream Offset。和LSN差不多,不过LSN在计算的时候包括了日志头的便宜,LSO则只是真实日志数据。LSO和LSN是可以自由转化的,只要把头的那些字节去掉就可以了。

从LSN的定义可以看出,每一条数据只要一变化,必然有一条LSN。

恢复的时候,是不是所有的日志都重做呢(这里的重做不是上面的那个redo的意思,主要是效率上的考虑)?其实不是。每个page的头部都会有一个pageLSN,这个pageLSN记录该页最后一条记录的LSN。那比如我的事务是update XXX set a=5;update XXX set a=6;这个时候pageLSN就是a=6这条日志的LSN。于是恢复的时候通过比较,发现pageLSN比a=5大,那么这个重做就可以不做了。

MinbuffLSN:系统中最古老的还没有被刷入磁盘的脏页的日志记录锁对应的pageLSN。换句话说,就是系统中还没有被刷如磁盘中的所有脏页中,最早的那个数据页的页头里的pageLSN
LowtranLSN:系统中最古老的还没被提交的事务的第一条日志记录锁对应的LSN

数据库从min(minbufLSN,lowtranLSN)开始崩溃恢复。这个LSN之后的日志文件称为活动日志文件。MinbuffLSN和LowtranLSN存在SQLOGCTL.LFH中。恢复的时候,先从min(minbufLSN,lowtranLSN)开始redo,看看同时记录那些没有commit的事务,最后在开始undo。这种undo/redo的规则可以参考上面的资料,也就是说:在由于某个事务T所做改变而修改发生在磁盘上的数据元素X前,更新记录必须出现在磁盘上。

SOFTMAX:用于控制崩溃恢复的时候需要多少个日志。这个就是用于设置检查点的,原理可以看上面的东东。MinbuffLSN有时候会的很靠前很靠前,因此会的需要很多的日志,如果有了SOFTMAX,其会的调用异步页清理程序,这样的话就主动的把脏页写入到磁盘里了。不过一般出现这个的话也不大好。。。毕竟脏页这种东西都是建议通过脏页阀值那个参数通过IO_CLEANER来写如磁盘的。

–15.并发控制
并发控制的目的是保证在存在多个事务的时候,其执行结果和一个一个事务顺序执行的结果相同。这一点参考下面的冲突可串行化就知道了。
这里可以再回过头看下14中的那个事务局部地址空间。在实际情况中,某个事务T放入缓冲区中的数据库元素A在该缓冲区中可能不仅被T还被其它访问A的事务读或写。

事务需要满足:
事务的一致性,事务的2PL,调度的合法性。

冲突和不冲突(冲突的话就是说换了顺序会产生不一样的结果,不冲突的话顺序随便变,就当不知道另外还有一个事务存在一样)r表示read,w表示write,下标表示事务i和事务j,X、Y为数据库元素:
ri(X);rj(Y)不冲突,即便X=Y
ri(X);wj(Y)不冲突,只要X!=Y
wi(X);rj(Y)不冲突,只要X!=Y
wi(X);wj(Y)不冲突,只要X!=Y
ri(X);wi(Y)总是冲突。因为这个违反了单个事务的逻辑
wi(X);wj(X)冲突。
ri(X);wj(X)冲突。因为这可能影响事务i做的事情

事务可以写成:
T1:r1(A);w1(A);r1(B);w1(B);
T2:r2(A);w2(A);r2(B);w2(B);
其执行顺序可能是:
r1(A);w1(A);r2(A);w2(A);r1(B);w1(B);r2(B);w2(B);
看看能不能把它转成串行调度(串行调度就是先执行事务A的所有操作,再执行事务B的所有操作,而不是像上面这个交互着执行)#代表变了的东西:
r1(A);w1(A);r2(A);r1(B)#;w2(A)#;w1(B);r2(B);w2(B);
r1(A);w1(A);r1(B)#;r2(A)#;w2(A);w1(B);r2(B);w2(B);
r1(A);w1(A);r1(B);r2(A);w1(B)#;w2(A)#;r2(B);w2(B);
r1(A);w1(A);r1(B);w1(B)#;r2(A)#;w2(A);r2(B);w2(B); –然后就变成串行化了,先执行事务1的,然后执行事务2的。由于这个调度可以转成串行调度,所以这个调度称为冲突可串行化调度。

需要某种算法判定一个调度是否是冲突可串行化调度,这个算法可以是优先图算法之类的。

数据库事务系统的实现必须保证通过某种机制保证调度是冲突可串行化调度。也就是说能保证不管事务的执行顺序如何,其结果类似于事务一个接着一个的执行。实现这个调度的一个方式是使用锁。加锁解锁由封锁调度器来实现:
假设:
li(A)表示事务i对A加锁
ui(A)表示事务i对B解锁

于是事务变成了如下的顺序:
T1:l1(A);r1(A);A=A+100;w1(A);u1(A);l1(B);r1(B);B=B+100;w1(B);u1(B)
T2:l2(A);r2(A);A=A*2;w2(A);u2(A);l2(B);r2(B);B=B*2;w2(B);u2(B);
那么考虑下面的调度(初始A=B=25),这个调度不是可串行化的,因为其结果是A=250,B=150,和串行化的A=B=250不一样:
l1(A);r1(A);A=A+100;w1(A);u1(A);l2(A);r2(A);A=A*2;w2(A);u2(A);l2(B);r2(B);B=B*2;w2(B);u2(B);l1(B);r1(B);B=B+100;w1(B);u1(B);
为了保证合法调度是冲突可串行化的,需要使用“两阶段封锁”。

两阶段封锁(two-phase locking,2PL):在每个事务中,所有的封锁请求先于所有的解锁请求(IMPORTANT!!!核心思想!!!有了这个其实已经能保证所有的调度都是冲突可串行化了!!!感觉比最强的RS隔离级别还要牛叉!!!)。两个阶段是指获取锁的第一阶段和释放锁的第二阶段。
如:
T1:l1(A);r1(A);A=A+100;w1(A);l1(B);u1(A);r1(B);B=B+100;w1(B);u1(B)
T2:l2(A);r2(A);A=A*2;w2(A);l2(B);u2(A);r2(B);B=B*2;w2(B);u2(B);
在这个情况下,所有的合法调度都是冲突可串行化的。

上面的讨论都是基于最简单的锁的,相当于X锁,其并发性不大好,但是,关靠上面的这些讨论,已经能保证调度的可串行化了。

B树索引的灾难:
如果按照块来加锁,由于2PL的关系,需要从根节点开始加锁,那么基本上就没有并发了。所以对于这种树结构,有一套专门的协议。

除了用锁来保证外,还有很多别的方法,比如用时间戳。

–16.关于package
关于package的一些东东可以看odbcdb2bind.pdf这个pdf。

预编译就是将sqc文件变成c文件。默认情况下会的直接在数据库中创建程序包。但是,也可以指定绑定文件,绑定文件包含有关应用程序中的SQL语句的信息。而“绑定”指的是根据绑定文件创建程序包并将其存储到数据库的过程。绑定文件必须与应用程序需要访问的每个数据库绑定。如果应用程序访问多个数据库,那么必须为每个数据库创建一个包。

一般的开发过程是:
1.创建嵌入式SQL源文件,如XXX.sqc
2.连接到数据库,然后预编译源文件,生成.c文件。这个时候可以选择生成程序包或绑定文件。
3.编译.c文件
4.将对象文件与相关库做链接
5.如果想访问其它数据库,那么需要对绑定文件进行绑定以创建程序包。
6.运行可执行文件。

一般的预编译为:
db2 prep XXX.spc bindfile
这个会的生成四个输出:
1.经过修改的源代码
2.程序包。如果使用package参数,或者未指定任何bindfile,syntax或sqlflag参数,那么程序包就会存储在所连接的数据库中。
绑定文件,如果使用bindfile参数,那么会的生成一个bnd文件,这个文件包含创建程序包时需要的数据。以后,可以将这个东东通过bind命令来让应用程序和一个或多个数据库进行绑定。
消息文件,如果使用messages参数,就会生成消息文件。

于是绑定的含义就很清楚了:根据绑定文件创建程序包并将其存储到数据库的过程称为绑定。

–17.开发
CLI:Call Level Interface,调用级接口。是用于进行关系数据库访问的C和C++应用程序编程接口,次接口使用函数调用见动态SQL语句作为函数自变量进行传递。CLI可替代嵌入式动态SQL,但不同于嵌入式SQL的是它不需要主变量或预编译器。其不需要对每个数据库进行编译。符合ODBC规范。

嵌入式SQL:嵌入式SQL的语句包含在必须绑定至目标数据库服务器的程序包中。可以使用C、C++和COBOL进行嵌入式SQL的开发。构建嵌入式SQL应用程序的时候,必须在编译和链接应用程序前执行两个必要步骤:
1.使用DB2预编译器来准备包含嵌入式SQL语句的源文件。PREP命令用于调用预编译器。预编译器会的解析源代码,并转换为DB2运行时服务API调用,最后将输出写入新的源文件。同时,预编译器将生成SQL语句存取方案,这些存取方案以程序包的形式一起存储在数据库中。
2.将应用程序中的语句与目标数据库绑定。默认情况下,在PREP预编译的时候就已经绑定了。如果要延迟绑定,那么需要在PREP时候执行BINDFILE选项以生成绑定文件。

JDBC:DB2包含对两种类型的JDBC驱动体系结构的支持。根据JDBC规范,JDBC分为4类。

重点专注下嵌入式SQL:
主变量:就是:XXX这种东西
参数标记:就是?这种东西

对于c语言,默认的命名规范是:.sqc为源文件后缀,.c是预编译器输出文件后缀。

–18.上海培训
表太大的话,表空间单个大小就会很大。这个时候可能除了FS的限制还有内核参数的限制。

停不掉实例考虑修改SVCENAME,这个可以在线改。

db2diag -time 2013-11-11 -level “Severe,Error”

内存不够的时候,才建议打开STMM。否则如果内存充足,那么推荐使用手动。

私有排序:用的内存是代理自己的内存
共享排序:使用的是数据库的一块统一内存

10.1之前,组合索引:
(A,B,C) 等价于(A),(A,B),(A,B,C)
10.1之后,组合索引多了个:(A,C),只要A的区分度很高就行。

优化器对or的优化非常差!还不如改成union all

db2里唯一看latch的方法:db2pd -latch。这个如果过多会的严重影响速度,因此一般只有在sql慢的时候,可以考虑看看,平时就不用看这个了。

新系统,在安装之前,先把DD给测试好。写的速度标准大概在300MB左右(50G文件)。一般读的话速度不怎么关心,只要关心写的速度。低于200MB肯定有问题。V7000的写的速度可以达到800MB。

上线前用load runner测速度。

分区表滚入滚出的时候有秒级别的Z锁。

表空间的online转移会的做一个online的rebalance,这期间sql的速度可能会比平时慢10倍。

对于极大事务量的系统,在使用监控的时候,需要调整下面两个参数,防止系统停止响应:
db2set DB2_MAX_INACT_STMTS=1000 –表明不活动的语句最多就采集1000个
MON_HEAP_SZ设的大点

db2pd -app也要多用用

代理不够日志里会有说明。SQL1040N

日志一般都建议在400MB以上。LOGPRIMARY一般等于50,LOGSECONDAY一般等于80。LOGBUFFSZ一般等于64~128MB。日志的默认参数一定要调整!

LOCKTIMEOUT绝对不能等于-1,可以设置为30s

常规数据库,读写比就是80:20.如果达到了70:30,那么必须看下日志有没有瓶颈。

最对热表做runstats(db2pd -tcbstats)

backup-ending可以手工取消。。。db2dart

–19.关于package cache
这个东东是和大小写相关的!比如select * from act和selecT * from act就是两个不同的SQL。
这个参数pckcachesz是个soft limit的,所以有时候可以看到大于这个参数的东东。可以在线配置的,如果设置为-1那么就是maxappl * 8。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*