LOB segment上的HW enqueue问题 (9205 version)

June 29th, 2009

第一次遇到这个问题是在2006年,后来陆陆续续的遇到过好几次,都是在9205的版本上。

可是一直没有在自己的blog上好好总结一下。我以为自己写过了,今天想找给别人时才发现没有写过。

数据库版本当时为9205。问题表现为大量的active session等待在mod为6的HW enqueue上。

当前获得enqueue的session执行非常慢,平时小于一秒钟的DML操作现在大约需要几十秒钟才能完成。

查询v$lock得到下面的信息:

SID TY ID1 ID2 LMODE REQUEST CTIME BLOCK
———- — ———- ———- ———- ———- ———- ———-
3575 HW 16 159461642 6 0 18 1
3546 HW 16 159461642 0 6 257 0
3542 HW 16 159461642 0 6 348 0
3468 HW 16 159461642 0 6 31 0
3329 HW 16 159461642 0 6 46 0
2634 HW 16 159461642 0 6 171 0
2565 HW 16 159461642 0 6 94 0
2532 HW 16 159461642 0 6 77 0
2497 HW 16 159461642 0 6 304 0
2179 HW 16 159461642 0 6 386 0
1972 HW 16 159461642 0 6 116 0
1927 HW 16 159461642 0 6 183 0
1777 HW 16 159461642 0 6 209 0
1690 HW 16 159461642 0 6 249 0
1637 HW 16 159461642 0 6 164 0
1616 HW 16 159461642 0 6 17 0
1601 HW 16 159461642 0 6 109 0
1353 HW 16 159461642 0 6 134 0
1236 HW 16 159461642 0 6 320 0
1218 HW 16 159461642 0 6 64 0
1163 HW 16 159461642 0 6 234 0
1150 HW 16 159461642 0 6 291 0
1082 HW 16 159461642 0 6 150 0
1074 HW 16 159461642 0 6 219 0
987 HW 16 159461642 0 6 195 0
713 HW 16 159461642 0 6 374 0
683 HW 16 159461642 0 6 331 0
672 HW 16 159461642 0 6 275 0
535 HW 16 159461642 0 6 361 0
404 HW 16 159461642 0 6 400 0

ID2是DBA,我们可以用“oracle DBA convertor”工具得到相应的File#和Block#

SQL> !dba 159461642

Oracle DBA convertor

by Stephan Haisley, Center Of Expertise, Oracle Corporation
RDBA: 0×981310a (159461642) File#: 38 Block#: 78090

SQL> select segment_name,partition_name,header_file,header_block from
SQL> dba_segments where segment_name=’SYS_LOB0000009458C00013$$’;

SEGMENT_NAME PARTITION_NAME HEADER_FILE HEADER_BLOCK

—————————————- —————————— ———– ————
SYS_LOB0000009458C00013$$ SYS_LOB_P19 38 65289
SYS_LOB0000009458C00013$$ SYS_LOB_P20 46 52489
SYS_LOB0000009458C00013$$ SYS_LOB_P21 38 78089 — 78090 = 78089+1

然后你会发现这个Block并不是Lob segment header,而是Lob segment header后面一个block。注意这一点非常重要,这是这一类问题的标准特征。

如果等待的block是segment header,那么你遇到的不是本文所描述的问题。

一般的HW enqueue发生在segment header上,而后面这个block是什么呢?

说到这个block,要从Lob segment的undo方式说起。Lob segment将old image存放在自己的segment中,而不是undo segment中。
这块存放old image的空间的大小有两种方式来定义,pctversion或者是retention。
对此不熟悉的同学可以参考我以前的两篇文章当ORA-01555遇到了LOBPCTVERSION or RETENTION?

而这个block就是用来记录这些可以被回收的old image blocks,采用bitmap方式存储。

当可回收的old image space超过PCTVERSION定义的百分比时,新增加的数据可以从这些old image space中回收。

但是oracle在回收时有两个bug:

Bug 4867884(Note:4867884.8) - Lob HW lock contention with space reclaimation — fixed in 10.1.0.6, 10.2.0.3, 11g (Future version)
Bug 4113930(Note:4113930.8) - Space reclaimation shows HW enqueue contention with concurrent insert of LOBs — fixed in 9.2.0.8 , 10.1.0.5 , 10.2.0.1

触发这个bug有两个条件
1. 可回收的old image space超过PCTVERSION定义的百分比
2. concurrent transaction超过一定的数值,具体的值不清楚,我遇到问题时的concurrent transaction数目大约为30

所以解决这个问题就从上面两点来入手。

1.

最快解决问题的方法就是调大pctversion。

dump segment header后的那个block(38,78090)
alter system dump datafile 38 block 78090;

找到一行free blocks:10109615,根据这个可以算出free blocks所占的百分比,然后调整pctversion使其大于该百分比,这样就不会有回收空间的动作。
这一方法可以很快的解决问题,就是比较浪费空间。如果有维护的时间,可以通过move的方式来回收空间。

2.

从segment上的并发数入手,例如减少并发量,或者hash partition table使每个segment上面的并发数减少

Eagle Fan Life

如何快速安全的更改表的owner

June 27th, 2009

前两天有人问我这个问题,我说你先自己去搜索下答案吧。

后来他说没有找到合适的方法,我搜索了一下,竟然发现没有人提到exchange partition的方法。

exchange partition的方法是最安全,也是最快速的方法。

所以这里写出这种方法供大家参考。

假设有A,B两个用户,我们想把TEST表从A用户移到B用户。

我们以非分区表作为例子:

SQL> conn a/a
Connected.
SQL> create table test(x int);

Table created.

SQL> create index test_idx on test(x);

Index created.

SQL> insert into test select rownum from dual connect by level <10000;

9999 rows created.

SQL> commit;

Commit complete.

SQL> conn b/b
Connected.

SQL> create table temp(x int) partition by range (x)
2 (partition part0 values less than (-1),
3 partition part1 values less than (maxvalue));

Table created.

SQL> create table test(x int);

Table created.

SQL> create index temp_idx on temp(x) local;

Index created.

SQL> create index test_idx on test(x);

Index created.

SQL> alter table temp exchange partition part1 with table a.test including indexes without validation;

Table altered.

SQL> alter table temp exchange partition part1 with table test including indexes without validation;

Table altered.

SQL> select count(*) from a.test;

COUNT(*)
———-
0

SQL> select count(*) from b.test;

COUNT(*)
———-
9999

如果是分区表,操作过程如下:

SQL> conn a/a
Connected.

SQL> create table test(x int) partition by range (x)
2 (partition part0 values less than (100),
3 partition part1 values less than (maxvalue));

Table created.

SQL> create index test_idx on test(x) local;

Index created.

SQL> insert into test select rownum from dual connect by level <1000;

999 rows created.

SQL> commit;

Commit complete.

SQL> conn b/b
Connected.
SQL> create table temp(x int);

Table created.

SQL> create index temp_idx on temp(x);

Index created.

SQL> create table test(x int) partition by range (x)
2 (partition part0 values less than (100),
3 partition part1 values less than (maxvalue));

Table created.

SQL> create index test_idx on test(x) local;

Index created.

SQL> select count(*) from a.test;

COUNT(*)
———-
999

SQL> select count(*) from b.test;

COUNT(*)
———-
0

SQL> alter table a.test exchange partition part0 with table temp including indexes without validation;

Table altered.

SQL> alter table test exchange partition part0 with table temp including indexes without validation;

Table altered.

SQL> select count(*) from a.test;

COUNT(*)
———-
900

SQL> select count(*) from b.test;

COUNT(*)
———-
99

SQL> alter table a.test exchange partition part1 with table temp including indexes without validation;

Table altered.

SQL> alter table test exchange partition part1 with table temp including indexes without validation;

Table altered.

SQL> select count(*) from a.test;

COUNT(*)
———-
0

SQL> select count(*) from b.test;

COUNT(*)
———-
999

复合分区表的情况大同小异,大家可以自己试验一下。

Eagle Fan oracle

如何安全的split partition

June 25th, 2009

在split partition时,如果所有索引都是local index并且新split出来的partition为空时,oracle不需要rebuild index,这就是fast split。

但是当新的partition非空时,则index处于unusable状态,需要rebuild index。而在rebuild index这段时间内,如果有SQL进来,就会出问题了。

oracle并没有给出一个安全的split partition的方法,比方说如果不能做fast split则不要做split。而unusable index对于7*24系统是非常危险的。

当然你可以在split的时候去跟踪,一旦发现写入新的segment,则取消操作,但是这样还是有风险。而且一般都是定期做split,通过脚本实现,不太可能每次都手工做。

要实现fast split关键是找到正确的split point。一般我们首先跑一个查询,找出当前这个partition的最大partition key,然后再加一就是split point,然后根据这个split point来做split。但是这中间有一个时间间隔,如果这中间有新的数据插入的话,split point就不对了。

对于partition key为一个列的分区表来说,查询可以走index range(max) scan,这个时间间隔很短,可能遇到该问题的概率很小,但是如果partition key为多列的话,只能走fast full index scan,这个时间间隔可能很长,风险大大增加了。

为了避免这一风险,我采用了下面的方法:

1. 查询将要做split的partition的最大partition_key
select $sql_hint max(Partition_key+1) SPLITPOINT from $tablename partition(${maxpartname})
2. 以exclusive模式lock partition
lock table $tablename partition(${maxpartname}) in EXCLUSIVE mode
3. 再次查询要做split的partition的最大partition_key
select $sql_hint max(Partition_key+1) SPLITPOINT from $tablename partition(${maxpartname})
4. 如果step1和step3得到的结果相同,则跳到step6,不同则跳到step5
5. 提交transaction,释放lock,等待30分钟后跳到step1
6. 查看v$lock表看自己有没有block别人,如果有,跳到step5,如果没有跳到step7
7. split the partition, 如果成功,退出程序,如果失败,跳转到step5

通过上面的方法就可以保证中间没有其他的transaction去做DML。

除了保证安全外,当失败时,该程序可以sleep一段时间后重试。

Eagle Fan oracle 10g

解决sony reader软件在vista上不能启动的问题

May 16th, 2009

搬家一年,整面墙的书柜就几乎被塞满了。前两天入了sony的电子书阅读器PRS-505,以后终于可以不用再买书了:)
sony prs 505
机器很漂亮,破解后中文支持很稳定。网络上书籍资源很多,我一下子就塞进去了200多本书。

电子墨水的显示效果很好,我觉得比看真正的书还舒服。看三四个小时眼睛一点都不累,蛮超值的。

支持PDF,txt和sony自己的lrf格式。我试了几本oralce文档,支持pdf reflow,字体可以放大,但是放大后图片看不到了,需要回到原始字体才能看到图,图片不多的书还能接受。

sony的ebook library软件在vista上不能正常启动。google了一下,发现要关闭vista的DEP(Data Execution Prevention)

过程如下,供各位参考

1. Open System by clicking the Start button Picture of the Start button, clicking Control Panel, clicking System and Maintenance, and then clicking System.
2. Click Advanced System Settings. Administrator permission required If you are prompted for an administrator password or confirmation, type the password or provide confirmation.
3. Under Performance, click Settings.
4. Click the Data Execution Prevention tab, and then click Turn on DEP for all programs and services except those I select.

Eagle Fan Life

数据库sharding Lookup技术探讨

April 19th, 2009

数据库sharding技术最近几年越来越火热。Sharding是shared-nothing的缩写,也可称其为horizontal partitioning/horizontal split,即数据库切片。将一个大的数据库切成几个功能一样,逻辑关系和物理上毫不相干的数据库。

数据库sharding有几个主要的优点:
1. Database sharding提供了近似线性扩展的架构。可以随着应用的增长线性的增加更多的服务器。
2. 提高了数据库的可用性。如果只有一个数据库,一旦down掉的话,对其所提供的service影响是100%,如果拆成10台数据库,那么一台数据库down掉的影响只有10%。
3. 小的数据库压力比较小,风险更小,性能更好。做过DBA的都知道,管理一台3000 TPS的数据库和一台300 TPS的数据库的压力是完全不一样的。

其缺点在于:
1. 首先要业务逻辑支持,并不是任何类型的数据库都支持拆分。如果业务逻辑不支持拆成几个不相干的数据的话,拆开后各个数据库之间数据join会带来额外的开销,而且随着数据库的增多,开销越来越大。
2. 更多的数据库也带来一些维护上的开销,例如升级数据库,打patch等。
3. 因为数据分散了,所以要提供机制能够找到所需数据所在的数据库。这也是本篇文章讨论的重点,即数据的lookup技术。

下面重点讨论lookup技术。数据被分散在不同的数据库中,当应用需要查询数据时,要能够定位到相应的数据库中查询。如果没有Lookup机制,则需要到每一个数据库中查询,这样的话就不可能做到线性扩展,数据库Sharding也就失去了其主要的优势。

Lookup技术主要从以下几个方面来考虑:
1. 成本
2. 效率
3. 再次拆分的难度
4. 是否支持在线拆分

我接触到或者想到的Lookup技术有以下几种,下面分别讨论其优缺点。

1. 建立Lookup数据库
这是很自然想到的一种方法。Lookup数据库中记录 (ID, Server)的对应关系。
其优点在于灵活性很高,数据可以存放在任何一个数据库中,可以在不同的数据库之间在线迁移数据来平衡数据库压力,迁移数据时同时更新Lookup数据库中相应的记录。
缺点也很明显,就是需要一套Lookup数据库来支持,有不小的额外开销。Lookup数据库的数据必须集中存放,不好再做水平切割。虽然其数据结构简单,存放的数据量并不大,但是所有的应用都需要到Lookup数据库上查找数据,其查询的频率很高。而且Lookup数据库在这个方案中也成为了一个故障节点。所以不能用一台数据库做Lookup DB,否则前面提到的sharding数据库可用性的优势又失去了。我们可以用Master/Slave的方式来实现Lookup数据库的scalability和availability。Master数据库提供写操作,Slave数据库提供读的操作。
对于oracle来说,可以采用复制软件来实现master和slave之间的同步,例如shareplex,也可以采用oracle logical standby或者oracle active physical standby(11g)来实现。
MySQL数据库的话,memory engine很适合做Slave服务器,因为Lookup表的数据库不大,可以放在内存中,而且hash index很适合等式查找。Memory engine可以支持大并发量的查询。Mater数据库可以采用Innodb,文档中提到在高版本的MySQL中支持不同storage engine之间的复制。实际应用中不知道有没有公司这么使用。

2. 采用划分区间的方式
将数据按照range来划分。比方说以1万为一个区间长度,ID在1~10000的在数据库D1中,10001~20000的在数据库D2中,20001~30000在数据库D3中,依此类推。当分配的ID用完或者新增服务器时,继续分配后面的ID供其使用。可以通过sequence来实现。
其优点在于不需要额外的开销,应用通过简单的映射就可以得知数据存放于哪个数据库中,缺点在于各个数据库之间很难实现在线的数据迁移。如果应用的增长不是因为数据量增长而增长,而是因为执行次数的增加的话,很难做到在线的压力平衡。另外当区间内分配的ID数用完了,需要DBA手工分配新的区间。

3. 采用hash函数的方式
比方说最简单的hash函数—mod函数。将数据进行mod(ID, 13860) (13860= 2*3*5*6*7*11),如果有十台数据库,每个数据库中存放1386个mod。将mod和数据库之间的mapping关系存放于数据库中,应用服务器可以将其load进自己的内存中(这个表很小)。当新增服务器时,从各个数据库中转移一部分mod到新的服务器上。
其优点在于不需要额外的开销,可以通过查找应用端很小的内存链表就能获知数据存放的位置,缺点在于当新增服务器后转移数据过程中要保持数据的同步,需要同步机制。

4. 采用hash函数和Lookup数据库相结合的方式
基本划分的方法和第三种一样,但是多了一套Lookup数据库来提供miss查询。这套Lookup数据库解决了方法三中新增服务器数据同步的问题。新增服务器转移数据时就不需要同步了,而是采用move的方式,这样在旧的服务器中miss了,但可以通过Lookup数据库来定位到新的服务器,当单个mod完全转移了,可以更新mapping表,这样就可以直接定位到新的服务器上。

不知道大家是如何实现Lookup机制的,有什么好的方法或想法非常欢迎大家来分享。

Eagle Fan oracle