人生就是如此
===========================================================
Rich Niemiec 到alibaba讲学
===========================================================

Rich Niemiec 先生趁这次上海OOW的机会,在中国巡游。安排了到杭州我们公司做一堂课,选择了oracle internals at the block level ! 可能是听说我们公司dba在国内算是领先的,所以选择了这个题材,这也是在国内唯一一处地方讲了这个内容。

其实内容基本上我还是比较清楚的,只可惜总是觉得要想和他深入沟通难度太大,也怪自己口语不行了。后来课完之后一起吃午饭,单独做了一些交流。


bitirainy 发表于:2007.08.14 19:27 ::分类: ( Oracle is anything ) ::阅读:(78892次) :: 评论 (18)
===========================================================
IT168 网络采访
===========================================================

优秀数据库工程师评选,IT168 网络采访

1 我注意到您发在itpub论坛上的招聘广告了,您觉得目前在国内,招聘一个满足阿里巴巴这样的大型电子商务网站应用的数据库的DBA是否容易?为什么?

2 您心目中理想的DBA应该具备哪些技能?

3 您觉得造成目前中国合格数据库人才缺乏的原因有哪些?

4 您为何选择到专业论坛(itpub)发招聘帖子的方式来招揽人才?

5 据了解,目前中国从事数据库开发、管理的人才(包括开发人员)大约有20万,其中
专职的DBA,您估计目前中国有多少?

 查看全文
bitirainy 发表于:2006.06.23 15:06 ::分类: ( Oracle is anything ) ::阅读:(2651次) :: 评论 (3)
===========================================================
赛迪网络采访
===========================================================

优秀数据库工程师评选,赛迪网络采访

1、你认为国内数据库应用水平与人才状况如何?

2、结合你的工作与项目经历,请谈谈数据库工程师在应用上面对的难点。

3、要解决这些应用难点,工程师的个人经验能起到哪些作用?数据库技术发展能起到
什么作用?

4、您认为“2006年中国首届杰出数据库工程师评选”活动的意义?

 查看全文
bitirainy 发表于:2006.06.23 15:05 ::分类: ( Oracle is anything ) ::阅读:(1824次) :: 评论 (0)
===========================================================
今天在新系统上做了个压力测试
===========================================================

新系统IBM P590,AIX5.3,8 cpus /16G 内存,为了同时兼顾IO压力,我特地把 buffer cache 只给了1G大小。模拟应用的sql调用,用5台2 cpus的linux机器并发16*5 个线程同时循环跑一个client无IO的模拟应用的随机用户登陆流程(这个期间会运行一系列的sql)。linux机器上开16个线程同时跑的时候load 已经在2左右。而IBM服务器上cpu  idle 2%,IO 为 read  40MB/S,write  8MB/S ,网络 IN  12MB/S  ,  out  6MB/S。run queue (load)为 50。

在statspack采样数据load  profile 为

STATSPACK report for

DB Name DB Id Instance Inst Num Release Cluster Host
------------ ----------- ------------ -------- ----------- ------- ------------
OCNDB 3701801852 ocndb 1 9.2.0.6.0 NO ocndb1

Snap Id Snap Time Sessions Curs/Sess Comment
--------- ------------------ -------- --------- -------------------
Begin Snap: 104 19-May-05 17:27:53 91 27.3
End Snap: 105 19-May-05 17:57:54 91 27.8
Elapsed: 30.02 (mins)

Cache Sizes (end)
~~~~~~~~~~~~~~~~~
Buffer Cache: 1,024M Std Block Size: 8K
Shared Pool Size: 512M Log Buffer: 2,048K

Load Profile
~~~~~~~~~~~~ Per Second Per Transaction
--------------- ---------------
Redo size: 344,880.30 555.47
Logical reads: 198,459.35 319.64
Block changes: 2,494.51 4.02
Physical reads: 5,105.48 8.22
Physical writes: 48.74 0.08
User calls: 84,336.12 135.83
Parses: 6,208.79 10.00
Hard parses: 0.03 0.00
Sorts: 1,242.01 2.00
Logons: 0.00 0.00
Executes: 19,867.03 32.00
Transactions: 620.88

% Blocks changed per Read: 1.26 Recursive Call %: 26.51
Rollback per transaction %: 0.00 Rows per Sort: 23.61

Instance Efficiency Percentages (Target 100%)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Buffer Nowait %: 99.30 Redo NoWait %: 100.00
Buffer Hit %: 97.43 In-memory Sort %: 100.00
Library Hit %: 100.02 Soft Parse %: 100.00
Execute to Parse %: 68.75 Latch Hit %: 99.32
Parse CPU to Parse Elapsd %: 34.12 % Non-Parse CPU: 96.74

Shared Pool Statistics Begin End
------ ------
Memory Usage %: 20.97 21.15
% SQL with executions>1: 83.85 83.54
% Memory for SQL w/exec>1: 88.18 85.88

Top 5 Timed Events
~~~~~~~~~~~~~~~~~~ % Total
Event Waits Time (s) Ela Time
-------------------------------------------- ------------ ----------- --------
CPU time 8,558 52.19
db file sequential read 9,194,762 4,730 28.84
buffer busy waits 2,500,799 1,639 10.00
log file sync 1,076,173 771 4.70
latch free 174,822 588 3.58
-------------------------------------------------------------

看起来系统还是蛮强的,executions 为 19867次/S ,620个事务/S……


bitirainy 发表于:2005.05.19 21:33 ::分类: ( Oracle is anything ) ::阅读:(2883次) :: 评论 (8)
===========================================================
AIX5.3上vmtune已经被vmo和ioo所取代
===========================================================

我们通常希望把oracle sga锁定在内存中,并且使文件系统缓存比例控制到一定范围,在AIX5.3之前版本,一般是使用vmtune -p 5 -P 20 -S 1

而在AIX5.3中则使用

 vmo   -r -o v_pinshm=1
 vmo -o minperm%=5
 vmo -o maxclient%=20
 vmo -o  maxperm%=20

重新启动生效的参数记录在 /etc/tunables/nextboot 文件中,通过 vmo -L 可以查看相关系列参数。


bitirainy 发表于:2005.05.05 23:57 ::分类: ( Oracle is anything ) ::阅读:(3795次) :: 评论 (4)
===========================================================
如何让oracle从raw device的offset 0开始使用
===========================================================
在有些平台下,raw device的第一个block是control block,记录了一些系统信息,这会使得oracle不能使用这个block,一方面在dd文件的时候我们需要知道这个block多大从而skip这个block,另一方面这可能给oracle带来一定的产生坏快的可能。 查看全文
bitirainy 发表于:2005.05.05 17:14 ::分类: ( Oracle is anything ) ::阅读:(2916次) :: 评论 (2)
===========================================================
替换rootvg中的磁盘
===========================================================

rootvg 中原来mirror的是 hdisk0 和 hdisk1,由于这两块磁盘是同一个BUS上的,我想换另外一个BUS上的磁盘,用hdisk4替换掉hdisk1

root用户执行:

extend rootvg hdisk4
migratepv -l  hd5  hdisk1 hdisk4
bosboot -ad  /dev/hdisk4
chpv -c  hdisk1
bootlist -m  nornal  hdisk0 hdisk4

sysdumpdev -p /dev/sysdumpnull
migratepv hdisk1 hdisk4
sysdumpdev -p /dev/hd6

reducevg rootvg hdisk1

shutdown -Fr


bitirainy 发表于:2005.05.04 21:06 ::分类: ( Oracle is anything ) ::阅读:(1840次) :: 评论 (0)
===========================================================
新上项目第一周
===========================================================
时间过的真快,一周就快过去了,但好象做的事情不多。 查看全文
bitirainy 发表于:2005.04.15 14:58 ::分类: ( Oracle is anything ) ::阅读:(61118次) :: 评论 (9)
===========================================================
关于控制文件的事务
===========================================================

参考

http://www.cnoug.org/viewthread.php?tid=52545

有人问:

看ixora 中的文件。

上面說 在控制文件中除第一個 section 外﹐每個 邏輯塊都是兩個物理塊來表示.一個是當前的信息﹐另一個是舊的 copy 版本或未提交的信息.

因為 發生 controlfile transactions 時﹐這個 session 會有一個exclusive lock on the CF enqueue ﹐所以這個時候是不會允許任何操作﹐即使讀也不允許。

所以我有疑惑﹐因為一旦發生 Controlfile Transactions ﹐整個 contrfolfike 就會被 lock ﹐即對 全部 session 來說﹐要么能訪問﹐要么不能用.那么 用兩個 物理塊來作 recovery 的功能是不是就沒有必要了﹐隊非在 Controlfile Transactions 期間發生 system /instance 錯誤﹐或 hot backup。才有可能用到。不過文檔中又有說到當一個session 來讀 controlfile 時﹐它會首先去訪問 block version bitmap 以取得正確的塊版本﹐這說明應有其它情況存在.....

另有一個問題﹐因為在發生改變邏輯塊時﹐它會先更新一個物理的塊﹐那么這個邏輯塊所對應另一個物理塊什么時候更新﹐在這個 Controlfile Transactions 提交的時候嗎﹖好像不像﹐﹔因為它允許其中一個塊包括以前舊的拷貝。

 查看全文
bitirainy 发表于:2005.04.04 20:15 ::分类: ( Oracle is anything ) ::阅读:(60607次) :: 评论 (2)
===========================================================
国内IT技术书籍为何少好书?
===========================================================

今天有人在论坛上问起,顺便也说说我的看法。

一方面技术类的书籍,时效性比较高,生命中期不太长。通常出版的书册数不会太多。如果一个工薪族技术类高手要出一本书,出版商所出的价格,可能还不如其月薪,而写一本书,却是需要长达几个月的心血。首先在这个环节上,就降低了技术类高手出书的可能性,倾心血觉得不值得,不倾心血呢觉得没意思,技术类书籍出书目前只是一个短期效益而不是长期效益。因为工薪族,长期潜心技术的人相对是比较少的,国内环境下技术人才得到重视的不多,都琢磨着做别的呢。

以oracle技术方面的书籍为例,欧洲和美国出的非常经典书籍相对数量比较多一些,但是绝对数量恐怕也就在10来本左右。而这些书的作者,几乎都是早年在oracle工作了多年,潜心研究过oracle,不少人离开oracle公司之后,就是靠这方面的技能开公司做服务或者做个人服务。他们一直关注着相关的技术,并且把自己的经验很好的整理了下来,通过几年甚至十多年的积累,才出了一本书。这些人在internet上往往也非常活跃,出书既是他们经验的总结,也能给他们带来不错的经济和名声收益,为此同时还给他们带来更多的做服务的机会。

我想国内IT可能还需要进一步发展,等到IT类的服务更专业更成熟以后,使得潜心研究技术的人能通过长期的研究获得更多的回报,出书只是他们多年经验的汇集,他们不需要为月薪所累,可以通过出书、培训、服务等等方式挣钱,这个时候,应该就可以看到好书了。


bitirainy 发表于:2005.04.02 18:57 ::分类: ( Oracle is anything ) ::阅读:(2260次) :: 评论 (6)
===========================================================
oracle数据文件为什么存在 Rfile# and file#
===========================================================
归根结底的原因是因为 ROWID 的存储格式造成的,因为 rowid 中文件编号标志只有10bit,最大数据容量1024,由于不存在0编号文件,所以实际上只允许1023个文件编号。在oracle8 之前的版本的数据库中,rowid是受限的,只包括 file# /block#  /row#  ,则数据库最多只允许1023个文件。 查看全文
bitirainy 发表于:2005.03.19 12:29 ::分类: ( Oracle is anything ) ::阅读:(11001次) :: 评论 (6)
===========================================================
RAC上备份归档日志
===========================================================
今天在rac上备份归档日志出了错,居然半天没发现问题,还是个粗心的问题,晕。 查看全文
bitirainy 发表于:2005.03.12 00:09 ::分类: ( Oracle is anything ) ::阅读:(2372次) :: 评论 (2)
===========================================================
根据自己需要输出有色提示文字
===========================================================
我们有一个给sa运行检查db 信息的脚本,由于检查内容比较多,sa很可能没有注意警告或者错误提示,为此需要用颜色显示出来。 查看全文
bitirainy 发表于:2005.03.02 11:28 ::分类: ( Oracle is anything ) ::阅读:(1126次) :: 评论 (2)
===========================================================
利用logmnr恢复大量误删除数据
===========================================================
logmnr其实是非常有用的一个恢复数据的工具,今天有人提到恢复700万的数据,我也做过几万条财务数据的恢复,借此小结一下。 查看全文
bitirainy 发表于:2005.02.24 17:17 ::分类: ( Oracle is anything ) ::阅读:(2230次) :: 评论 (3)
===========================================================
如果让查询出来的数据和in中列表顺序一致
===========================================================
需求是公司一开发人员提出来的,一下想到了in的绑定的思想,决定尝试利用 in list 绑定+nl。 查看全文
bitirainy 发表于:2005.02.21 16:47 ::分类: ( Oracle is anything ) ::阅读:(2278次) :: 评论 (10)
===========================================================
linux的内核文件系统
===========================================================
linux把内核内存通过文件系统的方式提供 读写 接口给 用户,让用户非常方便。  查看全文
bitirainy 发表于:2005.01.30 22:02 ::分类: ( Oracle is anything ) ::阅读:(1127次) :: 评论 (4)
===========================================================
一个非常有意思的跨平台迁移的问题
===========================================================

10G 前数据库跨平台迁移非常的麻烦,为此我曾经思考过其他方案,但一直没有测试。最近有人提到这个问题,做一些探讨。

 查看全文

bitirainy 发表于:2005.01.20 15:18 ::分类: ( Oracle is anything ) ::阅读:(1939次) :: 评论 (2)
===========================================================
如何写SQL
===========================================================
SQL性能是一个经常被探讨的问题,那到底要如何才能书写高效正确的SQL呢? 查看全文
bitirainy 发表于:2005.01.06 21:11 ::分类: ( Oracle is anything ) ::阅读:(2011次) :: 评论 (5)
===========================================================
DB与OS之相似点
===========================================================

DB 与OS 之间,设计思想异曲同工,理解了,也不那么神秘了。

 查看全文
bitirainy 发表于:2005.01.06 14:24 ::分类: ( Oracle is anything ) ::阅读:(1645次) :: 评论 (5)
===========================================================
一个有趣的CBO下filter的现象
===========================================================
这个filter现象跟我们通常的理解不一致。  查看全文
bitirainy 发表于:2005.01.05 13:13 ::分类: ( Oracle is anything ) ::阅读:(1067次) :: 评论 (1)
===========================================================
输出schema中表和字段信息
===========================================================

小case,随笔记录下,或许有些人有用。

 查看全文
bitirainy 发表于:2004.12.20 16:30 ::分类: ( Oracle is anything ) ::阅读:(1306次) :: 评论 (3)
===========================================================
linux loadavg 算法
===========================================================
今天读linux  source  code关于cpu  load 的计算方法,同时在google上搜索到处参考,晕乎了半天,终于弄明白cpu  load 的计算方法了,并不是简单的移动算术平均。  查看全文
bitirainy 发表于:2004.11.22 21:37 ::分类: ( Oracle is anything ) ::阅读:(2669次) :: 评论 (6)
===========================================================
回滚段探究
===========================================================

由于显示问题,请参考

http://blog.csdn.net/biti_rainy/archive/2004/07/03/learn_oracle_20040703_6.aspx


bitirainy 发表于:2004.11.19 09:37 ::分类: ( Oracle is anything ) ::阅读:(1306次) :: 评论 (0)
===========================================================
linux下sar的原理
===========================================================
原本是为了找出系统load状况的异常,通过常规手段无法检查得到,不得以只好考究load的移动平均计算方法。先找了sar的source  code来看,最后却发现sar不过是读了linux  /proc 下的一系列内存文件,通过采样来得到数据而已。 查看全文
bitirainy 发表于:2004.11.18 17:55 ::分类: ( Oracle is anything ) ::阅读:(2248次) :: 评论 (0)
===========================================================
你想买股票吗?
===========================================================

也许对于普通大众来说,对于股票,可能具体地并不是很清楚,我也不是专家,但就我个人的认知,我来谈谈我为什么不想买股票。

 查看全文
bitirainy 发表于:2004.11.17 22:19 ::分类: ( Oracle is anything ) ::阅读:(1476次) :: 评论 (4)
===========================================================
阻止DDL并记警告日志
===========================================================
有时我们需要控制用户对表执行DDL操作,包括truncate等操作。为了达到灵活控制的目的,我们使用了DDL trigger。由于我们的系统每5分钟会检查alert log错误信息并短信报警,这样我采取了写alert log的方式来记录不明DDL操作。  查看全文
bitirainy 发表于:2004.11.17 19:30 ::分类: ( Oracle is anything ) ::阅读:(1970次) :: 评论 (5)
===========================================================
很奇怪的load问题
===========================================================
在某些天的早上8:30--10:00之间,数据库服务器system load     会平均上升到5以上(正常情况下system  load 在1左右),而cpu  load中user cpu相比以往是没有什么变化都是在10%左右system%也没变化。 查看全文
bitirainy 发表于:2004.11.15 20:27 ::分类: ( Oracle is anything ) ::阅读:(1088次) :: 评论 (0)
===========================================================
关于oracle的学习阶段
===========================================================
学习的过程也是从点到线,从线到网,从网到面的过程。 查看全文
bitirainy 发表于:2004.11.10 16:47 ::分类: ( Oracle is anything ) ::阅读:(3350次) :: 评论 (6)
===========================================================
db_block_checksum & db_block_checking
===========================================================
这两个参数的含义经常让人混淆,虽然都是对block进行检查。 查看全文
bitirainy 发表于:2004.11.05 12:12 ::分类: ( Oracle is anything ) ::阅读:(3605次) :: 评论 (2)
===========================================================
排序翻页的程序“BUG”
===========================================================

今天销售报告crm系统bug:翻页显示中有少量记录是重复的。检查sql发现写法没什么问题,于是我进一步进行检查。

 查看全文
bitirainy 发表于:2004.11.04 16:40 ::分类: ( Oracle is anything ) ::阅读:(2702次) :: 评论 (10)
===========================================================
删除重复记录的问题
===========================================================
这个问题本来很简单,但经常见有人问。采用过时的删除方法可能非常的慢,816以后版本可以采用新方法了。 查看全文
bitirainy 发表于:2004.10.29 15:25 ::分类: ( Oracle is anything ) ::阅读:(61758次) :: 评论 (7)
===========================================================
汉字模糊匹配BUG
===========================================================

当要求汉字进行模糊匹配的时候,由于oracle是采用存储的字节流进行查找的,所以有存在着一个汉字的后半截和另一个汉字的前半截拼起来正好构成某个汉字。这样则存在着模糊匹配出现错误的可能性。碰巧,今天我在生产数据库应用中遭遇了好几个。

 查看全文
bitirainy 发表于:2004.10.25 16:00 ::分类: ( Oracle is anything ) ::阅读:(2088次) :: 评论 (7)
===========================================================
what is the db file sequential read
===========================================================

'db file sequential read' ----- 表达的含义是 一次 IO 的 block 存放在一段连续的内存上,当然,1个block自然是连续内存,而2个以上 block 基本都是离散的内存,但是oracle从来没有说 2个以上的 block 一定是不相临的内存,只不过几乎等价于不相临的内存,这就是 'db file scattered read'。

 查看全文
bitirainy 发表于:2004.10.14 16:51 ::分类: ( Oracle is anything ) ::阅读:(1571次) :: 评论 (0)
===========================================================
SGA中存在少量离散的block可能导致IO次数的增加
===========================================================
IO时若部分block已经在SGA中,这个时候对于已经在内存中的block显然是不需要IO了,但是oracle是怎么处理的呢?是割散成更小的IO单元吗? 查看全文
bitirainy 发表于:2004.10.14 16:23 ::分类: ( Oracle is anything ) ::阅读:(1456次) :: 评论 (6)
===========================================================
what is the clustering_factor ?
===========================================================

clustering_factor 是表征表中数据的存储顺序和某索引字段顺序的符合程度。

 查看全文
bitirainy 发表于:2004.10.12 09:51 ::分类: ( Oracle is anything ) ::阅读:(3447次) :: 评论 (7)
===========================================================
可恶的bug产生1555错误
===========================================================

oralce的内部事务计时机制是有问题的,v$transaction.start_time(事务开始时间) 以及 v$undostat.begin_time  都是数据库内部计时的。原先的设计方案是只有lgwr定期更新这个sga中的时间钟,结果由于其他相关人员在32处source code中也调用了类似操作,使得server process也能去更新这个时间,由于时间更新不存在latch机制使得这个时间可以走的飞快。

 查看全文

bitirainy 发表于:2004.09.30 16:29 ::分类: ( Oracle is anything ) ::阅读:(1622次) :: 评论 (6)
===========================================================
itpub人物印象记--oldwain
===========================================================

easyfree喜欢称oldwain为老斗,他们俩大抵是在我还不知道oracle是什么的时候就认识了吧。更巧的是两人居然是同年同月同日生的60年代的老革命。

 查看全文

bitirainy 发表于:2004.09.24 20:32 ::分类: ( Oracle is anything ) ::阅读:(1943次) :: 评论 (1)
===========================================================
oracle920 standby可以接收来自standby的归档日志
===========================================================
这无疑给我们提供了一个建立多个standby的另一种手段。 查看全文
bitirainy 发表于:2004.09.24 20:28 ::分类: ( Oracle is anything ) ::阅读:(1016次) :: 评论 (0)
===========================================================
一条sql导致数据库整体性能下降的诊断和解决的全过程
===========================================================

今天早上一来,数据库load就比往常高了许多。想想数据库唯一的变化是昨天早上我曾经重新分析过数据库对象。

 查看全文

bitirainy 发表于:2004.09.20 11:42 ::分类: ( Oracle is anything ) ::阅读:(7940次) :: 评论 (8)
===========================================================
hdparm参数对IDE磁盘IO的重大影响
===========================================================

我们将要测试IO的工具介绍参考

http://www.textuality.com/bonnie/advice.html

接下来我在自己的pc(redhat linux AS2.1)上对比测试IO状况  ,AS3.0 是自动打开的

 查看全文
bitirainy 发表于:2004.09.17 15:06 ::分类: ( Oracle is anything ) ::阅读:(1863次) :: 评论 (1)
===========================================================
count(*)、count(1)、count(rowid)的对比
===========================================================

经常有人说count(1)比count(*)快。实际上是这样吗?不是的,作为数据库的衡量性能的逻辑读来说,是没有差异的。但是在实际的多次反复测试中,我们可以来对比看看真实状况。

 查看全文
bitirainy 发表于:2004.09.13 11:23 ::分类: ( Oracle is anything ) ::阅读:(4564次) :: 评论 (6)
===========================================================
如何控制子查询比外部连接先运行(push_subq提示)
===========================================================

先子查询再做表连接比较有利,也就是说子查询本身代价不大,并且过滤掉很多记录,产生一个很小的结果集再做表连接,比较有利。

 查看全文
bitirainy 发表于:2004.09.12 18:22 ::分类: ( Oracle is anything ) ::阅读:(2068次) :: 评论 (0)
===========================================================
raid5 与EMC CX200 write cache
===========================================================
  EMC CX200,昨晚重新启动数据库服务器和阵列,结果standby恢复奇慢无比。 查看全文
bitirainy 发表于:2004.09.08 21:06 ::分类: ( Oracle is anything ) ::阅读:(1098次) :: 评论 (2)
===========================================================
standby open read only 出错的处理过程
===========================================================

一个出错的诊断过程

 查看全文
bitirainy 发表于:2004.09.08 17:17 ::分类: ( Oracle is anything ) ::阅读:(1128次) :: 评论 (0)
===========================================================
如何捕捉temp表空间出错的session信息和SQL
===========================================================

我们有时候会遇见这样的烦恼,在特定的时候总有某个大查询导致临时表空间出错。但我们总不能守侯着捕获相关sql以优化或者处理。可以通过events来诊断

 

 查看全文
bitirainy 发表于:2004.09.08 17:17 ::分类: ( Oracle is anything ) ::阅读:(1187次) :: 评论 (5)
===========================================================
checkpoint queue 和 write list
===========================================================

在oracle中存在 dirty list(也就是write list)的说法,但是同时又有checkpoint queue 。这两个东西往往让人容易混淆。他们是同一个东西吗?之间关系如何?

 查看全文
bitirainy 发表于:2004.09.08 17:16 ::分类: ( Oracle is anything ) ::阅读:(1403次) :: 评论 (0)
===========================================================
32bit oracle中SGA_MAX_SIZE 与 单个进程 PGA 的制约关系
===========================================================

阅读本文前请先阅读<<32bit oracle 扩展SGA原理>>

 

 查看全文
bitirainy 发表于:2004.09.08 17:15 ::分类: ( Oracle is anything ) ::阅读:(1153次) :: 评论 (2)
===========================================================
32bit oracle 扩展SGA原理
===========================================================
 32bit oracle由于位数限制,使得oracle进程只能访问4g(2的32次方)以下的虚拟内存地址,在很多时候这是一个很让人头疼的问题,因为空着许多内存而不能使用,而默认情况下SGA不能超过1.7g。比如我们的linux下有8g内存,却有部分空着不能用干着急。这个时候我们就要考虑怎样扩展oracle的SGA。 查看全文
bitirainy 发表于:2004.09.08 17:13 ::分类: ( Oracle is anything ) ::阅读:(1579次) :: 评论 (0)
===========================================================
如何查找隐藏参数(x$ksppi and x$ksppsv)
===========================================================

隐藏参数不过也保存在 x$ 中而已

 查看全文
bitirainy 发表于:2004.09.08 17:10 ::分类: ( Oracle is anything ) ::阅读:(727次) :: 评论 (0)
===========================================================
无聊之通过修改数据字典为表换owner
===========================================================

简单的做了一个测试  

 查看全文
bitirainy 发表于:2004.09.08 17:10 ::分类: ( Oracle is anything ) ::阅读:(964次) :: 评论 (0)
===========================================================
如何知道哪些表是FTS(Full table Scan)
===========================================================

SQL> desc obj$
  名称                                     空?      类型
  ---------------------------------------- -------- ---------------------------
  OBJ#                                     NOT NULL NUMBER
  DATAOBJ#                                          NUMBER
  OWNER#                                   NOT NULL NUMBER
  NAME                                     NOT NULL VARCHAR2(30)
  NAMESPACE                                NOT NULL NUMBER
  SUBNAME                                           VARCHAR2(30)
  
FTS 的表
  
SELECT o.name FROM obj$ o,x$bh x  
WHERE x.obj=o.dataobj#
AND standard.bitand(x.flag,524288)>0  
AND o.owner# = 41;
  
SQL> SELECT o.name FROM obj$ o,x$bh x  
   2  WHERE x.obj=o.dataobj#
   3  AND standard.bitand(x.flag,524288)>0  
   4  AND o.owner# = 41;
NAME
------------------------------
T1
  
desc x$bh
  
SQL> desc x$bh
  名称                                     空?      类型
  ---------------------------------------- -------- ------------------
  ADDR                                              RAW(4)
  INDX                                              NUMBER
  INST_ID                                           NUMBER
  BUF#                                              NUMBER
  HLADDR                                            RAW(4)
  NXT_HASH                                          RAW(4)
  PRV_HASH                                          RAW(4)
  NXT_REPL                                          RAW(4)
  PRV_REPL                                          RAW(4)
  FLAG                                              NUMBER
  LRU_FLAG                                          NUMBER
  TS#                                               NUMBER
  FILE#                                             NUMBER
  DBARFIL                                           NUMBER
  DBABLK                                            NUMBER
  CLASS                                             NUMBER
  STATE                                             NUMBER
  MODE_HELD                                         NUMBER
  CHANGES                                           NUMBER
  CSTATE                                            NUMBER
  X_TO_NULL                                         NUMBER
  FORCED_READS                                      NUMBER
  FORCED_WRITES                                     NUMBER
  LE_ADDR                                           RAW(4)
  DIRTY_QUEUE                                       NUMBER
  SET_DS                                            RAW(4)
  OBJ                                               NUMBER
  BA                                                RAW(4)
  CR_SCN_BAS                                        NUMBER
  CR_SCN_WRP                                        NUMBER
  CR_XID_USN                                        NUMBER
  CR_XID_SLT                                        NUMBER
  CR_XID_SQN                                        NUMBER
  CR_UBA_FIL                                        NUMBER
  CR_UBA_BLK                                        NUMBER
  CR_UBA_SEQ                                        NUMBER
  CR_UBA_REC                                        NUMBER
  CR_SFL                                            NUMBER
  LRBA_SEQ                                          NUMBER
  LRBA_BNO                                          NUMBER
  HRBA_SEQ                                          NUMBER
  HRBA_BNO                                          NUMBER
  RRBA_SEQ                                          NUMBER
  RRBA_BNO                                          NUMBER
  US_NXT                                            RAW(4)
  US_PRV                                            RAW(4)
  WA_NXT                                            RAW(4)
  WA_PRV                                            RAW(4)
  TCH                                               NUMBER
  TIM                                               NUMBER
  
SQL>  


bitirainy 发表于:2004.09.08 17:09 ::分类: ( Oracle is anything ) ::阅读:(679次) :: 评论 (0)
===========================================================
update对consistent gets的影响
===========================================================

chao_ping:

        经过别的session Update之后,select 语句的buffer gets 会变大,这是可以理解的.
因为Oracle需要从别的Block里面得到他需要的Block的之前的映像。

但是,这里的这个参数就好像有点南里理解:
  
SQL> select * from t where object_id=100;
  
  OBJECT_ID OWNER
---------- ------------------------------
        100 cc
  
Execution Plan
----------------------------------------------------------
    0      SELECT STATEMENT Optimizer=FIRST_ROWS (Cost=2 Card=14 Bytes=          420)
    1    0   TABLE ACCESS (BY INDEX ROWID) OF 'T' (Cost=2 Card=14 ytes
           =420)
    2    1     INDEX (RANGE SCAN) OF 'IDX_T' (NON-UNIQUE) (Cost=1 rd=        14)
  
Statistics
----------------------------------------------------------
           0  recursive calls
           0  db block gets
           4  consistent gets
           3  physical reads
           0  redo size
         424  bytes sent via SQL*Net to client
         426  bytes received via SQL*Net from client
           2  SQL*Net roundtrips to/from client
           1  sorts (memory)
           0  sorts (disk)
           1  rows processed
  
另外开一个session, 把这个表全部都UPdate掉. 在这边再次select:  
  
SQL> /
  
  OBJECT_ID OWNER
---------- ------------------------------
        100 cc
  
Execution Plan
----------------------------------------------------------
    0      SELECT STATEMENT Optimizer=FIRST_ROWS (Cost=2 Card=14 Bytes=          420)
  
    1    0   TABLE ACCESS (BY INDEX ROWID) OF 'T' (Cost=2 Card=14 Bytes          =420)
  
    2    1     INDEX (RANGE SCAN) OF 'IDX_T' (NON-UNIQUE) (Cost=1 Card=          14)
  
Statistics
----------------------------------------------------------
           0  recursive calls
           0  db block gets
         557  consistent gets
           0  physical reads
          52  redo size
         424  bytes sent via SQL*Net to client
         426  bytes received via SQL*Net from client
           2  SQL*Net roundtrips to/from client
           0  sorts (memory)
           0  sorts (disk)
           1  rows processed
  
SQL> /
  
  OBJECT_ID OWNER
---------- ------------------------------
        100 cc
  
Execution Plan
----------------------------------------------------------
    0      SELECT STATEMENT Optimizer=FIRST_ROWS (Cost=2 Card=14 Bytes=          420)
  
    1    0   TABLE ACCESS (BY INDEX ROWID) OF 'T' (Cost=2 Card=14 Bytes          =420)
  
    2    1     INDEX (RANGE SCAN) OF 'IDX_T' (NON-UNIQUE) (Cost=1 Card=          14)
  
Statistics
----------------------------------------------------------
           0  recursive calls
           0  db block gets
           4  consistent gets
           0  physical reads
           0  redo size
         424  bytes sent via SQL*Net to client
         426  bytes received via SQL*Net from client
           2  SQL*Net roundtrips to/from client
           0  sorts (memory)
           0  sorts (disk)
           1  rows processed
  
SQL> /
  
  OBJECT_ID OWNER
---------- ------------------------------
        100 cc
  
Execution Plan
----------------------------------------------------------
    0      SELECT STATEMENT Optimizer=FIRST_ROWS (Cost=2 Card=14 Bytes=          420)
    1    0   TABLE ACCESS (BY INDEX ROWID) OF 'T' (Cost=2 Card=14 Bytes          =420)
    2    1     INDEX (RANGE SCAN) OF 'IDX_T' (NON-UNIQUE) (Cost=1 Card=          14)
  
Statistics
----------------------------------------------------------
           0  recursive calls
           0  db block gets
           4  consistent gets
           0  physical reads
           0  redo size
         424  bytes sent via SQL*Net to client
         426  bytes received via SQL*Net from client
           2  SQL*Net roundtrips to/from client
           0  sorts (memory)
           0  sorts (disk)
           1  rows processed
  
SQL> select count(*) from t;
  
   COUNT(*)
----------
       6996
  
Execution Plan
----------------------------------------------------------
    0      SELECT STATEMENT Optimizer=FIRST_ROWS (Cost=2 Card=1)
    1    0   SORT (AGGREGATE)
    2    1     TABLE ACCESS (FULL) OF 'T' (Cost=2 Card=1307)
  
Statistics
----------------------------------------------------------
           0  recursive calls
          12  db block gets
          20  consistent gets
           0  physical reads
           0  redo size
         368  bytes sent via SQL*Net to client
         426  bytes received via SQL*Net from client
           2  SQL*Net roundtrips to/from client
           1  sorts (memory)
           0  sorts (disk)
           1  rows processed

这里如果update 只是很少记录的话,那么这边的consistent gets地数量也会小很多.
这个时可以理解的.
  
update 整个表,为什么检索这条记录,Buffer gets会增加这么多?
标被另外sesision update 之后:
SQL> /
  
   COUNT(*)
----------
       6996
  
Execution Plan
----------------------------------------------------------
    0      SELECT STATEMENT Optimizer=FIRST_ROWS (Cost=2 Card=1)
    1    0   SORT (AGGREGATE)
    2    1     TABLE ACCESS (FULL) OF 'T' (Cost=2 Card=1307)
  
Statistics
----------------------------------------------------------
           0  recursive calls
          12  db block gets
        7033  consistent gets
           0  physical reads
         832  redo size
         368  bytes sent via SQL*Net to client
         426  bytes received via SQL*Net from client
           2  SQL*Net roundtrips to/from client
           0  sorts (memory)
           0  sorts (disk)
           1  rows processed
  
那个session commit 之后:
SQL> /
  
   COUNT(*)
----------
       6996
  
Execution Plan
----------------------------------------------------------
    0      SELECT STATEMENT Optimizer=FIRST_ROWS (Cost=2 Card=1)
    1    0   SORT (AGGREGATE)
    2    1     TABLE ACCESS (FULL) OF 'T' (Cost=2 Card=1307)
  
Statistics
----------------------------------------------------------
           0  recursive calls
          12  db block gets
          21  consistent gets
           0  physical reads
          60  redo size
         368  bytes sent via SQL*Net to client
         426  bytes received via SQL*Net from client
           2  SQL*Net roundtrips to/from client
           0  sorts (memory)
           0  sorts (disk)
           1  rows processed

但是,如果即使update 其他整个表(除了object_id=100)对应的记录所在地lock,

  Quote:

  
SQL>  update t set owner='c' where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)<>17470;
  
6380 rows updated.
  
Execution Plan
----------------------------------------------------------
    0      UPDATE STATEMENT Optimizer=FIRST_ROWS (Cost=2 Card=4 Bytes=9
           6)
  
    1    0   UPDATE OF 'T'
    2    1     TABLE ACCESS (FULL) OF 'T' (Cost=2 Card=4 Bytes=96)
  
Statistics
----------------------------------------------------------
           0  recursive calls
        6530  db block gets
          19  consistent gets
           0  physical reads
     1561860  redo size
         622  bytes sent via SQL*Net to client
         577  bytes received via SQL*Net from client
           3  SQL*Net roundtrips to/from client
           1  sorts (memory)
           0  sorts (disk)
        6380  rows processed


这个select 的成本还是不变
SQL> select * from t where object_id=100;
  
  OBJECT_ID OWNER
---------- ------------------------------
        100 zc
  
Execution Plan
----------------------------------------------------------
    0      SELECT STATEMENT Optimizer=FIRST_ROWS (Cost=1 Card=1 Bytes=3
           0)
  
    1    0   TABLE ACCESS (BY INDEX ROWID) OF 'T' (Cost=1 Card=1 Bytes=
           30)
  
    2    1     INDEX (RANGE SCAN) OF 'IDX_T' (NON-UNIQUE) (Cost=1 Card=
           1)
  
Statistics
----------------------------------------------------------
           0  recursive calls
           0  db block gets
           4  consistent gets
           0  physical reads
           0  redo size
         436  bytes sent via SQL*Net to client
         504  bytes received via SQL*Net from client
           2  SQL*Net roundtrips to/from client
           0  sorts (memory)
           0  sorts (disk)
           1  rows processed
  
如果只是update 改记录所在地Block的某一个记录,那么buffer gets增加为2:

  Quote:

SQL> update t set owner='c' where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=17470 and rownum=1;
  
1 row updated.
  
Execution Plan
----------------------------------------------------------
    0      UPDATE STATEMENT Optimizer=FIRST_ROWS (Cost=2 Card=1 Bytes=2
           4)
  
    1    0   UPDATE OF 'T'
    2    1     COUNT (STOPKEY)
    3    2       TABLE ACCESS (FULL) OF 'T' (Cost=2 Card=1 Bytes=24)
  
Statistics
----------------------------------------------------------
          37  recursive calls
           2  db block gets
           8  consistent gets
           0  physical reads
         384  redo size
         623  bytes sent via SQL*Net to client
         588  bytes received via SQL*Net from client
           3  SQL*Net roundtrips to/from client
           1  sorts (memory)
           0  sorts (disk)
           1  rows processed


SQL> /
  
  OBJECT_ID OWNER
---------- ------------------------------
        100 zc
  
Execution Plan
----------------------------------------------------------
    0      SELECT STATEMENT Optimizer=FIRST_ROWS (Cost=1 Card=1 Bytes=3
           0)
  
    1    0   TABLE ACCESS (BY INDEX ROWID) OF 'T' (Cost=1 Card=1 Bytes=
           30)
  
    2    1     INDEX (RANGE SCAN) OF 'IDX_T' (NON-UNIQUE) (Cost=1 Card=
           1)
  
Statistics
----------------------------------------------------------
           0  recursive calls
           0  db block gets
           6  consistent gets
           0  physical reads
          52  redo size
         436  bytes sent via SQL*Net to client
         504  bytes received via SQL*Net from client
           2  SQL*Net roundtrips to/from client
           0  sorts (memory)
           0  sorts (disk)
           1  rows processed

如果update 了这个记录所在地Block的全部记录,那么consisteng gets 就会是何update 整个表所在地记录一样的结果:
SQL> update t set owner='c' where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=17470 ;  
  
616 rows updated.
  
Execution Plan
----------------------------------------------------------
    0      UPDATE STATEMENT Optimizer=FIRST_ROWS (Cost=2 Card=1 Bytes=2
           4)
  
    1    0   UPDATE OF 'T'
    2    1     TABLE ACCESS (FULL) OF 'T' (Cost=2 Card=1 Bytes=24)
  
Statistics
----------------------------------------------------------
           0  recursive calls
         630  db block gets
          19  consistent gets
           0  physical reads
      150724  redo size
         624  bytes sent via SQL*Net to client
         576  bytes received via SQL*Net from client
           3  SQL*Net roundtrips to/from client
           1  sorts (memory)
           0  sorts (disk)
         616  rows processed
那么select 的成本:
SQL> /
  
  OBJECT_ID OWNER
---------- ------------------------------
        100 zc
  
Execution Plan
----------------------------------------------------------
    0      SELECT STATEMENT Optimizer=FIRST_ROWS (Cost=1 Card=1 Bytes=3
           0)
  
    1    0   TABLE ACCESS (BY INDEX ROWID) OF 'T' (Cost=1 Card=1 Bytes=
           30)
  
    2    1     INDEX (RANGE SCAN) OF 'IDX_T' (NON-UNIQUE) (Cost=1 Card=
           1)
  
Statistics
----------------------------------------------------------
           0  recursive calls
           0  db block gets
         622  consistent gets
           0  physical reads
          52  redo size
         436  bytes sent via SQL*Net to client
         504  bytes received via SQL*Net from client
           2  SQL*Net roundtrips to/from client
           0  sorts (memory)
           0  sorts (disk)
           1  rows processed

 

biti_rainy  回复: buffer gets?


因为 产生 consistent gets 的时候决定于  CR block 的生成 ,select count(*) from x$bh where state = 3; 这里的数量的变化代表着  CR block 重构的生成 ,对于oracle来说,若查询到一条记录,发现已经被更改,于是拷贝当前block到新的地方,从回滚段取回before image 再rollback而重构block,这样的block的 state 为3 ,对于block中每一条记录都会去重构一次( 发现被修改,拷贝到新的block(CR),然后row--->ITL  --> transaction table ---> undo record  --->rollback新的blcok(CR)),所以跟一个块中的记录数有关。
  
      但这里要注意,虽然重构了很多CR,但是在我的曾经的测试中发现一次select中一个block的CR block似乎,不超过 3 blocks .也就是说同一个 DBA 的 cache  buffer chain 下的 CR 类型的blocks不超过3,我估计是为了节约内存和降低 该  latch(cache  buffer chain) 上由于搜索所等待的时间的一种策略。因为 CR block一旦产生后就没有用处了(实际上这个数是一个oracle隐藏参数控制的)

 

测试如下

SQL> create table test(a number);
  
Table created.
  
SQL> insert into test select rownum from t where rownum < 1001;
  
1000 rows created.
  
SQL> commit;
  
Commit complete.
  
SQL> select count(*) from t;
  
   COUNT(*)
----------
     154986
  
SQL> update test set a = 0 where a = 1;
  
1 row updated.
  
SQL> update test set a = 0 where a < 100;
  
99 rows updated.
  
SQL>  
  
通过打开不同的sqlplus反复执行下面的语句观察 后面 的 x$bh 查询表的变化
  
SQL>  select * from test where a < 101;
  
100 rows selected.
  
Execution Plan
----------------------------------------------------------
    0      SELECT STATEMENT Optimizer=CHOOSE
    1    0   TABLE ACCESS (FULL) OF 'TEST'
  
Statistics
----------------------------------------------------------
           0  recursive calls
           4  db block gets
         110  consistent gets
           0  physical reads
          52  redo size
        2475  bytes sent via SQL*Net to client
        1091  bytes received via SQL*Net from client
           8  SQL*Net roundtrips to/from client
           0  sorts (memory)
           0  sorts (disk)
         100  rows processed
  
SQL>  
  
SQL> select ts#,file#,dbablk,state,obj from x$bh where state = 3 order by 1,2,3,4;
  
        TS#      FILE#     DBABLK      STATE        OBJ
---------- ---------- ---------- ---------- ----------
          0          1        133          3 4294967294
          0          1        925          3          6
          0          1        930          3          6
          0          1      11579          3         10
          0          1      11579          3         10
          0          1      27273          3         34
          0          1      33837          3          6
          0          1      34264          3          8
          0          1      34264          3          8
          0          1      34264          3          8
          0          1      34264          3          8
  
        TS#      FILE#     DBABLK      STATE        OBJ
---------- ---------- ---------- ---------- ----------
          0          1      34268          3          8
          0          1      34268          3          8
          0          1      34268          3          8
          0          1      34268          3          8
          0          1      34768          3          2
          0          1      34768          3          2
          0          1      34788          3         18
          2          3          3          3      24825
          2          3          3          3      24825
          2          3          3          3      24825
          2          3          3          3      24825
  
        TS#      FILE#     DBABLK      STATE        OBJ
---------- ---------- ---------- ---------- ----------
          2          3          3          3      24825
          2          3        643          3      27155
          2          3        643          3      27155
          2          3        643          3      27155
          2          3        643          3      27155
  
27 rows selected.


bitirainy 发表于:2004.09.08 17:08 ::分类: ( Oracle is anything ) ::阅读:(961次) :: 评论 (0)
===========================================================
如果在shared_pool中keep cursor(sql)
===========================================================
SQL> desc sys.dbms_shared_pool
PROCEDURE ABORTED_REQUEST_THRESHOLD
  Argument Name                  Type                    In/Out Default?
  ------------------------------ ----------------------- ------ --------
  THRESHOLD_SIZE                 NUMBER                  IN
PROCEDURE KEEP
  Argument Name                  Type                    In/Out Default?
  ------------------------------ ----------------------- ------ --------
  NAME                           VARCHAR2                IN
  FLAG                           CHAR                    IN     DEFAULT
PROCEDURE SIZES
  Argument Name                  Type                    In/Out Default?
  ------------------------------ ----------------------- ------ --------
  MINSIZE                        NUMBER                  IN
PROCEDURE UNKEEP
  Argument Name                  Type                    In/Out Default?
  ------------------------------ ----------------------- ------ --------
  NAME                           VARCHAR2                IN
  FLAG                           CHAR                    IN     DEFAULT
  
SQL>  
  
也就是说如果我们想要keep一个sql的话
我们可以查询 v$sqlarea  
查到 'address' and 'hash_value'  
  
然后执行 dbms_shared_pool.keep('0034CDFF, 20348871', 'C')
这样就把该 cursor keep 在内存中
   
  
  procedure keep(name varchar2, flag char DEFAULT 'P');
   --  Keep an object in the shared pool.  Once an object has been keeped in
   --    the shared pool, it is not subject to aging out of the pool.  This
   --    may be useful for certain semi-frequently used large objects since
   --    when large objects are brought into the shared pool, a larger
   --    number of other objects (much more than the size of the object
   --    being brought in, may need to be aged out in order to create a
   --    contiguous area large enough.
   --    WARNING:  This procedure may not be supported in the future when
   --    and if automatic mechanisms are implemented to make this
   --    unnecessary.
   --  Input arguments:
   --    name
   --      The name of the object to keep.  There are two kinds of objects:
   --      PL/SQL objects, triggers, sequences, types and Java objects,
   --      which are specified by name, and
   --      SQL cursor objects which are specified by a two-part number
   --      (indicating a location in the shared pool).  For example:
   --        dbms_shared_pool.keep('scott.hispackage')
   --      will keep package HISPACKAGE, owned by SCOTT.  The names for
   --      PL/SQL objects follows SQL rules for naming objects (i.e.,
   --      delimited identifiers, multi-byte names, etc. are allowed).
   --      A cursor can be keeped by
   --        dbms_shared_pool.keep('0034CDFF, 20348871', 'C')
   --      The complete hexadecimal address must be in the first 8 characters.
   --      The value for this identifier is the concatenation of the
   --      'address' and 'hash_value' columns from the v$sqlarea view.  This
   --      is displayed by the 'sizes' call above.
   --      Currently 'TABLE' and 'VIEW' objects may not be keeped.
   --    flag
   --      This is an optional parameter.  If the parameter is not specified,
   --        the package assumes that the first parameter is the name of a
   --        package/procedure/function and will resolve the name.  Otherwise,
   --        the parameter is a character string indicating what kind of object
   --        to keep the name identifies.  The string is case insensitive.
   --        The possible values and the kinds of objects they indicate are
   --        given in the following table:
   --        Value        Kind of Object to keep
   --        -----        ----------------------
   --        P          package/procedure/function
   --        Q          sequence
   --        R          trigger
   --        T          type
   --          JS         java source
   --          JC         java class
   --        JR         java resource
   --        JD         java shared data
   --        C          cursor
   --      If and only if the first argument is a cursor address and hash-value,
   --        the flag parameter should be set to 'C' (or 'c').
   --  Exceptions:
   --    An exception will raised if the named object cannot be found.
bitirainy 发表于:2004.09.08 17:08 ::分类: ( Oracle is anything ) ::阅读:(978次) :: 评论 (0)
===========================================================
v$waitstat 和 save undo segment header /save undo block
===========================================================

比如等待中也有
  
SQL> select * from v$waitstat;
  
CLASS                   COUNT       TIME
------------------ ---------- ----------
data block                  2          1
sort block                  0          0
save undo block             0          0
segment header              1          0
save undo header            0          0
free list                   0          0
extent map                  0          0
1st level bmb               0          0
2nd level bmb               0          0
3rd level bmb               0          0
bitmap block                0          0
  
CLASS                   COUNT       TIME
------------------ ---------- ----------
bitmap index block          0          0
file header block           0          0
unused                      0          0
system undo header          0          0
system undo block           0          0
undo header                 0          0
undo block                  0          0
  
已选择18行。
  
SQL>  
  
在 x$bh.class 中  
5 : save  undo  segment  header  
3:save undo block
  
其表示的是defferd rollback segment 的 block (参考oracle  concepts)
  
SQL> select count(*) from x$bh where class = 3;
  
   COUNT(*)
----------
          0
  
SQL> select count(*) from x$bh where class = 5;
  
   COUNT(*)
----------
          0
  
SQL> delete rainy.t where rownum &lt; 11;
  
已删除10行。
  
SQL> alter tablespace users offline;
  
表空间已更改。
  
SQL>  select count(*) from x$bh where class = 5;
  
   COUNT(*)
----------
          1
  
SQL>  select count(*) from x$bh where class = 3;
  
   COUNT(*)
----------
          1
  
SQL>  alter tablespace users online;
  
表空间已更改。
  
SQL> select count(*) from rainy.t;
  
   COUNT(*)
----------
         90
  
SQL>  select count(*) from x$bh where class = 5;
  
   COUNT(*)
----------
          0
  
SQL>  select count(*) from x$bh where class = 3;
  
   COUNT(*)
----------
          0
  
SQL>

 


bitirainy 发表于:2004.09.08 17:07 ::分类: ( Oracle is anything ) ::阅读:(991次) :: 评论 (0)
===========================================================
9i rman block recover
===========================================================

http://otn.oracle.com/docs/products/oracle9i/doc_library/901_doc/server.901/a90136/rcmsyn10.htm#79197
  
Examples
Recovering a Group of Corrupt Blocks: Example
This example recovers corrupt blocks in three datafiles:  
  
BLOCKRECOVER DATAFILE 2 BLOCK 12, 13 DATAFILE 7 BLOCK 5, 98, 99 DATAFILE 9 BLOCK 19;
  
Limiting Block Media Recovery by Type of Restore: Example
The following example recovers a series of blocks and restores only from datafile copies:  
  
RUN
{
   BLOCKRECOVER
     DATAFILE 3 BLOCK 2,3,4,5
     TABLESPACE sales DBA 4194405, 4194409, 4194412
   FROM DATAFILECOPY;
}
  
Limiting Block Media Recovery by Backup Tag: Example
This example recovers blocks and restores only from the backup set with the tag weekly_backup:  
  
BLOCKRECOVER TABLESPACE SYSTEM DBA 4194404, 4194405 FROM TAG "weekly_backup";
  
Limiting Block Media Recovery by Time: Example
The following example recovers two blocks in the SYSTEM tablespace and forces the blocks to be restored from backups created at least two days ago:  
  
BLOCKRECOVER TABLESPACE SYSTEM DBA 4194404, 4194405 RESTORE UNTIL TIME 'SYSDATE-2';

 

rman 可以在数据文件 online 的情况下 恢复逻辑坏块
手工关闭数据库编辑数据文件导致损坏,然后测试
  
SQL> select count(*) from rainy.test;
select count(*) from rainy.test
                            *
ERROR at line 1:
ORA-01578: ORACLE data block corrupted (file # 2, block # 12)
ORA-01110: data file 2: 'E:ORACLEORADATARAINYTEST.DBF'
  
RMAN> run{
2> blockrecover datafile 2 block 12 from datafilecopy;}
  
Starting blockrecover at 2003-09-08 12:22:27
using target database controlfile instead of recovery catalog
allocated channel: ORA_DISK_1
channel ORA_DISK_1: sid=16 devtype=DISK
  
channel ORA_DISK_1: restoring block(s) from datafilecopy E:TEST.DBF
  
starting media recovery
media recovery complete
  
Finished blockrecover at 2003-09-08 12:22:34
  
RMAN>
  
SQL>  select count(*) from rainy.test;
  
   COUNT(*)
----------
      29725
  
SQL>  

 


bitirainy 发表于:2004.09.08 17:06 ::分类: ( Oracle is anything ) ::阅读:(3127次) :: 评论 (0)
===========================================================
关于 cursor_sharing = similar
===========================================================
我们先看看在表没有分析无统计数据情况下的表现 

SQL> alter session set cursor_sharing = similar;
 
Session altered.

 查看全文
bitirainy 发表于:2004.09.08 17:06 ::分类: ( Oracle is anything ) ::阅读:(2799次) :: 评论 (1)
===========================================================
statspack 中 Execute to Parse
===========================================================

Instance Efficiency Percentages (Target 100%)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
             Buffer Nowait %:   99.82       Redo NoWait %:              100.00
             Buffer  Hit   %:   97.18    In-memory Sort %:               99.90
             Library Hit   %:   99.89        Soft Parse %:               99.84
          Execute to Parse %:   12.11         Latch Hit %:               99.54
Parse CPU to Parse Elapsd %:   57.50     % Non-Parse CPU:               95.41
  
  Shared Pool Statistics        Begin   End
                                ------  ------
              Memory Usage %:   59.91   60.20
     % SQL with executions>1:   91.78   91.34
   % Memory for SQL w/exec>1:   99.62   99.62

 

 Execute to Parse  =  round(100*(1-:prse/:exe),2)                     
  
prse = select value from v$sysstat where name = 'parse count (total)';
exe = select value from v$sysstat where name = 'execute count';

 

  Quote:
wanghai wrote:

  Quote:
biti_rainy wrote:
这个值的高低跟 bind var没有必然联系
  
建议你做的是增加 session_cached_cursors 到 100 或者 200,pga足够的话

没绑定的话导致不能重用也是一个原因,当然sharedpool太小也有可能,单纯的加session_cached_cursors也不是根治的办法,不同的sql还是不能重用,还要解析,呵呵,运气好的话能有性能提高.如果内存够的话完全可以一试,不过根治还是需要bind:)


        即使是soft  parse 也会被统计入 parse count,所以这个指标并不能反应出  fast soft(pga 中)/soft (shared pool中)/hard (shared pool 中新解析)  几种解析的比例。只有在pl/sql的类似循环这种程序中使用使用变量才能避免大量parse 所以这个指标跟是否使用bind并没有必然联系 ,增加session_cached_cursors 是为了在大量parse的情况下把soft转化为fast  soft而 节约资源

 

原文参考

http://www.cnoug.org/viewthread.php?tid=1796

 


bitirainy 发表于:2004.09.08 17:05 ::分类: ( Oracle is anything ) ::阅读:(944次) :: 评论 (0)
===========================================================
证明truncate发出了可观察的commit命令
===========================================================

一般来说,我们通常认为,truncate 的伪代码是:

commit; ---------- 1
truncate;
commit; ------------ 2

那我们有个疑问,commit 1和 truncate 是不可分割的吗?具有原子性吗?那我们来看看下面的演示

session 1:
SQL> create table t as select * from all_objects where rownum < 11;

Table created.

SQL> delete from t where rownum = 1;

1 row deleted.

SQL>



session 2:

SQL> lock table t in exclusive mode;



很明显,session 2 在这里被hang住了


session 1:

SQL> create table t as select * from all_objects where rownum < 11;

Table created.

SQL> delete from t where rownum = 1;

1 row deleted.

SQL> commit;

Commit complete.

SQL>


再看 session 2

SQL> lock table t in exclusive mode;

Table(s) Locked.

SQL>


加锁成功

这时我们再在 session 1上执行delete操作

session 1:

SQL> create table t as select * from all_objects where rownum < 11;

Table created.

SQL> delete from t where rownum = 1;

1 row deleted.

SQL> commit;

Commit complete.

SQL> delete from t where rownum = 1;


这时被阻塞了


接下来我们去 session 2中去尝试truncate

session 2:

SQL> lock table t in exclusive mode;

Table(s) Locked.

SQL> truncate table t;
truncate table t
*
ERROR at line 1:
ORA-00054: resource busy and acquire with NOWAIT specified


SQL>

发现truncate 失败,切到 session 1 看看
SQL> create table t as select * from all_objects where rownum < 11;

Table created.

SQL> delete from t where rownum = 1;

1 row deleted.

SQL> commit;

Commit complete.

SQL> delete from t where rownum = 1;

1 row deleted.

SQL>


    发现 session 1的删除居然成功了,而session 2的truncate 却失败了。这说明,当 session 2 truncate发出commit 1 之后,还没有执行truncate,在这之前session 1的delete操作由于是被 lock table 阻塞但是时间却在 truncate 之前,所以在这个间歇中 session 抢在了前面,从而导致 truncate 失败。

    其实之所以要做这个测试,也不纯粹是无聊,是因为我们有一个程序,需要首先truncate一个表,然后另一个进程去做一些数据的操作,如果truncate失败则后续的操作就有问题,但是truncate失败却不能阻止后面的进程的操作。当时我在想能否在truncate之前使用lock  table 去加锁,直到其他进程dml完毕这个进程获得了独占表锁然后truncate,看看这样是否可行。结果很遗憾,我的测试结果发现这个想法是不大现实的。 


bitirainy 发表于:2004.09.08 17:03 ::分类: ( Oracle is anything ) ::阅读:(806次) :: 评论 (5)
===========================================================
关于checkpoint的讨论
===========================================================

http://www.cnoug.org/viewthread.php?tid=21969

贴主: wanghai

 

checkpoint小议

什么是checkpoint?
checkpoint是一个数据库事件,它将已修改的数据从高速缓存刷新到磁盘,并更新控制文件和数据文件。

什么时候发生checkpoint?
我们知道了checkpoint会刷新脏数据,但什么时候会发生checkpoint呢?以下几种情况会触发checkpoint。
1.当发生日志组切换的时候
2.当符合LOG_CHECKPOINT_TIMEOUT,LOG_CHECKPOINT_INTERVAL,fast_start_io_target,fast_start_mttr_target参数设置的时候
3.当运行ALTER SYSTEM SWITCH LOGFILE的时候
4.当运行ALTER SYSTEM CHECKPOINT的时候
5.当运行alter tablespace XXX begin backup,end backup的时候
6.当运行alter tablespace ,datafile offline的时候;

增量检查点(incremental checkpoint)
oracle8以后推出了incremental checkpoint的机制,在以前的版本里每次checkpoint时都会做一个full thread checkpoint,这样的话所有脏数据会被写到磁盘,巨大的i/o对系统性能带来很大影响。为了解决这个问题,oracle引入了checkpoint queue机制,每一个脏块会被移到检查点队列里面去,按照low rdb(第一次对此块修改对应的redo block address)来排列,靠近检查点队列尾端的数据块的low rba值是最小的,而且如果这些赃块被再次修改后它在检查点队列里的顺序也不会改变,这样就保证了越早修改的块越早写入磁盘。每隔3秒钟ckpt会去更新控制文件和数据文件,记录checkpoint执行的情况。

数据字典
完全检查点
select * from X$KCCRT where indx=0;

ADDR           INDX    INST_ID      RTNUM      RTSTA RTCKP_SCN        RTCKP_TIM             RTCKP_THR RTCKP_RBA_SEQ RTCKP_RBA_BNO RTCKP_RBA_BOF RTCKP_ETB         RTOTF      RTOTB      RTNLF      RTLFH      RTLFT      RTCLN      RTSEQ RTENB            RTETS    RTDIS            RTDIT                     RTLHP RTSID            RTOTS
-------- ---------- ---------- ---------- ---------- ---------------- -------------------- ---------- ------------- ------------- ------------- ---------------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------------- -------------------- ---------------- -------------------- ---------- ---------------- --------------------
4084B228          0          1          1         15 720368521        06/25/2004 18:49:37           1           949             2     16 0600000000000000          2          0          3          1          3          1        949 1                05/16/2004 13:29:03  0                                               1389 tbdb2in1         06/12/2004 12:30:50

这里显示了上一次的完全检查点是在06/25/2004 18:49:37发生,所以我们推断06/25/2004 18:49:37发生了一次日志切换,再去操作系统上去看生产的归档,果然18:49有一个归档生产。
-rw-r-----    1 oracle   oinstall 83532800 Jun 25 18:49 1_948.dbf




增量检查点
SQL> select * from X$KCCCP where indx=0;

ADDR           INDX    INST_ID      CPTNO      CPSTA      CPFLG      CPDRT      CPRDB CPLRBA_SEQ CPLRBA_BNO CPLRBA_BOF  CPODR_SEQ  CPODR_BNO  CPODR_BOF CPODS                CPODT                   CPODT_I      CPHBT CPRLS                 CPRLC      CPMID  CPSDR_SEQ  CPSDR_BNO  CPSDR_ADB
-------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------------- -------------------- ---------- ---------- ---------------- ---------- ---------- ---------- ---------- ----------
4084B45C          0          1          1          2          0      10762      29753        949      76847          0        949     106814              0 721554970        06/25/2004 21:05:10   529794310  529036227 1                 526310932 1413781667        949              1          0

这里显示了low-rba,on-disk rba,checkpoint time等信息。

 

biti_rainy:

每隔3秒钟ckpt会去更新控制文件和数据文件,记录checkpoint执行的情况。


这里应该是只更新控制文件,每3秒不是更新数据文件
说 记录 checkpoint 的执行情况,这个说法,没错,但不够详细,应该说,由于增量检查点和 checkpoint  queue 的原理,ckpt 进程每次只是告诉 dbwr ,写dirty  buffer将要一直写到最新这个位置,仅仅是告诉 dbwr 一个 checkpoint queue  中的结束点,而 ckpt 每3秒中,在控制文件中报告一下 dbwr 最新写入的位置。 这样使得,比如数据库要做恢复的时候(instance  recovery)可以从这个最新位置开始做恢复,而不是从数据文件中的 checkpoint  scn 开始做恢复,这样将缩短恢复时间,尤其是 instance  crash 的情况下启动更快

另外要注意的是,检查点发生的时候,ckpt 去更新数据文件头和控制文件,并不是把当前检查点发生时候的 scn 更新进去,而是把上一次dbwr写入已经完成的检查点发生时候的  scn 更新进去 ,也就是说,更新控制文件和数据文件头 是 滞后于检查点的发生的,这个从恢复的原理也很容易理解,因为检查点发生的时候 dirty buffer还没有写入,自然不能立即更新成当前的 scn 了。

jerrysun 转:

something about checkpoint queue latch

wanghai, the following is just for your reference. :)

Firstly we have two queue structures associated with checkpoints - the checkpoint queue - or thread queue - (CKPTQ), and the file queue.For each buffer to be checkpointed, it is linked to these two queues. The CKPTQ contains all buffers that need to be checkpointed for this instance.  The File queue contains all buffers that belong to a specific file that need to  be checkpointed. There is one file queue per file. The file queues are used by tablespace checkpoint requests.

Both these queues constitute a set of checkpoint queues.

Before a process can put a buffer on a checkpoint queue, it must make sure that the queue is not being used. There is one checkpoint queue latch per set of  checkpoint queues, that is used to control access to these queues. To reduce contention on this latch, the set of thread and file queues is replicated as per the number of working sets for the instance.

The determination of the number of working sets has changed across the DB versions.  For 9.2, the default number is calculated
internally as #CPUs  / 2 * 8.

The maximum of DBWR's (db_writer_processes) you can have at 9.2 is 20.  Working sets are assigned to DBWR's in a round-robin
fashion at startup.

 

 查看全文
bitirainy 发表于:2004.09.08 17:03 ::分类: ( Oracle is anything ) ::阅读:(1039次) :: 评论 (0)
===========================================================
深度分析数据库的热点块问题
===========================================================

热点块的定义

        数据库的热点块,从简单了讲,就是极短的时间内对少量数据块进行了过于频繁的访问。定义看起来总是很简单的,但实际在数据库中,我们要去观察或者确定热点块的问题,却不是那么简单了。要深刻地理解数据库是怎么通过一些数据特征来表示热点块的,我们需要了解一些数据库在这方面处理机制的特性。

数据缓冲区的结构

        我们都知道,当查询开始的时候,进程首先去数据缓冲区中查找是否存在查询所需要的数据块,如果没有,就去磁盘上把数据块读到内存中来。在这个过程中,涉及到数据缓冲区中LRU链的管理(8i开始以接触点计数为标准衡量buffer冷热从而决定buffer是在LRU的冷端还是热端),关于这部分内容,从oracle concepts 中就能得到详尽的文档,我不准备去论述这部分内容,这也不是本文的重点。现在我们的重点是,到底进程是如何地去快速定位到自己所想要的block的,或者如何快速确定想要的block不在内存中而去进行物理读的。

        我们仔细想一想,随着硬件的发展,内存越来越大,cache buffer也越来越大,我们如何才能在大量的内存中迅速定位到自己想要的block?总不能去所有buffer中遍历吧!在此数据库引出了hash的概念(oracle中快速定位信息总是通过hash算法的,比如快速定位sql是否在shared pool size中存在就是通过hash value来定位的,也就是说shared pool size中对象也是通过hash table来管理的),了解一点数据结构的基本知识就知道,hash 的一大重要功能就是快速地查找。举个最简单的例子,假设我们有一个hash table 就是一个二维数组a[200][100],现在有1000个无序数字,我们要从这1000个数字里面查找某个值是否存在,或者说当我们接收到某个数字的时候必须判断是否已经存在,当然,我们可以遍历这1000个数字,但这样的效率就很低。但现在我们考虑这样一种方法,那就是把1000个数字除以200,根据其余数,放在a[200][100]里面(假设相同余数的最大数量不超过100),余数就是数组的下标。这样,平均来说一个数组a[i]里面可能有5个左右的数字。当我们要去判别一个数字是否存在的时候,对这个数字除以200(这就是一个最简单的hash算法),根据余数i作为下标去数组a[i]中查找,大约进行5次查找就能判别是否已经存在,这样通过开辟内存空间a[200][100]来换取了时间(当然hash 算法的选取和hash table的大小是一个很关键的问题)。

        明白了基本的hash原理之后,我们再来看oracle的block的管理。数据库为这些block也开辟了hash table,假设是a,则在一维上的数量是由参数_db_block_hash_buckets 来决定的,也就是存在hash table a[_db_block_hash_buckets ],从oracle8i开始,_db_block_hash_buckets =db_block_buffers*2。而一个block被放到哪个buckets里面,则是由block的文件编号、块号(x$bh.dbarfl、x$bh.dbablk对应了block的文件属于表空间中的相关编号和block在文件中的编号,x$bh是所有cache buffer的header信息,通过表格的形式可以查询)做hash 算法决定放到哪个bucket的,而bucket里面就存放了这些buffers的地址。这样当我们要访问数据的时候,可以获得segment的extent(可以通过dba_extents查到看,详细的信息来源这里不做探讨),自然知道要访问的文件编号和block编号,根据文件和block编号可以通过hash算法计算出hash bucket,然后就可以去hash bucket里面去找block对应的buffer。

        除此之外,为了维护对这些block的访问和更改,oracle还提供了一种latch来保护这些block。因为要避免不同的进程随意地径直并发修改和访问这些block,这样很可能会破坏block的结构的。latch是数据库内部提供的一种维护内部结构的一种低级锁,latch的生存周期极短(微秒以下级别),进程加latch后快速的进行某个访问或者修改动作然后释放latch(关于latch不再过多的阐述,那可能又是需要另一篇文章才能阐述清楚)。这种latch数量是通过参数_db_block_hash_latches 来定义的,一个latch对应的保护了多个buckets。从8i开始,这个参数的default规则为:

当cache buffers 少于2052 buffers

_db_block_hash_latches  =  power(2,trunc(log(2, db_block_buffers - 4) - 1))

当cache buffers多于131075 buffers

_db_block_hash_latches  =  power(2,trunc(log(2, db_block_buffers - 4) - 6))

当cache buffers位于2052与131075 buffers之间

_db_block_hash_latches  =  1024

        通过这个规则我们可以看出,一个latch大约可以维护128个左右的buffers。由于latch使得对block的操作的串行化(9i中有改进,读与读可以并行,但读与写、写与写依然要串行),很显然我们可以想到一个道理,如果大量进程对相同的block进程进行操作,必然在这些latch上造成竞争,也就是说必然形成latch的等待。这在宏观上就表现为系统级的等待。明白了这些原理,为我们下面的在数据库中的诊断奠定了基础。

如何确定热点对象

            如果我们经常关注statspack报告,会发现有时候出现cache buffer chains的等待。这个cache buffer chains就是_db_block_hash_latches所定义的latch的总称,通过查询v$latch也可得到:

select">sys@OCN>select  latch#,name,gets,misses,sleeps from v$latch where name   like 'cache buffer%';

    LATCH# NAME                                 GETS     MISSES     SLEEPS
---------- ------------------------------ ---------- ---------- ----------
        93 cache buffers lru chain          54360446      21025        238
        98 cache buffers chains           6760354603    1680007      27085
        99 cache buffer handles               554532          6                   0

  在这个查询结果里我们可以看到记录了数据库启动以来的所有cahce buffer chains的latch的状况,gets表示总共有这么多次请求,misses表示请求失败的次数(加锁不成功),而sleeps 表示请求失败休眠的次数,通过sleeps我们可以大体知道数据库中latch的竞争是否严重,这也间接的表征了热点块的问题是否严重。由于v$latch是一个聚合信息,我们并不能获得哪些块可能存在频繁访问。那我们要来看另一个view信息,那就是v$latch_children,v$latch_children.addr记录的就是这个latch的地址。

select">sys@OCN>select addr,LATCH#,CHILD#,gets,misses,sleeps from v$latch_children
  2  where name = 'cache buffers chains'  and rownum < 21;

ADDR         LATCH#     CHILD#       GETS     MISSES     SLEEPS
-------- ---------- ---------- ---------- ---------- ----------
91B23B74         98       1024   10365583       3957         33
91B23374         98       1023    5458174        964         25
91B22B74         98       1022    4855668        868         15
91B22374         98       1021    5767706        923         22
91B21B74         98       1020    5607116        934         31
91B21374         98       1019    9389325       1111         25
91B20B74         98       1018    5060207        994         31
91B20374         98       1017   18204581       1145         18
91B1FB74         98       1016    7157081        920         23
91B1F374         98       1015    4660774        922         22
91B1EB74         98       1014    6954644        976         32
91B1E374         98       1013    4881891        970         19
91B1DB74         98       1012    5371135        971         28
91B1D374         98       1011    5154497        990         26
91B1CB74         98       1010    5013796        936         18
91B1C374         98       1009    5667446        939         25
91B1BB74         98       1008    4673421        883         14
91B1B374         98       1007    4589646        986         17
91B1AB74         98       1006   10380781       1020         20
91B1A374         98       1005    5142009       1110         19

20 rows selected.

到此我们可以根据v$latch_child.addr关联到对应的x$bh.hladdr(这是buffer header中记录的当前buffer所处的latch地址),通过x$bh可以获得块的文件编号和block编号。

select">sys@OCN>select dbarfil,dbablk 
    from x$bh
   where hladdr in
    (select addr
    from (select addr
        from v$latch_children
        order by sleeps desc) 
          where rownum < 11);

   DBARFIL     DBABLK
---------- ----------
         4       6498
        40      14915
        15      65564
        28      34909
        40      17987
         1      24554
         8      21404
        39      29669
        28      46173
        28      48221

……………………

      由此我们就打通了cache buffers chains和具体block之间的关系,那再继续下来,知道了block,我们需要知道究竟是哪些segment。这个可以通过dba_extents来获得。

select distinct a.owner,a.segment_name from
dba_extents a,
(select dbarfil,dbablk
from x$bh
where hladdr in
    (select addr
    from (select addr
        from v$latch_children
        order by sleeps desc)
        where rownum < 11)) b
where a.RELATIVE_FNO = b.dbarfil
and a.BLOCK_ID <= b.dbablk and a.block_id + a.blocks > b.dbablk;


OWNER                          SEGMENT_NAME                   SEGMENT_TYPE
------------------------------ ------------------------------         ------------------
ALIBABA                        BIZ_SEARCHER                               TABLE
ALIBABA                        CMNTY_USER_MESSAGE             TABLE
ALIBABA                        CMNTY_VISITOR_INFO_PK          INDEX
ALIBABA                        COMPANY_AMID_IND                   INDEX
ALIBABA                        COMPANY_DRAFT                         TABLE
ALIBABA                        FEEDBACK_POST                           TABLE
ALIBABA                        IM_BLACKLIST_PK                         INDEX
ALIBABA                        IM_GROUP                                        TABLE
ALIBABA                        IM_GROUP_LID_IND                      INDEX
ALIBABA                        MEMBER                                           TABLE
ALIBABA                        MEMBER_PK                                    INDEX
ALIBABA                        MLOG$_SAMPLE                            TABLE

……………………

  我们还有另外一种方式

   select object_name
from dba_objects
where data_object_id in
(select obj
from x$bh
where hladdr in
    (select addr
    from (select addr
        from v$latch_children
        order by sleeps desc)
        where rownum < 11)) ;


OBJECT_NAME
------------------------------------
I_CCOL2
RESOURCE_PLAN$
DUAL
FGA_LOG$
AV_TRANSACTION
COMPANY_DRAFT
MEMBER
SAMPLE
SAMPLE_GROUP
VERTICAL_COMPONENT
MEMBER_PK
SAMPLE_GROUP_PK
IM_BLACKLIST_PK
IM_CONTACT
IM_GROUP
CMNTY_USER_MESSAGE
CMNTY_VISITOR_INFO_PK
IM_OFFLINEMSG_TID_IND
OFFER
OFFER_PK
OFFER_EMAIL_IND
OFFER_DRAFT
CMNTY_USER_MESSAGE_TD_BSM_IND
CMNTY_MESSAGE_NUM_PK
BIZ_EXPRESS_MEMBER_ID_IND

……………………

 

 

        到这里我们基本能找到热点块对对应的对象。但实际上还有另外一个途径来获取这些信息,那就是和x$bh.tch 相关的一种方法。对于8i开始oracle提供了接触点(touch count)来作为block是冷热的标志,在一定条件满足的情况下block被进程访问一次touch count 增加一,到某个标准之后被移动到LRU热端(关于touch count 在这里不做详细介绍,那又将是一大篇文章)。那在短时间内从某种意义上讲,touch count 大的block可能暗示着在当前某个周期内被访问次数比较多。

select distinct a.owner,a.segment_name,a.segment_type from
dba_extents a,
(select dbarfil,dbablk
from (select dbarfil,dbablk
    from x$bh order by tch desc) where rownum < 11) b
where a.RELATIVE_FNO = b.dbarfil
and a.BLOCK_ID <= b.dbablk and a.block_id + a.blocks > b.dbablk;

OWNER                          SEGMENT_NAME                   SEGMENT_TYPE
------------------------------ ------------------------------         ------------------
ALIBABA                        CMNTY_USER_MESSAGE              TABLE
ALIBABA                        MEMBER_PK                                      INDEX
ALIBABA                        OFFER_DRAFT_GMDFY_IND          INDEX

同上面一样还有这个方法

select object_name
from dba_objects
where data_object_id in
(select obj
from (select obj
    from x$bh order by tch desc) where rownum < 11) ;
OBJECT_NAME
---------------------------------------------------
DUAL
MEMBER_PK
SAMPLE_GROUP_PK
CMNTY_USER_MESSAGE_TD_BSM_IND
OFFER_DRAFT_MID_GMDFY_IND
OFFER_MID_GPOST_IND
OFFER_DRAFT_PK
MEMBER_GLLOGIN_IND
OFFER_MID_STAT_GEXPIRE_IND
SAMPLE_MID_STAT_IND

10 rows selected.

 

        到这里,我们寻找热点块和热点对象的工作算是完成了,但我们还并没有解决问题。

 

热点问题的解决

           热点块和热点对象我们都找到了,但是我们该怎么来解决这个问题呢?一般来说,热点块会导致cache buffers chains竞争等待,但并不是说cache buffer chains一定是因为热点块而起,在特别情况下有可能是因为latch数量的问题导致的,也就是一个latch管理的buffers数量太多而导致竞争激烈。但是latch数量我们一般是不会轻易去设置的,这是oracle的隐藏参数。

          实际上最有效的办法,是从优化sql入手,不良的sql往往带来大量的不必要的访问,这是造成热点块的根源。比如本该通过全表扫描的查询却走了索引的range scan,这样将带来大量的对块的重复访问。从而形成热点问题。再或者比如不当地走了nested loops的表连接,也可能对非驱动表造成大量的重复访问。那么在这个时候,我们的目标就是找出这些sql来并尝试优化。在statspack报告中,根据报告中sql列表,我们如果是通过dba_extents确定的热点对象而不是通过dba_objects确定的,则可以通过查找出的热点segment转换为对应的表,对于非分区的索引,index_name就是segment_name,通过dba_indexes很容易的找到对应的table_name,对于分区表和分区索引也能通过和dba_tab_partition和dba_ind_partitions找到segment和table的对应关系。通过这些table到statspack报告中去找相关的sql。

select sql_text
from stats$sqltext a,
(select distinct a.owner,a.segment_name,a.segment_type from
dba_extents a,
(select dbarfil,dbablk
from (select dbarfil,dbablk
    from x$bh order by tch desc) where rownum < 11) b
where a.RELATIVE_FNO = b.dbarfil
and a.BLOCK_ID <= b.dbablk and a.block_id + a.blocks > b.dbablk) b
where a.sql_text like '%'||b.segment_name||'%' and b.segment_type = 'TABLE'
order by  a.hash_value,a.address,a.piece;

SQL_TEXT
----------------------------------------------------------------
SELECT SEQ_SMS_TRANSACTION.nextval FROM DUAL
SELECT SEQ_BIZ_EXPRESS.nextval FROM DUAL
SELECT bizgroup.seq_grp_post.NextVal FROM DUAL
SELECT SEQ_SAMPLE.nextval FROM DUAL
SELECT bizgroup.seq_grp_user.NextVal FROM DUAL
SELECT SEQ_BIZ_SEARCHER.nextval FROM DUAL
SELECT SEQ_OFFER_DRAFT.nextval FROM DUAL
select seq_Company_Draft.NextVal from DUAL
SELECT SEQ_SAMPLE_GROUP.nextval FROM DUAL
SELECT SEQ_CMNTY_USER_MESSAGE.nextval FROM DUAL
SELECT SYSDATE FROM DUAL
select seq_News_Forum.NextVal from DUAL
SELECT SEQ_SMS_USER.nextval FROM DUAL
select seq_Biz_Member.NextVal from DUAL
select seq_Pymt_Managing.NextVal from DUAL
E= '+08:00' NLS_DUAL_CURRENCY = '$' NLS_TIME_FORMAT = 'HH.MI.SSX
SELECT SEQ_COMPANY_DRAFT.nextval FROM DUAL
SELECT 1 FROM DUAL
select seq_offer_draft.NextVal from DUAL
select seq_Biz_Express_Category.NextVal from DUAL

20 rows selected.

当然这里是从statspack搜集的stats$sqltext中去找的(你可以在statspack的文本报告中去找),实际上,我们可以直接在当前数据库中的v$sqlarea或者v$sqltext里面去找到这些sql,然后来尝试优化。

select sql_text
from v$sqltext a,
(select distinct a.owner,a.segment_name,a.segment_type from
dba_extents a,
(select dbarfil,dbablk
from (select dbarfil,dbablk
    from x$bh order by tch desc) where rownum < 11) b
where a.RELATIVE_FNO = b.dbarfil
and a.BLOCK_ID <= b.dbablk and a.block_id + a.blocks > b.dbablk) b
where a.sql_text like '%'||b.segment_name||'%' and b.segment_type = 'TABLE'
order by  a.hash_value,a.address,a.piece;
SQL_TEXT
----------------------------------------------------------------
SELECT NULL FROM DUAL FOR UPDATE NOWAIT
SELECT SEQ_SMS_TRANSACTION.nextval FROM DUAL
SELECT SEQ_BIZ_EXPRESS.nextval FROM DUAL
SELECT SEQ_IM_GROUP.nextval FROM DUAL
SELECT SEQ_SAMPLE.nextval FROM DUAL
='DD-MON-RR HH.MI.SSXFF AM TZR' NLS_DUAL_CURRENCY='$' NLS_COMP='
SELECT SEQ_BIZ_SEARCHER.nextval FROM DUAL
SELECT SEQ_OFFER_DRAFT.nextval FROM DUAL
SELECT SEQ_SAMPLE_GROUP.nextval FROM DUAL
DD-MON-RR HH.MI.SSXFF AM TZR' NLS_DUAL_CURRENCY='$' NLS_COMP='BI
SELECT SEQ_CMNTY_USER_MESSAGE.nextval FROM DUAL
SELECT SYSDATE FROM DUAL
SELECT SEQ_SMS_USER.nextval FROM DUAL
IMESTAMP_TZ_FORMAT='DD-MON-RR HH.MI.SSXFF AM TZR' NLS_DUAL_CURRE
SELECT SEQ_COMPANY_DRAFT.nextval FROM DUAL
SELECT 1 FROM DUAL
SELECT USER FROM DUAL
SELECT DECODE('A','A','1','2') FROM DUAL

18 rows selected.

    除了优化sql外,当然对于热点的表或者索引来说,如果小的话,我们可以考虑cache在内存中,这样可能降低物理读提高sql运行速度(这并不会减少cache buffer chains的访问次数),对于序列,我们可以对序列多设置一些cache。如果是并行服务器环境中的索引对象,并且这个索引是系列递增类型,我们可以考虑反向索引(关于反向索引这里就不过多地做介绍了)。

热点块的其他相关症状

        在数据库中还可能存在一些其他方面的热点块症状,通过v$waitstat的等待可以看出一些端倪,v$waitstat是根据数据缓冲区中各种block的类型(x$bh.class)而分类统计的等待状况。

select">sys@OCN>select * from v$waitstat;

CLASS                   COUNT       TIME
------------------ ---------- ----------
data block            1726977     452542
sort block                  0          0
save undo block             0          0
segment header             40         11
save undo header            0          0
free list                   0          0
extent map                  0          0
1st level bmb             611        112
2nd level bmb              42         13
3rd level bmb               0          0
bitmap block                0          0
bitmap index block          0          0
file header block          13         92
unused                      0          0
system undo header        111         28
system undo block           7          0
undo header              2765        187
undo block                633        156

比如在ASSM表空间出现之前,由于freelist的存在,如果表经常被并发的进程DML,则可能存在大量的data block的等待,或者有free list的等待。那么这个时候我们发现这样的segment之后需要考虑增加freelist数量。再比如经常发生长时间的DML的表被频繁地访问,这样将会造成过多的对回滚段中块的访问,这时可能undo  block 的等待会比较多。那么我们可能需要控制DML的时间长度或者想办法从应用程序入手来解决问题。如果是undo header的等待比较多,没使用undo tablespace 之前,可能需要考虑增加回滚段的数量。

总结

      本文从热点块的原理入手,详细地由oracle的内部结构特征开始介绍了热点块的产生和表现特征。进而阐述了诊断热点对象和找出造成热点对象的sql的方法。并从解决热点问题方面提供了解决方向。

Comments
  • # 回复:深度分析数据库的热点块问题
    MIT
    Posted @ 2004-07-08 1:11 AM
    <quotes>
    除了优化sql外,当然对于热点的表或者索引来说,如果小的话,我们可以考虑cache在内存中,
    </quotes>

    cached in memory would cause buffer cache chains again? 删除
  • # 回复:深度分析数据库的热点块问题
    biti_rainy
    Posted @ 2004-07-08 9:12 AM
    cache 在 内存中依然会有 cache buffer chains 的等待,这句话在这里不确切,只是这样可能会缩短sql执行时间,减少热点的竞争机会,但并不是说减少 cache buffer chains 的访问次数 删除
  • # 回复:深度分析数据库的热点块问题
    prada_gu
    Posted @ 2004-07-08 3:31 PM
    呵呵,BITI就是对internal肯花时间研究呀, :) 删除
  • # 回复:深度分析数据库的热点块问题
    LI2
    Posted @ 2004-07-09 9:52 PM
    下午一不小心把分析数据全删了,结果就出来热点块问题.原先用是choose 方案,我想应该是使用rule 方案之后数据库胡乱用索引造成的.后来重新分析了相关表,故障就消失了.
    看来胡乱管理数据库是不行了,还要好好学习. 删除
  • # 回复:深度分析数据库的热点块问题
    LoveWinter
    Posted @ 2004-07-13 9:31 PM
    有几个问题:
    1. 10G的数据库,检测过程中发现'cache buffers chains' 很多, 用文中的SQL
    查出来一些热点表和索引,但是它们所在
    的表空间都是ASSM管理的,是不是不能
    再人为的调整freelist了?

    2. 9i/10G 的数据库,已经启用了undo tablespace,但是undo header的等待
    依然比较多? 怎样调整?

    谢谢! 删除
  • # 回复:深度分析数据库的热点块问题
    biti_rainy
    Posted @ 2004-07-15 9:52 PM
    你的这个问题,恐怕只能从业务和sql的角度来考虑了,数据库本身难以有什么调整的 删除
  • # 回复:深度分析数据库的热点块问题
    Yong Huang
    Posted @ 2004-07-21 2:55 AM
    Excellent article. One suggestion: change order by misses to order by sleeps when you select from x$bh. The statement "gets表示总共有这么多次请求,misses表示请求失败的次数" is not accurate. Gets does not include Spin Gets. Besides, Steve Adams' book p.23 says if the process sleeps and wakes up later (and/or does this multiple times), the numbers gets, misses and spin gets will NOT be incremented any more. That's probably the reason people use sleeps to indirectly measure the contention to acquire latches. 删除
  • # 回复:深度分析数据库的热点块问题
    biti_rainy
    Posted @ 2004-07-21 9:25 AM
    ok.谢谢 yong huang 的提醒,我做过修正 删除
  • # 回复:深度分析数据库的热点块问题
    Yong Huang
    Posted @ 2004-07-21 10:10 PM
    Correction for my "correction". I said "change order by misses to order by sleeps when you select from x$bh". I should have said "... when you select from v$latch(_children)". I know you spotted this minor error. But sorry for that. 删除
  • # 回复:深度分析数据库的热点块问题
    biti_rainy
    Posted @ 2004-07-22 2:34 AM
    其实若你所言,使用 misses 或者 sleeps ,未必存在哪个一定更准确,若sleeps极少(或者说进程总是多次misses而很少sleep),则misses相对准确一些,若sleeps很多,则sleeps相对准确一些。但是一个进程通常 在获取一个latch的过程中sleeps不会太多,这样使得misses总是经常远大于sleeps,除非 _spin_count 非常的小 或者 系统latch竞争严重到sleeps异常的多的情况下,sleeps才更准确一些

    删除
  • # 回复:深度分析数据库的热点块问题
    Yong Huang
    Posted @ 2004-08-04 12:02 AM
    You have a good point. I'm trying to figure out why most people order by sleeps, instead of gets or misses. What bothers me is the fact that Steve Adams says that after one sleep, none of the numbers except sleeps are incremented. But as you say, if most gets turn out to be successful (i.e. gets lead to obtaining latches), then sleeps are such small numbers that errors (wu4 cha1) become inevitable.

    However, I've never seen anybody mention the potentially very useful column wait_time in v$latch(_children), new in 9i. I believe wait_time is a better indicator. Although I doubt it, I don't know if it suffers the same limitation as most other columns, i.e. the number freezes after one sleep. 删除


bitirainy 发表于:2004.09.08 16:59 ::分类: ( Oracle is anything ) ::阅读:(983次) :: 评论 (0)
===========================================================
关于oracle怎么保证读一致性
===========================================================

假设这样一种情况,一个大查询开始FTS查询大表 ,然后另外一个session更新了表末尾的某条数据并提交,更新了N次 ,由于多次事务的DML使得该block上的相关XID和LOCK信息早已荡然无存 ,该记录行上的 XID 已经被设置为 0  了,也就是根本找不着  ITL 信息 ,那这样的情况下,oracle 是怎么找出事务的 before  image ?
  
根据 dump 出来的数据是看不出端倪的。 
  
简单一致性验证测试
  
session 1 :
  
SQL> create table t nologging as select rownum a,aa.* from dba_objects aa,dba_objects bb where rownu
m &lt; 1000000;
  
Table created.
  
SQL> create index t_index on t(a) nologging;
  
Index created.
  
SQL> select max(a) from t;
  
     MAX(A)
----------
     999999
SQL> declare
   2  v1 number;
   3  v2 number;
   4  begin
   5  v1 := 0;
   6  v2:= 0;
   7  for c in (select * from t) loop
   8  if c.a > v1 then
   9  v1:=c.a;
  10  end if;
  11  end loop;
  12  dbms_output.put_line(to_char(v1));
  13  end;
  14  /
  
这时我打开 session 2 :
session 2:  
SQL> update t set a = a+1 where a = (select max(a) from t);
  
1 row updated.
  
SQL> commit;
  
Commit complete.
  
SQL>  update t set a = a+1 where a = (select max(a) from t);
  
1 row updated.
  
SQL> commit;
  
Commit complete.
  
SQL> update t set a = a+1 where a = (select max(a) from t);
  
1 row updated.
  
SQL> commit;
  
Commit complete.
  
SQL> update t set a = a+1 where a = (select max(a) from t);
  
1 row updated.
  
SQL> commit;
  
Commit complete.
  
SQL>  update t set a = a+1 where a = (select max(a) from t);
  
1 row updated.
  
SQL> commit;
  
Commit complete.
  
SQL>  update t set a = a+1 where a = (select max(a) from t);
  
1 row updated.
  
SQL> commit;
  
Commit complete.
  
SQL>  update t set a = a+1 where a = (select max(a) from t);
  
1 row updated.
  
SQL> commit;
  
Commit complete.
  
SQL> select max(a) from t;
  
     MAX(A)
----------
    1000006
  
SQL>  update t set a = a+1 where a = (select max(a) from t);
  
1 row updated.
  
SQL>  commit;
  
Commit complete.
  
SQL>  update t set a = a+1 where a = (select max(a) from t);
  
1 row updated.
  
SQL>  commit;
  
Commit complete.
  
SQL>  update t set a = a+1 where a = (select max(a) from t);
  
1 row updated.
  
SQL>  commit;
  
Commit complete.
  
SQL>  update t set a = a+1 where a = (select max(a) from t);
  
1 row updated.
  
SQL>  commit;
  
Commit complete.
  
SQL>  
  
这时session 1 依然还没有执行完
  
一会输出结果 :
999999
  
PL/SQL procedure successfully completed.
  
如果在session 2  更新数据后再 dump 数据将会看见 dump 结果中不能提供足够的信息。那我就疑惑了,这个时候一致性是怎么来维护的,淡然 before image 应该是来自  回滚段 ,但是,oracle这个时候怎么知道这行数据跟回滚段哪部分数据相关?这个信息保存在哪里? 难道 dump 出来的内容实际上掩盖了一些内容?
  
也就是说即使我们读到某个 行 的数据,上面没有lock,没有XID信息,但是oracle 依然会去找回滚段中有没有相关的before image,并确认block 是干净的 ?这个代价是不是太高了?

 

另外一个测试,可以看出
  
同一个BLOCK中的数据,只要有一条被更新,即使只查询没有被更新的数据,甚至通过索引直接由  ROWID 查询数据,我们可以想象为根本就不看被更新的数据一眼,但是,依然会产生 CR  block  
  
SQL> create table t1(a number, b number);
  
Table created.
  
SQL> insert into t1 select rownum ,rownum from t where rownum &lt; 11;
  
10 rows created.
  
SQL> create index t1_index on t1(a);
  
Index created.
  
SYS在这里做一个更新b字段
SQL> update rainy.t1  set b = 0 where a = 1;
  
1 row updated.
  
SQL>  select count(*) from x$bh where state = 3;  
  
   COUNT(*)
----------
         50
  

  
SQL> select * from t1 where a = 2;
  
          A          B
---------- ----------
          2          2
  
          A          B
---------- ----------
          2          2
  
Execution Plan
----------------------------------------------------------
    0      SELECT STATEMENT Optimizer=CHOOSE
    1    0   TABLE ACCESS (BY INDEX ROWID) OF 'T1'
    2    1     INDEX (RANGE SCAN) OF 'T1_INDEX' (NON-UNIQUE)
  
Statistics
----------------------------------------------------------
           0  recursive calls
           0  db block gets
           5  consistent gets
           0  physical reads
          52  redo size
         424  bytes sent via SQL*Net to client
         503  bytes received via SQL*Net from client
           2  SQL*Net roundtrips to/from client
           0  sorts (memory)
           0  sorts (disk)
           1  rows processed
  
SQL>  
  
SQL>  select * from t1 where a = 2;
  
          A          B
---------- ----------
          2          2
  
SYS查询
SQL>  select count(*) from x$bh where state = 3;  
  
   COUNT(*)
----------
         51
  

  
SQL>  select * from t1 where a = 2;
  
          A          B
---------- ----------
          2          2
  
SQL>  
  
SYS查询
SQL> select count(*) from x$bh where state = 3;  
  
   COUNT(*)
----------
         52
  
SQL>  

  
SQL>  select * from t1 where a > 8;
  
          A          B
---------- ----------
          9          9
         10         10
  
Execution Plan
----------------------------------------------------------
    0      SELECT STATEMENT Optimizer=CHOOSE
    1    0   TABLE ACCESS (BY INDEX ROWID) OF 'T1'
    2    1     INDEX (RANGE SCAN) OF 'T1_INDEX' (NON-UNIQUE)
  
Statistics
----------------------------------------------------------
           0  recursive calls
           0  db block gets
           6  consistent gets
           0  physical reads
          52  redo size
         462  bytes sent via SQL*Net to client
         503  bytes received via SQL*Net from client
           2  SQL*Net roundtrips to/from client
           0  sorts (memory)
           0  sorts (disk)
           2  rows processed
  
SQL>  
  
SYS查询
SQL> select count(*) from x$bh where state = 3;  
  
   COUNT(*)
----------
         54
  
SQL>  

  
这证明一个过程,那就是 CR 的产生,我们原来以为是这样的流程:
查看到某行,发现行上有XID信息(XID>0),根据XID找到ITL,根据ITL找到回滚段信息……  产生 CR  BLOCK
  
现在看来这是错误的!
  
CR并不是因为查看到的数据被更改过才去产生一致读,而是只要block里面存在活动事务、或者 block SCN > 查询SCN 都会产生 CR block
若存在没有活动的事务但是还没有产生commit SCN 的(ITL 中 fsc 项为0) 则先产生 delay  block  cleanout
  
那关于 CR 到底怎么从 block 定位到 undo block ,至今还没有很明确的答案(有ITL存在很容易理解,但是没有呢?), transaction table 中也只是一个  链表头 的结构……  

 

搞了半天,发现

  
ITL 中
0x1 xid: 0x0005.040.00000117 uba: 0x0080233e.01f9.11 --U- 10 fsc 0x0000.0272a3b7  
  
XID 是当前事务的事务表信息
而 uba 该事务的回滚段的地址

忽略了这样一个事实,那就是 回滚段中记录的关于block的改变,除了数据的改变还有 ITL 的改变

  
所以根据当前 ITL 信息可以产生CR block,CR block 里面包含了原来的ITL信息, 这样根据before ITL 信息产生 CR block ,这个CR block 里面包含了before  ITL ,这样又继续重复这个过程,直到 commit scn 都小于 查询SCN 的 block 或者不再有ITL为止,产生 CR 的过程结束,若没有找到就返回 1555 错误
  
当然,不一定非要是查询到的记录上有XID 信息,这依然成立,行上的 lock 主要是针对 DML 起作用

 

顺便补一句也许是废话的话
对于select 是以时间点为准的,对于 DML 不是以时间点为准的,也就是说current  mode ,当前看见的是什么就是什么,而不管时间点的一致性和过去是什么
  
SQL> select count(*) from t;
  
   COUNT(*)
----------
     999999
  
SQL>  
SQL> desc t
  Name                                                  Null?    Type
  ----------------------------------------------------- -------- ----------------------
  A                                                              NUMBER
  OWNER                                                          VARCHAR2(30)
  OBJECT_NAME                                                    VARCHAR2(128)
  SUBOBJECT_NAME                                                 VARCHAR2(30)
  OBJECT_ID                                                      NUMBER
  DATA_OBJECT_ID                                                 NUMBER
  OBJECT_TYPE                                                    VARCHAR2(18)
  CREATED                                                        DATE
  LAST_DDL_TIME                                                  DATE
  TIMESTAMP                                                      VARCHAR2(19)
  STATUS                                                         VARCHAR2(7)
  TEMPORARY                                                      VARCHAR2(1)
  GENERATED                                                      VARCHAR2(1)
  SECONDARY                                                      VARCHAR2(1)
  
SQL>  select max(a) from t;
  
     MAX(A)
----------
    1000010
  
SQL> select rowid from t where a = 1000010;
  
ROWID
------------------
AAAHhXAAJAAAHinAAX
  
该表无索引!
打开session 1 执行
SQL>  update t set owner = 'QQQQQQ'  where a = 1000010 ;
  
立即打开session 2 执行
  
SQL>  update  t set owner = 'WWWWWW' ,a = 1000011 where rowid = 'AAAHhXAAJAAAHinAAX';
  
1 row updated.
  
SQL> commit;
  
Commit complete.
  
这是 session 1 还在执行的过程中,一会就返回结果:
SQL>  update t set owner = 'QQQQQQ'  where a = 1000010 ;
  
0 rows updated.
  
SQL>  
  
这就是  query  mode 和 current mode 的重大差异
query mode  --------  consistent gets
current mode -------- db block gets


bitirainy 发表于:2004.09.08 16:59 ::分类: ( Oracle is anything ) ::阅读:(581次) :: 评论 (0)
===========================================================
关于freelist的一些深入的探讨
===========================================================

 

http://www.cnoug.org/viewthread.php?tid=367&highlight=freelist%2Bbiti_rainy

http://www.itpub.net/showthread.php?threadid=90981

 

 


bitirainy 发表于:2004.09.08 16:58 ::分类: ( Oracle is anything ) ::阅读:(779次) :: 评论 (0)
===========================================================
关于回滚段与事务的一些探讨
===========================================================

http://www.cnoug.org/viewthread.php?tid=645

http://www.itpub.net/showthread.php?threadid=100750


bitirainy 发表于:2004.09.08 16:58 ::分类: ( Oracle is anything ) ::阅读:(1190次) :: 评论 (0)
===========================================================
v$sqlarea,v$sql,v$sqltext的区别和联系
===========================================================

v$sqltext 存储的是完整的SQL,SQL被分割

SQL> desc v$sqltext
Name Null? Type
----------------------------------------- -------- ----------------------------
ADDRESS RAW(4) ---------
HASH_VALUE NUMBER --------- 和 address 一起唯一标志一条sql
COMMAND_TYPE NUMBER
PIECE NUMBER ---------- 分片之后的顺序编号
SQL_TEXT VARCHAR2(64) -------------- 注意长度



v$sqlarea --------- 存储的SQL 和一些相关的信息,比如累计的执行次数,逻辑读,物理读等统计信息
SQL> desc v$sqlarea
Name Null? Type
----------------------------------------- -------- ----------------------------
SQL_TEXT VARCHAR2(1000)
SHARABLE_MEM NUMBER
PERSISTENT_MEM NUMBER
RUNTIME_MEM NUMBER
SORTS NUMBER
VERSION_COUNT NUMBER
LOADED_VERSIONS NUMBER
OPEN_VERSIONS NUMBER
USERS_OPENING NUMBER
FETCHES NUMBER
EXECUTIONS NUMBER
USERS_EXECUTING NUMBER
LOADS NUMBER
FIRST_LOAD_TIME VARCHAR2(38)
INVALIDATIONS NUMBER
PARSE_CALLS NUMBER
DISK_READS NUMBER
BUFFER_GETS NUMBER
ROWS_PROCESSED NUMBER
COMMAND_TYPE NUMBER
OPTIMIZER_MODE VARCHAR2(25)
PARSING_USER_ID NUMBER
PARSING_SCHEMA_ID NUMBER
KEPT_VERSIONS NUMBER
ADDRESS RAW(4)
HASH_VALUE NUMBER
MODULE VARCHAR2(64)
MODULE_HASH NUMBER
ACTION VARCHAR2(64)
ACTION_HASH NUMBER
SERIALIZABLE_ABORTS NUMBER
CPU_TIME NUMBER
ELAPSED_TIME NUMBER
IS_OBSOLETE VARCHAR2(1)
CHILD_LATCH NUMBER




v$sql ----------
存储的是具体的SQL 和执行计划相关信息,实际上,v$sqlarea 可以看做 v$sql 根据 sqltext 等 做了 group by 之后的信息


SQL> desc v$sql
Name Null? Type
----------------------------------------- -------- ----------------------------
SQL_TEXT VARCHAR2(1000)
SHARABLE_MEM NUMBER
PERSISTENT_MEM NUMBER
RUNTIME_MEM NUMBER
SORTS NUMBER
LOADED_VERSIONS NUMBER
OPEN_VERSIONS NUMBER
USERS_OPENING NUMBER
FETCHES NUMBER
EXECUTIONS NUMBER
USERS_EXECUTING NUMBER
LOADS NUMBER
FIRST_LOAD_TIME VARCHAR2(38)
INVALIDATIONS NUMBER
PARSE_CALLS NUMBER
DISK_READS NUMBER
BUFFER_GETS NUMBER
ROWS_PROCESSED NUMBER
COMMAND_TYPE NUMBER
OPTIMIZER_MODE VARCHAR2(10)
OPTIMIZER_COST NUMBER
PARSING_USER_ID NUMBER
PARSING_SCHEMA_ID NUMBER
KEPT_VERSIONS NUMBER
ADDRESS RAW(4)
TYPE_CHK_HEAP RAW(4)
HASH_VALUE NUMBER
PLAN_HASH_VALUE NUMBER
CHILD_NUMBER NUMBER ---------- 注意这个
MODULE VARCHAR2(64)
MODULE_HASH NUMBER
ACTION VARCHAR2(64)
ACTION_HASH NUMBER
SERIALIZABLE_ABORTS NUMBER
OUTLINE_CATEGORY VARCHAR2(64)
CPU_TIME NUMBER
ELAPSED_TIME NUMBER
OUTLINE_SID NUMBER -------------- 注意这里跟 outline 有关
CHILD_ADDRESS RAW(4)
SQLTYPE NUMBER
REMOTE VARCHAR2(1)
OBJECT_STATUS VARCHAR2(19)
LITERAL_HASH_VALUE NUMBER
LAST_LOAD_TIME VARCHAR2(38)
IS_OBSOLETE VARCHAR2(1)
CHILD_LATCH NUMBER


另外注意这个
QL> desc v$sql_plan
Name Null? Type
----------------------------------------- -------- ----------------------------
ADDRESS RAW(4)
HASH_VALUE NUMBER
CHILD_NUMBER NUMBER ------------ 注意这个和 v$sql 里面的相同字段
OPERATION VARCHAR2(60)
OPTIONS VARCHAR2(60)
OBJECT_NODE VARCHAR2(20)
OBJECT# NUMBER
OBJECT_OWNER VARCHAR2(30)
OBJECT_NAME VARCHAR2(64)
OPTIMIZER VARCHAR2(40)
ID NUMBER
PARENT_ID NUMBER
DEPTH NUMBER
POSITION NUMBER
SEARCH_COLUMNS NUMBER
COST NUMBER
CARDINALITY NUMBER
BYTES NUMBER
OTHER_TAG VARCHAR2(70)
PARTITION_START VARCHAR2(10)
PARTITION_STOP VARCHAR2(10)
PARTITION_ID NUMBER
OTHER VARCHAR2(4000)
DISTRIBUTION VARCHAR2(40)
CPU_COST NUMBER
IO_COST NUMBER
TEMP_SPACE NUMBER
ACCESS_PREDICATES VARCHAR2(4000)
FILTER_PREDICATES VARCHAR2(4000)


实际上,看起来同样的一句SQL ,往往具有不同的执行计划,如果是不同的数据库用户,那么相应的涉及的 对象 可能都不一样,注意v$sql 中
OBJECT# NUMBER
OBJECT_OWNER VARCHAR2(30)
OBJECT_NAME VARCHAR2(64)
OPTIMIZER VARCHAR2(40)

即使是相同的数据库用户,若 session 的优化模式、session 级的参数 等不一样,执行计划也能不同。所以即使相同的sql,也可能具有不同的执行计划!

v$sql join to v$sql_plan 就代表了具体的sql的执行计划,通过下面3个字段做连接

ADDRESS RAW(4)
HASH_VALUE NUMBER
CHILD_NUMBER NUMBER


而v$SQLAREA 忽略了 执行计划 等差异,只是在形式上sql文本看起来一样!相当于做了个聚合,是多个不同执行计划的sql的聚合和累计信息


bitirainy 发表于:2004.09.08 16:57 ::分类: ( Oracle is anything ) ::阅读:(1024次) :: 评论 (0)
===========================================================
怎样去定位你所未知的对象
===========================================================
前几天看见有人问怎么查找某个view引用了什么表,或者某个表被那些对象引用,偶当时没有注意,今天朋友问到了.
    我想是有view表达这种依赖关系的,于是,偶打开 dba studio ,进入 方案---视图---sys,然后快速浏览 dba_* 这样的view,根据经验判断很多view不是所需要的,终于看到一个view : dba_dependencies,英文不大利索,猜测可能是。

SQL> desc dba_dependencies
Name Null? Type
----------------------------------------- -------- ----------------------------
OWNER NOT NULL VARCHAR2(30)
NAME NOT NULL VARCHAR2(30)
TYPE VARCHAR2(17)
REFERENCED_OWNER VARCHAR2(30)
REFERENCED_NAME VARCHAR2(64)
REFERENCED_TYPE VARCHAR2(17)
REFERENCED_LINK_NAME VARCHAR2(128)
DEPENDENCY_TYPE VARCHAR2(4)

select u.name, o.name,
decode(o.type#, 0, 'NEXT OBJECT', 1, 'INDEX', 2, 'TABLE', 3, 'CLUSTER',
4, 'VIEW', 5, 'SYNONYM', 6, 'SEQUENCE', 7, 'PROCEDURE',
8, 'FUNCTION', 9, 'PACKAGE', 10, 'NON-EXISTENT',
11, 'PACKAGE BODY', 12, 'TRIGGER',
13, 'TYPE', 14, 'TYPE BODY',
28, 'JAVA SOURCE', 29, 'JAVA CLASS', 56, 'JAVA DATA',
'UNDEFINED'),
decode(po.linkname, null, pu.name, po.remoteowner), po.name,
decode(po.type#, 0, 'NEXT OBJECT', 1, 'INDEX', 2, 'TABLE', 3, 'CLUSTER',
4, 'VIEW', 5, 'SYNONYM', 6, 'SEQUENCE', 7, 'PROCEDURE',
8, 'FUNCTION', 9, 'PACKAGE', 10, 'NON-EXISTENT',
11, 'PACKAGE BODY', 12, 'TRIGGER',
13, 'TYPE', 14, 'TYPE BODY',
28, 'JAVA SOURCE', 29, 'JAVA CLASS', 56, 'JAVA DATA',
'UNDEFINED'),
po.linkname,
decode(d.property, 2, 'REF', 'HARD')
from sys.obj$ o, sys.disk_and_fixed_objects po, sys.dependency$ d, sys.user$ u,
sys.user$ pu
where o.obj# = d.d_obj#
and o.owner# = u.user#
and po.obj# = d.p_obj#
and po.owner# = pu.user#



    到这里基本已经99% 确认是了,再测试,果然是这个view记录了相关的依赖关系。

    下面再说一个我常用的办法,那么多东西要记住很麻烦,我也记不了那么多,有时觉得打开 OEM /dba studio 也很麻烦于是,假如我要查有关 role 相关的东西,于是

SQL> select object_name from dba_objects where object_name like '%ROLE%';

OBJECT_NAME
--------------------------------------------------------------------------------
DBA_ROLES
DBA_ROLE_PRIVS
DEFROLE$
I_DEFROLE1
JIS$ROLE_TRIGGER$
ROLE_ROLE_PRIVS
ROLE_SYS_PRIVS
ROLE_TAB_PRIVS
SESSION_ROLES
USER_ROLE_PRIVS
DBA_ROLES

OBJECT_NAME
--------------------------------------------------------------------------------
DBA_ROLE_PRIVS
ROLE_ROLE_PRIVS
ROLE_SYS_PRIVS
ROLE_TAB_PRIVS
SESSION_ROLES
USER_ROLE_PRIVS
VBZ$RG_ROLE_OIDX
VBZ$ROLE_GRANTS

19 rows selected.

     大家在这里结果里面一看,是不是就知道哪些视图记录了相关内容?最多,在 desc 看一下,或者再实验来检验一下,这时查文档也可以,很容易就定位了,这是个人在不熟悉view的时候长期使用的一种办法,说起来很简单,但是却很有效!!!!

bitirainy 发表于:2004.09.08 16:57 ::分类: ( Oracle is anything ) ::阅读:(974次) :: 评论 (0)
===========================================================
跟事务相关的日志生成量的对比
===========================================================
: 插入相同数量记录的单条提交和成皮提交之间的对比
我们可以看出分别是 25112152 - 8024588 = 17087564
8024588 - 13384 = 8011204
对比相差达到9M


SQL> show parameters transaction_auditing

NAME TYPE VALUE
------------------------------------ ------- --------------------
transaction_auditing boolean TRUE
SQL>


SQL> select name,value from v$sysstat where name like 'redo size';

NAME VALUE
---------------------------------------------------------------- ----------
redo size 13384

SQL> begin
2 for i in 1..25835 loop
3 insert into rainy.t select * from dba_objects where rownum = 1;
4 end loop;
5 end;
6 /

PL/SQL procedure successfully completed.

SQL> commit;

Commit complete.

SQL> select name,value from v$sysstat where name like 'redo size';

NAME VALUE
---------------------------------------------------------------- ----------
redo size 8024588

SQL> begin
2 for i in 1..25835 loop
3 insert into rainy.t select * from dba_objects where rownum = 1;
4 commit;
5 end loop;
6 end;
7 /

PL/SQL procedure successfully completed.

SQL> select name,value from v$sysstat where name like 'redo size';

NAME VALUE
---------------------------------------------------------------- ----------
redo size 25112152


2 : 关于设置 transaction_auditing = false/true 前后的对比
13892572 - 12332 = 13880240

对比 1 中相同状况下的结果 17087564 ,可以看出体制生成两相差了大约3M

SQL> show parameters transaction_auditing

NAME TYPE VALUE
------------------------------------ ------- --------------------
transaction_auditing boolean FALSE
SQL>

SQL> select name,value from v$sysstat where name like 'redo size';

NAME VALUE
---------------------------------------------------------------- ----------
redo size 12332

SQL> begin
2 for i in 1..25835 loop
3 insert into rainy.t select * from dba_objects where rownum = 1;
4 commit;
5 end loop;
6 end;
7 /

PL/SQL procedure successfully completed.

SQL> select name,value from v$sysstat where name like 'redo size';

NAME VALUE
---------------------------------------------------------------- ----------
redo size 13892572



结论:
成批提交和但条提交所产生的日志量差异巨大,成批提交通常远小于单条提交

关于transaction_auditing ,我们先看oracle的解释
If TRANSACTION_AUDITING is true, Oracle generates a special redo record that contains the user logon name, username, the session ID, some operating system information, and client information. For each successive transaction, Oracle generates a record that contains only the session ID. These subsequent records link back to the first record, which also contains the session ID.

These records might be useful if you are using a redo log analysis tool. You can access the records by dumping the redo log.

If TRANSACTION_AUDITING is false, no redo record will be generated

      这个意思是,如果设置为true,则oracle将对事务进行审计,也就是日志里面为事务产生一几乎固定大小的信息,包括用户名、session is、一些操作信息、client信等(通过dump logfile可以看到),这些信息在使用logmnr等工具分析的时候可以有用的。如果设置该参数为false,则在大量的短小事务的情况下将产生不可忽略的日志生成量的差异

bitirainy 发表于:2004.09.08 16:56 ::分类: ( Oracle is anything ) ::阅读:(863次) :: 评论 (0)
===========================================================
基于外部OS验证的数据库用户
===========================================================

采用操作外部系统认证(unix or linux 等 OS)
SQL> show parameters os_authent_prefix

NAME TYPE VALUE
------------------------------------ ----------- ------------------------
os_authent_prefix string OPS$
SQL> CREATE USER "OPS$ORACLE" PROFILE "DEFAULT"
IDENTIFIED EXTERNALLY DEFAULT TABLESPACE "USERS"
TEMPORARY TABLESPACE "TEMP";

User created.

SQL> grant connect to ops$oracle;

Grant succeeded.

SQL> conn /
Connected.
SQL>
SQL> show user
USER is "OPS$ORACLE"
SQL>
SQL> exit
Disconnected from Oracle10i Enterprise Edition Release 10.1.0.0.0 - Beta
With the Partitioning, OLAP and Oracle Data Mining options
[oracle@linux oracle]$ sqlplus /

SQL*Plus: Release 10.1.0.0.0 - Beta on Tue Aug 19 16:04:46 2003

Copyright (c) 1982, 2003, Oracle Corporation. All rights reserved.


Connected to:
Oracle10i Enterprise Edition Release 10.1.0.0.0 - Beta
With the Partitioning, OLAP and Oracle Data Mining options

SQL> show user
USER is "OPS$ORACLE"
SQL>


创建用户的时候名称为 os_authent_prefix + OS NAME
这样当该 os name 登陆数据库的时候只要使用 / 就可以了

通常 userid = user/pass
现在 userid = /

 

windows下稍微有点差异,请去 www.itpub.net 搜索


bitirainy 发表于:2004.09.08 16:56 ::分类: ( Oracle is anything ) ::阅读:(582次) :: 评论 (0)
===========================================================
oracle9i 怎样自动捕获DML 语句
===========================================================

SQL> desc t1
Name Null? Type
---- --------

A NUMBER

SQL> desc t_sql
Name Null? Type
---------------------------
USERNAME VARCHAR2(30)
CLIENT_IP VARCHAR2(20)
SQL_TEXT VARCHAR2(4000)
TABLE_NAME VARCHAR2(30)
OWNER VARCHAR2(30)

SQL> create or replace trigger capt_sql
2 BEFORE DELETE OR INSERT OR UPDATE ON t1
3 declare
4 n number;
5 stmt varchar2(4000);
6 sql_text ora_name_list_t;
7 begin
8 n := ora_sql_txt(sql_text);
9 FOR i IN 1..n LOOP
10 stmt := stmt || sql_text(i);
11 END LOOP;
12
13 insert into t_sql(USERNAME,CLIENT_IP,SQL_TEXT,TABLE_NAME,OWNER)
14 values(user,sys_context('userenv','ip_address'),stmt,'T1','RAINY');
15
16 end;
17 /

Trigger created.

SQL> insert into t1(a) values(1);

1 row created.

SQL> update t1 set a = 2 where a = 1;

1 row updated.

SQL> delete t1;

3 rows deleted.

SQL> commit;

Commit complete.

SQL> select * from t_sql;

USERNAME CLIENT_IP SQL_TEXT TABLE_NAME OWNER
------------------------------ ------------------------------
RAINY 10.1.30.19 insert into t1(a) values(1) T1 RAINY

RAINY 10.1.30.19 update t1 set a = 2 where a =1 T1 RAINY

RAINY 10.1.30.19 delete t1 T1 RAINY


bitirainy 发表于:2004.09.08 16:55 ::分类: ( Oracle is anything ) ::阅读:(813次) :: 评论 (0)
===========================================================
如何查询出是整数的某个字符串
===========================================================

SQL> select 1 from dual where ltrim('12385x2','0123456789') is null;

未选定行

SQL> select 1 from dual where ltrim('123852','0123456789') is null;

1
----------
1


bitirainy 发表于:2004.09.08 16:54 ::分类: ( Oracle is anything ) ::阅读:(901次) :: 评论 (0)
===========================================================
oracle IO的影响因素
===========================================================

 数据库的IO能力,在很多时候会影响到查询的性能,尤其是FTS 的性能,所以对于oracle来说,这会影响到执行计划的选择。

数据库中统计的 physical  reads和 IO 不是一个概念,数据库中物理读统计的是 blocks ,而操作系统中是 io requests ,

一次 request 可能是 128k/256k/1024k 等等数据,当然也可能是几十k 或者更少的,取决于数据库的要求。 io request 次数,简单点说是 table blocks / DB_FILE_MULTIBLOCK_READ_COUNT。

暂时我们不考虑File  system 中os  block不连续的问题(除非经历了大量小文件的创建和删除才可能造成大量os碎片)。

 查看全文
bitirainy 发表于:2004.09.08 16:53 ::分类: ( Oracle is anything ) ::阅读:(1578次) :: 评论 (1)
===========================================================
一道著名的号称微软的试题的oracle解
===========================================================

TABLE如下
日期 收入 支出
2000/3/1 50 30
2000/3/2 45 60
2000/3/5 60 10

能否用SELECT語句得出以下結果

日期 收入 支出 余額
2000/3/1 50 30 20
2000/3/2 45 60 5
2000/3/3 0 0 5
2000/3/4 0 0 5
2000/3/5 60 10 55

这是一个网上广为流传的题目,以前曾经有人说这是微软的试题,这个不去管了,我们先看oracle的解

SQL> select * from test;

A B C
---------- ---------- ----------
1 1 0
2 2 1
4 4 2
6 1 3
8 4 4

已用时间: 00: 00: 00.00
SQL> select tt1.tta,tt1.ttb,tt1.ttc,sum(tt2.ttb) - sum(tt2.ttc) youwant
2 from
3 (
4 select t1.aa tta,decode(t2.a,null,0,t2.b) ttb,decode(t2.a,null,0,t2.c) ttc
5 from
6 (select ((select min(a) from test) + rownum - 1) aa from all_objects
7 where rownum <= (select max(a) - min(a) +1 from test)) t1,
8 test t2
9 where t1.aa = t2.a(+)
10 ) tt1,
11 (
12 select t1.aa tta,decode(t2.a,null,0,t2.b) ttb,decode(t2.a,null,0,t2.c) ttc
13 from
14 (select ((select min(a) from test) + rownum - 1) aa from all_objects
15 where rownum <= (select max(a) - min(a) +1 from test)) t1,
16 test t2
17 where t1.aa = t2.a(+)
18 ) tt2
19 where tt1.tta >= tt2.tta
20 group by tt1.tta,tt1.ttb,tt1.ttc;

TTA TTB TTC YOUWANT
---------- ---------- ---------- ----------
1 1 0 1
2 2 1 2
3 0 0 2
4 4 2 4
5 0 0 4
6 1 3 2
7 0 0 2
8 4 4 2

已选择8行。

已用时间: 00: 00: 00.00

 

816 以上版本使用analistic function 的简化版答案

SQL> select * from test;

A B C
---------- ---------- ----------
5 5 5
2 2 1
4 4 5
6 1 3
8 4 4

SQL> select tt2.tta, tt2.ttb, tt2.ttc, sum(tt2.ttb - tt2.ttc) over(order by tt2.tta) youwant
2 from
3 (
4 select t1.aa tta,decode(t2.a,null,0,t2.b) ttb,decode(t2.a,null,0,t2.c) ttc
5 from
6 (select ((select min(a) from test) + rownum - 1) aa from all_objects
7 where rownum <= (select max(a) - min(a) +1 from test)) t1,
8 test t2
9 where t1.aa = t2.a(+)
10 ) tt2;

TTA TTB TTC YOUWANT
---------- ---------- ---------- ----------
2 2 1 1
3 0 0 1
4 4 5 0
5 5 5 0
6 1 3 -2
7 0 0 -2
8 4 4 -2

已选择7行。

SQL>

 

        其实这类问题都是月度微通解的,主要就是构造连续的数字集合,如果all_objects记录数不足完全可以做个自连接。比如曾经有求某段时间内的工作日天数,这类问题,也是如此解法。

 


bitirainy 发表于:2004.09.08 16:53 ::分类: ( Oracle is anything ) ::阅读:(1035次) :: 评论 (0)
===========================================================
OPTIMIZER_INDEX_COST_ADJ & RBO
===========================================================
 相关的有几个帖子,偶参与了讨论但是不能把别人的文章整理过来,所以给出了连接

http://www.itpub.net/showthread.php?threadid=235922

http://www.eygle.com/sql/OPTIMIZER_INDEX_COST_ADJ.htm


bitirainy 发表于:2004.09.08 16:52 ::分类: ( Oracle is anything ) ::阅读:(855次) :: 评论 (0)
===========================================================
direct insert下不会产生数据的 UNDO
===========================================================
从理论上来说,对于 direct insert 的 undo (rowid)实在没有存在的必要,因为HWM 在移动的过程中,这些block是不能被其他process使用的,那么,意味着,只要记录下该次direct insert所涉及到的 空间的redo 和 undo ,在失败回滚的时候,只需要把这些空间修改为原来的状态就可以而而不用逐个记录去delete。也就是说不管表是否处于nologging下,direct insert都不会产生数据的undo。



为此我将在这里做几个组合实验





SQL> select * from v$version;

BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.1.0.2.0 - Prod
PL/SQL Release 10.1.0.2.0 - Production
CORE 10.1.0.2.0 Production
TNS for Linux: Version 10.1.0.2.0 - Production
NLSRTL Version 10.1.0.2.0 - Production

SQL>
SQL> drop table t;

Table dropped.

SQL> create table t as select * from dba_objects ;

Table created.

SQL> select usn,RSSIZE ,WRITES from v$rollstat;

USN RSSIZE WRITES
---------- ---------- ----------
0 385024 7620
1 253952 11168864
2 2220032 11335220
3 2220032 9668346
4 1171456 8887572
5 122880 84154
6 122880 0
7 1171456 234
8 122880 0
9 122880 0
10 122880 0

11 rows selected.

SQL> insert into t select * from dba_objects;

13197 rows created.

SQL> commit;

Commit complete.

SQL> select usn,RSSIZE ,WRITES from v$rollstat;

USN RSSIZE WRITES
---------- ---------- ----------
0 385024 7620
1 253952 11168864
2 2220032 11336024
3 2220032 9668346
4 2220032 9810066
5 122880 85452
6 122880 0
7 1171456 234
8 122880 0
9 122880 0
10 122880 0

11 rows selected.

SQL> select 9810066 - 8887572 from dual;

9810066-8887572
---------------
922494 正常情况下产生的回滚段信息量
SQL> select 85452 - 84152 from dual;

85452-84152
-----------
1300 undo中这个细微变化目前不详,估计可能是空间的变化导致的


SQL> alter table t nologging;设置表为nologging

Table altered.

SQL> insert /*+ append */ into t select * from dba_objects; direct insert

13197 rows created.

SQL> commit;

Commit complete.

SQL> select usn,RSSIZE ,WRITES from v$rollstat;

USN RSSIZE WRITES
---------- ---------- ----------
0 385024 7620
1 253952 11169498
2 2220032 11339548
3 2220032 9670376
4 2220032 9813076
5 122880 85452
6 122880 0
7 1171456 234
8 122880 0
9 122880 0
10 122880 0

11 rows selected.

SQL> select 9670376 - 9668346 from dual;

9670376-9668346
---------------
2030 发现回滚段信息极少

SQL> select 9813076-9810066 from dual;

9813076-9810066
---------------
3010

SQL>






SQL> alter table t logging; 把表置回logging状态

Table altered.

SQL> select usn,RSSIZE ,WRITES from v$rollstat;

USN RSSIZE WRITES
---------- ---------- ----------
0 385024 7620
1 253952 11173844
2 2220032 11343052
3 2220032 9676700
4 2220032 9816500
5 122880 89668
6 122880 0
7 1171456 234
8 122880 0
9 122880 0
10 122880 0

11 rows selected.

SQL> insert into t select * from dba_objects; 正常插入数据

13197 rows created.

SQL> commit;

Commit complete.

SQL> select usn,RSSIZE ,WRITES from v$rollstat;

USN RSSIZE WRITES
---------- ---------- ----------
0 385024 7620
1 253952 11173844
2 2220032 12266342
3 2220032 9676918
4 2220032 9817356
5 122880 90478
6 122880 0
7 1171456 234
8 122880 0
9 122880 0
10 122880 0

11 rows selected.

SQL> select 12266342 - 11343052 from dual;

12266342-11343052
-----------------
923290 产生的回滚段信息

SQL> insert /*+ append */ into t select * from dba_objects; direct insert

13197 rows created.

SQL> commit;

Commit complete.

SQL> select usn,RSSIZE ,WRITES from v$rollstat;

USN RSSIZE WRITES
---------- ---------- ----------
0 385024 7620
1 253952 11174528
2 2220032 12266768
3 2220032 9678420
4 2220032 9817356
5 122880 92562
6 122880 0
7 1171456 234
8 122880 0
9 122880 0
10 122880 0

11 rows selected.

SQL>

SQL> select 11174528 - 11173844 from dual;

11174528-11173844
-----------------
684

SQL> select 92562 - 90478 from dual;

92562-90478
-----------
2084

SQL>
在这里我们核对任意一个回滚段的生成量,发现几乎很少

由此我们几乎可以下个结论,不管表是否在nologging 下,只要是 direct insert,就不会对数据内容生成undo,也就是不会为insert而记录 rowid






接下来我们进一步实验,在表同样处于 logging 状态下测试

SQL> select * from v$sysstat where name = 'redo size';

STATISTIC# NAME CLASS VALUE STAT_ID
---------- -------------------- ---------- ---------- ----------
133 redo size 2 133173584 1236385760

SQL> insert into t select * from dba_objects;

13197 rows created.

SQL> select * from v$sysstat where name = 'redo size';

STATISTIC# NAME CLASS VALUE STAT_ID
---------- -------------------- ---------- ---------- ----------
133 redo size 2 137974492 1236385760

SQL> select 137974492 - 133173584 from dual;

137974492-133173584
-------------------
4800908 正常插入产生的日志

SQL> roll;
Rollback complete.
SQL> select * from v$sysstat where name = 'redo size';

STATISTIC# NAME CLASS VALUE STAT_ID
---------- -------------------- ---------- ---------- ----------
133 redo size 2 140087680 1236385760

SQL> select 140087680 - 137974492 from dual;

140087680-137974492
-------------------
2113188 正常插入后回滚所产生的日志

SQL> insert /*+ append */ into t select * from dba_objects;

13197 rows created.

SQL> select * from v$sysstat where name = 'redo size';

STATISTIC# NAME CLASS VALUE STAT_ID
---------- -------------------- ---------- ---------- ----------
133 redo size 2 141531644 1236385760

SQL> select 141531644 - 140087680 from dual;

141531644-140087680
-------------------
1443964 direct 插入产生的日志

SQL> roll;
Rollback complete.
SQL> select * from v$sysstat where name = 'redo size';

STATISTIC# NAME CLASS VALUE STAT_ID
---------- -------------------- ---------- ---------- ----------
133 redo size 2 141534344 1236385760

SQL> select 141534344 - 141531644 from dual;

141534344-141531644
-------------------
2700 direct插入后回滚产生的日志

SQL>

从这里的实验可以看出,在 direct insert 后回滚数据,实际上并没有进行数据的 删除操作,而是仅仅对空间进行了回收。若是删除,不可能只产生这么少的 redo,这里从另一个侧面证明,即使 logging 下的 direct insert 对于回滚信息,也是不会对数据产生 undo 而仅仅产生空间变化的 undo

 

 

带索引表,表和索引均是logging状态,测试结果及过程如下

----------------------------常规插入-------direct插入

插入日志生成量----------------8350864--------2364484

插入回滚段生成量--------------2343894--------426838

回滚日志生成量----------------4018204--------76032

回滚本身不存在产生回滚-------------------------------------


结论是很显然的,也许这里有人要问,既然direct有这么多好处,那为什么还用常规?
因为920前的版本sqlldr direct导致trigger无用、函数无用
direct直接在hwm上移动而不使用delete删除释放的空间可能导致空间浪费
direct的时候,据说同一个extent只能由一个进程使用(未测试,目前无LMT表空间环境)

direct + nologging 由于不产生数据日志导致恢复会出现问题

SQL> truncate table t;

Table truncated.

SQL> create index t_index on t(object_id);

Index created.

SQL> col name format a20
SQL> select usn,RSSIZE ,WRITES from v$rollstat;

USN RSSIZE WRITES
---------- ---------- ----------
0 385024 7620
1 516096 14166140
2 1171456 17003930
3 2220032 13918700
4 1171456 13550540
5 122880 756246
6 122880 0
7 1171456 312
8 122880 0
9 122880 0
10 122880 0

11 rows selected.

SQL> select * from v$sysstat where name = 'redo size';

STATISTIC# NAME CLASS VALUE STAT_ID
---------- -------------------- ---------- ---------- ----------
133 redo size 2 181757168 1236385760

SQL> insert into t select * from dba_objects;

13198 rows created.

SQL> select usn,RSSIZE ,WRITES from v$rollstat;

USN RSSIZE WRITES
---------- ---------- ----------
0 385024 7620
1 516096 14166140
2 4317184 19347824
3 2220032 13918700
4 1171456 13551396
5 122880 756246
6 122880 0
7 1171456 312
8 122880 0
9 122880 0
10 122880 0

11 rows selected.

SQL> select * from v$sysstat where name = 'redo size';

STATISTIC# NAME CLASS VALUE STAT_ID
---------- -------------------- ---------- ---------- ----------
133 redo size 2 190108032 1236385760

SQL> select 19347824 - 17003930 from dual;

19347824-17003930
-----------------
2343894 存在索引,常规插入方式下产生的回滚量

SQL> select 190108032 - 181757168 from dual;

190108032-181757168
-------------------
8350864 存在索引,常规插入方式下产生的日志量

SQL> roll;
Rollback complete.
SQL> select usn,RSSIZE ,WRITES from v$rollstat;

USN RSSIZE WRITES
---------- ---------- ----------
0 385024 7620
1 516096 14166140
2 4317184 19347824
3 2220032 13918700
4 1171456 13551396
5 122880 757102
6 122880 0
7 1171456 312
8 122880 0
9 122880 0
10 122880 0

11 rows selected.

SQL> select * from v$sysstat where name = 'redo size';

STATISTIC# NAME CLASS VALUE STAT_ID
---------- -------------------- ---------- ---------- ----------
133 redo size 2 194126236 1236385760

SQL> select 194126236 - 190108032 from dual;

194126236-190108032
-------------------
4018204 存在索引,常规插入方式下然后回滚所产生的日志量

SQL> truncate table t;

Table truncated.

SQL> select usn,RSSIZE ,WRITES from v$rollstat;

USN RSSIZE WRITES
---------- ---------- ----------
0 385024 7620
1 581632 14185742
2 4317184 19356862
3 2220032 13936438
4 1171456 13566936
5 122880 757102
6 122880 0
7 1171456 312
8 122880 0
9 122880 0
10 122880 0

11 rows selected.

SQL> select * from v$sysstat where name = 'redo size';

STATISTIC# NAME CLASS VALUE STAT_ID
---------- -------------------- ---------- ---------- ----------
133 redo size 2 194287336 1236385760

SQL> insert /*+ append */ into t select * from dba_objects;

13198 rows created.

SQL> select usn,RSSIZE ,WRITES from v$rollstat;

USN RSSIZE WRITES
---------- ---------- ----------
0 385024 7620
1 2088960 14612580
2 4317184 19356862
3 2220032 13936438
4 1171456 13569090
5 122880 757102
6 122880 0
7 1171456 312
8 122880 0
9 122880 0
10 122880 0

11 rows selected.

SQL> select * from v$sysstat where name = 'redo size';

STATISTIC# NAME CLASS VALUE STAT_ID
---------- -------------------- ---------- ---------- ----------
133 redo size 2 196651820 1236385760

SQL> select 14612580 - 14185742 from dual;

14612580-14185742
-----------------
426838 存在索引,direct插入方式下产生的回滚量

SQL> select 196651820 - 194287336 from dual;

196651820-194287336
-------------------
2364484 存在索引,direct插入方式下产生的日志量

SQL> roll;
Rollback complete.
SQL> select usn,RSSIZE ,WRITES from v$rollstat;

USN RSSIZE WRITES
---------- ---------- ----------
0 385024 7620
1 2088960 14612580
2 4317184 19360722
3 2220032 13936438
4 1171456 13569946
5 122880 757908
6 122880 0
7 1171456 312
8 122880 0
9 122880 0
10 122880 0

11 rows selected.

SQL> select * from v$sysstat where name = 'redo size';

STATISTIC# NAME CLASS VALUE STAT_ID
---------- -------------------- ---------- ---------- ----------
133 redo size 2 196727852 1236385760

SQL> select 196727852 - 196651820 from dual;

196727852-196651820
-------------------
76032 存在索引,direct插入方式下回滚产生的日志量

SQL>


bitirainy 发表于:2004.09.08 16:51 ::分类: ( Oracle is anything ) ::阅读:(553次) :: 评论 (0)
===========================================================
oracle内存分配与调整
===========================================================

l       前言

对于oracle的内存的管理,截止到9iR2,都是相当重要的环节,管理不善,将可能给数据库带来严重的性能问题。下面我们将一步一步就内存管理的各个方面进行探讨。

 

l       概述

oracle的内存可以按照共享和私有的角度分为系统全局区和进程全局区,也就是SGAPGA(process global area or private global area)。对于SGA区域内的内存来说,是共享的全局的,在UNIX上,必须为oracle设置共享内存段(可以是一个或者多个),因为oracleUNIX上是多进程;而在WINDOWSoracle是单进程(多个线程),所以不用设置共享内存段。PGA是属于进程(线程)私有的区域。在oracle使用共享服务器模式下(MTS,PGA中的一部分,也就是UGA会被放入共享内存large_pool_size中。

对于SGA部分,我们通过sqlplus中查询可以看到:

SQL> select * from v$sga;

 

NAME                      VALUE

--------------------              ----------

Fixed Size                   454032

Variable Size                 109051904

Database Buffers              385875968

Redo Buffers                  667648

 

Fixed Size

oracle 的不同平台和不同版本下可能不一样,但对于确定环境是一个固定的值,里面存储了SGA各部分组件的信息,可以看作引导建立SGA的区域

 

Variable Size

包含了shared_pool_sizejava_pool_sizelarge_pool_size等内存设置和用于管理数据缓冲区等内存结构的hash table、块头信息(比如x$bh消耗内存)

 

Database Buffers

    指数据缓冲区,在8i中包含default poolbuffer_pool_keepbuffer_pool_recycle三部分内存。在9i中包含db_cache_sizedb_keep_cache_sizedb_recycle_cache_sizedb_nk_cache_size。这里要注意在8i中三部分内存总和为db_block_buffers*db_block_size

 

Redo Buffers

指日志缓冲区,log_buffer。在这里要额外说明一点的是,对于v$parameterv$sgastatv$sga查询值可能不一样。v$parameter里面的值,是指用户在初始化参数文件里面设置的值,v$sgastatoracle实际分配的日志缓冲区大小(因为缓冲区的分配值实际上是离散的,也不是以block为最小单位进行分配的),v$sga里面查询的值,是在oracle分配了日志缓冲区后,为了保护日志缓冲区,设置了一些保护页,通常我们会发现保护页大小大约是11k(不同环境可能不一样)。参考如下内容

 

SQL>  select substr(name,1,10) name,substr(value,1,10) value

  2  from v$parameter where name = 'log_buffer';

 

NAME                 VALUE

--------------------     --------------------

log_buffer               524288

 

 

SQL> select * from v$sgastat ;

 

POOL  NAME             BYTES

----------- -------------------

fixed_sga                   454032

buffer_cache                385875968

log_buffer                  656384

 

SQL> select * from v$sga;

 

NAME                     VALUE

--------------------              ----------

Fixed Size                  454032

Variable Size                109051904

Database Buffers             385875968

Redo Buffers                667648

 

关于各部分内存的作用,参考oracle体系结构,在此不再叙述。

 

l       SGA的大小

那么我们现在来考察内存参数的设置。实际上,对于特定的环境,总是存在着不同的最优设置的,没有任何一种普遍适用的最优方案。但为什么在这里我们还要来谈设置这个话题呢,那仅仅是出于一个目的,避免过度的犯错误。事实上,在任何一个生产系统正式投入使用之前,我们不拥有任何系统运行信息让我们去调整,这样就只有两种可能,一是根据文档推荐设置,另外一种就是根据经验设置。相对来说,根据经验的设置比根据文档的设置要可靠一些。尤其是那些24*7的系统,我们更要减少错误的发生。那么我们尝试去了解不同的系统不同的应用的具体设置情况,从而提供一个参照信息给大家。

为了得出一个参照设置,我们就必须假定一个参照环境。以下所有设置我们基于这样一个假定,那就是硬件服务器上只考虑存在操作系统和数据库,在这个单一的环境中,我们来考虑内存的设置。

在设置参数之前呢,我们首先要问自己几个问题

一:物理内存多大

二:操作系统估计需要使用多少内存

三:数据库是使用文件系统还是裸设备

四:有多少并发连接

五:应用是OLTP类型还是OLAP类型

根据这几个问题的答案,我们可以粗略地为系统估计一下内存设置。那我们现在来逐个问题地讨论,首先物理内存多大是最容易回答的一个问题,然后操作系统估计使用多少内存呢?从经验上看,不会太多,通常应该在200M以内(不包含大量进程PCB)。

接下来我们要探讨一个重要的问题,那就是关于文件系统和裸设备的问题,这往往容易被我们所忽略。操作系统对于文件系统,使用了大量的buffer来缓存操作系统块。这样当数据库获取数据块的时候,虽然SGA中没有命中,但却实际上可能是从操作系统的文件缓存中获取的。而假如数据库和操作系统支持异步IO,则实际上当数据库写进程DBWR写磁盘时,操作系统在文件缓存中标记该块为延迟写,等到真正地写入磁盘之后,操作系统才通知DBWR写磁盘完成。对于这部分文件缓存,所需要的内存可能比较大,作为保守的估计,我们应该考虑在 0.2——0.3 倍内存大小。但是如果我们使用的是裸设备,则不考虑这部分缓存的问题。这样的情况下SGA就有调大的机会。

关于数据库有多少并发连接,这实际上关系到PGA的大小(MTS下还有large_pool_size)。事实上这个问题应该说还跟OLTP类型或者OLAP类型相关。对于OLTP类型oracle倾向于可使用MTS,对于OLAP类型使用独立模式,同时OLAP还可能涉及到大量的排序操作的查询,这些都影响到我们内存的使用。那么所有的问题综合起来,实际上主要反映在UGA的大小上。UGA主要包含以下部分内存设置

SQL> show parameters area_size

NAME                                 TYPE         VALUE

------------------------------------               -------          -------------

bitmap_merge_area_size                   integer         1048576

create_bitmap_area_size                   integer         8388608

hash_area_size                           integer         131072

sort_area_size                            integer         65536

SQL>

 

在这部分内存中我们最关注的通常是sort_area_size,这是当查询需要排序的时候,数据库会话将使用这部分内存进行排序,当内存大小不足的时候,使用临时表空间进行磁盘排序。由于磁盘排序效率和内存排序效率相差好几个数量级,所以这个参数的设置很重要。这四个参数都是针对会话进行设置的,是单个会话使用的内存的大小,而不是整个数据库使用的。偶尔会看见有人误解了这个参数以为是整个数据库使用的大小,这是极其严重的错误。假如设置了MTS,则UGA被分配在large_pool_size,也就是说放在了共享内存里面,不同进程(线程)之间可以共享这部分内存。在这个基础上,我们假设数据库存在并发执行server  process100个,根据上面我们4个参数在oracle8.1.7下的默认值,我们来计算独立模式下PGA的大致大小。由于会话并不会经常使用create_bitmap_area_sizebitmap_merge_area_size,所以我们通常不对四个参数求和。在考虑到除这四个参数外会话所保存的变量、堆栈等信息,我们估计为2M,则100个进程最大可能使用200MPGA

现在,根据上面这些假定,我们来看SGA实际能达到多少内存。在1G的内存的服务器上,我们能分配给SGA的内存大约为400—500M。若是2G的内存,大约可以分到1G的内存给SGA8G的内存可以分到5G的内存给SGA。当然我们这里是以默认的排序部分内存sort_area_size=64k进行衡量的,假如我们需要调大该参数和hash_area_size等参数,然后我们应该根据并发的进程的数量,来衡量考虑这个问题。

事实上,通常我们更习惯通过直观的公式化来表达这样的问题:

OS使用内存+SGA+并发执行进程数*(sort_area_size+hash_ara_size+2M) < 0.7*总内存

   

    (公式是死的,系统是活的,实际应用的调整不必框公式,这不过是一个参考建议)

    在我们的实际应用中,假如采用的是裸设备,我们可适当的增大SGA(如果需要的话)。由于目前几乎所有的操作系统都使用虚拟缓存,所以实际上如果就算SGA设置的比较大也不会导致错误,而是可能出现频繁的内存页的换入与换出(page in/out)。在操作系统一级如果观察到这个现象,那么我们就需要调整内存的设置。

 

l       SGA内参数设置

Log_buffer

对于日志缓冲区的大小设置,通常我觉得没有过多的建议,因为参考LGWR写的触发条件之后,我们会发现通常超过3M意义不是很大。作为一个正式系统,可能考虑先设置这部分为log_buffer=1—3M 大小,然后针对具体情况再调整。

 

Large_pool_size

对于大缓冲池的设置,假如不使用MTS,建议在20—30M 足够了。这部分主要用来保存并行查询时候的一些信息,还有就是RMAN在备份的时候可能会使用到。如果设置了MTS,则由于UGA部分要移入这里,则需要具体根据server process数量和相关会话内存参数的设置来综合考虑这部分大小的设置。

 

Java_pool_size

假如数据库没有使用JAVA,我们通常认为保留10—20M大小足够。事实上可以更少,甚至最少只需要32k,但具体跟安装数据库的时候的组件相关(比如http server)

 

shared_pool_size

这是迄今为止最具有争议的一部分内存设置。按照很多文档的描述,这部分内容应该几乎和数据缓冲区差不多大小。但实际上情况却不是这样的。首先我们要考究一个问题,那就是这部分内存的作用,它是为了缓存已经被解析过的SQL,而使其能被重用,不再解析。这样做的原因是因为,对于一个新的SQLshared_pool里面不存在已经解析的可用的相同的SQL),数据库将执行硬解析,这是一个很消耗资源的过程。而若已经存在,则进行的仅仅是软分析(在共享池中寻找相同SQL),这样消耗的资源大大减少。所以我们期望能多共享一些SQL,并且如果该参数设置不够大,经常会出现ora-04031错误,表示为了解析新的SQL,没有可用的足够大的连续空闲空间,这样自然我们期望该参数能大一些。但是该参数的增大,却也有负面的影响,因为需要维护共享的结构,内存的增大也会使得SQL的老化的代价更高,带来大量的管理的开销,所有这些可能会导致CPU的严重问题。

在一个充分使用绑定变量的比较大的系统中,shared_pool_size的开销通常应该维持在300M以内。除非系统使用了大量的存储过程、函数、包,比如oracle erp这样的应用,可能会达到500M甚至更高。于是我们假定一个1G内存的系统,可能考虑设置该参数为100M2G的系统考虑设置为150M,8G的系统可以考虑设置为200—300M

对于一个没有充分使用或者没有使用绑定变量系统,这可能给我们带来一个严重的问题。所谓没有使用bind var SQL,我们称为Literal SQL。也就是比如这样的两句SQL我们认为是不同的SQL,需要进行2次硬解析:

select * from EMP where name = ‘TOM’;

select * from EMP where name = ‘JERRY’;

假如把’TOM’ ‘JERRY’ 换做变量V,那就是使用了bind var,我们可以认为是同样的SQL从而能很好地共享。共享SQL本来就是shared_pool_size这部分内存存在的本意,oracle的目的也在于此,而我们不使用bind var就是违背了oracle的初衷,这样将给我们的系统带来严重的问题。当然,如果通过在操作系统监控,没有发现严重的cpu问题,我们如果发现该共享池命中率不高可以适当的增加shred_pool_size。但是通常我们不主张这部分内存超过800M(特殊情况下可以更大)。

事实上,可能的话我们甚至要想办法避免软分析,这在不同的程序语言中实现方式有差异。我们也可能通过设置session_cached_cursors 参数来获得帮助(这将增大PGA)。

 

Data buffer

现在我们来谈数据缓冲区,在确定了SGA的大小并分配完了前面部分的内存后,其余的,都分配给这部分内存。通常,在允许的情况下,我们都尝试使得这部分内存更大。这部分内存的作用主要是缓存 DB BLOCK,减少甚至避免从磁盘上获取数据,在8i中是由db_block_buffers*db_block_size来决定大小的(包含defaultkeeprecycle)。如果我们设置了buffer_pool_keep buffer_pool_recycle,这两部分内存的大小包含在前面设置中(db_block_buffers*db_block_size)。

buffer_pool_keep 是用来取代8i版本以前的缓存频繁小表于LUR MOST  USED端的。通过开辟一段独立的内存用于缓存频繁的小表,在创建表的时候可以指定存储参数,或者也可以动态修改表的存储参数(alter table t storage(buffer_pool  keep);)。

Buffer_pool_recycle 作为一块单独开辟出来的内存,主要用于很少执行的大表全表扫描的查询,使得这些大表扫描不会影响到default里面LRU而冲击整个数据库缓冲区的性能。虽然这样有可能降低大表的全表扫描的性能,但是保护了整体性能不间歇性的受到较大的冲击。同样,除了设置参数外还需要在创建表的过程中使用存储参数或者动态修改表的存储参数(alter table t storage(buffer_pool  recycle);

 

l       9i下参数的变化

oracle的版本的更新,总是伴随着参数的变化,并且越来越趋向于使得参数的设置更简单,因为复杂的参数设置使得DBA们经常焦头烂额。关于内存这部分的变化,我们可以考察下面的参数。事实上在9i中数据库本身可以给出一组适合当前运行系统的SGA相关部分的参数调整值(参考V$DB_CACHE_ADVICEV$SHARED_POOL_ADVICE),关于PGA也有相关视图V$PGA_TARGET_ADVICE等。

 

Data buffer

9i中保留了8i中的参数,如设置了新的参数,则忽略旧的参数。9i中用db_cache_size来取代db_block_buffers,用db_keep_cache_size取代buffer_pool_keep,db_recycle_cache_size取代buffer_pool_recycle;这里要注意9i中设置的是实际的缓存大小而不再是块的数量。另外9i新增加了db_nk_cache_size,这是为了支持在同一个数据库中使用不同的块大小而设置的。对于不同的表空间,可以定义不同的数据块的大小,而缓冲区的定义则依靠该参数的支持。其中n可以为246816等不同的值。在这里顺便提及的一个参数就是db_block_lru_latches,该参数在9i中已经成为了保留参数,不推荐手工设置。

 

PGA

    9i里面这部分也有了很大的变化。在独立模式下,9i已经不再主张使用原来的UGA相关的参数设置,而代之以新的参数。假如workarea_size_policy=AUTO(缺省),则所有的会话的UGA共用一大块内存,该内存在 pga_aggregate_target bitirainy 发表于:2004.09.08 16:50 ::分类: ( Oracle is anything ) ::阅读:(928次) :: 评论 (1)

===========================================================
关于 v$waitstat 和 x$bh.class 的关系
===========================================================

x$bh.class 中的分类,依次对应于 v$waitstat 中记录

SQL> select * from v$version;

BANNER
----------------------------------------------------------------
Oracle9i Enterprise Edition Release 9.2.0.1.0 - Production
PL/SQL Release 9.2.0.1.0 - Production
CORE 9.2.0.1.0 Production
TNS for 32-bit Windows: Version 9.2.0.1.0 - Production
NLSRTL Version 9.2.0.1.0 - Production

SQL>

SQL> select * from v$waitstat;

CLASS COUNT TIME
------------------ ---------- ----------
data block 2 1
sort block 0 0
save undo block 0 0
segment header 1 0
save undo header 0 0
free list 0 0
extent map 0 0
1st level bmb 0 0
2nd level bmb 0 0
3rd level bmb 0 0
bitmap block 0 0

CLASS COUNT TIME
------------------ ---------- ----------
bitmap index block 0 0
file header block 0 0
unused 0 0
system undo header 0 0
system undo block 0 0
undo header 0 0
undo block 0 0

已选择18行。

SQL>


SQL> select distinct class from x$bh order by class ;

CLASS
----------
1
4
8
9
12
13
15
17 从这里开始依次为undo header /undo block
18
19
20

CLASS
----------
21
22
23
24
25
26
27
28
29
30
31

CLASS
----------
32
33
34
35
36

已选择27行。

SQL>

oracle 7 非系统回滚段是从9开始,8i是从13开始

从这里,基本就可以明白,v$waitstat 就是针对 data buffer 中各类型的block的等待进行统计的

 


bitirainy 发表于:2004.09.08 16:48 ::分类: ( Oracle is anything ) ::阅读:(922次) :: 评论 (0)
===========================================================
update的时候数据库的处理情况
===========================================================

有人问到:

    今天听一个IBM的人说数据库在update时是先删除原记录,然后在插入,对此我有些疑问,oracle好像不是这样的,请大家能否阐述一下update的过程,更新主键和不更新主键的处理是否一样?不胜感激。另外,那个IBM的人还说update和insert不是一个数量级的动作,我认为要具体分析,对同数据量的操作,update需要定位,但是慢多少呢?

 

答:

其他数据库跟oracle可能不同 ,但是就 oracle 而言,update 的时候,如果存在索引字段的更新,则删除原索引条目(不是真正的删除数据,仅仅是在该行标记为 删除)插入新的索引条目,对于row本身的更新,是在行物理地更改,如果行的长度增加到当前位置无法容纳,则行的位置被提到 block的最上面一条记录的位置之上,假如该块已经无法容纳,则在原来行的物理位置保留一个指针,行被迁移到新的block,而保留的指针就是指向新的block的位置。这时索引中rowid不用发生变化,查询的时候先找到 那保留的指针,再去找 实际的新的位置。假如行迁移后再发生update 又导致迁移,则oracle首先看原来的位置的block是否具有容纳该行的空间,如果有就又迁移回去,如果没有就迁移到新的 块,修改原来最早块处的指针。 也就是说不会存在2个指针的查找才能找到 行.更新的时候,变化前后变化后的数据都被写入 redo ,变化前数据还被写入 回滚段,变化后数据被应用于 data buffer 。

若假设存在表t(a,b,c)
update t set a = ... where b = ... and c = ...
则回滚段中只记录 a 的变化前的值,关于update 的时候通常需要通过索引去定位,否则是全表扫描就很慢了
而insert 则只是找个 block 插进去,两者的差异可能很大,可能完全不是一个数量级的时间和资源的消耗

 

更多的详细的探讨请参考:http://www.itpub.net/showthread.php?threadid=129524

 


bitirainy 发表于:2004.09.08 16:48 ::分类: ( Oracle is anything ) ::阅读:(490次) :: 评论 (0)
===========================================================
关于索引块空间的重用
===========================================================
在oracle document中所说的索引块要等所有数据完全删除后才可重用,这是站在块的重新分配的角度上说的,也就是说,相当于索引块的 pctused=0 ,要完全删除后才回到 freelist(assm不存在freelist而是用bitmap来表示),但其实在索引块的内部,并不是说被删除的空间就不能被重用。所谓重用 的观点应该针对block来说而不应该针对row所在的位置,只要key value 落在该block可接受的范围内都可以重用,而不必关心是否完全相同。对于block里面的记录来说,物理位置并不是跟key顺序有关,可能由于 delete/update 等而发身变化

    测试如下

SQL> create table t as select * from dba_objects where object_id is not null;

表已创建。

SQL> create index t_index on t(object_id);

索引已创建。
SQL> desc t
名称 是否为空? 类型
----------------------------------------- -------- ----------------------------
OWNER VARCHAR2(30)
OBJECT_NAME VARCHAR2(128)
SUBOBJECT_NAME VARCHAR2(30)
OBJECT_ID NUMBER
DATA_OBJECT_ID NUMBER
OBJECT_TYPE VARCHAR2(18)
CREATED DATE
LAST_DDL_TIME DATE
TIMESTAMP VARCHAR2(19)
STATUS VARCHAR2(7)
TEMPORARY VARCHAR2(1)
GENERATED VARCHAR2(1)
SECONDARY VARCHAR2(1)


SQL> select FILE_ID,EXTENT_ID,BLOCK_ID from dba_extents where segment_name = 'T_INDEX';

FILE_ID EXTENT_ID BLOCK_ID
---------- ---------- ----------
9 0 569
9 1 577
9 2 585
9 3 593
9 4 601
9 5 609
9 6 617
9 7 625
9 8 633
9 9 641

已选择10行。

SQL> select min(object_id) from t;

MIN(OBJECT_ID)
--------------
2
由于是ASSM 类型的表空间,我通过其他方式找到索引叶子节点的开始位置为 block_id 573


SQL> alter system dump datafile 9 block 573;

kdxleprv 0=0x0
kdxledsz 0
kdxlebksz 8036
row#0[8024] flag: -----, lock: 0 8024表示block中的偏移bytes,也就是位置
col 0; len 2; (2): c1 03 --------这个值是2
col 1; len 6; (6): 02 40 00 6d 00 1f
row#1[8012] flag: -----, lock: 0
col 0; len 2; (2): c1 04
col 1; len 6; (6): 02 40 02 2f 00 26
row#2[8000] flag: -----, lock: 0
col 0; len 2; (2): c1 05
col 1; len 6; (6): 02 40 00 94 00 16



SQL> delete from t where object_id = 2;

已删除 1 行。

SQL> commit;

提交完成。


SQL> alter system dump datafile 9 block 573;

系统已更改。
row#0[8024] flag: ---D-, lock: 2 ---D-表示被删除
col 0; len 2; (2): c1 03
col 1; len 6; (6): 02 40 00 6d 00 1f
row#1[8012] flag: -----, lock: 0
col 0; len 2; (2): c1 04
col 1; len 6; (6): 02 40 02 2f 00 26
row#2[8000] flag: -----, lock: 0
col 0; len 2; (2): c1 05
col 1; len 6; (6): 02 40 00 94 00 16



SQL> insert into t values(1,1,1,1,1,1,sysdate,sysdate,1,1,1,1,1);

已创建 1 行。

SQL> commit;

提交完成。


SQL> alter system dump datafile 9 block 573;


kdxleprv 0=0x0
kdxledsz 0
kdxlebksz 8036
row#0[1822] flag: -----, lock: 2 -插入位置发生了变化,相当于是update提升到了pctfree保留部分
col 0; len 2; (2): c1 02
col 1; len 6; (6): 02 40 01 be 00 00
row#1[8012] flag: -----, lock: 0
col 0; len 2; (2): c1 04
col 1; len 6; (6): 02 40 02 2f 00 26
row#2[8000] flag: -----, lock: 0
col 0; len 2; (2): c1 05
col 1; len 6; (6): 02 40 00 94 00 16



SQL> delete t where object_id = 1;

已删除 1 行。

SQL> commit;

提交完成。

SQL> insert into t values(1,1,1,1,1,1,sysdate,sysdate,1,1,1,1,1);

已创建 1 行。

SQL> commit;

提交完成。

SQL>

row#0[1810] flag: -----, lock: 2 我们发现,即使删除后立即插入相同的值,也不会立即重用以前的空间1822,而是依然在新的位置1810
col 0; len 2; (2): c1 02
col 1; len 6; (6): 02 40 01 be 00 01
row#1[8012] flag: -----, lock: 0
col 0; len 2; (2): c1 04
col 1; len 6; (6): 02 40 02 2f 00 26
row#2[8000] flag: -----, lock: 0
col 0; len 2; (2): c1 05
col 1; len 6; (6): 02 40 00 94 00 16


如果继续测试下去会发生适当的时候block会重整,进一步测试后的结论

1:索引的 pctfree 是用来插入新的可容纳入该block可接受的范围的key value 的
当然pctfree也会跟 initrans ---> maxtrans 的 ITL 有关

2:删除时候的 索引上的记录被标记为 D ,该block上被查询的时候该记录存在,若该block上一旦发生下一个事务,该被标记删除记录立即被清除

 

更多内容可参考: http://www.itpub.net/showthread.php?threadid=149768


bitirainy 发表于:2004.09.08 16:47 ::分类: ( Oracle is anything ) ::阅读:(572次) :: 评论 (0)
===========================================================
关于数据库open的深入探究
===========================================================

SQL> shutdown immediate
Database closed.
Database dismounted.
ORACLE instance shut down.
SQL> startup mount
ORACLE instance started.
Total System Global Area 131142648 bytes
Fixed Size 451576 bytes
Variable Size 104857600 bytes
Database Buffers 25165824 bytes
Redo Buffers 667648 bytes
Database mounted.
SQL> alter session set sql_trace = true;

Session altered.

SQL> alter database open;

Database altered.

SQL>

首先我们来参考跟踪文件的前部分(参考附件)
这是第一个对象的创建
create table bootstrap$ ( line# number not null, obj#
number not null, sql_text varchar2(4000) not null) storage (initial
50K objno 56 extents (file 1 block 377))

接下来我们来看执行的是
select line#, sql_text
from
bootstrap$ where obj# != :1
(这里实际上是逐步提取内容建立字典表本身的结构,根据后面内容可知)


那么在这里我们在数据库中来看看 bootstap$
SQL> desc bootstrap$
Name Null? Type
----------------------------------------- -------- ----------------------------
LINE# NOT NULL NUMBER
OBJ# NOT NULL NUMBER
SQL_TEXT NOT NULL VARCHAR2(4000)

SQL>

SQL> select count(*) from bootstrap$;
57

SQL>

SQL> select obj#,sql_text from bootstrap$ where rownum<11;
-1
8.0.0.0.0

0
CREATE ROLLBACK SEGMENT SYSTEM STORAGE ( INITIAL 112K NEXT 1024K MINEXTENTS 1 M
AXEXTENTS 32765 OBJNO 0 EXTENTS (FILE 1 BLOCK 9))

8
CREATE CLUSTER C_FILE#_BLOCK#("TS#" NUMBER,"SEGFILE#" NUMBER,"SEGBLOCK#" NUMBER)
PCTFREE 10 PCTUSED 40 INITRANS 2 MAXTRANS 255 STORAGE ( INITIAL 24K NEXT 1024K
MINEXTENTS 1 MAXEXTENTS 2147483645 PCTINCREASE 0 OBJNO 8 EXTENTS (FILE 1 BLOCK
73)) SIZE 225

9
CREATE INDEX I_FILE#_BLOCK# ON CLUSTER C_FILE#_BLOCK# PCTFREE 10 INITRANS 2 MAXT
RANS 255 STORAGE ( INITIAL 64K NEXT 1024K MINEXTENTS 1 MAXEXTENTS 2147483645 PC
TINCREASE 0 OBJNO 9 EXTENTS (FILE 1 BLOCK 81))

14
CREATE TABLE SEG$("FILE#" NUMBER NOT NULL,"BLOCK#" NUMBER NOT NULL,"TYPE#" NUMBE
R NOT NULL,"TS#" NUMBER NOT NULL,"BLOCKS" NUMBER NOT NULL,"EXTENTS" NUMBER NOT N
ULL,"INIEXTS" NUMBER NOT NULL,"MINEXTS" NUMBER NOT NULL,"MAXEXTS" NUMBER NOT NUL
L,"EXTSIZE" NUMBER NOT NULL,"EXTPCT" NUMBER NOT NULL,"USER#" NUMBER NOT NULL,"LI
STS" NUMBER,"GROUPS" NUMBER,"BITMAPRANGES" NUMBER NOT NULL,"CACHEHINT" NUMBER NO
T NULL,"SCANHINT" NUMBER NOT NULL,"HWMINCR" NUMBER NOT NULL,"SPARE1" NUMBER,"SPA
RE2" NUMBER) STORAGE ( OBJNO 14 TABNO 2) CLUSTER C_FILE#_BLOCK#(TS#,FILE#,BLOCK
#)

5
CREATE TABLE CLU$("OBJ#" NUMBER NOT NULL,"DATAOBJ#" NUMBER,"TS#" NUMBER NOT NULL
,"FILE#" NUMBER NOT NULL,"BLOCK#" NUMBER NOT NULL,"COLS" NUMBER NOT NULL,"PCTFRE
E$" NUMBER NOT NULL,"PCTUSED$" NUMBER NOT NULL,"INITRANS" NUMBER NOT NULL,"MAXTR
ANS" NUMBER NOT NULL,"SIZE$" NUMBER,"HASHFUNC" VARCHAR2(30),"HASHKEYS" NUMBER,"F
UNC" NUMBER,"EXTIND" NUMBER,"FLAGS" NUMBER,"DEGREE" NUMBER,"INSTANCES" NUMBER,"A
VGCHN" NUMBER,"SPARE1" NUMBER,"SPARE2" NUMBER,"SPARE3" NUMBER,"SPARE4" NUMBER,"S
PARE5" VARCHAR2(1000),"SPARE6" VARCHAR2(1000),"SPARE7" DATE) STORAGE ( OBJNO 5
TABNO 2) CLUSTER C_OBJ#(OBJ#)

6
CREATE CLUSTER C_TS#("TS#" NUMBER) PCTFREE 10 PCTUSED 40 INITRANS 2 MAXTRANS 255
STORAGE ( INITIAL 64K NEXT 1024K MINEXTENTS 1 MAXEXTENTS 2147483645 PCTINCREAS
E 0 OBJNO 6 EXTENTS (FILE 1 BLOCK 57))

7
CREATE INDEX I_TS# ON CLUSTER C_TS# PCTFREE 10 INITRANS 2 MAXTRANS 255 STORAGE (
INITIAL 64K NEXT 1024K MINEXTENTS 1 MAXEXTENTS 2147483645 PCTINCREASE 0 OBJNO
7 EXTENTS (FILE 1 BLOCK 65))

18
CREATE TABLE OBJ$("OBJ#" NUMBER NOT NULL,"DATAOBJ#" NUMBER,"OWNER#" NUMBER NOT N
ULL,"NAME" VARCHAR2(30) NOT NULL,"NAMESPACE" NUMBER NOT NULL,"SUBNAME" VARCHAR2(
30),"TYPE#" NUMBER NOT NULL,"CTIME" DATE NOT NULL,"MTIME" DATE NOT NULL,"STIME"
DATE NOT NULL,"STATUS" NUMBER NOT NULL,"REMOTEOWNER" VARCHAR2(30),"LINKNAME" VAR
CHAR2(128),"FLAGS" NUMBER,"OID$" RAW(16),"SPARE1" NUMBER,"SPARE2" NUMBER,"SPARE3
" NUMBER,"SPARE4" VARCHAR2(1000),"SPARE5" VARCHAR2(1000),"SPARE6" DATE) PCTFREE
10 PCTUSED 40 INITRANS 1 MAXTRANS 255 STORAGE ( INITIAL 16K NEXT 1024K MINEXTEN
TS 1 MAXEXTENTS 2147483645 PCTINCREASE 0 OBJNO 18 EXTENTS (FILE 1 BLOCK 121))

36
CREATE UNIQUE INDEX I_OBJ1 ON OBJ$(OBJ#) PCTFREE 10 INITRANS 2 MAXTRANS 255 STOR
AGE ( INITIAL 64K NEXT 1024K MINEXTENTS 1 MAXEXTENTS 2147483645 PCTINCREASE 0 O
BJNO 36 EXTENTS (FILE 1 BLOCK 217))


10 rows selected.

SQL>

由这里我们可以看出,bootstrap$ 中实际上是记录了一些数据库系统基本对象的创建语句,那对于我们数据库来说,也可以看做建立一个表结构,通过这个结构可以通过关系型数据库的方式去获取文件中数据,ok,接下来我们看看trace文件中的内容,发现

CREATE ROLLBACK SEGMENT SYSTEM STORAGE ( INITIAL 112K NEXT 1024K MINEXTENTS
1 MAXEXTENTS 32765 OBJNO 0 EXTENTS (FILE 1 BLOCK 9))

在数据库系统表空间的头部创建了系统回滚段(block 9 开始,1---8 属于数据文件头)

再往下
CREATE CLUSTER C_OBJ#("OBJ#" NUMBER) PCTFREE 5 PCTUSED 40 INITRANS 2 MAXTRANS
255 STORAGE ( INITIAL 136K NEXT 1024K MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 OBJNO 2 EXTENTS (FILE 1 BLOCK 25)) SIZE 800
这里同样是直接指定了该cluster 的段头位置 block 25


SQL> select file_id,block_id from dba_extents where segment_name = 'C_OBJ#';
1 25
1 33
1 41
1 3241
1 4441
1 4473
1 4489
1 4513
1 4529
1 4561
1 4585
1 4609
1 4641
1 4681
1 4753
1 4833
1 4873
1 12681
1 23689
1 26249

20 rows selected.




CREATE INDEX I_OBJ# ON CLUSTER C_OBJ# PCTFREE 10 INITRANS 2 MAXTRANS 255
STORAGE ( INITIAL 64K NEXT 1024K MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 OBJNO 3 EXTENTS (FILE 1 BLOCK 49))


CREATE TABLE TAB$("OBJ#" NUMBER NOT NULL,"DATAOBJ#" NUMBER,"TS#" NUMBER NOT
NULL,"FILE#" NUMBER NOT NULL,"BLOCK#" NUMBER NOT NULL,"BOBJ#" NUMBER,"TAB#"
NUMBER,"COLS" NUMBER NOT NULL,"CLUCOLS" NUMBER,"PCTFREE$" NUMBER NOT NULL,
"PCTUSED$" NUMBER NOT NULL,"INITRANS" NUMBER NOT NULL,"MAXTRANS" NUMBER NOT
NULL,"FLAGS" NUMBER NOT NULL,"AUDIT$" VARCHAR2(38) NOT NULL,"ROWCNT" NUMBER,
"BLKCNT" NUMBER,"EMPCNT" NUMBER,"AVGSPC" NUMBER,"CHNCNT" NUMBER,"AVGRLN"
NUMBER,"AVGSPC_FLB" NUMBER,"FLBCNT" NUMBER,"ANALYZETIME" DATE,"SAMPLESIZE"
NUMBER,"DEGREE" NUMBER,"INSTANCES" NUMBER,"INTCOLS" NUMBER NOT NULL,
"KERNELCOLS" NUMBER NOT NULL,"PROPERTY" NUMBER NOT NULL,"TRIGFLAG" NUMBER,
"SPARE1" NUMBER,"SPARE2" NUMBER,"SPARE3" NUMBER,"SPARE4" VARCHAR2(1000),
"SPARE5" VARCHAR2(1000),"SPARE6" DATE) STORAGE ( OBJNO 4 TABNO 1) CLUSTER
C_OBJ#(OBJ#)

我们可以看到,从tab$ 开始,存储发生了变化,
STORAGE ( OBJNO 4 TABNO 1) CLUSTER C_OBJ#(OBJ#)
那这里的 objno 4 tabno 1 表示什么意思呢?(通过 cluster C_OBJ#(OBJ#)我们就可以找到表tab$自身)


SQL> select * from tab$ where obj# = 4 and tab# = 1;

OBJ# DATAOBJ# TS# FILE# BLOCK# BOBJ# TAB#
---------- ---------- ---------- ---------- ---------- ---------- ----------
COLS CLUCOLS PCTFREE$ PCTUSED$ INITRANS MAXTRANS FLAGS
---------- ---------- ---------- ---------- ---------- ---------- ----------
AUDIT$ ROWCNT BLKCNT EMPCNT
-------------------------------------- ---------- ---------- ----------
AVGSPC CHNCNT AVGRLN AVGSPC_FLB FLBCNT ANALYZETI SAMPLESIZE
---------- ---------- ---------- ---------- ---------- --------- ----------
DEGREE INSTANCES INTCOLS KERNELCOLS PROPERTY TRIGFLAG SPARE1
---------- ---------- ---------- ---------- ---------- ---------- ----------
SPARE2 SPARE3
---------- ----------
SPARE4
--------------------------------------------------------------------------------
SPARE5
--------------------------------------------------------------------------------
SPARE6
---------
4 2 0 1 25 2 1
37 1 0 0 0 0 17
-------------------------------- 921 428 83
2381 0 139 6368 7 11-AUG-03 921
37 37 1024 0 0



06-AUG-03


SQL>

    由这里可以看出,存储参数从这里开始,就从数据文件中固定表的记录中取出来,由此再往trace文件看下去,就会发现这个时候数据库已经可以通过已有信息从系统表空间文件中提取结构信息和数据信息,我们看上面关于存储的定义
CREATE CLUSTER C_OBJ#("OBJ#" NUMBER) PCTFREE 5 PCTUSED 40 INITRANS 2 MAXTRANS
255 STORAGE ( INITIAL 136K NEXT 1024K MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 OBJNO 2 EXTENTS (FILE 1 BLOCK 25)) SIZE 800
这里的extents 指明了是 block 25,我们假设在初始化参数文件中db_block_size设置不当,则必然导致文件位置定位错误


    之所以要来研究这个问题(以前研究了今天写出来而已),是因为最早思考这样一个问题,我们要查询自己系统的一个表,则首先是去数据字典中找到该表的结构性信息,这些结构性信息存储在数据字典表中,但我们又从哪里获得数据字典表本身的结构性信息呢?

    虽然从表中我们可以查询到字典表本身的结构性信息,那在数据库open的时候最初是如何确定结构的?是写死在程序中呢?还是怎样处理的。由上面内容我们可以看出,程序中只要提供bootstrap$ 的创建脚本,确定好bootstap$的结构同时指定了段头的位置,然后就能通过段头去获取bootstrap$中的内容,而这些内容就是为一些字典表创建结构的sql_text,这样就因了bootstrap$这个天生的蛋,孵化出了c_obj#,tab$等等鸡,这些鸡又继续生蛋,则完成了数据库字典表结构的建立。也就是说,oracle用关系型表来实现了自身结构的建立。

    在这里要注意的一个问题就是,启动过程中create这些数据字典对象,并不是在物理上真正地去创建段,而是在内存中创建对应的结构,根据这个结构可以去获取系统表空间中的信息。也就是说,为了生成 tab$ 等结构,这个结构的 sql 又来自 bootstrap$ ,最开始,oracle只要拥有了 bootstrap$ 的结构信息,在sga中创建了结构,则立即可以去 system tablespace中获取bootstrap$的内容(而这个create的语句完全可以放在可执行程序或者dll中作为天生的一个蛋),然后逐步完成sga中字典表结构的建立。 当然所有这些过程中并没有真实地创建物理对象。

 


关于trace文件,大家可以自己在mount状态下trace  session 再startup ,tkprof格式化  trace 文件得到

 

 

 

原文讨论参考: http://www.itpub.net/showthread.php?threadid=199099


bitirainy 发表于:2004.09.08 16:46 ::分类: ( Oracle is anything ) ::阅读:(924次) :: 评论 (0)
===========================================================
open cursor 是否去获取数据?
===========================================================

cursor open 的时候到底有没有去获取数据
是不是fetch的时候才获取数据

请看下面实验


SQL> conn test/test
Connected.
SQL> alter session set events '10046 trace name context forever,level 12';

Session altered.

SQL> declare
2 cursor c is select * from test1;
3 begin
4 open c;
5 close c;
6 end;
7 /

PL/SQL procedure successfully completed.

SQL> alter session set events '10046 trace name context off';

Session altered.


[oracle@jumper udump]$ cat *.trc
/opt/oracle/admin/hsjf/udump/hsjf_ora_26966.trc
Oracle9i Enterprise Edition Release 9.2.0.3.0 - Production
With the Partitioning, OLAP and Oracle Data Mining options
JServer Release 9.2.0.3.0 - Production
ORACLE_HOME = /opt/oracle/product/9.2.0
System name: Linux
Node name: jumper.hurray.com.cn
Release: 2.4.18-14
Version: #1 Wed Sep 4 13:35:50 EDT 2002
Machine: i686
Instance name: hsjf
Redo thread mounted by this instance: 1
Oracle process number: 11
Unix process pid: 26966, image: oracle@jumper.hurray.com.cn (TNS V1-V3)

*** 2004-02-22 23:51:41.363
*** SESSION ID20.3141) 2004-02-22 23:51:41.363
APPNAME mod='SQL*Plus' mh=3669949024 act='' ah=4029777240
=====================
PARSING IN CURSOR #1 len=68 dep=0 uid=41 oct=42 lid=41 tim=1052268263050484 hv=1346161232 ad='54d7e004'
alter session set events '10046 trace name context forever,level 12'
END OF STMT
EXEC #1:c=0,e=211,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=4,tim=1052268263049839
WAIT #1: nam='SQL*Net message to client' ela= 8 p1=1650815232 p2=1 p3=0
*** 2004-02-22 23:52:06.023
WAIT #1: nam='SQL*Net message from client' ela= 24081533 p1=1650815232 p2=1 p3=0
=====================
PARSING IN CURSOR #1 len=71 dep=0 uid=41 oct=47 lid=41 tim=1052268287142522 hv=190018789 ad='54d87df0'
declare
cursor c is select * from test1;
begin
open c;
close c;
end;
END OF STMT
PARSE #1:c=9765,e=9616,p=0,cr=0,cu=0,mis=1,r=0,dep=0,og=4,tim=1052268287142473
BINDS #1:
=====================
PARSING IN CURSOR #3 len=48 dep=2 uid=0 oct=3 lid=0 tim=1052268287143761 hv=3997906522 ad='53696c28'
select user# from sys.user$ where name = 'OUTLN'
END OF STMT
PARSE #3:c=0,e=232,p=0,cr=0,cu=0,mis=0,r=0,dep=2,og=4,tim=1052268287143735
BINDS #3:
EXEC #3:c=0,e=190,p=0,cr=0,cu=0,mis=0,r=0,dep=2,og=4,tim=1052268287144166
FETCH #3:c=0,e=180,p=0,cr=2,cu=0,mis=0,r=1,dep=2,og=4,tim=1052268287144402
STAT #3 id=1 cnt=1 pid=0 pos=1 obj=22 op='TABLE ACCESS BY INDEX ROWID OBJ#(22) (cr=2 r=0 w=0 time=145 us)'
STAT #3 id=2 cnt=1 pid=1 pos=1 obj=44 op='INDEX UNIQUE SCAN OBJ#(44) (cr=1 r=0 w=0 time=81 us)'
=====================
PARSING IN CURSOR #2 len=19 dep=1 uid=41 oct=3 lid=41 tim=1052268287145054 hv=1259978721 ad='54d7b05c'
SELECT * from test1
END OF STMT
PARSE #2:c=1953,e=1802,p=0,cr=2,cu=0,mis=0,r=0,dep=1,og=4,tim=1052268287145039
BINDS #2:
EXEC #2:c=0,e=134,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=4,tim=1052268287145318
EXEC #1:c=3907,e=2694,p=0,cr=2,cu=0,mis=0,r=1,dep=0,og=4,tim=1052268287145505
WAIT #1: nam='SQL*Net message to client' ela= 9 p1=1650815232 p2=1 p3=0
WAIT #1: nam='SQL*Net message from client' ela= 4961677 p1=1650815232 p2=1 p3=0
STAT #2 id=1 cnt=0 pid=0 pos=1 obj=14498 op='TABLE ACCESS FULL TEST1 (cr=0 r=0 w=0 time=0 us)'
=====================
PARSING IN CURSOR #1 len=56 dep=0 uid=41 oct=42 lid=41 tim=1052268292109059 hv=527042363 ad='54d8b9c4'
alter session set events '10046 trace name context off'
END OF STMT
PARSE #1:c=0,e=811,p=0,cr=0,cu=0,mis=1,r=0,dep=0,og=4,tim=1052268292109029
BINDS #1:
EXEC #1:c=0,e=197,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=4,tim=1052268292109432






SQL> alter session set events '10046 trace name context forever,level 12';

Session altered.

SQL> declare
2 v varchar2(30);
3 cursor c is select a from test1;
4 begin
5 open c;
6 fetch c into v;
7 close c;
8 end;
9 /

PL/SQL procedure successfully completed.

SQL> alter session set events '10046 trace name context off';

Session altered.
=====================
PARSING IN CURSOR #1 len=68 dep=0 uid=41 oct=42 lid=41 tim=1052268348282083 hv=1346161232 ad='54d7e004'
alter session set events '10046 trace name context forever,level 12'
END OF STMT
EXEC #1:c=0,e=190,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=4,tim=1052268348282051
WAIT #1: nam='SQL*Net message to client' ela= 9 p1=1650815232 p2=1 p3=0
*** 2004-02-22 23:54:15.491
WAIT #1: nam='SQL*Net message from client' ela= 65283612 p1=1650815232 p2=1 p3=0
=====================
PARSING IN CURSOR #1 len=100 dep=0 uid=41 oct=47 lid=41 tim=1052268413585464 hv=4163332771 ad='54d76b58'
declare
v varchar2(30);
cursor c is select a from test1;
begin
open c;
fetch c into v;
close c;
end;
END OF STMT
PARSE #1:c=9765,e=18819,p=0,cr=0,cu=0,mis=1,r=0,dep=0,og=4,tim=1052268413585420
BINDS #1:
=====================
PARSING IN CURSOR #2 len=19 dep=1 uid=41 oct=3 lid=41 tim=1052268413587757 hv=3226909281 ad='54d7af2c'
SELECT a from test1
END OF STMT
PARSE #2:c=1954,e=1601,p=0,cr=0,cu=0,mis=1,r=0,dep=1,og=4,tim=1052268413587721
BINDS #2:
EXEC #2:c=0,e=151,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=4,tim=1052268413588187
WAIT #2: nam='db file scattered read' ela= 221 p1=11 p2=770 p3=2
FETCH #2:c=1953,e=1194,p=2,cr=6,cu=1,mis=0,r=1,dep=1,og=4,tim=1052268413589563
EXEC #1:c=3907,e=4119,p=2,cr=6,cu=1,mis=0,r=1,dep=0,og=4,tim=1052268413589885
WAIT #1: nam='SQL*Net message to client' ela= 8 p1=1650815232 p2=1 p3=0
WAIT #1: nam='SQL*Net message from client' ela= 6374702 p1=1650815232 p2=1 p3=0
STAT #2 id=1 cnt=1 pid=0 pos=1 obj=14498 op='TABLE ACCESS FULL TEST1 (cr=6 r=2 w=0 time=1158 us)'
=====================
PARSING IN CURSOR #1 len=56 dep=0 uid=41 oct=42 lid=41 tim=1052268419965801 hv=527042363 ad='54d8b9c4'
alter session set events '10046 trace name context off'
END OF STMT
PARSE #1:c=0,e=182,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=4,tim=1052268419965781
BINDS #1:
EXEC #1:c=0,e=203,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=4,tim=1052268419966144
[oracle@jumper udump]$

我们可以看出在 open cursor 的过程中并未曾去获取过 数据
也就是对于数据文件 11 块编号为 770的 block进行数据的获取

WAIT #2: nam='db file scattered read' ela= 221 p1=11 p2=770 p3=2

 

 

oracle  document 有描述

SG:Introduction to oracle:sql and pl/sql 21-6上上关于CURSOR的一段话:THE OPEN statement executes the query associated with the cursor,identifies the active set,and positions the
cursor(pointer)bifore the first row.The FETCH statement retrieves the current row and advances the cursor to the next row.

还有21-9中的关于OPEN STATEMENT的原文:Open the cursor to execute
the query and identify the active set,which consists of all rows that
meet the query search criteria.The cursor now points to the first row
in the active set.

 

游标在打开没有执行的时候,本就是没有获取到任何数据

查询SCN确实是在 open cursor 的时候确定的,但IO或者真正查询是在 fetch 的时候才产生的

关于 IO 上面已经证明了
关于查询SCN的确定
下面演示

首先执行一段pl/sql

SQL> set serverout on
SQL> declare
2 v varchar2(30);
cursor c is select d from test1 where a = 1;
3 4 begin
open c;
dbms_lock.sleep(60); 在休眠这60秒内去新session中更新将被查到的行
fetch c into v;
5 6 7 8 dbms_output.put_line('the value : '|| v);
close c;
end; 9 10
11 /
the value : 2

PL/SQL procedure successfully completed.

新session中

SQL> select * from test1 where a = 1;

A B C D
-------------------- -------------------- -------------------- ----------
1 BBBBBBBBBBBBBBBBBBBB CCCCCCCCCCCCCCCCCCCC 2

SQL> update test1 set d = 0 where a = 1;

1 row updated.

SQL> commit;

Commit complete.


cursor 输出依然是 更新前数据
close c;
end; 9 10
11 /
the value : 2

PL/SQL procedure successfully completed.



因为查询结果集的确立,只需要获取系统SCN 就可以了,这是在open 的时候就确定了的
cursor 虚拟的指向了集合的第一行,但是并没有真正地获取数据,因为集合本身并没有产生。


下面是trace file

=====================
PARSING IN CURSOR #1 len=185 dep=0 uid=41 oct=47 lid=41 tim=1052309975752642 hv=2103278157 ad='54d236cc'
declare
v varchar2(30);
cursor c is select d from test1 where a = 1;
begin
open c;
dbms_lock.sleep(60);
fetch c into v;
dbms_output.put_line('the value : '|| v);
close c;
end;
END OF STMT
PARSE #1:c=0,e=557,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=4,tim=1052309975752624
BINDS #1:
=====================
PARSING IN CURSOR #2 len=31 dep=1 uid=41 oct=3 lid=41 tim=1052309975753431 hv=2424123046 ad='54d0d49c'
SELECT d from test1 where a = 1
END OF STMT
PARSE #2:c=0,e=175,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=4,tim=1052309975753416
BINDS #2:
EXEC #2:c=0,e=129,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=4,tim=1052309975753684
*** 2004-02-23 11:44:36.612
WAIT #1: nam='PL/SQL lock timer' ela= 60000938 p1=6000 p2=0 p3=0 open cursor 之后开始sleep
FETCH #2:c=0,e=291,p=0,cr=3,cu=0,mis=0,r=1,dep=1,og=4,tim=1052310035755394 fetch 的时候真正地产生了 cr=3 ,r=1 表示逻辑读3,rows 1
EXEC #1:c=1953,e=60003409,p=0,cr=3,cu=0,mis=0,r=1,dep=0,og=4,tim=1052310035756319
WAIT #1: nam='SQL*Net message to client' ela= 8 p1=1650815232 p2=1 p3=0


综合以上论述,与SG 描述并不矛盾。我们要把这里的 the active set 看做一个虚拟的并没有真实存在的集合。

 

即使在游标的查询中有大量的排序甚至join,在open的时候也不会去读数据

SQL> create table tt as select * from t;

Table created.

SQL> desc tt
Name Null? Type
----------------------------------------- -------- ----------------------------
OWNER VARCHAR2(30)
OBJECT_NAME VARCHAR2(128)
SUBOBJECT_NAME VARCHAR2(30)
OBJECT_ID NUMBER
DATA_OBJECT_ID NUMBER
OBJECT_TYPE VARCHAR2(18)
CREATED DATE
LAST_DDL_TIME DATE
TIMESTAMP VARCHAR2(19)
STATUS VARCHAR2(7)
TEMPORARY VARCHAR2(1)
GENERATED VARCHAR2(1)
SECONDARY VARCHAR2(1)

SQL> select count(*) from tt;

COUNT(*)
----------
81920
SQL> set serverout on
SQL> alter session set events '10046 trace name context forever,level 12';

Session altered.

SQL>
SQL> declare
2 v varchar2(30);
3 cursor c is select object_name from tt order by DATA_OBJECT_ID ;
begin
open c;
4 5 6 dbms_lock.sleep(5);
7 fetch c into v;
dbms_output.put_line('the value 1 : '|| v);
8 9 fetch c into v;
dbms_output.put_line('the value 2 : '|| v);
10 11
close c;
12 13 end;
14
15 /
the value 1 : CLU$
the value 2 : COL$

PL/SQL procedure successfully completed.

SQL> alter session set events '10046 trace name context off';

Session altered.

SQL> exit
Disconnected from Oracle9i Enterprise Edition Release 9.2.0.3.0 - Production
With the Partitioning, OLAP and Oracle Data Mining options
JServer Release 9.2.0.3.0 - Production
[oracle@jumper udump]$ cat *
/opt/oracle/admin/hsjf/udump/hsjf_ora_3613.trc
Oracle9i Enterprise Edition Release 9.2.0.3.0 - Production
With the Partitioning, OLAP and Oracle Data Mining options
JServer Release 9.2.0.3.0 - Production
ORACLE_HOME = /opt/oracle/product/9.2.0
System name: Linux
Node name: jumper.hurray.com.cn
Release: 2.4.18-14
Version: #1 Wed Sep 4 13:35:50 EDT 2002
Machine: i686
Instance name: hsjf
Redo thread mounted by this instance: 1
Oracle process number: 15
Unix process pid: 3613, image:
oracle@jumper.hurray.com.cn (TNS V1-V3)

*** 2004-03-12 12:47:53.987
*** SESSION ID16.353) 2004-03-12 12:47:53.986
APPNAME mod='SQL*Plus' mh=3669949024 act='' ah=4029777240
=====================
PARSING IN CURSOR #1 len=68 dep=0 uid=41 oct=42 lid=41 tim=1053832494128016 hv=1346161232 ad='53dd510c'
alter session set events '10046 trace name context forever,level 12'
END OF STMT
EXEC #1:c=0,e=201,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=4,tim=1053832494127359
WAIT #1: nam='SQL*Net message to client' ela= 8 p1=1650815232 p2=1 p3=0
*** 2004-03-12 12:48:04.817
WAIT #1: nam='SQL*Net message from client' ela= 10576349 p1=1650815232 p2=1 p3=0
=====================
PARSING IN CURSOR #1 len=261 dep=0 uid=41 oct=47 lid=41 tim=1053832504706787 hv=1653057355 ad='53d68e9c'
declare
v varchar2(30);
cursor c is select object_name from tt order by DATA_OBJECT_ID ;
begin
open c;
dbms_lock.sleep(5);
fetch c into v;
dbms_output.put_line('the value 1 : '|| v);
fetch c into v;
dbms_output.put_line('the value 2 : '|| v);
close c;
end;
END OF STMT
PARSE #1:c=1953,e=1535,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=4,tim=1053832504706759
BINDS #1:
=====================
PARSING IN CURSOR #3 len=48 dep=2 uid=0 oct=3 lid=0 tim=1053832504707818 hv=3997906522 ad='53672788'
select user# from sys.user$ where name = 'OUTLN'
END OF STMT
PARSE #3:c=0,e=158,p=0,cr=0,cu=0,mis=0,r=0,dep=2,og=4,tim=1053832504707790
BINDS #3:
EXEC #3:c=0,e=188,p=0,cr=0,cu=0,mis=0,r=0,dep=2,og=4,tim=1053832504708208
FETCH #3:c=0,e=148,p=0,cr=2,cu=0,mis=0,r=1,dep=2,og=4,tim=1053832504708421
STAT #3 id=1 cnt=1 pid=0 pos=1 obj=22 op='TABLE ACCESS BY INDEX ROWID OBJ#(22) (cr=2 r=0 w=0 time=118 us)'
STAT #3 id=2 cnt=1 pid=1 pos=1 obj=44 op='INDEX UNIQUE SCAN OBJ#(44) (cr=1 r=0 w=0 time=62 us)'
=====================
PARSING IN CURSOR #2 len=51 dep=1 uid=41 oct=3 lid=41 tim=1053832504709113 hv=1161579795 ad='53d5bd50'
SELECT object_name from tt order by DATA_OBJECT_ID
END OF STMT
PARSE #2:c=1953,e=1671,p=0,cr=2,cu=0,mis=0,r=0,dep=1,og=4,tim=1053832504709096
BINDS #2:
EXEC #2:c=0,e=171,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=4,tim=1053832504709413
WAIT #1: nam='PL/SQL lock timer' ela= 5000254 p1=500 p2=0 p3=0
WAIT #2: nam='db file scattered read' ela= 412 p1=11 p2=2194 p3=7
WAIT #2: nam='db file scattered read' ela= 349 p1=11 p2=2201 p3=8
WAIT #2: nam='db file scattered read' ela= 371 p1=11 p2=2209 p3=8
WAIT #2: nam='db file scattered read' ela= 385 p1=11 p2=2217 p3=8
WAIT #2: nam='db file scattered read' ela= 425 p1=11 p2=2225 p3=8
WAIT #2: nam='db file scattered read' ela= 390 p1=11 p2=2233 p3=8
WAIT #2: nam='db file scattered read' ela= 384 p1=11 p2=2241 p3=8
WAIT #2: nam='db file scattered read' ela= 374 p1=11 p2=2249 p3=8
WAIT #2: nam='db file scattered read' ela= 380 p1=11 p2=2257 p3=8
WAIT #2: nam='db file scattered read' ela= 407 p1=11 p2=2265 p3=8
WAIT #2: nam='db file scattered read' ela= 418 p1=11 p2=2273 p3=8
WAIT #2: nam='db file scattered read' ela= 360 p1=11 p2=2281 p3=8
WAIT #2: nam='db file scattered read' ela= 375 p1=11 p2=2289 p3=8
WAIT #2: nam='db file scattered read' ela= 361 p1=11 p2=2297 p3=8
WAIT #2: nam='db file scattered read' ela= 383 p1=11 p2=2305 p3=8
WAIT #2: nam='db file scattered read' ela= 370 p1=11 p2=3081 p3=8
WAIT #2: nam='db file scattered read' ela= 3892 p1=11 p2=3209 p3=32
WAIT #2: nam='db file scattered read' ela= 3675 p1=11 p2=3241 p3=32
WAIT #2: nam='db file scattered read' ela= 3595 p1=11 p2=3273 p3=32
WAIT #2: nam='db file scattered read' ela= 3506 p1=11 p2=3305 p3=32
WAIT #2: nam='db file scattered read' ela= 3525 p1=11 p2=3337 p3=32
WAIT #2: nam='db file scattered read' ela= 3603 p1=11 p2=3369 p3=32
WAIT #2: nam='db file scattered read' ela= 3618 p1=11 p2=3401 p3=32
WAIT #2: nam='db file scattered read' ela= 3578 p1=11 p2=3433 p3=32
WAIT #2: nam='db file scattered read' ela= 3845 p1=11 p2=3465 p3=32
WAIT #2: nam='db file scattered read' ela= 3397 p1=11 p2=3497 p3=32
WAIT #2: nam='db file scattered read' ela= 3499 p1=11 p2=3529 p3=32
WAIT #2: nam='db file scattered read' ela= 3523 p1=11 p2=3561 p3=32
WAIT #2: nam='db file scattered read' ela= 3339 p1=11 p2=3593 p3=32
WAIT #2: nam='db file scattered read' ela= 4179 p1=11 p2=3625 p3=32
WAIT #2: nam='db file scattered read' ela= 3512 p1=11 p2=3657 p3=32
WAIT #2: nam='db file scattered read' ela= 3580 p1=11 p2=3689 p3=32
WAIT #2: nam='db file scattered read' ela= 3342 p1=11 p2=3721 p3=32
WAIT #2: nam='db file scattered read' ela= 3436 p1=11 p2=3753 p3=32
WAIT #2: nam='db file scattered read' ela= 3573 p1=11 p2=3785 p3=32
WAIT #2: nam='db file scattered read' ela= 3443 p1=11 p2=3817 p3=32
WAIT #2: nam='db file scattered read' ela= 3512 p1=11 p2=3849 p3=32
WAIT #2: nam='db file scattered read' ela= 4157 p1=11 p2=3881 p3=32
WAIT #2: nam='db file scattered read' ela= 3400 p1=11 p2=3913 p3=32
WAIT #2: nam='db file scattered read' ela= 3515 p1=11 p2=3945 p3=32
WAIT #2: nam='db file scattered read' ela= 3485 p1=11 p2=3977 p3=32
WAIT #2: nam='db file scattered read' ela= 3519 p1=11 p2=4009 p3=32
WAIT #2: nam='db file scattered read' ela= 3577 p1=11 p2=4041 p3=32
WAIT #2: nam='db file scattered read' ela= 3342 p1=11 p2=4073 p3=31
WAIT #2: nam='direct path write' ela= 11 p1=201 p2=764 p3=7
WAIT #2: nam='direct path write' ela= 22 p1=201 p2=771 p3=7
WAIT #2: nam='direct path write' ela= 1 p1=201 p2=778 p3=7
WAIT #2: nam='direct path write' ela= 2 p1=201 p2=785 p3=5
WAIT #2: nam='direct path read' ela= 79 p1=201 p2=769 p3=7
WAIT #2: nam='direct path read' ela= 35 p1=201 p2=521 p3=7
WAIT #2: nam='direct path read' ela= 35 p1=201 p2=645 p3=4
WAIT #2: nam='direct path read' ela= 33 p1=201 p2=726 p3=7
WAIT #2: nam='direct path read' ela= 34 p1=201 p2=678 p3=7
FETCH #2:c=523437,e=592460,p=1076,cr=1027,cu=6,mis=0,r=1,dep=1,og=4,tim=1053832510302853
FETCH #2:c=0,e=70,p=0,cr=0,cu=0,mis=0,r=1,dep=1,og=4,tim=1053832510304332
UNMAP #2:c=1953,e=478,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=4,tim=1053832510305241
WAIT #2: nam='direct path read' ela= 19 p1=201 p2=776 p3=1
WAIT #2: nam='direct path read' ela= 4 p1=201 p2=528 p3=7
WAIT #2: nam='direct path read' ela= 2 p1=201 p2=733 p3=7
WAIT #2: nam='direct path read' ela= 25 p1=201 p2=685 p3=7
EXEC #1:c=529296,e=5599226,p=1076,cr=1029,cu=6,mis=0,r=1,dep=0,og=4,tim=1053832510306313
WAIT #1: nam='SQL*Net message to client' ela= 7 p1=1650815232 p2=1 p3=0
WAIT #1: nam='SQL*Net message from client' ela= 829 p1=1650815232 p2=1 p3=0
=====================
PARSING IN CURSOR #3 len=52 dep=0 uid=41 oct=47 lid=41 tim=1053832510308690 hv=1307714173 ad='53dd2cb0'
BEGIN DBMS_OUTPUT.GET_LINES(:LINES, :NUMLINES); END;
END OF STMT
PARSE #3:c=1953,e=677,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=4,tim=1053832510308675
BINDS #3:
bind 0: dty=1 mxl=2000(255) mal=25 scl=00 pre=00 oacflg=43 oacfl2=10 size=2000 offset=0
bfp=404edeb8 bln=255 avl=00 flg=05
bind 1: dty=2 mxl=22(02) mal=00 scl=00 pre=00 oacflg=01 oacfl2=0 size=24 offset=0
bfp=404efb80 bln=22 avl=02 flg=05
value=25
WAIT #3: nam='SQL*Net message to client' ela= 8 p1=1650815232 p2=1 p3=0
EXEC #3:c=0,e=1006,p=0,cr=0,cu=0,mis=0,r=1,dep=0,og=4,tim=1053832510309876
WAIT #3: nam='SQL*Net message from client' ela= 8459860 p1=1650815232 p2=1 p3=0
STAT #2 id=1 cnt=2 pid=0 pos=1 obj=0 op='SORT ORDER BY (cr=1027 r=1076 w=268 time=592412 us)'
STAT #2 id=2 cnt=81920 pid=1 pos=1 obj=14568 op='TABLE ACCESS FULL TT (cr=1027 r=1022 w=0 time=257994 us)'
=====================
PARSING IN CURSOR #1 len=55 dep=0 uid=41 oct=42 lid=41 tim=1053832518771069 hv=4110456808 ad='53d84de8'
alter session set events '10046 trace name context off'
END OF STMT
PARSE #1:c=0,e=183,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=4,tim=1053832518771046
BINDS #1:
EXEC #1:c=0,e=216,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=4,tim=1053832518771440
[oracle@jumper udump]$

 

原文参考:http://www.itpub.net/showthread.php?threadid=197416

 


bitirainy 发表于:2004.09.08 16:46 ::分类: ( Oracle is anything ) ::阅读:(1134次) :: 评论 (0)
===========================================================
关于block中数据的存储和重组的探究
===========================================================

前言: 在block内部oracle的数据到底是怎么存储的,通过rowid方式的时候又是怎样的,insert/delete/update发生的时候又是怎样的,想仔细探讨一下

先交代block里面数据的基本结构:

SQL> create table tn(a number, b varchar2(1000));

Table created.

SQL> insert into tn select rownum, 'wwweeerrrttt' from all_tables where rownum < 11;

10 rows created.

SQL> commit;

Commit complete.

SQL> exec show_space('tn');
Free Blocks.............................1
Total Blocks............................16
Total Bytes.............................131072
Unused Blocks...........................14
Unused Bytes............................114688
Last Used Ext FileId....................3
Last Used Ext BlockId...................1954
Last Used Block.........................2

SQL> alter system dump datafile 3 block 1955;

System altered.


Block header dump: 0x00c007a3
Object id on Block? Y
seg/obj: 0x66b7 csc: 0x00.1891b8a itc: 1 flg: O typ: 1 - DATA
fsl: 0 fnx: 0x0 ver: 0x01

Itl Xid Uba Flag Lck Scn/Fsc
0x01 xid: 0x0001.011.000000e8 uba: 0x00803494.0147.07 --U- 10 fsc 0x0000.01891b8c

data_block_dump
===============
tsiz: 0x1fb8
hsiz: 0x26
pbl: 0x0ba76c44
bdba: 0x00c007a3
flag=-----------
ntab=1
nrow=10
frre=-1
fsbo=0x26
fseo=0x1efa
avsp=0x1ed4
tosp=0x1ed4
0xeti[0] nrow=10 offs=0 本块存在10条记录
0x12: pri[0] offs=0x1efa ---- 记录的起始物理位置

0x14: pri[1] offs=0x1f0d
0x16: pri[2] offs=0x1f20
0x18: pri[3] offs=0x1f33
0x1a: pri[4] offs=0x1f46
0x1c: pri[5] offs=0x1f59
0x1e: pri[6] offs=0x1f6c
0x20: pri[7] offs=0x1f7f
0x22: pri[8] offs=0x1f92
0x24: pri[9] offs=0x1fa5
block_row_dump:
tab 0, row 0, @0x1efa tl: 19 fb: --H-FL-- lb: 0x1 cc: 2 --- -- lb: 表示属于XID 0x1,cc 表示有2个字段
col 0: [ 2] c1 02 ---- 字段1 长度为2,数据为 c1 02
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74 ---- 字段而长度 12

tab 0, row 1, @0x1f0d
tl: 19 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 03
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 2, @0x1f20
tl: 19 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 04
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 3, @0x1f33
tl: 19 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 05
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 4, @0x1f46
tl: 19 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 06
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 5, @0x1f59
tl: 19 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 07
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 6, @0x1f6c
tl: 19 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 08
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 7, @0x1f7f
tl: 19 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 09
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 8, @0x1f92
tl: 19 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 0a
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 9, @0x1fa5
tl: 19 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 0b
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
end_of_block_dump
End dump data blocks tsn: 2 file#: 3 minblk 1954 maxblk 1955

SQL> delete from tn where a =8 or a = 7;

2 rows deleted.

SQL> commit;

Commit complete.

SQL> alter system dump datafile 3 block 1955;

System altered.

删除2条记录后我们来看block中的变化

Block header dump: 0x00c007a3
Object id on Block? Y
seg/obj: 0x66b7 csc: 0x00.1891b8d itc: 1 flg: O typ: 1 - DATA
fsl: 0 fnx: 0x0 ver: 0x01

Itl Xid Uba Flag Lck Scn/Fsc
0x01 xid: 0x0002.01a.000000e9 uba: 0x00800314.00d0.24 --U- 2 fsc 0x0022.01891b8f

data_block_dump
===============
tsiz: 0x1fb8
hsiz: 0x26
pbl: 0x0ba76c44
bdba: 0x00c007a3
flag=-----------
ntab=1
nrow=10
frre=-1
fsbo=0x26
fseo=0x1efa
avsp=0x1ed4
tosp=0x1efa
0xe: pti[0] nrow=10 offs=0
0x12: pti[0] offs=0x1efa
0x14: pti[1] offs=0x1f0d
0x16: pti[2] offs=0x1f20
0x18: pti[3] offs=0x1f33
0x1a: pti[4] offs=0x1f46
0x1c: pti[5] offs=0x1f59
0x1e: pti[6] offs=0x1f6c --- 这里暂时没有发生变化
0x20: pti[7] offs=0x1f7f --- 这里暂时没有发生变化

0x22: pti[8] offs=0x1f92
0x24: pti[9] offs=0x1fa5
block_row_dump:
tab 0, row 0, @0x1efa
tl: 19 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 02
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 1, @0x1f0d
tl: 19 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 03
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 2, @0x1f20
tl: 19 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 04
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 3, @0x1f33
tl: 19 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 05
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 4, @0x1f46
tl: 19 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 06
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 5, @0x1f59
tl: 19 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 07
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 6, @0x1f6c
tl: 2 fb: --HDFL-- lb: 0x1 ----记录已经被删除
tab 0, row 7, @0x1f7f
tl: 2 fb: --HDFL-- lb: 0x1 ----记录已经被删除

tab 0, row 8, @0x1f92
tl: 19 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 0a
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 9, @0x1fa5
tl: 19 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 0b
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
end_of_block_dump
End dump data blocks tsn: 2 file#: 3 minblk 1955 maxblk 1955

 

SQL> insert into tn values(19,'q');

1 row created.

SQL> commit;

Commit complete.

SQL> alter system dump datafile 3 block 1955;

System altered.

插入一条记录,我们再看
Block header dump: 0x00c007a3
Object id on Block? Y
seg/obj: 0x66b7 csc: 0x00.1891b90 itc: 1 flg: O typ: 1 - DATA
fsl: 0 fnx: 0x0 ver: 0x01

Itl Xid Uba Flag Lck Scn/Fsc
0x01 xid: 0x0003.054.000000e8 uba: 0x00800da8.00d9.19 --U- 1 fsc 0x0000.01891b91

data_block_dump
===============
tsiz: 0x1fb8
hsiz: 0x28
pbl: 0x0ba76c44
bdba: 0x00c007a3
flag=-----------
ntab=1
nrow=11
frre=6
fsbo=0x28
fseo=0x1ef2
avsp=0x1eef
tosp=0x1eef
0xe: pti[0] nrow=11 offs=0
0x12: pti[0] offs=0x1efa
0x14: pti[1] offs=0x1f0d
0x16: pti[2] offs=0x1f20
0x18: pti[3] offs=0x1f33
0x1a: pti[4] offs=0x1f46
0x1c: pti[5] offs=0x1f59
0x1e: pti[6] sfll=7 ----被删除
0x20: pti[7] sfll=-1 ------被删除
0x22: pti[8] offs=0x1f92
0x24: pti[9] offs=0x1fa5
0x26: pti[10] offs=0x1ef2 --------新插入记录

block_row_dump:
tab 0, row 0, @0x1efa
tl: 19 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 02
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 1, @0x1f0d
tl: 19 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 03
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 2, @0x1f20
tl: 19 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 04
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 3, @0x1f33
tl: 19 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 05
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 4, @0x1f46
tl: 19 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 06
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 5, @0x1f59
tl: 19 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 07
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74 ---- row 6,7 已经被清除

tab 0, row 8, @0x1f92
tl: 19 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 0a
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 9, @0x1fa5
tl: 19 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 0b
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 10, @0x1ef2 ----------------------------新插入记录
tl: 8 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 14
col 1: [ 1] 71

end_of_block_dump
End dump data blocks tsn: 2 file#: 3 minblk 1955 maxblk 1955


 

SQL> insert into tn values(19,'qqq');

1 row created.

SQL> commit;

Commit complete.

SQL> alter system dump datafile 3 block 1955;

System altered.

再插入记录我们来看

Block header dump: 0x00c007a3
Object id on Block? Y
seg/obj: 0x66b7 csc: 0x00.1891b92 itc: 1 flg: O typ: 1 - DATA
fsl: 0 fnx: 0x0 ver: 0x01

Itl Xid Uba Flag Lck Scn/Fsc
0x01 xid: 0x0004.02e.000000e7 uba: 0x00800617.00df.1c --U- 1 fsc 0x0000.01891b94

data_block_dump
===============
tsiz: 0x1fb8
hsiz: 0x28
pbl: 0x0ba76c44
bdba: 0x00c007a3
flag=-----------
ntab=1
nrow=11
frre=7
fsbo=0x28
fseo=0x1ee8
avsp=0x1ee5
tosp=0x1ee5
0xe: pti[0] nrow=11 offs=0
0x12: pti[0] offs=0x1efa
0x14: pti[1] offs=0x1f0d
0x16: pti[2] offs=0x1f20
0x18: pti[3] offs=0x1f33
0x1a: pti[4] offs=0x1f46
0x1c: pti[5] offs=0x1f59
0x1e: pti[6] offs=0x1ee8 ------ 新插入记录使用了新的空间,注意 offs 表示物理位置
0x20: pti[7] sfll=-1

0x22: pti[8] offs=0x1f92
0x24: pti[9] offs=0x1fa5
0x26: pti[10] offs=0x1ef2
block_row_dump:
tab 0, row 0, @0x1efa
tl: 19 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 02
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 1, @0x1f0d
tl: 19 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 03
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 2, @0x1f20
tl: 19 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 04
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 3, @0x1f33
tl: 19 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 05
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 4, @0x1f46
tl: 19 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 06
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 5, @0x1f59
tl: 19 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 07
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 6, @0x1ee8 ------------------------新插入记录

tl: 10 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 14
col 1: [ 3] 71 71 71
tab 0, row 8, @0x1f92
tl: 19 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 0a
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 9, @0x1fa5
tl: 19 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 0b
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 10, @0x1ef2
tl: 8 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 14
col 1: [ 1] 71
end_of_block_dump
End dump data blocks tsn: 2 file#: 3 minblk 1955 maxblk 1955

 

 

SQL> update tn set b = 'qqqqqq' where a = 19;

2 rows updated.

SQL> commit;

Commit complete.

SQL> alter system dump datafile 3 block 1955;

System altered.

更新新插入的两条记录
Block header dump: 0x00c007a3
Object id on Block? Y
seg/obj: 0x66b7 csc: 0x00.1891b95 itc: 1 flg: O typ: 1 - DATA
fsl: 0 fnx: 0x0 ver: 0x01

Itl Xid Uba Flag Lck Scn/Fsc
0x01 xid: 0x0005.047.000000e7 uba: 0x00803819.0154.04 --U- 2 fsc 0x0000.01891b97

data_block_dump
===============
tsiz: 0x1fb8
hsiz: 0x28
pbl: 0x0ba76c44
bdba: 0x00c007a3
flag=-----------
ntab=1
nrow=11
frre=7
fsbo=0x28
fseo=0x1ece
avsp=0x1ede
tosp=0x1ede
0xe: pti[0] nrow=11 offs=0
0x12: pti[0] offs=0x1efa
0x14: pti[1] offs=0x1f0d
0x16: pti[2] offs=0x1f20
0x18: pti[3] offs=0x1f33
0x1a: pti[4] offs=0x1f46
0x1c: pti[5] offs=0x1f59
0x1e: pti[6] offs=0x1edb --------首先更新这条, 更新后由于原来地方空间不足,被挪到了0x26: pti[10] offs=0x1ef2 之上
0x20: pti[7] sfll=-1
0x22: pti[8] offs=0x1f92
0x24: pti[9] offs=0x1fa5
0x26: pti[10] offs=0x1ece ------- 更新后又由于先更新了0x1e: pti[6],位置不足,又挪到了0x1ece

block_row_dump:
tab 0, row 0, @0x1efa
tl: 19 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 02
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 1, @0x1f0d
tl: 19 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 03
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 2, @0x1f20
tl: 19 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 04
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 3, @0x1f33
tl: 19 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 05
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 4, @0x1f46
tl: 19 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 06
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 5, @0x1f59
tl: 19 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 07
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 6, @0x1edb
tl: 13 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 14
col 1: [ 6] 71 71 71 71 71 71
tab 0, row 8, @0x1f92
tl: 19 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 0a
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 9, @0x1fa5
tl: 19 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 0b
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 10, @0x1ece
tl: 13 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 14
col 1: [ 6] 71 71 71 71 71 71
end_of_block_dump
End dump data blocks tsn: 2 file#: 3 minblk 1955 maxblk 1955

 

SQL> update tn set b = 'qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq
qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq' where a = 19;

2 rows updated.

SQL> commit;

Commit complete.

SQL> alter system dump datafile 3 block 1955;

System altered.

继续更新一次看看,又重复了上面的步骤,提升了 物理位置

Block header dump: 0x00c007a3
Object id on Block? Y
seg/obj: 0x66b7 csc: 0x00.1891b98 itc: 1 flg: O typ: 1 - DATA
fsl: 0 fnx: 0x0 ver: 0x01

Itl Xid Uba Flag Lck Scn/Fsc
0x01 xid: 0x0006.044.000000f2 uba: 0x00801660.00da.0f --U- 2 fsc 0x0000.01891b99

data_block_dump
===============
tsiz: 0x1fb8
hsiz: 0x28
pbl: 0x0ba76c44
bdba: 0x00c007a3
flag=-----------
ntab=1
nrow=11
frre=7
fsbo=0x28
fseo=0x1de0
avsp=0x1e0a
tosp=0x1e0a
0xe: pti[0] nrow=11 offs=0
0x12: pti[0] offs=0x1efa
0x14: pti[1] offs=0x1f0d
0x16: pti[2] offs=0x1f20
0x18: pti[3] offs=0x1f33
0x1a: pti[4] offs=0x1f46
0x1c: pti[5] offs=0x1f59
0x1e: pti[6] offs=0x1e57 -------发生变化

0x20: pti[7] sfll=-1
0x22: pti[8] offs=0x1f92
0x24: pti[9] offs=0x1fa5
0x26: pti[10] offs=0x1de0 ------ 发生变化

block_row_dump:
tab 0, row 0, @0x1efa
tl: 19 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 02
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 1, @0x1f0d
tl: 19 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 03
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 2, @0x1f20
tl: 19 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 04
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 3, @0x1f33
tl: 19 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 05
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 4, @0x1f46
tl: 19 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 06
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 5, @0x1f59
tl: 19 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 07
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 6, @0x1e57
tl: 119 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 14
col 1: [112] ---------------数据长度大大增加
71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71
71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71
71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71
71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71
71 71 71 71 71 71 71 71 71 71 71 71

tab 0, row 8, @0x1f92
tl: 19 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 0a
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 9, @0x1fa5
tl: 19 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 0b
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 10, @0x1de0
tl: 119 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 14
col 1: [112] ---------------数据长度大大增加
71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71
71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71
71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71
71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71
71 71 71 71 71 71 71 71 71 71 71 71

end_of_block_dump
End dump data blocks tsn: 2 file#: 3 minblk 1955 maxblk 1955

 

 

SQL> alter table tn add ( c varchar2(30));

Table altered.

SQL> alter system dump datafile 3 block 1955;

System altered.

给表增加一个字段,我们发现数据没有变化

Block header dump: 0x00c007a3
Object id on Block? Y
seg/obj: 0x66b7 csc: 0x00.1891b98 itc: 1 flg: O typ: 1 - DATA
fsl: 0 fnx: 0x0 ver: 0x01

Itl Xid Uba Flag Lck Scn/Fsc
0x01 xid: 0x0006.044.000000f2 uba: 0x00801660.00da.0f --U- 2 fsc 0x0000.01891b99

data_block_dump
===============
tsiz: 0x1fb8
hsiz: 0x28
pbl: 0x0ba76c44
bdba: 0x00c007a3
flag=-----------
ntab=1
nrow=11
frre=7
fsbo=0x28
fseo=0x1de0
avsp=0x1e0a
tosp=0x1e0a
0xe: pti[0] nrow=11 offs=0
0x12: pti[0] offs=0x1efa
0x14: pti[1] offs=0x1f0d
0x16: pti[2] offs=0x1f20
0x18: pti[3] offs=0x1f33
0x1a: pti[4] offs=0x1f46
0x1c: pti[5] offs=0x1f59
0x1e: pti[6] offs=0x1e57
0x20: pti[7] sfll=-1
0x22: pti[8] offs=0x1f92
0x24: pti[9] offs=0x1fa5
0x26: pti[10] offs=0x1de0
block_row_dump:
tab 0, row 0, @0x1efa
tl: 19 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 02
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 1, @0x1f0d
tl: 19 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 03
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 2, @0x1f20
tl: 19 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 04
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 3, @0x1f33
tl: 19 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 05
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 4, @0x1f46
tl: 19 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 06
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 5, @0x1f59
tl: 19 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 07
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 6, @0x1e57
tl: 119 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 14
col 1: [112]
71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71
71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71
71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71
71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71
71 71 71 71 71 71 71 71 71 71 71 71
tab 0, row 8, @0x1f92
tl: 19 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 0a
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 9, @0x1fa5
tl: 19 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 0b
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
tab 0, row 10, @0x1de0
tl: 119 fb: --H-FL-- lb: 0x1 cc: 2
col 0: [ 2] c1 14
col 1: [112]
71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71
71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71
71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71
71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71
71 71 71 71 71 71 71 71 71 71 71 71
end_of_block_dump
End dump data blocks tsn: 2 file#: 3 minblk 1955 maxblk 1955


 

SQL> update tn set c = 'p';

10 rows updated.

SQL> commit;

Commit complete.

SQL> alter system dump datafile 3 block 1955;

System altered.

更新增加的字段,我们发现所有的行都被提升了物理位置

Block header dump: 0x00c007a3
Object id on Block? Y
seg/obj: 0x66b7 csc: 0x00.1891b9c itc: 1 flg: O typ: 1 - DATA
fsl: 0 fnx: 0x0 ver: 0x01

Itl Xid Uba Flag Lck Scn/Fsc
0x01 xid: 0x0001.00a.000000e8 uba: 0x00803494.0147.11 --U- 10 fsc 0x0000.01891b9e

data_block_dump
===============
tsiz: 0x1fb8
hsiz: 0x28
pbl: 0x0ba76c44
bdba: 0x00c007a3
flag=-----------
ntab=1
nrow=11
frre=7
fsbo=0x28
fseo=0x1c46
avsp=0x1df6
tosp=0x1df6
0xe: pti[0] nrow=11 offs=0
0x12: pti[0] offs=0x1dcb ---------所有行的物理位置都发生了变化,因为原来的位置装不下多出来的数据了
0x14: pti[1] offs=0x1db6
0x16: pti[2] offs=0x1da1
0x18: pti[3] offs=0x1d8c
0x1a: pti[4] offs=0x1d77
0x1c: pti[5] offs=0x1d62
0x1e: pti[6] offs=0x1ce9
0x20: pti[7] sfll=-1
0x22: pti[8] offs=0x1cd4
0x24: pti[9] offs=0x1cbf
0x26: pti[10] offs=0x1c46

block_row_dump:
tab 0, row 0, @0x1dcb
tl: 21 fb: --H-FL-- lb: 0x1 cc: 3
col 0: [ 2] c1 02
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
col 2: [ 1] 70
tab 0, row 1, @0x1db6
tl: 21 fb: --H-FL-- lb: 0x1 cc: 3
col 0: [ 2] c1 03
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
col 2: [ 1] 70
tab 0, row 2, @0x1da1
tl: 21 fb: --H-FL-- lb: 0x1 cc: 3
col 0: [ 2] c1 04
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
col 2: [ 1] 70
tab 0, row 3, @0x1d8c
tl: 21 fb: --H-FL-- lb: 0x1 cc: 3
col 0: [ 2] c1 05
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
col 2: [ 1] 70
tab 0, row 4, @0x1d77
tl: 21 fb: --H-FL-- lb: 0x1 cc: 3
col 0: [ 2] c1 06
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
col 2: [ 1] 70
tab 0, row 5, @0x1d62
tl: 21 fb: --H-FL-- lb: 0x1 cc: 3
col 0: [ 2] c1 07
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
col 2: [ 1] 70
tab 0, row 6, @0x1ce9
tl: 121 fb: --H-FL-- lb: 0x1 cc: 3
col 0: [ 2] c1 14
col 1: [112]
71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71
71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71
71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71
71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71
71 71 71 71 71 71 71 71 71 71 71 71
col 2: [ 1] 70
tab 0, row 8, @0x1cd4
tl: 21 fb: --H-FL-- lb: 0x1 cc: 3
col 0: [ 2] c1 0a
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
col 2: [ 1] 70
tab 0, row 9, @0x1cbf
tl: 21 fb: --H-FL-- lb: 0x1 cc: 3
col 0: [ 2] c1 0b
col 1: [12] 77 77 77 65 65 65 72 72 72 74 74 74
col 2: [ 1] 70
tab 0, row 10, @0x1c46
tl: 121 fb: --H-FL-- lb: 0x1 cc: 3
col 0: [ 2] c1 14
col 1: [112]
71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71
71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71
71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71
71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71 71
71 71 71 71 71 71 71 71 71 71 71 71
col 2: [ 1] 70
end_of_block_dump
End dump data blocks tsn: 2 file#: 3 minblk 1955 maxblk 1955


 

SQL> update tn set b = lpad('sd',999);

10 rows updated.

SQL> commit;

Commit complete.

SQL> alter system dump datafile 3 block 1955;

System altered.

SQL> update tn set b ='qqqqqqq';

10 rows updated.

SQL> commit;

Commit complete.

SQL> alter system dump datafile 3 block 1955;

System altered.

首先更新到让记录发生了行迁移,然后再更新回来,因为这时数据太大,就不把发生迁移后的数据全部贴出来,给个再更新回来的结果

Block header dump: 0x00c007a3
Object id on Block? Y
seg/obj: 0x66b7 csc: 0x00.1891ba5 itc: 1 flg: O typ: 1 - DATA
fsl: 1 fnx: 0x0 ver: 0x01

Itl Xid Uba Flag Lck Scn/Fsc
0x01 xid: 0x0004.047.000000e7 uba: 0x00800618.00df.08 --U- 11 fsc 0x1b2e.01891ba7

data_block_dump
===============
tsiz: 0x1fb8
hsiz: 0x28
pbl: 0x0ba76c44
bdba: 0x00c007a3
flag=-----------
ntab=1
nrow=11
frre=7
fsbo=0x28
fseo=0x2f6
avsp=0x3d0
tosp=0x1efe
0xe: pti[0] nrow=11 offs=0
0x12: pti[0] offs=0x35c -------位置都发生了变化
0x14: pti[1] offs=0x34b
0x16: pti[2] offs=0x33a
0x18: pti[3] offs=0x329
0x1a: pti[4] offs=0x318
0x1c: pti[5] offs=0x307
0x1e: pti[6] offs=0x2f6
0x20: pti[7] sfll=-1
0x22: pti[8] offs=0x7f0
0x24: pti[9] offs=0x7da
0x26: pti[10] offs=0x760

block_row_dump:
tab 0, row 0, @0x35c
tl: 17 fb: --H-FL-- lb: 0x1 cc: 3
col 0: [ 2] c1 02
col 1: [ 7] 71 71 71 71 71 71 71
col 2: [ 2] 70 70
tab 0, row 1, @0x34b
tl: 17 fb: --H-FL-- lb: 0x1 cc: 3
col 0: [ 2] c1 03
col 1: [ 7] 71 71 71 71 71 71 71
col 2: [ 2] 70 70
tab 0, row 2, @0x33a
tl: 17 fb: --H-FL-- lb: 0x1 cc: 3
col 0: [ 2] c1 04
col 1: [ 7] 71 71 71 71 71 71 71
col 2: [ 2] 70 70
tab 0, row 3, @0x329
tl: 17 fb: --H-FL-- lb: 0x1 cc: 3
col 0: [ 2] c1 05
col 1: [ 7] 71 71 71 71 71 71 71
col 2: [ 2] 70 70
tab 0, row 4, @0x318
tl: 17 fb: --H-FL-- lb: 0x1 cc: 3
col 0: [ 2] c1 06
col 1: [ 7] 71 71 71 71 71 71 71
col 2: [ 2] 70 70
tab 0, row 5, @0x307
tl: 17 fb: --H-FL-- lb: 0x1 cc: 3
col 0: [ 2] c1 07
col 1: [ 7] 71 71 71 71 71 71 71
col 2: [ 2] 70 70
tab 0, row 6, @0x2f6
tl: 17 fb: --H-FL-- lb: 0x1 cc: 3
col 0: [ 2] c1 14
col 1: [ 7] 71 71 71 71 71 71 71
col 2: [ 2] 70 70
tab 0, row 8, @0x7f0 -------------- 从row 8 --- row 10 的记录已经迁移到新的block中,这里保留的是新块的物理位置
tl: 9 fb: --H----- lb: 0x1 cc: 0
nrid: 0x00c007a4.0 ------------ 迁移到了 block编号为 0x00c007a4 的块中的 row 0 (本块为 0x00c007a3)
tab 0, row 9, @0x7da
tl: 9 fb: --H----- lb: 0x1 cc: 0
nrid: 0x00c007a4.1 迁移到了 block编号为 0x00c007a4 的块中的 row 1 (本块为 0x00c007a3)
tab 0, row 10, @0x760
tl: 9 fb: --H----- lb: 0x1 cc: 0
nrid: 0x00c007a4.2 迁移到了 block编号为 0x00c007a4 的块中的 row 2 (本块为 0x00c007a3)

end_of_block_dump
End dump data blocks tsn: 2 file#: 3 minblk 1955 maxblk 1955

 

 

SQL> insert into tn values(1,1,1);

1 row created.

SQL> commit;

Commit complete.

SQL> alter system dump datafile 3 block 1955;

System altered.

再插入记录,我们看看位置,我们发现该块并没有被插入记录,记录是插入到了 另外一个块(虽然该块现在空间使用率并不高)
这是因为在update的时候先脱离了freelist然后又回到freelist了,排在了 block 1956 之后

Block header dump: 0x00c007a3
Object id on Block? Y
seg/obj: 0x66b7 csc: 0x00.1891ba5 itc: 1 flg: O typ: 1 - DATA
fsl: 1 fnx: 0x0 ver: 0x01

Itl Xid Uba Flag Lck Scn/Fsc
0x01 xid: 0x0004.047.000000e7 uba: 0x00800618.00df.08 --U- 11 fsc 0x1b2e.01891ba7

data_block_dump
===============
tsiz: 0x1fb8
hsiz: 0x28
pbl: 0x0ba76c44
bdba: 0x00c007a3
flag=-----------
ntab=1
nrow=11
frre=7
fsbo=0x28
fseo=0x2f6
avsp=0x3d0
tosp=0x1efe
0xe: pti[0] nrow=11 offs=0
0x12: pti[0] offs=0x35c
0x14: pti[1] offs=0x34b
0x16: pti[2] offs=0x33a
0x18: pti[3] offs=0x329
0x1a: pti[4] offs=0x318
0x1c: pti[5] offs=0x307
0x1e: pti[6] offs=0x2f6
0x20: pti[7] sfll=-1
0x22: pti[8] offs=0x7f0
0x24: pti[9] offs=0x7da
0x26: pti[10] offs=0x760
block_row_dump:
tab 0, row 0, @0x35c
tl: 17 fb: --H-FL-- lb: 0x1 cc: 3
col 0: [ 2] c1 02
col 1: [ 7] 71 71 71 71 71 71 71
col 2: [ 2] 70 70
tab 0, row 1, @0x34b
tl: 17 fb: --H-FL-- lb: 0x1 cc: 3
col 0: [ 2] c1 03
col 1: [ 7] 71 71 71 71 71 71 71
col 2: [ 2] 70 70
tab 0, row 2, @0x33a
tl: 17 fb: --H-FL-- lb: 0x1 cc: 3
col 0: [ 2] c1 04
col 1: [ 7] 71 71 71 71 71 71 71
col 2: [ 2] 70 70
tab 0, row 3, @0x329
tl: 17 fb: --H-FL-- lb: 0x1 cc: 3
col 0: [ 2] c1 05
col 1: [ 7] 71 71 71 71 71 71 71
col 2: [ 2] 70 70
tab 0, row 4, @0x318
tl: 17 fb: --H-FL-- lb: 0x1 cc: 3
col 0: [ 2] c1 06
col 1: [ 7] 71 71 71 71 71 71 71
col 2: [ 2] 70 70
tab 0, row 5, @0x307
tl: 17 fb: --H-FL-- lb: 0x1 cc: 3
col 0: [ 2] c1 07
col 1: [ 7] 71 71 71 71 71 71 71
col 2: [ 2] 70 70
tab 0, row 6, @0x2f6
tl: 17 fb: --H-FL-- lb: 0x1 cc: 3
col 0: [ 2] c1 14
col 1: [ 7] 71 71 71 71 71 71 71
col 2: [ 2] 70 70
tab 0, row 8, @0x7f0
tl: 9 fb: --H----- lb: 0x1 cc: 0
nrid: 0x00c007a4.0
tab 0, row 9, @0x7da
tl: 9 fb: --H----- lb: 0x1 cc: 0
nrid: 0x00c007a4.1
tab 0, row 10, @0x760
tl: 9 fb: --H----- lb: 0x1 cc: 0
nrid: 0x00c007a4.2
end_of_block_dump
End dump data blocks tsn: 2 file#: 3 minblk 1955 maxblk 1955


 

 

SQL> insert into tn select 1,1,1 from all_objects where rownum < 1001;

1000 rows created.

SQL> commit;

Commit complete.

SQL> alter system dump datafile 3 block 1955;

System altered.

SQL>

再插入1000条记录,我们发现其中600条插到了 block 1956中,而本块只插入了400条,这是因为freelist中的顺序问题
并且我们发现,本块中原来存在的记录已经完全重新组织过,物理位置都因为insert而发生了变化
也就是说oracle 的 block中的记录物理位置是可能重组的,但不变的是 行号,这个行号和物理位置记录在 前部,供通过
rowid 查询的时候快速定位

Block header dump: 0x00c007a3
Object id on Block? Y
seg/obj: 0x66b7 csc: 0x00.1891bab itc: 1 flg: O typ: 1 - DATA
fsl: 0 fnx: 0x0 ver: 0x01

Itl Xid Uba Flag Lck Scn/Fsc
0x01 xid: 0x0006.01e.000000f2 uba: 0x00801660.00da.14 --U- 400 fsc 0x0000.01891bad

data_block_dump
===============
tsiz: 0x1fb8
hsiz: 0x346
pbl: 0x0ba76c44
bdba: 0x00c007a3
flag=-----------
ntab=1
nrow=410
frre=-1
fsbo=0x346
fseo=0xf86
avsp=0xc40
tosp=0xc40
0xe: pti[0] nrow=410 offs=0
0x12: pti[0] offs=0x1fa7 先前的7条记录物理位置已经发生变化但是行号没有改变
0x14: pti[1] offs=0x1f96 先前的7条记录物理位置已经发生变化但是行号没有改变
0x16: pti[2] offs=0x1f85 先前的7条记录物理位置已经发生变化但是行号没有改变
0x18: pti[3] offs=0x1f74 先前的7条记录物理位置已经发生变化但是行号没有改变
0x1a: pti[4] offs=0x1f63 先前的7条记录物理位置已经发生变化但是行号没有改变
0x1c: pti[5] offs=0x1f52 先前的7条记录物理位置已经发生变化但是行号没有改变
0x1e: pti[6] offs=0x1f41 先前的7条记录物理位置已经发生变化但是行号没有改变
0x20: pti[7] offs=0x18b4 该行号已经被新的记录插入
0x22: pti[8] offs=0x1f38 该行迁移发生的记录没有发生变化,这是因为这样不用更新索引
0x24: pti[9] offs=0x1f2f 该行迁移发生的记录没有发生变化,这是因为这样不用更新索引
0x26: pti[10] offs=0x1f26 该行迁移发生的记录没有发生变化,这是因为这样不用更新索引
0x28: pti[11] offs=0x18be 新插入的记录
0x2a: pti[12] offs=0x18c8 新插入的记录
0x2c: pti[13] offs=0x18d2 新插入的记录
0x2e: pti[14] offs=0x18dc 新插入的记录
0x30: pti[15] offs=0x18e6 新插入的记录
0x32: pti[16] offs=0x18f0 新插入的记录
0x34: pti[17] offs=0x18fa 新插入的记录
0x36: pti[18] offs=0x1904 新插入的记录
……………………………………………………
省略掉一些

block_row_dump:
tab 0, row 0, @0x1fa7
tl: 17 fb: --H-FL-- lb: 0x0 cc: 3
col 0: [ 2] c1 02
col 1: [ 7] 71 71 71 71 71 71 71
col 2: [ 2] 70 70
tab 0, row 1, @0x1f96
tl: 17 fb: --H-FL-- lb: 0x0 cc: 3
col 0: [ 2] c1 03
col 1: [ 7] 71 71 71 71 71 71 71
col 2: [ 2] 70 70
tab 0, row 2, @0x1f85
tl: 17 fb: --H-FL-- lb: 0x0 cc: 3
col 0: [ 2] c1 04
col 1: [ 7] 71 71 71 71 71 71 71
col 2: [ 2] 70 70
tab 0, row 3, @0x1f74
tl: 17 fb: --H-FL-- lb: 0x0 cc: 3
col 0: [ 2] c1 05
col 1: [ 7] 71 71 71 71 71 71 71
col 2: [ 2] 70 70
tab 0, row 4, @0x1f63
tl: 17 fb: --H-FL-- lb: 0x0 cc: 3
col 0: [ 2] c1 06
col 1: [ 7] 71 71 71 71 71 71 71
col 2: [ 2] 70 70
tab 0, row 5, @0x1f52
tl: 17 fb: --H-FL-- lb: 0x0 cc: 3
col 0: [ 2] c1 07
col 1: [ 7] 71 71 71 71 71 71 71
col 2: [ 2] 70 70
tab 0, row 6, @0x1f41
tl: 17 fb: --H-FL-- lb: 0x0 cc: 3
col 0: [ 2] c1 14
col 1: [ 7] 71 71 71 71 71 71 71
col 2: [ 2] 70 70
tab 0, row 7, @0x18b4
tl: 10 fb: --H-FL-- lb: 0x1 cc: 3
col 0: [ 2] c1 02
col 1: [ 1] 31
col 2: [ 1] 31
tab 0, row 8, @0x1f38
tl: 9 fb: --H----- lb: 0x0 cc: 0
nrid: 0x00c007a4.0
tab 0, row 9, @0x1f2f
tl: 9 fb: --H----- lb: 0x0 cc: 0
nrid: 0x00c007a4.1
tab 0, row 10, @0x1f26
tl: 9 fb: --H----- lb: 0x0 cc: 0
nrid: 0x00c007a4.2
tab 0, row 11, @0x18be
tl: 10 fb: --H-FL-- lb: 0x1 cc: 3
col 0: [ 2] c1 02
col 1: [ 1] 31
col 2: [ 1] 31
tab 0, row 12, @0x18c8
tl: 10 fb: --H-FL-- lb: 0x1 cc: 3
col 0: [ 2] c1 02
col 1: [ 1] 31
col 2: [ 1] 31
tab 0, row 13, @0x18d2
tl: 10 fb: --H-FL-- lb: 0x1 cc: 3
col 0: [ 2] c1 02
col 1: [ 1] 31
col 2: [ 1] 31
tab 0, row 14, @0x18dc
tl: 10 fb: --H-FL-- lb: 0x1 cc: 3
col 0: [ 2] c1 02
col 1: [ 1] 31
col 2: [ 1] 31
tab 0, row 15, @0x18e6
tl: 10 fb: --H-FL-- lb: 0x1 cc: 3
col 0: [ 2] c1 02
col 1: [ 1] 31
col 2: [ 1] 31
tab 0, row 16, @0x18f0
tl: 10 fb: --H-FL-- lb: 0x1 cc: 3
col 0: [ 2] c1 02
col 1: [ 1] 31

省略掉后面重复的数据

 

一个行跨越block的例子

注意,其中有字段跨越了 block

由这个例子可以看出,一个行在写入的时候实际上数据是倒着写的
假设存在着字段 1,2,3,4,5
先写字段5--4---3--2---1
当跨越block的时候,是字段1和2的前24字节在相临的第二个block
也就是说连字段数据都是靠近末尾部分先写进block
而行若存在索引,则rowid 指向的block实际上应该是在 第二个block,第二个block保存着剩下部分数据的指针


SQL> create table blocks(a char(2000),b char(2000), c char(2000),d char(2000),e char(2000));

Table created.

SQL> set serverout on
SQL> exec show_space('blocks');
Free Blocks.............................0
Total Blocks............................16
Total Bytes.............................131072
Unused Blocks...........................15
Unused Bytes............................122880
Last Used Ext FileId....................3
Last Used Ext BlockId...................32226
Last Used Block.........................1

PL/SQL procedure successfully completed.

SQL> insert into blocks values (1,1,1,1,1);

1 row created.

SQL> commit;

Commit complete.

SQL> alter system dump datafile 3 block min 32227 block max 32228;

System altered.

*** 2003-04-15 14:54:39.078
Start dump data blocks tsn: 2 file#: 3 minblk 32227 maxblk 32228
buffer tsn: 2 rdba: 0x00c07de3 (3/32227)
scn: 0x0000.01892035 seq: 0x01 flg: 0x02 tail: 0x20350601
frmt: 0x02 chkval: 0x0000 type: 0x06=trans data

Block header dump: 0x00c07de3
Object id on Block? Y
seg/obj: 0x66c3 csc: 0x00.1892033 itc: 1 flg: - typ: 1 - DATA
fsl: 0 fnx: 0x0 ver: 0x01

Itl Xid Uba Flag Lck Scn/Fsc
0x01 xid: 0x0003.01d.000000e9 uba: 0x00800dac.00d9.28 --U- 1 fsc 0x0000.01892035

data_block_dump
===============
tsiz: 0x1fb8
hsiz: 0x14
pbl: 0x02556c44
bdba: 0x00c07de3
flag=-----------
ntab=1
nrow=1
frre=-1
fsbo=0x14
fseo=0x81
avsp=0x6d
tosp=0x6d
0xeti[0] nrow=1 offs=0 本块只有一条记录
0x12ri[0] offs=0x81 本记录从 本块的 129个字节为开始 0x81 = 8*16 + 1 = 129
block_row_dump:
tab 0, row 0, @0x81
tl: 7991 本block中 行长度为 7991, 7991 + 129 = 8192 = block_size 8k fb: -----LP- lb: 0x1 cc: 4 (本block包含4个字段内容)

col 0: [1976] ------这里只存放了本字段的后1976个字节
20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
************************************************************************* ------- 表示省略相同部分数据的显示
20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
20
col 1: [2000]
31 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
***********************************************************************
20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
col 2: [2000]
31 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
**********************************************************************
20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
col 3: [2000]
31 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
*************************************************************************
20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
end_of_block_dump




buffer tsn: 2 rdba: 0x00c07de4 (3/32228)
scn: 0x0000.01892035 seq: 0x01 flg: 0x02 tail: 0x20350601
frmt: 0x02 chkval: 0x0000 type: 0x06=trans data

Block header dump: 0x00c07de4
Object id on Block? Y
seg/obj: 0x66c3 csc: 0x00.1892033 itc: 1 flg: O typ: 1 - DATA
fsl: 0 fnx: 0x0 ver: 0x01

Itl Xid Uba Flag Lck Scn/Fsc
0x01 xid: 0x0003.01d.000000e9 uba: 0x00800dac.00d9.29 --U- 1 fsc 0x0000.01892035

data_block_dump
===============
tsiz: 0x1fb8
hsiz: 0x14
pbl: 0x02556c44
bdba: 0x00c07de4
flag=-----------
ntab=1
nrow=1
frre=-1
fsbo=0x14
fseo=0x17c3
avsp=0x17af
tosp=0x17af
0xeti[0] nrow=1 offs=0
0x12ri[0] offs=0x17c3
block_row_dump:
tab 0, row 0, @0x17c3
tl: 2037 fb: --H-F--N lb: 0x1 cc: 2 -----本block只保存着本行的2个字段 ,关于 fb 的内容偶暂时不能解读,应该跟 行迁移 和行连接有关
nrid: 0x00c07de3.0 ------- 表示存在着部分数据在 block 0x00c07de3 的 row 0 的行上

col 0: [2000]
31 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
**************************************************************************
20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
col 1: [24] ---第二个字段在本块只保存了24个字节
31 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
end_of_block_dump
End dump data blocks tsn: 2 file#: 3 minblk 32227 maxblk 32228

 

 

最后结论

1: 当block中开始插入数据的时候,正常插入
2:当删除记录后的空间,如果新插入的数据能容纳进去,则重用
3: 当更新的时候,如果 row 长度没有增加,则位置不变,如果长度增加,则被迁移到整个块的最前记录之前(靠近 block header 一侧)
4:当发生行迁移的时候,在原物理位置保留 迁移后的 block位置和 row number
5:当block重新返回freelist 再插入记录的时候,可能发生block数据的重组(row number不变但是物理位置发生变化),到底什么时候重组,我发现,在block中空间比较零散或者被使用的少部分空间位于block的header一侧而tail一侧是空闲的话,在insert的时候会导致空间重构,也就是说原来在靠近header一侧位置存储的数据,在insert发生之前被 迁移 到block的tail位置,发生了空间的重组,事实上,当插入数据或者update的时候,若块中总剩余空间足够但是都是很零碎的,而零碎空间无法容纳则也发生块内重构,这样在本质上就是block内空间的重组,也就是说 在block级别的空间重组是自动的,而segment 级别的就必须采用exp/imp, alter ...... move ,ctas 等等方式

 

 

原文参考:  http://www.itpub.net/showthread.php?threadid=112239


bitirainy 发表于:2004.09.08 16:44 ::分类: ( Oracle is anything ) ::阅读:(597次) :: 评论 (0)
===========================================================
关于 9iR2 的 compress table 的研究
===========================================================

基本功能介绍请看连接

http://otn.oracle.com/oramag/oracle/04-mar/o24tech_data.html

 

下面开始探讨内部存储细节
由于压缩是以block为单位的,因此在批插入的时候若数据本身不超过1 block容量是不会压缩的 (未严格证明,如下面例子,
若插入数据100条可存放在block中则不会产生压缩)



SQL> create table test1(a varchar2(20),b varchar2(20),c varchar2(20)) compress;


SQL> select TABLESPACE_NAME ,EXTENT_ID ,blocks from dba_extents where segment_name = 'TEST1';

TABLESPACE_NAME EXTENT_ID BLOCKS
------------------------------ ---------- ----------
USERS 0 8

SQL> select file_id,block_id from dba_extents where segment_name = 'TEST1';

FILE_ID BLOCK_ID
---------- ----------
11 769


SQL> insert /*+ append */ into test1 select 'AAAAAAAAAAAAAAAAAAAA','BBBBBBBBBBBBBBBBBBBB','CCCCCCCCCCCCCCCCCCCC' from dba_objects where rownum < 1001;

1000 rows created.

SQL> commit;

Commit complete.

SQL> alter system dump datafile 11 block min 769 block max 771;

System altered.



trace 部分内容


data_block_dump,data header at 0xadb4674
===============
tsiz: 0x1f88
hsiz: 0x5ce
pbl: 0x0adb4674
bdba: 0x02c00302
76543210
flag=-0------
ntab=2
nrow=724
frre=-1
fsbo=0x5ce
fseo=0x1127
avsp=0xd
tosp=0xd
r0_9ir2=0x0
mec_kdbh9ir2=0x1
r1_9ir2=0x0
76543210
flag_9ir2=-------C
fcls_9ir2[4]={ 0 32768 32768 32768 }
0x1e: pti[0] nrow=1 offs=0
0x22: pti[1] nrow=723 offs=1
0x26: pri[0] offs=0x1f46
0x28: pri[1] offs=0x1f41
0x2a: pri[2] offs=0x1f3c
0x2c: pri[3] offs=0x1f37

x5b6: pri[712] offs=0x115e
0x5b8: pri[713] offs=0x1159
0x5ba: pri[714] offs=0x1154
0x5bc: pri[715] offs=0x114f
0x5be: pri[716] offs=0x114a
0x5c0: pri[717] offs=0x1145
0x5c2: pri[718] offs=0x1140
0x5c4: pri[719] offs=0x113b
0x5c6: pri[720] offs=0x1136
0x5c8: pri[721] offs=0x1131
0x5ca: pri[722] offs=0x112c
0x5cc: pri[723] offs=0x1127 总记录条数724条,每条消耗5字节存储
block_row_dump:
tab 0, row 0, @0x1f46
tl: 66 fb: --H-FL-- lb: 0x0 cc: 3
col 0: [20] 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41
col 1: [20] 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42
col 2: [20] 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43
bindmp: 02 d3 03 dc 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 dc 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 dc 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43

这部分为集中存储

tab 1, row 0, @0x1f41
tl: 5 fb: --H-FL-- lb: 0x0 cc: 3
col 0: [20] 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41
col 1: [20] 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42
col 2: [20] 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43
bindmp: 2c 00 01 03 00 这里的5个字节就是实际存储内容,03 表示压缩了3个字段,估计这里最后00表示指针指向第一个存储了完全数据的 ROW 0
tab 1, row 1, @0x1f3c
tl: 5 fb: --H-FL-- lb: 0x0 cc: 3
col 0: [20] 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41
col 1: [20] 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42
col 2: [20] 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43
bindmp: 2c 00 01 03 00
tab 1, row 2, @0x1f37
tl: 5 fb: --H-FL-- lb: 0x0 cc: 3
col 0: [20] 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41
col 1: [20] 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42
col 2: [20] 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43
bindmp: 2c 00 01 03 00
tab 1, row 3, @0x1f32
tl: 5 fb: --H-FL-- lb: 0x0 cc: 3
col 0: [20] 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41
col 1: [20] 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42
col 2: [20] 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43
bindmp: 2c 00 01 03 00
tab 1, row 4, @0x1f2d
tl: 5 fb: --H-FL-- lb: 0x0 cc: 3
col 0: [20] 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41
col 1: [20] 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42
col 2: [20] 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43
bindmp: 2c 00 01 03 00
tab 1, row 5, @0x1f28
tl: 5 fb: --H-FL-- lb: 0x0 cc: 3
col 0: [20] 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41
col 1: [20] 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42
col 2: [20] 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43
bindmp: 2c 00 01 03 00
tab 1, row 6, @0x1f23







SQL> truncate table test1;

Table truncated.

SQL> conn test/test
Connected.
SQL> insert /*+ append */ into test1 select rownum,'BBBBBBBBBBBBBBBBBBBB','CCCCCCCCCCCCCCCCCCCC' from dba_objects where rownum < 1001;

1000 rows created.

SQL> commit;

Commit complete.

SQL> alter system dump datafile 11 block min 769 block max 771;

System altered.





data_block_dump,data header at 0xadb4674
===============
tsiz: 0x1f88
hsiz: 0x5b2
pbl: 0x0adb4674
bdba: 0x02c00302
76543210
flag=-0------
ntab=2
nrow=709
frre=-1
fsbo=0x5b2
fseo=0x6e3
avsp=0xc5
tosp=0xc5
r0_9ir2=0x0
mec_kdbh9ir2=0x1
r1_9ir2=0x0
76543210
flag_9ir2=------OC
fcls_9ir2[3]={ 0 32768 32768 }
perm_9ir2[3]={ 2 0 1 }
这部分很关键,表示下面的实际存储字段顺序
也就是说,后面物理存储的顺序COL2,COL0,COL1对应数据字典中列的顺序应该是col0,col1,col2

0x20: pti[0] nrow=1 offs=0
0x24: pti[1] nrow=708 offs=1
0x28: pri[0] offs=0x1f5b
0x2a: pri[1] offs=0x1f54
0x2c: pri[2] offs=0x1f4d
0x2e: pri[3] offs=0x1f46
0x30: pri[4] offs=0x1f3f
0x32: pri[5] offs=0x1f38
0x34: pri[6] offs=0x1f31
0x36: pri[7] offs=0x1f2a
0x38: pri[8] offs=0x1f23

x25c: pri[282] offs=0x1571
0x25e: pri[283] offs=0x1568
0x260: pri[284] offs=0x155f
0x262: pri[285] offs=0x1556
0x264: pri[286] offs=0x154d
0x266: pri[287] offs=0x1544
0x268: pri[288] offs=0x153b
0x26a: pri[289] offs=0x1532
0x26c: pri[290] offs=0x1529
0x26e: pri[291] offs=0x1520
0x270: pri[292] offs=0x1516
block_row_dump:
tab 0, row 0, @0x1f5b
tl: 45 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [20] 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42
col 1: [20] 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43
bindmp: 01 24 02 dc 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 dc 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43
tab 1, row 0, @0x1f52
tl: 9 fb: --H-FL-- lb: 0x0 cc: 3
col 0: [20] 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42
col 1: [20] 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43
col 2: [ 3] 37 30 39
bindmp: 2c 00 02 02 00 cb 37 30 39 我们注意这里的02表示压缩2字段,本来的插入的rownum对应数据字典中表创建顺序最前的字段被存储在最末尾
tab 1, row 1, @0x1f49
tl: 9 fb: --H-FL-- lb: 0x0 cc: 3
col 0: [20] 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42
col 1: [20] 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43
col 2: [ 3] 37 31 30
bindmp: 2c 00 02 02 00 cb 37 31 30
tab 1, row 2, @0x1f40
tl: 9 fb: --H-FL-- lb: 0x0 cc: 3
col 0: [20] 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42
col 1: [20] 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43
col 2: [ 3] 37 31 31
bindmp: 2c 00 02 02 00 cb 37 31 31
tab 1, row 3, @0x1f37
tl: 9 fb: --H-FL-- lb: 0x0 cc: 3
col 0: [20] 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42
col 1: [20] 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43
col 2: [ 3] 37 31 32
bindmp: 2c 00 02 02 00 cb 37 31 32
tab 1, row 4, @0x1f2e
tl: 9 fb: --H-FL-- lb: 0x0 cc: 3
col 0: [20] 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42
col 1: [20] 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43
col 2: [ 3] 37 31 33
bindmp: 2c 00 02 02 00 cb 37 31 33
tab 1, row 5, @0x1f25
tl: 9 fb: --H-FL-- lb: 0x0 cc: 3
col 0: [20] 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42
col 1: [20] 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43
col 2: [ 3] 37 31 34
bindmp: 2c 00 02 02 00 cb 37 31 34
tab 1, row 6, @0x1f1c
tl: 9 fb: --H-FL-- lb: 0x0 cc: 3
col 0: [20] 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42
col 1: [20] 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43
col 2: [ 3] 37 31 35
bindmp: 2c 00 02 02 00 cb 37 31 35
tab 1, row 7, @0x1f13
tl: 9 fb: --H-FL-- lb: 0x0 cc: 3
col 0: [20] 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42
col 1: [20] 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43
col 2: [ 3] 37 31 36






SQL> drop table test1;

Table dropped.

SQL> create table test1 (a varchar2(20),b varchar2(20), c varchar2(20),d varchar2(10)) compress;

Table created.

SQL> insert /*+ append */ into test1 select rownum,'BBBBBBBBBBBBBBBBBBBB','CCCCCCCCCCCCCCCCCCCC','DDDDDD' from dba_objects where rownum < 1001;

1000 rows created.

SQL> commit;

Commit complete.

SQL> select file_id,block_id from dba_extents where segment_name = 'TEST1';

FILE_ID BLOCK_ID
---------- ----------
11 769

SQL> conn test/test
Connected.
SQL> alter system dump datafile 11 block min 769 block max 771;

System altered.





data_block_dump,data header at 0xadb4674
===============
tsiz: 0x1f88
hsiz: 0x5d4
pbl: 0x0adb4674
bdba: 0x02c00302
76543210
flag=-0------
ntab=2
nrow=725
frre=-1
fsbo=0x5d4
fseo=0x64c
avsp=0xc
tosp=0xc
r0_9ir2=0x0
mec_kdbh9ir2=0x1
r1_9ir2=0x0
76543210
flag_9ir2=------OC
fcls_9ir2[4]={ 0 32768 32768 32768 }
perm_9ir2[4]={ 3 0 1 2 }
这部分很关键,表示下面的实际存储字段顺序
也就是说,后面物理存储的顺序COL3,COL0,COL1,COL2对应数据字典中列的顺序应该是col0,col1,col2,col3

0x22: pti[0] nrow=1 offs=0
0x26: pti[1] nrow=724 offs=1
0x2a: pri[0] offs=0x1f54
0x2c: pri[1] offs=0x1f4d
0x2e: pri[2] offs=0x1f46
0x30: pri[3] offs=0x1f3f
0x32: pri[4] offs=0x1f38
0x34: pri[5] offs=0x1f31
0x36: pri[6] offs=0x1f2a

0x244: pri[269] offs=0x15df
0x246: pri[270] offs=0x15d6
0x248: pri[271] offs=0x15cd
0x24a: pri[272] offs=0x15c4
0x24c: pri[273] offs=0x15bb
0x24e: pri[274] offs=0x15b2
0x250: pri[275] offs=0x15a9
0x252: pri[276] offs=0x159f
block_row_dump:
tab 0, row 0, @0x1f54
tl: 52 fb: --H-FL-- lb: 0x0 cc: 3
col 0: [20] 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42
col 1: [20] 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43
col 2: [ 6] 44 44 44 44 44 44
bindmp: 01 14 03 dc 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 dc 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 ce 44 44 44 44 44 44
tab 1, row 0, @0x1f4b
tl: 9 fb: --H-FL-- lb: 0x0 cc: 4
col 0: [20] 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42
col 1: [20] 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43
col 2: [ 6] 44 44 44 44 44 44
col 3: [ 3] 37 32 35
bindmp: 2c 00 02 03 00 cb 37 32 35
tab 1, row 1, @0x1f42
tl: 9 fb: --H-FL-- lb: 0x0 cc: 4
col 0: [20] 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42
col 1: [20] 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43
col 2: [ 6] 44 44 44 44 44 44
col 3: [ 3] 37 32 36
bindmp: 2c 00 02 03 00 cb 37 32 36
tab 1, row 2, @0x1f39
tl: 9 fb: --H-FL-- lb: 0x0 cc: 4
col 0: [20] 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42
col 1: [20] 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43
col 2: [ 6] 44 44 44 44 44 44
col 3: [ 3] 37 32 37
bindmp: 2c 00 02 03 00 cb 37 32 37
tab 1, row 3, @0x1f30
tl: 9 fb: --H-FL-- lb: 0x0 cc: 4
col 0: [20] 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42
col 1: [20] 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43
col 2: [ 6] 44 44 44 44 44 44
col 3: [ 3] 37 32 38
bindmp: 2c 00 02 03 00 cb 37 32 38




SQL> truncate table test1;

Table truncated.

SQL> conn test/test
Connected.
SQL> insert /*+ append */ into test1 select rownum,'BBBBBBBBBBBBBBBBBBBB','CCCCCCCCCCCCCCCCCCCC',rownum+1 from dba_objects where rownum < 1001;

1000 rows created.

SQL> commit;

Commit complete.

SQL> alter system dump datafile 11 block min 769 block max 771;

System altered.




data_block_dump,data header at 0xadb4674
===============
tsiz: 0x1f88
hsiz: 0x46e
pbl: 0x0adb4674
bdba: 0x02c00302
76543210
flag=-0------
ntab=2
nrow=547
frre=-1
fsbo=0x46e
fseo=0x477
avsp=0x9
tosp=0x9
r0_9ir2=0x0
mec_kdbh9ir2=0x1
r1_9ir2=0x0
76543210
flag_9ir2=------OC
fcls_9ir2[3]={ 0 32768 32768 }
perm_9ir2[4]={ 2 0 1 3 }
这部分很关键,表示下面的实际存储字段顺序
也就是说,后面物理存储的顺序COL2,COL0,COL1,COL3对应数据字典中列的顺序应该是col0,col1,col2,col3

0x20: pti[0] nrow=1 offs=0
0x24: pti[1] nrow=546 offs=1
0x28: pri[0] offs=0x1f5b
0x2a: pri[1] offs=0x1f52
0x2c: pri[2] offs=0x1f49
0x2e: pri[3] offs=0x1f40
0x30: pri[4] offs=0x1f37
0x32: pri[5] offs=0x1f2e
0x34: pri[6] offs=0x1f25

x464: pri[542] offs=0x4ab
0x466: pri[543] offs=0x49e
0x468: pri[544] offs=0x491
0x46a: pri[545] offs=0x484
0x46c: pri[546] offs=0x477
block_row_dump:
tab 0, row 0, @0x1f5b
tl: 45 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [20] 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42
col 1: [20] 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43
bindmp: 02 47 02 dc 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 dc 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 4
3 43 43 43
tab 1, row 0, @0x1f52
tl: 9 fb: --H-FL-- lb: 0x0 cc: 4
col 0: [20] 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42
col 1: [20] 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43
col 2: [ 1] 31
col 3: [ 1] 32
bindmp: 2c 00 03 02 00 c9 31 c9 32
tab 1, row 1, @0x1f49
tl: 9 fb: --H-FL-- lb: 0x0 cc: 4
col 0: [20] 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42
col 1: [20] 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43
col 2: [ 1] 32
col 3: [ 1] 33





对于 bindmp 一行的更详细的内容和假如同一block中存在两组或者两组以上相同数据,oracle怎么处理存储本人未做更深入研究,本例子只演示了雷同数据为一组,本例子说明,在同一block内,若某些列具有相同数据,则oracle存储一份副本,并把这些列压缩存储的指针放在bindmp前方
bindmp后面部分为各无法压缩的个列的值。这些列与数据字典中列的映射关系由block header 的 perm_9ir2[4]={ 2 0 1 3 } 部分说明

这种压缩表,其实类似浓缩的 cluster 的处理 ,cluster 是 多个表共享字段 , 而这里是 block 内多行共享部分字段 ,当发生更新压缩字段的时候,该行会产生行迁移,即使再更新回原来的值,该行不会回到原来的压缩block上,只不过这个时候 bindmp 中也记录了新的行迁移数据地址


bitirainy 发表于:2004.09.08 16:43 ::分类: ( Oracle is anything ) ::阅读:(97325次) :: 评论 (96)
===========================================================
show sga 的描述
===========================================================

关于 show sga 结果的描述
Total System Global Area AAAAA bytes
Fixed Size BBBBB bytes
Variable Size CCCCC bytes
Database Buffers DDDDD bytes
Redo Buffers EEEEE bytes

fixes size : oracle 的不同平台和不同版本下可能不一样,但对于确定环境是一个固定的值,里面存储了 SGA 各部分 组件 的信息,可以看作 引导 建立 SGA 的区域

Variable Size : 包括 shared pool ,java pool ,large pool, 管理DB_BLOCK_BUFFERS 的内存,管理控制文件信息的内存,等等其他管理和控制 oracle 内部结构的内存

redo buffer

1: 设置参数

SQL> show parameters log_buffer

NAME TYPE VALUE
------------------------------------ ------- ------------------------------
log_buffer integer 524288


2:日志内存大小

SQL> select * from v$sgastat where name like '%log%';

POOL NAME BYTES
----------- -------------------------- ----------
log_buffer 656384



3 : 为了保护日志内存,而增加了辅助的内存,也就是保护页
SQL> show sga

Total System Global Area 496049552 bytes
Fixed Size 454032 bytes
Variable Size 109051904 bytes
Database Buffers 385875968 bytes
Redo Buffers 667648 bytes
SQL>

对于数据库来说,在不同 的平台下
log_buffer 是离散的 一组值,假设是集合 R,并且不是按照 os blockck 或者 db block 为步长增加的,(比如可能是 65k,128k,512k ,641k....这样的值) 当设置参数为某个值的时候,数据库选择的实际大小是 大于等于 该值 的 min(R) ,根据这组值,比如你设置了 log_buffer = 600k ,则实际选择的是641 k

然后,在实际分配内存的时候,为了 给 log buffer 做一些保护,还另外分配了一小部分空间,通常是 11 k 大小。则有641+11 = 652 k ,这才是 最后真正的 内存大小 ,也就是 show SGA 时候显示大小


bitirainy 发表于:2004.09.08 16:40 ::分类: ( Oracle is anything ) ::阅读:(1068次) :: 评论 (0)
===========================================================
9i 的flashback
===========================================================

原文:http://www.itpub.net/showthread.php?threadid=116297&pagenumber=

    在9i中,若使用undo tablespace则oracle自动管理回滚段,通过设置 undo_retention 可在undo tablespace中保留这么长时间的数据,这样可以避免 snapshot too old 错误,同时也可以通过 flashback 而得到某个时间点之前的数据,但这里要强调的几点是:

1: flashback 功能不支持DDL语句,如果已经truncate掉的数据是不能找回来的
2: 看起来是这样的
oracle 每隔5分钟会将产生的 SCN 对应一个 TIME 做记录
也就是说通常只记录了SCN,但是每5分钟会记录 SCN and TIME
当采用 time 来做flashback 的时候就有可能产生偏差

请关注实验4 !!!!




实验1: 使用flashback 功能

SQL> select dbms_flashback.get_system_change_number from dual;

GET_SYSTEM_CHANGE_NUMBER
------------------------
36501397

SQL> insert into tf values(3);

1 row created.

SQL> commit;

Commit complete.

SQL> exec dbms_flashback.enable_at_system_change_number(36501397);

PL/SQL procedure successfully completed.

SQL> select * from tf;

A
----------
1
2


实验2: flashback 不支持 DDL

接着上面的步骤
SQL> truncate table tf;

Table truncated.

SQL> exec dbms_flashback.enable_at_system_change_number(36501397);

PL/SQL procedure successfully completed.

SQL> select * from tf;
select * from tf
*
ERROR at line 1:
ORA-01466: unable to read data - table definition has changed


SQL> exec dbms_flashback.disable;


实验3 : 我们创建一个表,立即看看效果,所有过程在5分钟以内,很段的时间内完成的,我们将无法查询数据!

SQL> drop table tf;

Table dropped.

SQL> select dbms_flashback.get_system_change_number from dual;

GET_SYSTEM_CHANGE_NUMBER
------------------------
36509390

SQL> create table tf( a number);

Table created.

SQL> insert into tf values(1);

1 row created.

SQL> commit;

Commit complete.

SQL> select dbms_flashback.get_system_change_number from dual;

GET_SYSTEM_CHANGE_NUMBER
------------------------
36509490

SQL> insert into tf values(2);

1 row created.

SQL> commit;

Commit complete.

SQL> exec dbms_flashback.enable_at_system_change_number(36509390);

PL/SQL procedure successfully completed.

SQL> select * from tf;
select * from tf
*
ERROR at line 1:
ORA-01466: unable to read data - table definition has changed


SQL> exec dbms_flashback.disable;

PL/SQL procedure successfully completed.

SQL> exec dbms_flashback.enable_at_system_change_number(36509490);

PL/SQL procedure successfully completed.

SQL> select * from tf;
select * from tf
*
ERROR at line 1:
ORA-01466: unable to read data - table definition has changed


SQL> exec dbms_flashback.disable;

PL/SQL procedure successfully completed.


实验四:通过时间来做,我们仔细看下面的时间和数据的关系!

SQL> select to_char(sysdate,'yyyy-mm-dd hh24:mi:ss') from dual;

TO_CHAR(SYSDATE,'YY
-------------------
2003-04-26 17:09:04 创建表之前的时间 time1

SQL> create table test1 (a number);

Table created.

SQL> select to_char(sysdate,'yyyy-mm-dd hh24:mi:ss') from dual;

TO_CHAR(SYSDATE,'YY
-------------------
2003-04-26 17:09:20 创建表之后还没有插入数据的时间 time2

SQL> insert into test1 values(1);

1 row created.

SQL> commit;

Commit complete.

SQL> select to_char(sysdate,'yyyy-mm-dd hh24:mi:ss') from dual;

TO_CHAR(SYSDATE,'YY
-------------------
2003-04-26 17:09:43 创建表之后5分钟内插入了1条记录 time3

SQL> insert into test1 values(2);

1 row created.

SQL> commit;

Commit complete.

SQL> exec dbms_lock.sleep(300); 休眠5分钟


PL/SQL procedure successfully completed.

SQL> select to_char(sysdate,'yyyy-mm-dd hh24:mi:ss') from dual;

TO_CHAR(SYSDATE,'YY
-------------------
2003-04-26 17:15:55 休眠后再插入数据 time4


SQL> insert into test1 values(3);

1 row created.

SQL> commit;

Commit complete.

SQL> exec dbms_flashback.enable_at_time(to_date('2003-04-26 17:09:04','yyyy-mm-dd hh24:mi:ss')); time1

PL/SQL procedure successfully completed.

SQL> select * from test1;
select * from test1
*
ERROR at line 1:
ORA-01466: unable to read data - table definition has changed


SQL> exec dbms_flashback.disable;

PL/SQL procedure successfully completed.
SQL> exec dbms_flashback.enable_at_time(to_date('2003-04-26 17:09:20','yyyy-mm-dd hh24:mi:ss')); time2

PL/SQL procedure successfully completed.

SQL> select * from test1;

no rows selected

SQL> exec dbms_flashback.disable;


SQL> exec dbms_flashback.enable_at_time(to_date('2003-04-26 17:09:43','yyyy-mm-dd hh24:mi:ss')); time3

PL/SQL procedure successfully completed.

SQL> select * from test1;

no rows selected

SQL> exec dbms_flashback.disable;

PL/SQL procedure successfully completed.


PL/SQL procedure successfully completed.

SQL> exec dbms_flashback.enable_at_time(to_date('2003-04-26 17:15:55','yyyy-mm-dd hh24:mi:ss')); time4

PL/SQL procedure successfully completed.

SQL> select * from test1;

A
----------
1
2

SQL> exec dbms_flashback.disable;

PL/SQL procedure successfully completed.

SQL>


bitirainy 发表于:2004.09.08 16:39 ::分类: ( Oracle is anything ) ::阅读:(1388次) :: 评论 (0)
===========================================================
bitmap index 的研究
===========================================================

原文:http://www.itpub.net/showthread.php?threadid=114023&pagenumber=

 

1:bitmap 索引是分段存储的,也就是说很多条记录可能是分做了N段来存储,也就是有N个begin/end ,当新的记录 insert 而使用以前未曾使用过的物理地址的时候,会产生一个bitmap 段来存储,就算只有一条记录

2: 当删除一条记录的时候,在bitmap 索引上做了一个delete 的标记并用一新的记录来标记了,下面请看具体的演示

3: 当 dml发生的时候,会lock住某个值的存储bit的那一rowid所在的记录,参考下面的 row 中 lock ,这样显然会影响并发


SQL> create table tn(a number, b number);

Table created.

SQL> insert into tn select rownum,mod(rownum,5) from all_objects where rownum < 21;

20 rows created.

SQL> commit;

Commit complete.

SQL> create bitmap index tn_bitmap on tn(b);

Index created.

SQL> exec show_space('tn_bitmap',user,'INDEX');
Free Blocks.............................0
Total Blocks............................16
Total Bytes.............................131072
Unused Blocks...........................14
Unused Bytes............................114688
Last Used Ext FileId....................3
Last Used Ext BlockId...................1954
Last Used Block.........................2

PL/SQL procedure successfully completed.

SQL> select * from tn;

A B
---------- ----------
1 1
2 2
3 3
4 4
5 0
6 1
7 2
8 3
9 4
10 0
11 1

A B
---------- ----------
12 2
13 3
14 4
15 0
16 1
17 2
18 3
19 4
20 0

20 rows selected.

SQL> alter system dump datafile 3 block 1955;

System altered.

Block header dump: 0x00c007a3
Object id on Block? Y
seg/obj: 0x66da csc: 0x00.18a0d77 itc: 2 flg: - typ: 2 - INDEX
fsl: 0 fnx: 0x0 ver: 0x01

Itl Xid Uba Flag Lck Scn/Fsc
0x01 xid: 0x0000.000.00000000 uba: 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
0x02 xid: 0x0002.040.000000ea uba: 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000

Leaf block dump
===============
header address 125987932=0x7826c5c
kdxcolev 0
kdxcolok 0
kdxcoopc 0x80: opcode=0: iot flags=--- is converted=Y
kdxconco 4
kdxcosdc 0
kdxconro 5
kdxcofbo 46=0x2e
kdxcofeo 7918=0x1eee
kdxcoavs 7872
kdxlespl 0
kdxlende 0
kdxlenxt 0=0x0
kdxleprv 0=0x0
kdxledsz 0
kdxlebksz 8036
row#0[8013] flag: -----, lock: 0
col 0; len 1; (1): 80 ---表示值为0
col 1; len 6; (6): 00 c0 7e 03 00 00 ---rowid 起点的block和行号
col 2; len 6; (6): 00 c0 7e 03 00 17 ---rowid 结束的block和行号,注意17 = 16+7 = 23 ,也就是下面转换后的有效位置截止到23bit
col 3; len 4; (4): ca 10 42 08 ---把该值按照16进制数转化为 11001010 (首字节不表示rowid信息) 00010000 01000010 00001000 ,
凡是从起点到结束点内的1表示该值存在,这里有 一个必须要注意的问题是,这样转化后的位置并不是真实的物理位置,在每个字节内部bit还要颠倒一下顺序,首字节不表示位置信息
也就是说上面的应该转换为 00001000 01000010 00010000 ,发现正好每5个存在一个值为0的记录

row#1[7990] flag: -----, lock: 0
col 0; len 2; (2): c1 02 ---表示值为1
col 1; len 6; (6): 00 c0 7e 03 00 00
col 2; len 6; (6): 00 c0 7e 03 00 0f ---注意这里是f,也就是一共只有16位,因为1是第一条记录开始的,在16的位置就已经有5条了
col 3; len 3; (3): c9 21 84 注意这里的 21 84 正好16位,根据上面描述的规则转换后就是 10000100 00100001,4个1正好表示记录
row#2[7966] flag: -----, lock: 0
col 0; len 2; (2): c1 03 ---表示值为2
col 1; len 6; (6): 00 c0 7e 03 00 00
col 2; len 6; (6): 00 c0 7e 03 00 17
col 3; len 4; (4): ca 42 08 01
row#3[7942] flag: -----, lock: 0
col 0; len 2; (2): c1 04 ---表示值为3
col 1; len 6; (6): 00 c0 7e 03 00 00
col 2; len 6; (6): 00 c0 7e 03 00 17
col 3; len 4; (4): ca 84 10 02
row#4[7918] flag: -----, lock: 0
col 0; len 2; (2): c1 05 ---表示值为4
col 1; len 6; (6): 00 c0 7e 03 00 00
col 2; len 6; (6): 00 c0 7e 03 00 17
col 3; len 4; (4): ca 08 21 04
----- end of leaf block dump -----
End dump data blocks tsn: 2 file#: 3 minblk 1955 maxblk 1955



SQL> delete from tn where a = 2;

1 row deleted.

SQL> commit;

Commit complete.

SQL> alter system dump datafile 3 block 1955;

System altered.

SQL>

Block header dump: 0x00c007a3
Object id on Block? Y
seg/obj: 0x66da csc: 0x00.18a0d77 itc: 2 flg: - typ: 2 - INDEX
fsl: 0 fnx: 0x0 ver: 0x01

Itl Xid Uba Flag Lck Scn/Fsc
0x01 xid: 0x0000.000.00000000 uba: 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
0x02 xid: 0x0003.047.000000e9 uba: 0x00800dba.00d9.1f --U- 2 fsc 0x001a.018a0d7d

Leaf block dump
===============
header address 125987932=0x7826c5c
kdxcolev 0
kdxcolok 0
kdxcoopc 0x80: opcode=0: iot flags=--- is converted=Y
kdxconco 4
kdxcosdc 0
kdxconro 6
kdxcofbo 48=0x30
kdxcofeo 7894=0x1ed6
kdxcoavs 7846
kdxlespl 0
kdxlende 1
kdxlenxt 0=0x0
kdxleprv 0=0x0
kdxledsz 0
kdxlebksz 8036
row#0[8013] flag: -----, lock: 0
col 0; len 1; (1): 80
col 1; len 6; (6): 00 c0 7e 03 00 00
col 2; len 6; (6): 00 c0 7e 03 00 17
col 3; len 4; (4): ca 10 42 08
row#1[7990] flag: -----, lock: 0
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 c0 7e 03 00 00
col 2; len 6; (6): 00 c0 7e 03 00 0f
col 3; len 3; (3): c9 21 84
row#2[7894] flag: -----, lock: 2 ---这是删除后的拷贝,我们发现删除的时候该行已经加锁 lock : 2
col 0; len 2; (2): c1 03
col 1; len 6; (6): 00 c0 7e 03 00 00
col 2; len 6; (6): 00 c0 7e 03 00 17
col 3; len 4; (4): ca 40 08 01 ---我们发现 ca 42 已经变成 ca 40 ,也就是已经少掉一位bit了,正好是删除的那一条记录
row#3[7966] flag: ---D-, lock: 2 ---这里我们发现值为2的记录已经有删除过的 ---D- ,D表示delete
col 0; len 2; (2): c1 03
col 1; len 6; (6): 00 c0 7e 03 00 00
col 2; len 6; (6): 00 c0 7e 03 00 17
col 3; len 4; (4): ca 42 08 01
row#4[7942] flag: -----, lock: 0
col 0; len 2; (2): c1 04
col 1; len 6; (6): 00 c0 7e 03 00 00
col 2; len 6; (6): 00 c0 7e 03 00 17
col 3; len 4; (4): ca 84 10 02
row#5[7918] flag: -----, lock: 0
col 0; len 2; (2): c1 05
col 1; len 6; (6): 00 c0 7e 03 00 00
col 2; len 6; (6): 00 c0 7e 03 00 17
col 3; len 4; (4): ca 08 21 04
----- end of leaf block dump -----
End dump data blocks tsn: 2 file#: 3 minblk 1955 maxblk 1955

 

再继续补充

首先truncate 表所有数据
truncate table tn;

SQL> exec show_space('tn_bitmap','i');
Free Blocks.............................0
Total Blocks............................16
Total Bytes.............................131072
Unused Blocks...........................14
Unused Bytes............................114688
Last Used Ext FileId....................3
Last Used Ext BlockId...................1954
Last Used Block.........................2

PL/SQL procedure successfully completed.

可以看出索引是空的

然后插入一条数据

SQL> insert into tn values(1,1);

1 row created.

SQL> commit;

Commit complete.

SQL> alter system dump datafile 3 block 1955;

System altered.



row#0[8009] flag: -----, lock: 2
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 c0 7e 03 00 00
col 2; len 6; (6): 00 c0 7e 03 00 07
col 3; len 1; (1): 00
row#1[8030] flag: ---D-, lock: 2
col 0; NULL
col 1; NULL
col 2; NULL
col 3; NULL




SQL> insert into tn values(1,1);

1 row created.

SQL> commit;

Commit complete.

SQL> alter system dump datafile 3 block 1955;

System altered.



row#0[8009] flag: ---D-, lock: 2 -- 标记删除,下面一份是拷贝
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 c0 7e 03 00 00
col 2; len 6; (6): 00 c0 7e 03 00 07
col 3; len 1; (1): 00
row#1[7987] flag: -----, lock: 2
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 c0 7e 03 00 00 00---07 正好表示8 rows
col 2; len 6; (6): 00 c0 7e 03 00 07
col 3; len 2; (2): c8 03 -- 03 正好表示2条记录被插入



SQL> insert into tn values(1,1);

1 row created.

SQL> alter system dump datafile 3 block 1955;

System altered.

row#0[7987] flag: ---D-, lock: 2
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 c0 7e 03 00 00
col 2; len 6; (6): 00 c0 7e 03 00 07
col 3; len 2; (2): c8 03
row#1[7965] flag: -----, lock: 2
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 c0 7e 03 00 00
col 2; len 6; (6): 00 c0 7e 03 00 07
col 3; len 2; (2): c8 07 -- 07 正好表示3条记录被插入



SQL> insert into tn values(1,1);

1 row created.

SQL> insert into tn values(1,1);

1 row created.

SQL> insert into tn values(1,1);

1 row created.

SQL> insert into tn values(1,1);

1 row created.

SQL> insert into tn values(1,1);

1 row created.

SQL> commit;

Commit complete.

SQL> alter system dump datafile 3 block 1955;

System altered.

我们在同一个session中同一个事务连续插入5条记录,发现在bitmap中居然做了5个拷贝

row#0[7987] flag: ---D-, lock: 2
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 c0 7e 03 00 00
col 2; len 6; (6): 00 c0 7e 03 00 07
col 3; len 2; (2): c8 03
row#1[7965] flag: ---D-, lock: 2
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 c0 7e 03 00 00
col 2; len 6; (6): 00 c0 7e 03 00 07
col 3; len 2; (2): c8 07
row#2[7943] flag: ---D-, lock: 2
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 c0 7e 03 00 00
col 2; len 6; (6): 00 c0 7e 03 00 07
col 3; len 2; (2): c8 0f
row#3[7921] flag: ---D-, lock: 2
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 c0 7e 03 00 00
col 2; len 6; (6): 00 c0 7e 03 00 07
col 3; len 2; (2): c8 1f
row#4[7899] flag: ---D-, lock: 2
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 c0 7e 03 00 00
col 2; len 6; (6): 00 c0 7e 03 00 07
col 3; len 2; (2): c8 3f
row#5[7877] flag: ---D-, lock: 2
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 c0 7e 03 00 00
col 2; len 6; (6): 00 c0 7e 03 00 07
col 3; len 2; (2): c8 7f
row#6[7855] flag: -----, lock: 2
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 c0 7e 03 00 00
col 2; len 6; (6): 00 c0 7e 03 00 07
col 3; len 2; (2): c8 ff -- ff 正好表示8条记录被插入


SQL> insert into tn values(1,1);

1 row created.

SQL> commit;

Commit complete.

SQL> alter system dump datafile 3 block 1955;

System altered.

SQL>

-- 上一个bitmap段存储表示8条记录,我们再插入第9条记录再来看

row#0[7855] flag: -----, lock: 2
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 c0 7e 03 00 00
col 2; len 6; (6): 00 c0 7e 03 00 07
col 3; len 2; (2): c8 ff -- 8条记录已满,也把前面的前8条的多拷贝给清除掉了
row#1[7834] flag: -----, lock: 2
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 c0 7e 03 00 08
col 2; len 6; (6): 00 c0 7e 03 00 0f
col 3; len 1; (1): 00 新插入的第9条记录被新的从 08 -- 0f 这8个字节用来存储


综合上面的实验可以看出,当单条insert发生的时候,会以8条记录为一个bitmap row 来存储,这正好是一个字节的bit,并且就算是相同事务中的insert也会导致大量的拷贝和lock产生,严重影响性能,甚至可能发生行迁移等严重问题,所以在经常发生变化的表中我们不应该采用 bitmap index ,当发生update 的时候情形更为复杂,暂时不予讨论了 。

 

转 yangtingkun 的帖子

http://itpub.net/showthread.php?threadid=115221

SQL> truncate table tn;

表已截掉。

SQL> exec show_space('tn_bitmap',user,'INDEX');
Free Blocks.............................0
Total Blocks............................3
Total Bytes.............................12288
Unused Blocks...........................1
Unused Bytes............................4096
Last Used Ext FileId....................1
Last Used Ext BlockId...................26474
Last Used Block.........................2

PL/SQL 过程已成功完成。

SQL> alter system dump datafile 1 block 26475;

系统已更改。

Leaf block dump
===============
header address 83060828=0x4f3685c
kdxcolev 0
KDXCOLEV Flags = - - -
kdxcolok 0
kdxcoopc 0x80: opcode=0: iot flags=--- is converted=Y
kdxconco 4
kdxcosdc 0
kdxconro 0
kdxcofbo 36=0x24
kdxcofeo 3940=0xf64
kdxcoavs 3904
kdxlespl 0
kdxlende 0
kdxlenxt 0=0x0
kdxleprv 0=0x0
kdxledsz 0
kdxlebksz 3940
----- end of leaf block dump -----
End dump data blocks tsn: 0 file#: 1 minblk 26475 maxblk 26475
索引已经清空

SQL> insert into tn values (1, 1);

已创建 1 行。

SQL> /

已创建 1 行。

SQL> /

已创建 1 行。

SQL> /

已创建 1 行。

SQL> /

已创建 1 行。

SQL> /

已创建 1 行。

SQL> /

已创建 1 行。

SQL> /

已创建 1 行。

SQL> /

已创建 1 行。

SQL> /

已创建 1 行。

依次插入10行数据。

SQL> commit;

提交完成。

SQL> alter system dump datafile 1 block 26475;

系统已更改。

Leaf block dump
===============
header address 83060828=0x4f3685c
kdxcolev 0
KDXCOLEV Flags = - - -
kdxcolok 0
kdxcoopc 0x80: opcode=0: iot flags=--- is converted=Y
kdxconco 4
kdxcosdc 0
kdxconro 11
kdxcofbo 58=0x3a
kdxcofeo 3716=0xe84
kdxcoavs 3658
kdxlespl 0
kdxlende 9
kdxlenxt 0=0x0
kdxleprv 0=0x0
kdxledsz 0
kdxlebksz 3940
row#0[3913] flag: ---D-, lock: 2
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 40 67 68 00 00
col 2; len 6; (6): 00 40 67 68 00 07
col 3; len 1; (1): 00
row#1[3891] flag: ---D-, lock: 2
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 40 67 68 00 00
col 2; len 6; (6): 00 40 67 68 00 07
col 3; len 2; (2): c8 03
row#2[3869] flag: ---D-, lock: 2
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 40 67 68 00 00
col 2; len 6; (6): 00 40 67 68 00 07
col 3; len 2; (2): c8 07
row#3[3847] flag: ---D-, lock: 2
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 40 67 68 00 00
col 2; len 6; (6): 00 40 67 68 00 07
col 3; len 2; (2): c8 0f
row#4[3825] flag: ---D-, lock: 2
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 40 67 68 00 00
col 2; len 6; (6): 00 40 67 68 00 07
col 3; len 2; (2): c8 1f
row#5[3803] flag: ---D-, lock: 2
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 40 67 68 00 00
col 2; len 6; (6): 00 40 67 68 00 07
col 3; len 2; (2): c8 3f
row#6[3781] flag: ---D-, lock: 2
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 40 67 68 00 00
col 2; len 6; (6): 00 40 67 68 00 07
col 3; len 2; (2): c8 7f
row#7[3759] flag: -----, lock: 2
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 40 67 68 00 00
col 2; len 6; (6): 00 40 67 68 00 07
col 3; len 2; (2): c8 ff
row#8[3738] flag: ---D-, lock: 2
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 40 67 68 00 08
col 2; len 6; (6): 00 40 67 68 00 0f
col 3; len 1; (1): 00
row#9[3716] flag: -----, lock: 2
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 40 67 68 00 08
col 2; len 6; (6): 00 40 67 68 00 0f
col 3; len 2; (2): c8 03
row#10[3934] flag: ---D-, lock: 2
col 0; NULL
col 1; NULL
col 2; NULL
col 3; NULL
----- end of leaf block dump -----
End dump data blocks tsn: 0 file#: 1 minblk 26475 maxblk 26475

oracle对每次插入的数据都进行索引。


SQL> truncate table tn;

表已截掉。

SQL> alter system dump datafile 1 block 26475;

系统已更改。

Leaf block dump
===============
header address 83060828=0x4f3685c
kdxcolev 0
KDXCOLEV Flags = - - -
kdxcolok 0
kdxcoopc 0x80: opcode=0: iot flags=--- is converted=Y
kdxconco 4
kdxcosdc 0
kdxconro 0
kdxcofbo 36=0x24
kdxcofeo 3940=0xf64
kdxcoavs 3904
kdxlespl 0
kdxlende 0
kdxlenxt 0=0x0
kdxleprv 0=0x0
kdxledsz 0
kdxlebksz 3940
----- end of leaf block dump -----

一次性插入10条数据
SQL> insert into tn select 1,1 from user_objects where rownum < 11;

已创建10行。

SQL> commit;

提交完成。

SQL> alter system dump datafile 1 block 26475;

系统已更改。

Leaf block dump
===============
header address 83060828=0x4f3685c
kdxcolev 0
KDXCOLEV Flags = - - -
kdxcolok 0
kdxcoopc 0x80: opcode=0: iot flags=--- is converted=Y
kdxconco 4
kdxcosdc 0
kdxconro 2
kdxcofbo 40=0x28
kdxcofeo 3911=0xf47
kdxcoavs 3871
kdxlespl 0
kdxlende 1
kdxlenxt 0=0x0
kdxleprv 0=0x0
kdxledsz 0
kdxlebksz 3940
row#0[3911] flag: -----, lock: 2
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 40 67 68 00 00
col 2; len 6; (6): 00 40 67 68 00 0f
col 3; len 3; (3): c9 ff 03 一次性插入的数据,oracle会对整批数据进行bitmap索引。
row#1[3934] flag: ---D-, lock: 2
col 0; NULL
col 1; NULL
col 2; NULL
col 3; NULL
----- end of leaf block dump -----
End dump data blocks tsn: 0 file#: 1 minblk 26475 maxblk 26475

因此我认为对于包含bitmap索引的表应当减少对数据库操作的数量。运用批量入库的方法会使性能得到较大的提高。

有什么不对的地方还请指正。


my reply:

综合上面的实验可以看出,当单条insert发生的时候,会以8条记录为一个bitmap row 来存储,这正好是一个字节的bit,并且就算是相同事务中的insert也会导致大量的拷贝和lock产生,严重影响性能,甚至可能发生行迁移等严重问题,所以在经常发生变化的表中,我们不应该采用 bitmap index ,当发生update 的时候情形更为复杂,暂时不予讨论了



结合
前面的第一条:
:bitmap 索引是分段存储的,也就是说很多条记录可能是分做了N段来存储,也就是有N个begin/end ,当新的记录 insert 而使用以前未曾使用过的物理地址的时候,会产生一个bitmap 段来存储,就算只有一条记录 ,所以我的实验中可能没有很好的做归纳,但是现象都是有的,描述的比较零散 ,就是说假如你们每天入库 1000万条记录 ,那创建 bitmap 索引的这个列有多少不同的值,如果有10万个不同的值以上,那采用 bitmap 是否合适就值得考虑了


bitirainy 发表于:2004.09.08 16:38 ::分类: ( Oracle is anything ) ::阅读:(760次) :: 评论 (0)
===========================================================
关于查询排序的空间使用问题
===========================================================

在9i之前的版本

    针对每个session,排序首先会使用sort_area_size ,如果不足则会使用临时表空间。但这里面又到底是怎么一个过程呢?下面阐述一下,也许对大家有用处(如果有什么不清楚或者不恰当的地方欢迎大家探讨)

    假设sort_area_size = 100k,正好能盛下100条记录进行排序,当排序记录小于等于100条,ok,所有排序在内存中进行,很快,但若超过100条,则会使用临时表空间(利用磁盘进行)我们选取一个临界值来说明,假设需要排序的记录有10010条,这个时候我们进行的排序会分为101组进行,每读100条进行一次小组排序,然后写入磁盘,第101组只有10条,排序后也写入磁盘.

    这是进行第二次排序,这次排序将在前100小组里面各抽取一条进行排序。《按照我个人的猜测,应该是排好后每写入一条入磁盘则将该记录所在小组重新抽取一条出来进行排序(这时是有序记录组里面所以很快)》。当这个过程完成后,这时所需要的磁盘空间大约为 实际记录存储空间的2倍(这也是多数书上提到的排序空间大约是记录空间的2倍的原因),由于还剩下10条记录,于是这10条记录需要跟前面排序的10000条记录进行排序合并,这个代价也是相当大的!

    所以,我们通常推荐,假如你需要排序的记录最大为100万条,则sort_area_size最小要能装下1000条,否则如上面的例子,那多余的10条,仅仅10条将会带来巨大的代价!如果,设置的极度不合理的情况下,排序记录达到了 sort_area_size所能容纳的三次方以上,比如上面例子中排序需要100万记录,那么同样的,重复这个过程,当每一万条记录如上排序后,再如上从这100小组(每组10000条记录)各抽一条进行排序…… 在这个过程中,磁盘的消耗和时间的代价大家都应该有个感性认识了。所以,我们建议: sprt_area_size 所能容纳记录数至少大于排序记录数的 平方根。

    从 9i开始oracle用  PGA_AGGREGATE_TARGET  来取代了 关于 session 级别的一些设置,也就是说,当 workarea_size_policy  = AUTO 的时候,所有 *_area_size 都不在session级别设置了(仅仅针对dedicated模式的session而不针对 shared 模式),这些 session 共享数据库instance级别共同设置的  PGA_AGGREGATE_TARGET 

show parameters area

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
bitmap_merge_area_size               integer     1048576
create_bitmap_area_size              integer     8388608
hash_area_size                       integer     248576
sort_area_retained_size              integer     0
sort_area_size                       integer     124288
workarea_size_policy                 string      AUTO

并且各 session 使用 PGA_AGGREGATE_TARGET 遵循一个规则,那就是每个 session 使用空间不能超过 PGA_AGGREGATE_TARGET * 5%  ,若超过则使用 临时表空间


bitirainy 发表于:2004.09.08 16:37 ::分类: ( Oracle is anything ) ::阅读:(822次) :: 评论 (1)
===========================================================
跳跃式索引扫描的结构或算法特征
===========================================================

2002年的时候有一天看到 oracle 支持索引的skip  scan ,也就是说

假设存在  index (a,b)

则查询 select * from table where b = ?  将能用到索引

从索引的结构想来,当时有些迷惑,对此进行过思考从而写下此文

 

跳跃式索引扫描的结构猜想

 那我们可以从算法的角度来考虑,在索引的搜索算法上可以从多个root ,branch进入进行扫描数据,这样当索引层次多的时候代价获取会高一些,不用增加维护结构的变化。
举个例子
假设数据分布:
a b c
1 1 1
1 2 2
1 2 3
1 3 1
2 1 1
2 2 2
2 2 3

当查询 b = 2 的时候
不存在树结构: 先进入a=1, 然后顺序((也可2分法等方法,因为是排序好的)
)b=1,b=2,发现2条,不用比较3了,因为是有序的
存在树结构: 进入a= 1 ,迅速定位到2,然后找出和服条件记录

然后进入a=2 重复上面的过程

当查询 c = 3 的时候,先进入 a = 1,b=1,发现没有
进入 a=1,b=2,无树结构能快速定位到3(也可顺序可2分法,因为是排序好的),如此重复上面的步骤


由这个猜想
当数据量特别大并且数据可选择性很大的时候这种方式具有很大的优点
但这种猜测方式,假如 a 不具有相同值,而b 或者c 选择性比较小,那么这种跳跃式扫描将是一种灾难

 

 

几个月后做了次测试

 

存在index(a,b)
当a的选择性不高的时候,oracle进行 跳跃式扫描的 优点明显
当a的选择性高的时候,跳跃式扫描失去了意义
既然有跳跃一说,就是说每次扫描的时候,会有一个入口进去,扫描,然后从新的入口进去,扫描……如此反复

这个入口,就是 a 的不同的值



SQL> create table t as select * from all_objects ;

表已创建。

SQL> create index t_index1 on t(owner,object_name);

索引已创建。
SQL> analyze table t compute statistics;

表已分析。

SQL> set autotrace on
SQL> select DATA_OBJECT_ID,created from t where object_name = 'DBA_EXTENTS';

DATA_OBJECT_ID CREATED
-------------- ----------
12-5月 -02
12-5月 -02

Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=35 Card=2 Bytes=64)
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'T' (Cost=35 Card=2 Bytes
=64)

2 1 INDEX (SKIP SCAN) OF 'T_INDEX1' (NON-UNIQUE) (Cost=34 Ca
rd=1)

Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
26 consistent gets
0 physical reads
0 redo size
467 bytes sent via SQL*Net to client
425 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
2 rows processed

SQL> drop index t_index1;

索引已丢弃。

SQL> create index t_index1 on t (object_name,owner);

索引已创建。

SQL> analyze table t compute statistics;

表已分析。

SQL> select * from t where owner = 'TEST';

OWNER OBJECT_NAME
------------------------------ ------------------------------
SUBOBJECT_NAME OBJECT_ID DATA_OBJECT_ID OBJECT_TYPE
------------------------------ ---------- -------------- ------------------
CREATED LAST_DDL_T TIMESTAMP STATUS T G S
---------- ---------- ------------------- ------- - - -
TEST A
30882 30882 TABLE
06-12月-02 06-12月-02 2002-12-06:17:35:38 VALID N N N

TEST B
33237 33237 TABLE
11-2月 -03 11-2月 -03 2003-02-11:10:07:49 VALID N N N

OWNER OBJECT_NAME
------------------------------ ------------------------------
SUBOBJECT_NAME OBJECT_ID DATA_OBJECT_ID OBJECT_TYPE
------------------------------ ---------- -------------- ------------------
CREATED LAST_DDL_T TIMESTAMP STATUS T G S
---------- ---------- ------------------- ------- - - -

TEST DBEXPERT_PLAN1
30333 30333 TABLE
02-12月-02 02-12月-02 2002-12-02:17:26:29 VALID N N N

TEST LMT
33468 33468 TABLE

OWNER OBJECT_NAME
------------------------------ ------------------------------
SUBOBJECT_NAME OBJECT_ID DATA_OBJECT_ID OBJECT_TYPE
------------------------------ ---------- -------------- ------------------
CREATED LAST_DDL_T TIMESTAMP STATUS T G S
---------- ---------- ------------------- ------- - - -
27-2月 -03 27-2月 -03 2003-02-27:09:22:18 VALID N N N

TEST LOGON_HISTORY
31284 TRIGGER
30-12月-02 30-12月-02 2002-12-30:19:02:08 VALID N N N

TEST MANLMT

OWNER OBJECT_NAME
------------------------------ ------------------------------
SUBOBJECT_NAME OBJECT_ID DATA_OBJECT_ID OBJECT_TYPE
------------------------------ ---------- -------------- ------------------
CREATED LAST_DDL_T TIMESTAMP STATUS T G S
---------- ---------- ------------------- ------- - - -
33469 33469 TABLE
27-2月 -03 27-2月 -03 2003-02-27:10:07:10 VALID N N N

TEST PLAN_TABLE
33482 33482 TABLE
03-3月 -03 03-3月 -03 2003-03-03:10:49:09 VALID N N N

OWNER OBJECT_NAME
------------------------------ ------------------------------
SUBOBJECT_NAME OBJECT_ID DATA_OBJECT_ID OBJECT_TYPE
------------------------------ ---------- -------------- ------------------
CREATED LAST_DDL_T TIMESTAMP STATUS T G S
---------- ---------- ------------------- ------- - - -
TEST SESSION_HISTORY
31287 31287 TABLE
30-12月-02 30-12月-02 2002-12-30:19:00:41 VALID N N N

TEST T
33485 33485 TABLE
03-3月 -03 03-3月 -03 2003-03-03:17:12:42 VALID N N N

OWNER OBJECT_NAME
------------------------------ ------------------------------
SUBOBJECT_NAME OBJECT_ID DATA_OBJECT_ID OBJECT_TYPE
------------------------------ ---------- -------------- ------------------
CREATED LAST_DDL_T TIMESTAMP STATUS T G S
---------- ---------- ------------------- ------- - - -

TEST TEST
33483 33483 TABLE
03-3月 -03 03-3月 -03 2003-03-03:10:52:08 VALID N N N

TEST TEST_INDEX
33484 33484 INDEX

OWNER OBJECT_NAME
------------------------------ ------------------------------
SUBOBJECT_NAME OBJECT_ID DATA_OBJECT_ID OBJECT_TYPE
------------------------------ ---------- -------------- ------------------
CREATED LAST_DDL_T TIMESTAMP STATUS T G S
---------- ---------- ------------------- ------- - - -
03-3月 -03 03-3月 -03 2003-03-03:10:52:34 VALID N N N

已选择11行。

Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=43 Card=927 Bytes=79
722)

1 0 TABLE ACCESS (FULL) OF 'T' (Cost=43 Card=927 Bytes=79722)

Statistics
----------------------------------------------------------
71 recursive calls
0 db block gets
450 consistent gets
0 physical reads
0 redo size
2264 bytes sent via SQL*Net to client
425 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
11 rows processed

 

感谢 chao_ping 当时给出的一个例子

 

SELECT OWNER,OBJECT_ID FROM YAFENG WHERE object_name='DBA_TABLES'
16:44:01 scott@ORA9> /

OWNER OBJECT_ID
------------------------------------------------------------ ----------
PUBLIC 1813
SYS 1812

Elapsed: 00:00:00.15

Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=11 Card=1 Bytes=22)
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'YAFENG' (Cost=11 Card=1 Bytes=22)
2 1 INDEX (SKIP SCAN) OF 'IDX_YAFENG' (NON-UNIQUE) (Cost=10 Card=1)

12 consistent gets
16:44:02 scott@ORA9> select distinct owner from yafeng;

OWNER
------------------------------------------------------------
BIDDER
EACHPAY
OUTLN
PERFSTAT
PUBLIC
SCOTT
SYS
SYSTEM

8 rows selected.
12 consistent gets:

root block -> branch block(begin with bidder:no object_name like DBA_tables) ---one block gets
-> Branch block(begin with eachpay:no object_name like dba_tables) ---one block gets
-> Branch block(begin with outln:no object_name like dba_tables) --one block gets
-> Branch block(begin with perfstat:no object_name like dba_tables) ---one block gets
-> Branch block(begin with public:find one object named dba_tabes)->rowid-row (2 block gets)
-> Branch block(begin with scott:no object_name like dba_tables) --one block gets
-> Branch block(begin with system:no object_name like dba_tables) --one block gets
-> Branch block(begin with sys:no object_name like dba_tables) ->rowid-row(2 block gets)

 


bitirainy 发表于:2004.09.08 16:31 ::分类: ( Oracle is anything ) ::阅读:(729次) :: 评论 (0)
===========================================================
我的oracle + blog之旅
===========================================================

大约几个星期前有个网友在灌水的地方问:

求:oracle与sql的区别

一校友回答:

oracle只是一个关系数据库产品,一个dbms而已。

sql是所有关系数据库的操作语言。

学数据库,sql最重要。

每个dbms都会自己多多少少的扩展标准sql,提供更多功能和服务,但是扩展sql可移植性极差,不过这个坛子里都是做oracle的,所以当然不会太理会这些。

 

为此我自然是有不同的意见,总结了下自己对此的理解

1:我猜测对于他来说是问的 oracle 与 mssql 的区别
2:sql,对于初步使用数据库的开发人员来说是最重要,若要进一步tuning sql 则是理解sql的执行计划、大致算法和数据库优化器的原理最重要,当然这里除了 优化器外 还没有脱离sql的范畴
3:要做到高级sql tuning,比如对于oracle数据库来说,必须深刻理解数据库的体系结构 和 很多概念原理以及一些管理方面的知识,明白数据库层的一些东西严重影响了sql的执行计划
4:若要说学好数据库,真正地重要的是,真正地理解体系结构和数据库概念,明白数据库为什么要这么设计,然后理解备份恢复、tuning方面的原理,再结合大量实践,验证原理,提升理论,从理论方面反过来指导实践,相辅相成,缺一不可!
5:再进一层,进入到数据库体系结构和基本概念的实现层,进入到internal阶段,明白了数据库的重要功能实现算法 和 处理机制,这个时候管理数据库和优化数据库对你来说基本只是如吃饭穿衣一样自然

基本,这是从我对oracle的理解的角度来阐述的,如果是mssql,可能通常你将止步于tuning sql 部分。你必须明白各种数据库的特性以最好地利用他们,除非,若你真正地要做一个通用的适合在任何数据库上跑的产品,也依然要明白各种数据库的特性才好,你将能更好地设计通用代码 和 明白通用的代码将损失太多的数据库功能

 

联想到更早一些的时候,大约是2年多前,偶曾就学习oracle作过一些层次上的认识,在此也搜刮出来,以我今日之感悟来参考一下

-1:不知道从何入手去寻找解决问题的方法
0:寻找不出问题的解决方法
1:针对问题寻找出一种不会出错的方法
2:哪些步骤或操作不能做或者慎做
3:为什么要这么做
4:为什么不能那样做,那样做造成哪些影响
5:什么情况下应该怎么做
6:oracle为什么要这样设计?
7:oracle这么设计有什么缺陷?应该怎样设计?

大部分人大部分时间寻求的就是在层次1上
新手为什么老出现很多莫名其妙的问题?
就是因为不知道1
为什么老手有时无法解答新手的问题
因为在1这个过程中,出现的可能太多了!!!

经验的积累就在于1和2上
3和4达到了,就算很熟悉了
5达到了,精通oracle
6达到了,恭喜你!


bitirainy 发表于:2004.09.08 16:29 ::分类: ( Oracle is anything ) ::阅读:(742次) :: 评论 (0)
===========================================================
ORALCE的执行计划稳定性
===========================================================
什么是执行计划

所谓执行计划,顾名思义,就是对一个查询任务,做出一份怎样去完成任务的详细方案。举个生活中的例子,我从珠海要去英国,我可以选择先去香港然后转机,也可以先去北京转机,或者去广州也可以。但是到底怎样去英国划算,也就是我的费用最少,这是一件值得考究的事情。同样对于查询而言,我们提交的SQL仅仅是描述出了我们的目的地是英国,但至于怎么去,通常我们的SQL中是没有给出提示信息的,是由数据库来决定的。

我们先简单的看一个执行计划的对比:

SQL> set autotrace traceonly

执行计划一:

SQL> select count(*) from t;

  COUNT(*)

----------

     24815

 

Execution Plan

----------------------------------------------------------

   0      SELECT STATEMENT Optimizer=CHOOSE

   1    0   SORT (AGGREGATE)

   2    1     TABLE ACCESS (FULL) OF 'T'

执行计划二:

SQL> select count(*) from t;

  COUNT(*)

----------

     24815

 

Execution Plan

----------------------------------------------------------

   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=26 Card=1)

   1    0   SORT (AGGREGATE)

   2    1     INDEX (FULL SCAN) OF 'T_INDEX' (NON-UNIQUE) (Cost=26 Card=28180)

这两个执行计划中,第一个表示求和是通过进行全表扫描来做的,把整个表中数据读入内存来逐条累加;第二个表示根据表中索引,把整个索引读进内存来逐条累加,而不用去读表中的数据。但是这两种方式到底哪种快呢?通常来说可能二比一快,但也不是绝对的。这是一个很简单的例子演示执行计划的差异。对于复杂的SQL(表连接、嵌套子查询等),执行计划可能几十种甚至上百种,但是到底那种最好呢?我们事前并不知道,数据库本身也不知道,但是数据库会根据一定的规则或者统计信息(statistics)去选择一个执行计划,通常来说选择的是比较优的,但也有选择失误的时候,这就是这次讨论的价值所在。

 ORACLE优化器模式

ORACLE优化器有两大类,基于规则的和基于代价的,在SQLPLUS中我们可以查看init文件中定义的缺省的优化器模式。

SQL> show parameters optimizer_mode

 

NAME                                 TYPE    VALUE

------------------------------- ------ --------

optimizer_mode                     string   CHOOSE

SQL>

这是ORACLE8.1.7 企业版,我们可以看出,默认安装后数据库优化器模式为CHOOSE,我们还可以设置为 RULEFIRST_ROWS,ALL_ROWS。可以在init文件中对整个instance的所有会话设置,也可以单独对某个会话设置:

SQL> ALTER SESSION SET optimizer_mode  = RULE;

会话已更改。

 

SQL>  ALTER SESSION SET optimizer_mode  = FIRST_ROWS;

会话已更改。

 

SQL>  ALTER SESSION SET optimizer_mode  = ALL_ROWS;

会话已更改。

基于规则的查询,数据库根据表和索引等定义信息,按照一定的规则来产生执行计划;基于代价的查询,数据库根据搜集的表和索引的数据的统计信息(通过analyze 命令或者使用dbms_stats包来搜集)综合来决定选取一个数据库认为最优的执行计划(实际上不一定最优)。RULE是基于规则的,CHOOSE表示如果查询的表存在搜集的统计信息则基于代价来执行(CHOOSE模式下ORACLE采用的是 FIRST_ROWS),否则基于规则来执行。在基于代价的两种方式中,FIRST_ROWS指执行计划采用最少资源尽快的返回部分结果给客户端,对于排序分页页显示这种查询尤其适用,ALL_ROWS指以总体消耗资源最少的方式返回结果给客户端。

基于规则的模式下,数据库的执行计划通常比较稳定。但在基于代价的模式下,我们才有更大的机会选择最优的执行计划。也由于ORACLE的很多查询方面的特性必须在基于代价的模式下才能体现出来,所以我们通常不选择RULE(并且ORACLE宣称从 ORACLE 10i版本数据库开始将不再支持 RULE)。既然是基于代价的模式,也就是说执行计划的选择是根据表、索引等定义和数据的统计信息来决定的,这个统计信息是根据 analyze 命令或者dbms_stats包来定期搜集的。首先存在着一种可能,就是由于搜集信息是一个很消耗资源和时间的动作,尤其当表数据量很大的时候,因为搜集信息是对整个表数据进行重新的完全统计,所以这是我们必须慎重考虑的问题。我们只能在服务器空闲的时候定期的进行信息搜集。这说明我们在一段时期内,统计信息可能和数据库本身的数据并不吻合;另外就是ORACLE的统计数据本身也存在着不精确部分(详细参考ORACLE DOCUMENT),更重要的一个问题就是及时统计数据相对已经比较准确,但是ORACLE的优化器的选择也并不是始终是最优的方案。这也倚赖于ORACLE对不同执行计划的代价的计算规则(我们通常是无法知道具体的计算规则的)。这好比我们决定从香港还是从北京去英国,车票、机票等实际价格到底是怎么核算出来的我们并不知道,或者说我们现在了解的价格信息,在我们乘车前往的时候,真实价格跟我们的预算已经发生了变化。所有的因素,都将影响我们的整个开销。

  执行计划稳定性能带给我们什么

ORACLE存在着执行计划选择失误的可能。这也是我们经常遇见的一些现象,比如总有人说我的程序在测试数据库中跑的很好,但在产品数据库上就是跑的很差,甚至后者硬件条件比前者还好,这到底是为什么?硬件资源、统计信息、参数设置都可能对执行计划产生影响。由于因素太多,我们总是对未来怀着一种莫名的恐惧,我的产品数据库上线后到底跑的好不好?于是ORACLE提供了一种稳定执行计划的能力,也就是把在测试环境中的运行良好的执行计划所产生的OUTLINES移植到产品数据库,使得执行计划不会随着其他因素的变化而变化。

那么OUTLINES是什么呢?先要介绍一个内容,ORACLE提供了在SQL中使用HINTS来引导优化器产生我们想要的执行计划的能力。这在多表连接、复杂查询中特别有效。HINTS的类型很多,可以设置优化器目标(RULECHOOSEFIRST_ROWSALL_ROWS),可以指定表连接的顺序,可以指定使用哪个表的哪个索引等等,可以对SQL进行很多精细的控制。通过这种方式产生我们想要的执行计划的这些HINTS,ORACLE可以存储这些HINTS,我们称之为OUTLINES。通过STORE OUTLINES可以使得我们拥有以后产生相同执行计划的能力,也就是使我们拥有了稳定执行计划的能力。

这里想给出一个附加的说明就是,实际上,我们通过工具改写SQL,比如使用SQL  EXPERT改写后的SQL,这些不仅仅是加了HINTS而且文本都已经发生了变化的SQL,也可以存储OUTLINES,并可被应用到应用中。但这不是一定生效,我们必须测试检查是否生效。但由于就算给了错误的OUTLINES,数据库在执行的时候,也只是忽略过去重新生成执行计划而不会返回错误,所以我们才敢放心的这么使用。当然在ORACLE文档中并没有指明可以这样做,文档中只是说明,如果存在OUTLINES的同时又在SQL中加了HINTS,则会使用OUTLINES而忽略HINTS。这个功能在LECCO将发布的产品中会使用这一功能,这样可以将SQL EXPERT的改写SQL的能力和稳定执行计划的能力结合起来,那么我们就对不能更改源代码的应用具有了相当强大的SQL优化能力。

也许我们会有疑问,假如稳定了执行计划,那还搜集统计信息干吗?这是因为几个原因造成的,首先,现在的执行计划对于未来发生了变化的数据未必就是合适的,存在着当前的执行计划不满足未来数据的变化后的效率,而新的统计信息的情况下所产生的执行计划也并不是全部都合理的。那这个时候,我们可以采用新搜集的统计信息,但是却对新统计信息下不良的执行计划采用ORACLE提供的执行计划稳定性这个能力固定执行计划,这样结合起来我们可以建立满意的高效的数据库运行环境。

我们还需要关注的一个东西,ORACLE提供的dbms_stats除了具有搜集统计信息的能力,还具有把数据库中统计信息(statisticsexport/import的能力,还具有只搜集统计信息而使得统计信息不应用于数据库的能力(统计信息搜集到一个特定的表中而不是立即生效),在这个基础上我们就可以把统计信息export出来再import到一个测试环境中,再运行我们的应用,在测试环境中我们观察最新的统计信息会导致哪些执行计划发生变化(DB EXPERTPlan Version Tracer是模拟不同环境并自动检查不同环境中执行计划变化的工具),是变好了还是变差了。我们可以把变差的这一部分在测试环境中使用hints或者利用工具(SQL EXPERT是在重写SQL这一领域目前最强有力的工具)产生良好的执行计划的SQL,利用这些SQL可以产生OUTLINES,然后在产品数据库应用最新的统计信息的同时移植进这些OUTLINES

最后说一下我们不得不使用执行计划稳定性能力的场合。我们假定ORACLE的优化器的选择都是准确的,但是优化器选择的基础就是我们的SQL,这些SQL才从根本上决定了运行效率,这是更重要的一个优化的环节。SQL是基础(当然数据库的设计是基础的基础),一个SQL写的好不好,就相当于我们同样是要想去英国,但是我的起点在珠海,你的起点却在西藏的最边缘偏僻的一个地方,那不管你做怎样的最优路线选择,你都不如我在珠海去英国所花费的代价小。由于这个原因,通常如果是我们自己设计程序,我们可以尝试着修改SQL代码,但是,如果应用程序是第三方开发的,或者我们是在别人的基础上进行的二次开发,比如我们的ERP系统是SAP的,那就算我们在数据库中发现SQL有严重的效率问题,我们也无力对应用程序进行修改。但是,我们可以在数据库中捕获这些SQL,然后为这些SQL产生一个良好的执行计划的OUTLINES,在利用执行计划稳定性来把SQL和产生的良好执行计划的OUTLINES绑定。这样就可以在不修改源代码的基础上提高程序的运行效率。这也是惟一的办法。

  怎么使用执行计划稳定性

我们先以一个最简单的例子演示怎么使用执行计划稳定性

首先我们得创建一个category,把我们所想稳定下来的执行计划放在这个category下,这是一种执行计划的分类,我们可以创建很多category,但是我们的每个session只能选择其中一个category以使用其中的定制好的执行计划。

通常我们采用一种最简单的方式来进行这个过程:

首先,为了生成执行和观察执行计划,我们创建一个保存执行计划的表。

SQL> @E:oracleora81RDBMSADMINutlxplan;

 

表已创建。

这个脚本utlxplan.sql $ORACLE_HOMERDBMSADMIN目录下

 

然后创建一个实验表。

SQL> create table t as select * from all_objects;

 

表已创建。

 

SQL> create index t_index on t(object_id);

 

索引已创建。(注意我们创建索引的字段是非空字段)

 

这里开始打开执行计划跟踪。

SQL> set autotrace on

SQL> select count(*) from t;

 

  COUNT(*)

----------

     30658

 

Execution Plan

----------------------------------------------------------

   0      SELECT STATEMENT Optimizer=CHOOSE

   1    0   SORT (AGGREGATE)

   2    1     TABLE ACCESS (FULL) OF 'T'

 

Statistics

----------------------------------------------------------

          0  recursive calls

          0  db block gets

        422  consistent gets

        418  physical reads

          0  redo size

        370  bytes sent via SQL*Net to client

        425  bytes received via SQL*Net from client

          2  SQL*Net roundtrips to/from client

          0  sorts (memory)

          0  sorts (disk)

          1  rows processed

这里我们可以看见生成的执行计划,查询进行了全表扫描,后面其实还跟了一系列的查询执行的时候的统计信息,但由于这不在我们的讨论范围之内,所以我们将忽略这些信息。

然后我们搜集这个表的统计信息,之后在执行查询发现执行计划已经发生了变化,不再是全表扫描而是根据索引进行扫描。

SQL> analyze table t compute statistics;

 

表已分析。

 

SQL> select count(*) from t;

 

  COUNT(*)

----------

     30658

 

Execution Plan

----------------------------------------------------------

   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=8 Card=1)

   1    0   SORT (AGGREGATE)

   2    1     INDEX (FAST FULL SCAN) OF 'T_INDEX' (NON-UNIQUE) (Cost=8Card=30658)

从这里开始,我们将尝试创建一个category

一直到会话结束或者set create_stored_outlines = false 之间的所有查询,我们都将为这些查询生成并保留一个执行计划,如下,这些执行计划保存在my_demo这个分类中。

SQL> alter session set create_stored_outlines = my_demo;

 

会话已更改。

SQL> select count(*) from t;

 

  COUNT(*)

----------

     30658

 

Execution Plan

----------------------------------------------------------

   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=8 Card=1)

   1    0   SORT (AGGREGATE)

   2    1     INDEX (FAST FULL SCAN) OF 'T_INDEX' (NON-UNIQUE) (Cost=8Card=30658)

 

SQL> alter session set  create_stored_outlines = false;

 

会话已更改。

在这里我们删除表的统计信息,然后再执行查询看看。

SQL> analyze table t delete statistics;

表已分析。

 

SQL> select count(*) from t;

  COUNT(*)

----------

     30658

 

 

Execution Plan

----------------------------------------------------------

   0      SELECT STATEMENT Optimizer=CHOOSE

   1    0   SORT (AGGREGATE)

   2    1     TABLE ACCESS (FULL) OF 'T'

我们发现这个时候执行计划已经恢复成全扫描。

于是我们尝试使session使用我们生成的category在执行查询。

SQL>  alter session set use_stored_outlines = my_demo;

 

会话已更改。

 

SQL> select count(*) from t;

 

  COUNT(*)

----------

     30658

 

Execution Plan

----------------------------------------------------------

   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=4 Card=1)

   1    0   SORT (AGGREGATE)

   2    1     INDEX (FAST FULL SCAN) OF 'T_INDEX' (NON-UNIQUE) (Cost=4 Card=35450)

bitirainy 发表于:2004.09.08 16:29 ::分类: ( Oracle is anything ) ::阅读:(964次) :: 评论 (0)
切换风格
新闻聚合
博客日历
文章归档...
最新发表...
博客统计...