在一个数据库中发现了一个异常增大的对象,经检查改对象为BITMAP索引。
在数据库中执行下面的脚本,发现了一个异常增大的对象:
SQL> SELECT SEGMENT_NAME, SEGMENT_TYPE, SUM(BYTES)/1024/1024/1024 G
2 FROM USER_SEGMENTS
3 GROUP BY SEGMENT_NAME, SEGMENT_TYPE
4 HAVING SUM(BYTES)/1024/1024/1024 > 15;
SEGMENT_NAME SEGMENT_TYPE G
------------------------------ ------------------ ----------
TU_ORD_ORDER_ZJ_K_CODE INDEX 16.7539215
检查该对象的详细信息:
SQL> SELECT INDEX_NAME, INDEX_TYPE FROM USER_INDEXES
2 WHERE INDEX_NAME = ’TU_ORD_ORDER_ZJ_K_CODE’;
INDEX_NAME INDEX_TYPE
------------------------------ ---------------------------
TU_ORD_ORDER_ZJ_K_CODE FUNCTION-BASED BITMAP
这个对象是一个基于函数的BITMAP索引。一般来说,BITMAP索引的大小要远远小于普通索引,而这里索引的大小达到了16G,远远超过表本身的大小,这里肯定存在问题。通过对DBMS_SPACE包中SPACE_USAGE过程的封装,检查这个对象的空间使用情况:
SQL> create or replace procedure p_space_usage (p_segment_name in varchar2,
2 p_segment_type in varchar2 default ’TABLE’,
3 p_segment_owner in varchar2 default user,
4 p_partition_name in varchar2 default ’’) as
5 v_unformatted_blocks number;
6 v_unformatted_bytes number;
7 v_fs1_blocks number;
8 v_fs1_bytes number;
9 v_fs2_blocks number;
10 v_fs2_bytes number;
11 v_fs3_blocks number;
12 v_fs3_bytes number;
13 v_fs4_blocks number;
14 v_fs4_bytes number;
15 v_full_blocks number;
16 v_full_bytes number;
17 begin
18 dbms_space.space_usage(upper(p_segment_owner), upper(p_segment_name), upper(p_segment_type), v_unformatted_blocks,
19 v_unformatted_bytes, v_fs1_blocks, v_fs1_bytes, v_fs2_blocks, v_fs2_bytes, v_fs3_blocks, v_fs3_bytes,
20 v_fs4_blocks, v_fs4_bytes, v_full_blocks, v_full_bytes, upper(p_partition_name));
21
22 dbms_output.put_line(’unformatted_blocks is ’ || v_unformatted_blocks);
23 dbms_output.put_line(’unformatted_bytes is ’ || v_unformatted_bytes);
24 dbms_output.put_line(’fs1_blocks is ’ || v_fs1_blocks);
25 dbms_output.put_line(’fs1_bytes is ’ || v_fs1_bytes);
26 dbms_output.put_line(’fs2_blocks is ’ || v_fs2_blocks);
27 dbms_output.put_line(’fs2_bytes is ’ || v_fs2_bytes);
28 dbms_output.put_line(’fs3_blocks is ’ || v_fs3_blocks);
29 dbms_output.put_line(’fs3_bytes is ’ || v_fs3_bytes);
30 dbms_output.put_line(’fs4_blocks is ’ || v_fs4_blocks);
31 dbms_output.put_line(’fs4_bytes is ’ || v_fs4_bytes);
32 dbms_output.put_line(’full_blocks is ’ || v_full_blocks);
33 dbms_output.put_line(’full_bytes is ’ || v_full_bytes);
34 end;
35 /
Procedure created.
SQL> SET SERVEROUT ON SIZE 10000
SQL> EXEC P_SPACE_USAGE(’TU_ORD_ORDER_ZJ_K_CODE’, ’INDEX’, ’NDMAIN’)
unformatted_blocks is 2038268
unformatted_bytes is 16697491456
fs1_blocks is 0
fs1_bytes is 0
fs2_blocks is 139652
fs2_bytes is 1144029184
fs3_blocks is 0
fs3_bytes is 0
fs4_blocks is 0
fs4_bytes is 0
full_blocks is 13365
full_bytes is 109486080
PL/SQL procedure successfully completed.
问题已经很显然了,这个基于函数的BITMAP索引包含了大量的unformatted blocks,造成大量空间的浪费,而实际上索引所需要的空间并没有这么大。
检查METALINK,发现Oracle处理ASSM表空间的BITMAP索引存在空间浪费的bug,详细bug描述参考:Bug No. 2915226。
Oracle给出了一个例子来重现这个bug:
SQL> SELECT TABLESPACE_NAME, SEGMENT_SPACE_MANAGEMENT
2 FROM DBA_TABLESPACES
3 WHERE TABLESPACE_NAME = ’USERS’;
TABLESPACE_NAME SEGMEN
------------------------------ ------
USERS AUTO
SQL> CREATE TABLE T (ID NUMBER NOT NULL, BITMAP_COL NUMBER NOT NULL);
表已创建。
SQL> INSERT INTO T SELECT ROWNUM, MOD(ROWNUM, 2) FROM DBA_OBJECTS;
已创建31016行。
SQL> CREATE BITMAP INDEX IND_B_T_BIT ON T(BITMAP_COL) TABLESPACE USERS;
索引已创建。
SQL> BEGIN
2 FOR I IN REVERSE 1..1000 LOOP
3 UPDATE T SET BITMAP_COL = 1
4 WHERE BITMAP_COL = 0 AND ID = I;
5 END LOOP;
6 END;
7 /
PL/SQL 过程已成功完成。
SQL> SET SERVEROUT ON SIZE 1000000
SQL> EXEC P_SPACE_USAGE(’IND_B_T_BIT’, ’INDEX’)
unformatted_blocks is 93536
unformatted_bytes is 766246912
fs1_blocks is 0
fs1_bytes is 0
fs2_blocks is 8211
fs2_bytes is 67264512
fs3_blocks is 0
fs3_bytes is 0
fs4_blocks is 0
fs4_bytes is 0
full_blocks is 18
full_bytes is 147456
PL/SQL 过程已成功完成。
这个结果和这里碰到的问题几乎完全一致,只不过这个例子是人为构造bug的产生,而数据库中则是由于正常修改在某些情况下触发了这个bug,检查当前的表空间信息:
SQL> SELECT INDEX_NAME, TABLESPACE_NAME FROM USER_INDEXES
2 WHERE INDEX_NAME = ’TU_ORD_ORDER_ZJ_K_CODE’;
INDEX_NAME TABLESPACE_NAME
------------------------------ ------------------------------
TU_ORD_ORDER_ZJ_K_CODE INDX
SQL> SELECT SEGMENT_SPACE_MANAGEMENT FROM USER_TABLESPACES
2 WHERE TABLESPACE_NAME = ’INDX’;
SEGMEN
------
AUTO
这个Bug在10.1.0.2被修正,对于9.2.0.2以上的版本,可以直接下载编号为2915226的patch来解决这个问题