2006 年 11 月 01 日, 星期三
bitmap索引超级异常无限扩大问题
今天遇到这么一个情况:
一朋友的现场发现表空间不够,就一直加表空间的size,一直加到6G,还是会提示不够,报告错误。
后来从系统视图得知,某表A,有约40M,在上面的3个字段上建了bitmap索引,该索引已扩展到5821M。
为了赶紧解决问题,把该索引直接drop了,系统开始正常,不到1秒就drop掉了。
========================================
原始表40M,bitmap索引5821M,明显异常。
然后从alert.log里检查错误,与此有关,只有唯一一个错误提示:
ORA-1654: unable to extend index MEES.A_IND_1 by 8192 in tablespace XXXSPACE
没有任何跟踪文件生成。
在第一次出现这个错误提示的紧接着,分配了N多undo segment(从 alert.log文件摘出)。
各位有遇到过这事的吗?知道什么原因吗?
附alert.log错误部分:
Thu Oct 26 00:58:01 2006
ORA-1654: unable to extend index MEES.A_IND_1 by 8192 in tablespace XXXSPACE
Thu Oct 26 01:51:59 2006
Created Undo Segment _SYSSMU11$
Undo Segment 11 Onlined
Thu Oct 26 01:52:12 2006
Created Undo Segment _SYSSMU12$
Undo Segment 12 Onlined
Thu Oct 26 01:52:23 2006
Created Undo Segment _SYSSMU13$
Undo Segment 13 Onlined
Thu Oct 26 01:53:00 2006
Created Undo Segment _SYSSMU14$
Undo Segment 14 Onlined
Thu Oct 26 01:53:37 2006
Created Undo Segment _SYSSMU15$
Undo Segment 15 Onlined
Thu Oct 26 01:53:51 2006
Created Undo Segment _SYSSMU16$
Undo Segment 16 Onlined
Thu Oct 26 01:54:19 2006
Created Undo Segment _SYSSMU17$
Undo Segment 17 Onlined
Thu Oct 26 01:55:04 2006
Created Undo Segment _SYSSMU18$
Undo Segment 18 Onlined
Thu Oct 26 01:55:16 2006
Created Undo Segment _SYSSMU19$
Undo Segment 19 Onlined
Thu Oct 26 01:55:22 2006
Created Undo Segment _SYSSMU20$
Undo Segment 20 Onlined
Thu Oct 26 01:56:26 2006
Created Undo Segment _SYSSMU21$
Undo Segment 21 Onlined
Thu Oct 26 01:58:14 2006
Created Undo Segment _SYSSMU22$
Undo Segment 22 Onlined
Thu Oct 26 01:58:33 2006
Created Undo Segment _SYSSMU23$
Undo Segment 23 Onlined
Thu Oct 26 01:58:52 2006
Created Undo Segment _SYSSMU24$
Undo Segment 24 Onlined
Thu Oct 26 01:59:18 2006
Created Undo Segment _SYSSMU25$
Undo Segment 25 Onlined
Thu Oct 26 01:59:31 2006
Created Undo Segment _SYSSMU26$
Undo Segment 26 Onlined
Thu Oct 26 01:59:36 2006
Created Undo Segment _SYSSMU27$
Undo Segment 27 Onlined
Thu Oct 26 02:00:09 2006
Created Undo Segment _SYSSMU28$
Undo Segment 28 Onlined
Thu Oct 26 02:00:09 2006
Created Undo Segment _SYSSMU29$
Undo Segment 29 Onlined
Thu Oct 26 02:11:12 2006
Created Undo Segment _SYSSMU30$
Thu Oct 26 02:11:12 2006
Undo Segment 30 Onlined
Thu Oct 26 02:11:13 2006
Created Undo Segment _SYSSMU31$
Undo Segment 31 Onlined
Thu Oct 26 02:16:47 2006
Created Undo Segment _SYSSMU32$
Undo Segment 32 Onlined
Thu Oct 26 03:57:34 2006
Thread 1 advanced to log sequence 621
============================
在我们的系统里,不仅不是在一个column上创建bitmap index,而是在多个column上联合起来创建bitmap index,从而可以想见,几乎等同于每个bitmap index entry对应的只有极少数的rowid,即只对应极少数的row,而且每个bitmap都要创建一个或多个bitmap segment,相应的,DML操作可能需要频繁的lock很多rows,影响并发性(影响也需要修改同样bitmap entry的用户),同时,由于pctfree等存储参数影响index空间的分配及管理,和由于数据操作导致物理地址更改从而修改index entry,扩展,链接bitmap index entry,频繁修改导致的磁盘碎片,block分配链接等。这是空间增长和系统性能下降的部分原因。
(关于空间扩展的一个简单测试:
通过测试我们可以观察的到(比如3个varchar2(20)的column联合,distinct card为10左右,即每100条数据有约10条的联合字段组成的值是相同的),同样的数据,一个table data block如果能装载约110条rows,那么其每个bitmap index data block约只能装载30条rows左右。这也就是说,如果该table的bitmap index要扩展到6G,即6291456K,即786432个Block(每个block为8K),在这个测试表,只要插入786432*30= 23592960条数据即可导致该bitmap index超过6G大小。(这种测试数据,根据组成表字段的个数,类型,长度等不同,还会有极大的变化,可能bitmap index变的比这个测试数据更加严重的糟糕,也可能比这个稍好,但不会太好)
何况bitmap index还与创建其index的colun类型(如是字符型?是数字形?是否可变长?字段长度等等)有很大关系,这样我们就可以非常明显的看出,高distinct card,即高基数值创建bitmap index的极其不合适。
Oracle参考视图:user_segments)
同时,由于用户操作而触发的Oracle递归操作(比如向表插入一条数据,就会做类似更改bitmap index,在data dictionary里记录index,table,segment,tablespace的变化等),递归的递归操作,这些操作有的也需要存储空间(有 的是隐性空间需求,比如在更改index entry完成前,old value会继续保留在index里),同时,在另外几个资源方面,如CPU,memory(比如,为了进行一次递归操作,需要从数据库发生物理I/O读 取数据到内存,此时会占用I/O资源和内存资源,CPU资源等,该操作占用了,那么同时其他操作的内存,I/O,CPU等资源就得不到有效保证,恶性的 话,就会导致更多的物理I/O,等多的无合理内存使用,如内存命中率降低等,直接导致整体性能下降),I/O,Oracle lock等资源,由于用户操作及用户触发的递归操作(包括bitmap index的修改操作)也会大量的被使用,以及使用这些资源时引发的资源竞争,这些都非正常的改变了原有系统资源分配,导致整体性能下降,严重的,由于阻 塞的关系,导致整个用户应用程序处于停顿状态,无法进行下一步操作。
==========================================
参考信息:
bitmap index方法从Oracle 7.3版本开始就加到oracle数据库里了。现在只在企业版和个人版里有,在标准版里没有。
bitmap的创建方法为,为table column里的每个key value(唯一值)创建一个bitmap,用来记录table里这些具有相同key value的rows的物理地址,bitmap里每个bit可能就对应了某个具有相同key value的row的物理地址(如果bit被set了,那么该bit对应的rowid的row就记录了key value,map函数能把一个bit的位置转换到实际的rowid),如果key value个数很少(即基数很小,比如该colun只记录性别,那么就只有男,女两种情况),用bitmap的index方法是可以节省很多磁盘空间的, 同时同一个table里多个column所创建的不同的bitmap之间,在实际access table前,能有效的进行merge,这也会很大的提高执行性能。
bitmap主要用于数据仓库,table有大量的数据并且列上基数很小(一般是column的distinct values占rows总数的1%以下,或重复出现超过100次以上,Oracle建议此时才可以把该column列为创建bitmap index的侯选字段),同时,还由于数据仓库上并行访问的事务非常少。bitmap index并不适用于OLTP业务,OLTP一般都是有大量的并发事务来修改同样的数据。bitmap主要就是设计来为数据仓库服务的,即应用于低基数超 级大数据量查询服务,而且只用在where clause里包含and ,or,not,或equality queries(比如在and和or条件的查询,在把bit转换成rowid以前,就能很快的得到相应的boolean操作)。
bitmap index 非常不利于DML操作,每次DML操作,bitmap segment就要被更新一次。每个key value的压缩bitmap被Oracle内部组织成一个或多个bitmap segment,每个bitmap segment一般要占一个block的一大半,加锁的间隔尺寸也是一个同样的bitmap segment,这样在同时执行DML语句的环境下,对性能影响非常大,如果发生很多的DML操作,就会导致bitmap index的size增大和降低查询性能,改变这种状况,一般使用alter index ... rebuild语句来compact index和恢复性能。一般的B-tree index,一个index entry只包含一个rowid,(所以B-tree index就会有很多个index entry),所以当一个index entry被lock时,也只是一条row被lock而已,但bitmap index,一个entry可能包含的是一个rowid的范围,(所以bitmap index只有很少的index entry),如果这个bitmap index entry被lock了(Oracle不能lock单个bit,只能以index entry为单位lock),那么那些与被bitmap index entry包含的rowid范围对应的rows都会被lock。这样就降低了应用的并发性(因为一个用户在更改bitmap index entry的时候,其他用户对该entry的更改只能被lock在外面等候,这样很明显的影响并发性,如同串行操作一样,在更改操作完成前,具有old value的index key entry和具有new value的index key entry都不可用)。如果一个bitmap segment的rowid增加了,也就是说,相应的并发性就降低了。(lock对DML操作和OLTP环境影响非常大,但对于查询性能没有影响)。更新 bitmap index是资源消费,即成本很大的操作。bitmap index对于固定长度的column比可变长度的column工作的更好。