2008年12月31日星期三

2008年个人总结

其实2008这一年对我来说还是挺重要的,今年的突破主要在以下几个方面:

1 ORACLE数据库上大有提高,对ORACLE数据库整体的认识在今年上了一个台阶,特别是对数据库体系结构比较清楚了,记得年前的时候,别人让我解释9i和10g架构上的差异,我都不知道如何回答。经过一年的刻苦修炼,我现在差不多能回答这个问题了。来年希望在数据库管理方面深入一下,并且在数据仓库,数据挖掘,OLAP系统方面能有所提高。

2 JAVA方面本年度提高不大,最大的进步是对RMI,CORBA,EJB的原理基本了解一点了, 还有对于基本的设计思想和设计模式算是有点感悟。希望来年把spring好好弄弄,熟悉熟悉功能,看看源码,希望提高比较大。

3 web展示层,应该是我今年提高最大方面之一,另一个是linux操作系统。从以前写根本并不会写页面到现在对 页面展示层还算比较了解,自我觉得提高很大。明年的目标就是结合spring再有点提高(嘿嘿,毕竟我的目标不是做前台)。

4 linux操作系统,给我带来的提高也很大。接触linux是因为ORACLE现在的很多架构转到linux上来了,得跟上技术的潮流。从刚开始接触,到现在已经有将近1年时间了,自己的感觉是对linux操作系统的一些基本的东西有所了解了,从开始的一点也不懂,到现在也能跟人砍一砍这个东西,进步还是蛮大的。希望来年在shell编程和服务器配置方面更进一步。

5 开源数据库和c语言,选择postgresql作为学习和研究的对象,主要是它号称基本都是c语言写的代码,而我自己c语言编程水平的提高很大一部分也来自于postgresql。当然今年只是学习了PG的SQL语言解析器部分的代码,但是仅仅这部分代码就已经让我进步很大了。 今年学会的C语言方面的知识有gcc,gdb,make,autoconfig,lex,yacc(bison),还有linux上的一大堆c语言库函数和API,回头看看进步神速,颇感欣慰。希望来年继续postgresql的学习和研究。

大概算算,今年学了很多东西,也浪费了很多时间,来年可真得抓紧时间,好好工作,努力学习了。

2008年12月28日星期日

ORACLE动态采样初探

一直以来,我都以为ORACLE的动态采样只有在没有统计信息的时候才有用,今天看了TOM老人家得一篇大作,受益匪浅啊。

dynamic sampling从ORACLE9iR2开始就可以使用了,CBO在执行hard parse的时候使用动态采样来收集相关表的统计信息同时来纠正自己的猜测数据。这个过程只在hard parse中产生,并且被用来生成更加准确的统计信息以供CBO使用,因此被称作动态采样。

优化器使用很多输入参数来产生合适的执行计划,比如它使用表上的约束,系统统计信息,查询涉及的相关对象的统计信息。优化器使用相应的统计信息来估算基数,而基数正是成本计算的最重要的变量。也可以说基数估算的正确与否,直接影响到执行计划的选择。这就是引入动态采样的直接动机--帮助优化器估算正确的基数,从而得到正确的执行计划。

动态采样提供了11个级别可以供使用(从0到10),后面我会详细解释每一个级别。ORACLE 9i R2的默认动态采样级别是1,而10g 11g的默认级别是2。

1 使用动态采样的方法
数据库级别可以调整optimizer_dynamic_sampling参数,会话级别可以使用alter session
在查询里可以使用dynamic_sampling提示

2 几个例子
针对没有统计信息的表:
SQL> create table t
2 as
3 select owner, object_type
4 from all_objects
5 /
Table created.

SQL> select count(*) from t;

COUNT(*)
------------------------
68076

下面的查询禁用了动态采样:
SQL> set autotrace traceonly explain
SQL> select /*+ dynamic_sampling(t 0) */ * from t;

Execution Plan
------------------------------
Plan hash value: 1601196873

--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 16010 | 437K| 55 (0)| 00:00:01 |
| 1 | TABLE ACCESS FULL| T | 16010 | 437K| 55 (0)| 00:00:01 |
--------------------------------------------------------------------------
下面的查询启用了动态采样:
SQL> select * from t;

Execution Plan
------------------------------
Plan hash value: 1601196873

--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 77871 | 2129K| 56 (2)| 00:00:01 |
| 1 | TABLE ACCESS FULL| T | 77871 | 2129K| 56 (2)| 00:00:01 |
--------------------------------------------------------------------------

Note
------------------------------------------
- dynamic sampling used for this statement

请注意,启用动态采样后的执行计划的基数(77871)更加接近实际的行数(68076),因此这个执行计划更可靠一点。

SQL>set autot off

当然也可能估算出差别很大的基数:

SQL> delete from t;
68076 rows deleted.

SQL> commit;
Commit complete.

SQL> set autotrace traceonly explain
禁用动态采样:
SQL> select /*+ dynamic_sampling(t 0) */ * from t;

Execution Plan
------------------------------
Plan hash value: 1601196873

--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 16010 | 437K| 55 (0)| 00:00:01 |
| 1 | TABLE ACCESS FULL| T | 16010 | 437K| 55 (0)| 00:00:01 |
--------------------------------------------------------------------------
启用动态采样:
SQL> select * from t;

Execution Plan
-----------------------------
Plan hash value: 1601196873

--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 28 | 55 (0)| 00:00:01 |
| 1 | TABLE ACCESS FULL| T | 1 | 28 | 5 (0)| 00:00:01 |
--------------------------------------------------------------------------

Note
---------------------------------------
- dynamic sampling used for this statement
实际上,表里现在没有数据,但是由于使用了delete而不是truncate,数据库并没有重置HWM,因此数据库猜错了结果,启用动态采样的基数要远远强于没有采样的。

以上两个例子,都是在没有对表进行统计的时候得到的,那么在有统计信息的情况下,动态采样还能有作用么?请考虑以下的例子,假设EMP表里有两个字段,birth_day date 和星座(varchar2),假设其中有1440000行数据(当然数据库表设计没有遵循第二范式)。在这种情况下,查询出生在1月份,并且星座是天平座的人,CBO会估算出多少行呢?答案是1W,但是实际上一行都不会返回,在这种数据库表设计有严重问题的时候,动态采样再一次向我们展示了其魅力所在。

SQL> create table t
2 as select decode( mod(rownum,2), 0, 'N', 'Y' ) flag1,
3 decode( mod(rownum,2), 0, 'Y', 'N' ) flag2, a.*
4 from all_objects a
5 /
Table created.

SQL > create index t_idx on t(flag1,flag2);
Index created.

SQL > begin
2 dbms_stats.gather_table_stats
3 ( user, 'T',
4 method_opt=>'for all indexed columns size 254' );
5 end;
6 /
PL/SQL procedure successfully completed.
这里做了统计,有直方图了,以下是一些统计信息:

SQL> select num_rows, num_rows/2,
num_rows/2/2 from user_tables
where table_name = 'T';

NUM_ROWS NUM_ROWS/2 NUM_ROWS/2/2
-------- ---------- ------------
68076 34038 17019

SQL> set autotrace traceonly explain
SQL> select * from t where flag1='N';

Execution Plan
------------------------------
Plan hash value: 1601196873

--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 33479 | 3432K| 292 (1)| 00:00:04 |
|* 1 | TABLE ACCESS FULL| T | 33479 | 3432K| 292 (1)| 00:00:04 |
--------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("FLAG1"='N')

SQL> select * from t where flag2='N';

Execution Plan
----------------------------
Plan hash value: 1601196873

---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 34597 | 3547K| 292 (1)| 00:00:04 |
|* 1 | TABLE ACCESS FULL| T | 34597 | 3547K| 292 (1)| 00:00:04 |
---------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

1 - filter("FLAG2"='N')

前两个执行计划都是准确的,会返回34597行数据,大概是表里数据的一半。接着执行以下查询:

SQL> select * from t where flag1 = 'N' and flag2 = 'N';

Execution Plan
----------------------------
Plan hash value: 1601196873

--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 17014 | 1744K| 292 (1)| 00:00:04 |
|* 1 | TABLE ACCESS FULL| T | 17014 | 1744K| 292 (1)| 00:00:04 |
--------------------------------------------------------------------------

Predicate Information (identified by operation id):
----------------------------------------------------

1 - filter("FLAG1" = 'N' AND "FLAG2" = 'N')

这时会返回表里四分之一左右的数据,但是实际上应该是一行数据都没有,并且由于错误的基数估算导致错误的执行计划。再看看下面的这个查询,强制CBO进行动态采样。

SQL> select /*+ dynamic_sampling(t 3) */ * from t where flag1 = 'N' and flag2 = 'N';

Execution Plan
-----------------------------
Plan hash value: 470836197

------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 6 | 630 | 2 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| T | 6 | 630 | 2 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | T_IDX | 6 | | 1 (0)| 00:00:01 |
------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
----------------------------------------------------

2 - access("FLAG1"='N' AND "FLAG2"='N')

CBO估计会返回6行数据,并且进行了索引范围扫描,成本也很低,总体来说这个执行计划还令人满意。至于CBO估算出来的基数为什么是6,而不是0,这个解释起来比较复杂,在此就不详述了。

3 CBO动态采样级别:
level 0:不使用动态采样
level 1:在以下情况进行采样所有查询涉及的表:(1)查询语句里至少有一个未anylze过的表;(2)unanalyzed table连接到其他表或者出现在子查询中或者在不可合并的视图中;(3)这个表上没索引;(4)表数据块比进行动态采样的数据块多。
level 2:应用动态采样到所有unanalyzed table,表数据块至少是动态采样块的2倍。
level 3:满足level2的所有条件,并且外加所有表的标准选择率可以使用动态采样的谓词来计算
level 4:满足level3的所有标准,并且表上有参照到多个列的单个谓词。
level 5,6,7,8 and 9:满足前一个级别的所有标准,使用使用2,4,8,32,128倍的默认动态采样率。
level 10:满足level9的所有标注,并且对表里的所有块进行动态采样。

当然,前面的关于11个level的论述我可能翻译的不是很准确,有兴趣的话可以参考oracle database performance tuning.

4 何时使用动态采样以及如何使用:
(1) OLAP系统中可以大量使用
(2) 尽量避免在OLTP系统中使用
(3) 如果想在OLTP系统中使用的话,建议通过sql profiles来使用,遗憾的是从10g以后才能使用这个功能。原理上来说,SQL profiles有点像收集统计数据的查询和存储信息的字典,因此降低动态抽样的时间和hard parse的时间。

欢迎有问题一起讨论。tom关于这个问题有更加详细的论述,如果有兴趣的话,可以参考。

2008年12月25日星期四

北林为什么强于哈佛大学?

从别人那里转过来的。

1.北林是我党领导下的社会主义国家公立大学,哈佛大学是没落的资本主义国家的私立大学.两种国家的性质决定了北林地位必然高于哈佛大学,这是谁也无法抹杀的.

2.学校占地规模决定了北林强于哈佛大学,北林两个校区共占地面积约为12289亩,而哈佛大学总共占地才500亩,小的可怜.

3.学生人数上哈佛大学再次告负.目前,北林学生人数达2万多人,而哈佛大学才1万多,相比北林少了1万人.为什么会少了1万人?!不正是说明世界人民心目中喜欢北林的人多余喜欢哈佛大学的人吗?

4. 学生的素质.北林的学生不但全部流利掌握中文,并且大多数通过了大学英语四六级考试.而哈佛的学生除了会说英语,懂中文的人实在少之又少.同样,作为学校教学和科研的主力军,北林有很多老师既有中国教育背景,又有国外留学背景.而哈佛大学,全校竟找不出几个有完整中国教育背景的老师来.

5.两个学校不同的校训预示了两个学校未来的命运.北林的校训是"知山知水,树木树人" ,读起来琅琅上口,一种朝气蓬勃的社会主义优越感,而哈佛大学的校训是"以柏拉图为友,以亚历士多德为友,更要以真理为友",听起来让人有同性恋的感觉,而且还有一种暮气沉沉的感觉.

6. 另外结合本人的个人经历,我更深深体会到北林作为一所世界级名校在所有学子心目中的崇高地位.我本人作为一名美貌与智慧并存,温柔而不失刚毅的优秀有为青年,除了天资聪慧,秉赋过人外,更是每日五更勤奋苦读,经过不懈努力,终于有幸被北林录取.而哈佛大学,在本人及周边所有认识的人的高考经历中,没有一个志愿填报过这所学校.

综上所述,我可以很自豪的说:"我选择了北林,无怨无悔,因为它就是世界上最好的大学."


我们承认哈佛大学在世界范围内的名气大于北林,但这是由于美帝国主义的媒体掌握着话语权,有意压制北林的结果,我们相信通过全校校友在网络上的宣传,我们一定可以让全世界人民认识并喜欢.

2008年12月22日星期一

char型数据和绑定变量

众所周之,在ORACLE数据库中使用绑定变量是比较好的作法。今天一个同事在使用绑定变量的时候碰到一点问题,花了一点时间来解决,这个问题应该很容易碰见,对一般程序员来说挺有挑战性的。

在ORACLE数据库中做以下操作:
create table char_test(tid char(2),nid number(2));
insert into char_test select to_char(rownum),rownum from all_objects where rownum<=10;
commit;

然后分别运行以下语句:
select * from char_test where tid='1';(可以查出来值)
select * from char_test where tid='10';(也可以查出来值)
select * from char_test where tid=:ptid;

在这里将绑定变量ptid设为'1','10',可以看到前者没有查出来值,而后者有值。
原因主要是数据库在做针对字面量的查询时,做了转换,将'1',转换成了占char2,因此前者的出来正确结果,而使用绑定变量的时候,数据库不会做这个转换,因此就查不出来值。当然在都是查询tid='10'的时候,就没有这个问题。

我们也可以查看char(2)和varchar2(2)的区别:
select dump('1') from dual;
select tid from char_test where nid=1;

解决方法是在使用绑定变量并且数据类型是char的时候,将查询语句改写一下:
select * from char_test where tid=rpad(:ptid,2);

2008年12月21日星期日

apex--ORACLE的云计算开发工具

前两天看见ORACLE的APEX(application express可以在OTN直接使用了,中文应该叫快捷应用吧),介绍APEX的文章在OTN上有的是,有兴趣的话可以去看看。值得一提的是可能ORACLE会把APEX作为自己云计算的开发工具来使用,看来ORACLE也想在云计算市场上分一杯羹。话说回来,其实网格计算就可以看做是一种低空云。可以通过http://apex.oracle.com来使用使用该工具,注册后ORACLE会免费提供10M的空间供你存储数据和应用程序。

APEX是一个及其傻瓜的工具,比ACCESS更傻瓜,不过功能很强大,可以作出很多应用来,具体效果看图就知道了。
以下是我抓的图片(用APEX做的小例子):







不知各位老大如果用JAVA或者.NET要用多长时间才能把这么大的一个网站做完,如果是我的话,至少需要2周,当然实在不包括CSS的情况下。可是使用APEX只需要2天就可以做完了,而且界面美观,没什么bug,性能应该也还可以,中小型应用应该够了,毕竟这个东西支撑起了asktom。

CBO基础笔记

cursor_sharing 用于控制字面量替换
db_file_multiblock_read_count DFMRC用于在全表扫描和索引全扫描时计算成本
optimizer_index_caching 索引缓存率
optimizer_index_cost_adj 用于调整索引访问的成本
PGA_AGGREGATE_TARGET 用于控制hash和排序区的大小
optimizer_mode all_rows,first_rows_n,first_rows
star_transformation_enabled 是否允许星型转换
v$sql_cs_statistics 保存绑定变量的执行信息
低索引集群因子说明相近的锁银行集中少量列上

v$sql_plan,v$sql_plan_statistics和v$sql_plan_statistics_all可知plan
select plan_table_output from table(dbms_xplan.display());
select * from table(dbms_xplan.display('plan_table',null,'All'));
alter session set db_file_multiblock_read_count=16;

闪回查询前导列如何使用
object_id,object_value与对象表相关
全表扫描的成本计算公式:
1+高水位线下的块的数目/调整后的参数
单表选择率算法:
(numrows-numnull)/num_rows
alter session set "_optimizer_cost_model"=io;

begin
dbms_stats.gather_table_stats(user,'t1',estimate_percent=>null,method_opt=>'for all collumns size 120');
end;/

sql trace:
1 alter session set tracefile_identifier='fangyuan';
2 alter session set sql_trace=true;
do sth
3 alter session set sql_trace=false;
4 show parameter use_dump_dest
5 tkprof
or select spid from v$session s, v$precess p
where s.paddr=p.addr
and s.username=user
and module='SQL*PLUS';

10053 event:
alter session set event '10053 trace name context forever';
alter session set event '10053 trace name context off';

查询计划中的view_pushed predicate表示进行了谓词推进
优化器默认使用嵌套循环来处理anti_join,但是如果使用merge_aj,hash_aj,nl_aj的话,优化器能进行相应的转换。
优化器默认使用嵌套循环来处理semi_join,但是如果使用merge_sj,hash_sj,nl_sj的话,优化器能进行相应的转换。
但是在11g并且有统计信息的情况下,优化器不一定会使用NL来默认处理上述两种情况

内联视图
select p.pname ,c1_sum1,c2_sum2 from p,
(select id,sum(*) c1_sum1 from s1 group by id) s1,
(select id,sum(*) c2_sum2 from s2 group by id) s2
where p.id=s1.id and p.id=s2.id

标量子查询
select p.pname,
(select sum(p1) c1_sum1 from s1 where s1.id=p.id) c1_sum1,
(select sum(p2) c2_sum2 from s2 where s2.id=p.id) c2_sum2
from p

将子查询转换为内联视图是比较好的做法
提高CBO对于not in 的选择率精度,必须保证连接操作两端都不为NULL,负责可能出现错误结果

star提示可以用于进行星型连接
alter session set star_transformation_enabled=...
B树索引访问成本:
blevel+ceiling(leaf_blocks*effectvie index sel)+ceiling(clustering_factor+effectvie index sel)

连接操作选择率公式:
sel=(
(num_rows(t1)-num_nulls(t1,c1))/num_rows(t1)
*(num_rows(t2)-num_nulls(t2,c2))/num_rows(t2)
/greater(num_distinct(t1,c1),num_distinct(t2,c2))
)
基数公式:
card=
sel*filtered card(t1)*filtered card(t2)
当过滤谓词仅仅出现在一侧时,需要使用另一侧的distinct来替代最大值。
如果是多列连接,需要将多个连接条件的选择率相乘,如果在10g以后,可能会使用两个相同的条件相乘,并且选择较大的选择率
如果是范围连接,优化器使用类似与绑定变量的选择率处理
如果是不等值连接,选择率为1-等值连接的选择率

优化器在处理不等值连接且条件间为or时可能会有错误,如果加入no_expand提示就可以得到正确的结果,或者讲等值连接的条件分别写成查询最后union all.

在有重复史书的时候,计算成本总为1000k或者1,在有直方图的情况下,某些查询的成本会好一些,但是在数据不重叠时候,直方图会带来新的问题。
在建立直方图的时候,只有size远远大于列中不同的值的数量的时候,才可能使用频率直方图,否则都会使用高度均衡直方图。

如果在连接条件上有一个过滤条件,CBO可能会做闭包传递,导致过滤条件不可用以及忽略有过滤条件的连接条件,如果连接条件两边都有过滤条件,则有可能是成本和基数的差异都很大。

在进行多表连接时,需要将选择率相乘,而计算公式中的参数直接来自于语句中的谓词,在执行复杂查询的时候,连接条件非常重要

在估算code-业务表这种类型的连接查询时,如果讲很多的代码并入一张CODE表中,并且添加type列来进行区分,那么优化器将有可能进行错误的成本计算,即使建立了直方图也有这个问题。解决方法是对code表按type进行分区。

绑定变量的选择率为:density or 1/num_distinct
optimizer_index_caching设为75在OLTP系统中比较合理

嵌套循环的成本:
从第一个表取数据的成本+从第一个表得到结果的基数*对第二个表访问一次的成本
其实说白了就是嵌套循环的原理

hash_join的成本:
(探查遍数+1)*ceiling(大表数据块大小/调整IO后的单表值)+ceiling(小表表数据块大小/调整IO后的单表值)+hash_join前的成本和
其实就是hash_join的原理

10104事件用于查看hash_join的细节

dbms_lock.request(1,dbms_lock.xmode,release_on_commit=>true);
用于加排他锁,这时在其他会话中执行童谣的代码时就会等待,可以用这个方法来模拟并发。

begin
execute immediate 'purge recylebin';
end;

alter session set work_size_policy=mamual;
later session set hash_area_size=;

event 10032用于报告排序时系统内部相关活动的统计信息
event 10046 level 8用于记录等待状态
event 10033列出并发的IO细节
alter session flush shared_pool;
alter session flush buffer_cache;

一般来说,增加sort_area_size可以改善排序,但也可以增加对CPU的占用
在使用union,minus,intersect操作时,不要使用distinct关键字,否则计算card,cost有错误

10053事件的追踪报告通常包括:
绑定变量
参数设定
查询块
基础统计信息
完备性检查
一般执行计划:
在这里会衡量各个访问路径和顺序,但是在表的数量较多(4个表就有24种可能,4个表通常ORACLE只会估算13-16个路径)的情况下,ORACLE不会遍历所有的路径,这也是SQL需要优化的原因。

2008年12月18日星期四

ORACLE REA --很牛!


这两天刚看见ORACLE RICH ENTERPRISE APPLICATIONS,这个东西可以在OTN上直接看到,感觉做的很强,里面有一些哥哥姐姐们的视频,可以用来学习ORACLE在JAVA方面的一些技术,主要是ADF。刚看完他们的视频,感觉jdeveloper这个工具的功能越来越强了,毕竟是ORACLE精心打造的。

可以点击这里去学习一下。