Hakan factor

Hakan factor

Hakan factor 用来表示在表中任何一个数据块所包含的记录数量的上限。对于一个表来说,Hakan factor 受一下几方面的影响:

  • 表中列的数量
  • 列的数量类型和长度
  • 列是否具有非空约束
在通常情况下,oracle认为每条记录最小占用11byte的存储空间,这是为了在发生行迁移的情况下,留出足够的空间保存行迁移信息(扩展rowid会占用10字节的空间)。因此我们可以归纳出如下结论:

数据库大小

每块可以存储的最大记录数(抛出部分控制信息)

2048

178(2048/11=186)

4096

364(4096/11=194)

8192

736(8192/11=744)

16384

1481(16384/11=1489)

32768

2971(32767/11=2978)


下面我们来论证一下,(show_hakan函数来源于网络,函数的内容请参见文章底部):

SQL> create table tab1(c1 char(1));

表已创建。

SQL> execute show_hakan('tab1');
--Hakan factor for object 364291 (SCOTT.tab1) is 736 with flags         0

PL/SQL 过程已成功完成。

SQL> insert into tab1 select 'a' from dual connect by level < 1000;

已创建999行。

SQL> commit;

提交完成。

SQL> execute show_hakan('tab1');
--Hakan factor for object 364291 (SCOTT.tab1) is 736 with flags         0

PL/SQL 过程已成功完成。

SQL> create table tab2(c1 char(100),c2 char(300));

表已创建。

SQL> execute show_hakan('tab2');
--Hakan factor for object 364292 (SCOTT.tab2) is 736 with flags         0

PL/SQL 过程已成功完成。

SQL> insert into tab2 select 'a','b' from dual connect by level < 1000;

已创建999行。

SQL> commit;

提交完成。

SQL> execute show_hakan('tab2');
--Hakan factor for object 364292 (SCOTT.tab2) is 736 with flags         0

PL/SQL 过程已成功完成。

SQL> alter table tab2 modify c1 not null;

表已更改。

SQL> alter table tab2 modify c2 not null;

表已更改。

SQL> execute show_hakan('tab2');
--Hakan factor for object 364292 (SCOTT.tab2) is 19 with flags         0

PL/SQL 过程已成功完成。
从上面的情况也可以看出,oracle在计算 hakan factor时,是以可能发生的最大值为其计算结果。

alter table minimize records_per_block

oracle并没有为我们提供显示修改或者查看hakan factor的功能,但是我们可以通过一些变通的方式来实现这些功能。alter table ... minimize RECOREDS_PER_BLOCK的工作过程如下:

  • 扫描整个表
  • 计算表中每个块中包含的记录数量
  • 设置表的hakan factor为块记录数量的最大值
  • 设置标志位为ox8000(),表示表的hakan factor通过records_per_block设置

通过recors_per_block设置hakan factor 后,当我们后续向表中添加记录时,每个数据块的记录数不会超过hakan factor设置的值。

SQL> create table tab1(c1 number);

表已创建。

SQL> alter table tab1 minimize records_per_block;
alter table tab1 minimize records_per_block
            *
--第 1 行出现错误:
--ORA-28603: 不允许对空表使用语句


SQL> insert into tab1 values(1);

已创建 1 行。

SQL> commit;

提交完成。

SQL> insert into tab1 select * from tab1;

已创建 1 行。

SQL> /

已创建2行。

SQL> commit;

提交完成。

SQL> alter table tab1 minimize records_per_block;

表已更改。

SQL> execute show_hakan('tab1');
Hakan factor for object 364293 (SCOTT.tab1) is 3 with flags      8000

PL/SQL 过程已成功完成。

SQL> insert into tab1 select * from tab1;

已创建4行。

SQL> /

已创建8行。

SQL> commit;

提交完成。

SQL> select 
        ct, count(*) 
from    (
                Select  Dbms_Rowid.Rowid_Block_Number(Rowid), Count(*) Ct  
                from    tab1
                group by
                        dbms_rowid.rowid_block_number(rowid)
        )
Group By Ct
order by ct  2    3    4    5    6    7    8    9   10  
 11  /

        CT   COUNT(*)
---------- ----------
         4          4

SQL> create bitmap index i1 on tab1(c1);

索引已创建。

SQL> alter table tab1 nominimize records_per_block;
alter table tab1 nominimize records_per_block
*
第 1 行出现错误:
ORA-28602: 不允许对包含位图索引的表使用语句


SQL> create table tab2(c1 number);

表已创建。

SQL> create bitmap index i2 on tab2(c1);

索引已创建。

SQL> insert into tab2 values(1);

已创建 1 行。

SQL> commit;

提交完成。

SQL> alter table tab2 minimize records_per_block;
alter table tab2 minimize records_per_block
*
--第 1 行出现错误:
--ORA-28602: 不允许对包含位图索引的表使用语句

SYS.TAB$.SPARE1

oracle没有给我们提供直接的视图来查询hakan factor,我们可以通过手工解析表SYS.TAB$的spare1字段来获取有个hakan factor的信息。下面对spare1字段内容的解释来源与网络

  • Lower bits (at least 12, perhaps as many as 15): Håkan factor. 
  • 0×08000: MINIMIZE RECORDS_PER_BLOCK in effect
  • 0×10000: Seems to mean that the Håkan factor has been fixed higher than the value calculated from non-null columns. This could be e.g. if set by event 14529 (see below) or if non-null columns have been added after a bitmap index has been created.
  • 0×20000: Table compression is enabled
综上,我们可以使用下面的过程来查看hakan factor
create or replace procedure show_hakan(
	i_table		in	varchar2,
	i_owner		in	varchar2 default user
) as
	m_obj		number(8,0);
	m_flags		varchar2(12);
	m_hakan		number(8,0);
begin
	select
		obj#, 
	to_char(
		bitand(
			spare1, to_number('ffff8000','xxxxxxxx')
		),
		'xxxxxxxx'
	)					flags,
	bitand(spare1, 32767)			hakan	-- 0x7fff	
	into
		m_obj,
		m_flags,
		m_hakan
	from
		tab$
	where	obj# in (
			select	object_id 
			from	dba_objects
			where	object_name = upper(i_table)
			and	object_type = 'TABLE'
			and	owner = upper(i_owner)
		)
	;

	dbms_output.put_line(
		'Hakan factor for object ' || 
		m_obj   || ' (' ||
		i_owner || '.' || 
		i_table || ') is ' || 
		m_hakan || ' with flags ' ||
		m_flags
	);
end;
/

drop public synonym show_hakan;
Create Public Synonym Show_Hakan For Show_Hakan;
grant execute on show_hakan to public;

Hakan factor 对位图索引的影响

我们知道位图索引是通过起始rowid和结束rowid和位图的联合作用来定位数据。因此,我们处理需要知道起始rowid和终止rowid之外,还需要知道在每个数据块中的记录数量,而这往往是很难做到的,因为每个块中存储的记录数量是不等和变化的。在这种情况下,oracle计算每个数据块中记录数量的最大值,并根据这个最大值在位图中为每个数据块分配对应数量的标识位。而计算最大值的方法受hakan factor的影响。因此,如果我们减小hakan factor,便可以控制位图中为每个数据块分配的标识位数量,从而降低位图索引的占用空间,仅一步会降低位图逻辑操作时对内存的消耗。
SQL> create table t1(c1 number);

表已创建。

SQL> insert into t1 values(1);

已创建 1 行。

SQL> /

已创建 1 行。

SQL> /

已创建 1 行。

SQL> alter table t1 minimize records_per_block;

表已更改。

SQL> create table t2 as select * from t1;

表已创建。

SQL> alter table t2 minimize records_per_block;

表已更改。

SQL> execute show_hakan('t1');
Hakan factor for object 78156 (EASY.t1) is 2 with flags      8000

PL/SQL 过程已成功完成。

SQL> execute show_hakan('t2');
Hakan factor for object 78157 (EASY.t2) is 2 with flags      8000

PL/SQL 过程已成功完成。

SQL> insert into t1 select * from t1;

已创建 3 行。

SQL> /

已创建 6 行。

SQL> /

已创建 12 行。

SQL> /

已创建 24 行。

SQL> /

已创建 48 行。

SQL> /

已创建 96 行。

SQL> /

已创建 192 行。

SQL> /

已创建 384 行。

SQL> /

已创建 768 行。

SQL> /

已创建 1536 行。

SQL> /

已创建 3072 行。

SQL> /

已创建 6144 行。

SQL> /

已创建 12288 行。

SQL> /

已创建 24576 行。

SQL> truncate table t2;

表被截断。

SQL> insert into t2 select * from t1;

已创建 49152 行。

SQL> commit;

提交完成。

 select   
        ct, count(*)   
from    (  
                Select  Dbms_Rowid.Rowid_Block_Number(Rowid), Count(*) Ct    
                from    t1  
                group by  
                        dbms_rowid.rowid_block_number(rowid)  
        )  
Group By Ct  
 10  order by ct
 11  /

	CT   COUNT(*)
---------- ----------
	 3	16384

 select   
        ct, count(*)   
from    (  
                Select  Dbms_Rowid.Rowid_Block_Number(Rowid), Count(*) Ct    
                from    t2  
                group by  
                        dbms_rowid.rowid_block_number(rowid)  
        )  
Group By Ct  
 10  order by ct
 11  /

	CT   COUNT(*)
---------- ----------
	 3	16384

SQL> alter table t1 nominimize records_per_block;

表已更改。

SQL> insert /*+append*/ into t1 select * from t1 where rownum<64;

已创建 63 行。

SQL> commit;

提交完成。

SQL> alter table t1 minimize records_per_block;

表已更改。

SQL> execute show_hakan('t1');
Hakan factor for object 78156 (EASY.t1) is 62 with flags      8000

PL/SQL 过程已成功完成。

select   
        ct, count(*)   
from    (  
                Select  Dbms_Rowid.Rowid_Block_Number(Rowid), Count(*) Ct    
                from    t1  
                group by  
                        dbms_rowid.rowid_block_number(rowid)  
        )  
Group By Ct  
 10  order by ct
 11  /

	CT   COUNT(*)
---------- ----------
	 3	16384
	63	    1

SQL> create bitmap index i1 on t1(c1);

索引已创建。

SQL> create bitmap index i2 on t2(c1);

索引已创建。

SQL> select segment_name,blocks from user_segments where segment_name like 'I%';

SEGMENT_NAME									      BLOCKS
--------------------------------------------------------------------------------- ----------
I1											  16
I2											   8

SQL> insert into t2 select * from t2 where rownum<64;

已创建 63 行。

SQL> commit;

提交完成。

SQL> select segment_name,blocks from user_segments where segment_name like 'I%';

SEGMENT_NAME									      BLOCKS
--------------------------------------------------------------------------------- ----------
I1											  16
I2											   8



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值