读书笔记 Expert one by one 第一章 开发成功的oracle应用

 

  1. 1 开发成功的oracle应用
    • 数据库为核心而不是应用程序
      • 开发人员需要了解数据库的原理和结构.
      • Tom的观点,对于一个应用的需求,如果可以的话
        • 只写一个sql
        • 不行的话用pl/sql
        • 在不行->jave-->c,还不行,看看你是否有开发这个功能的必要了:)
      • TOM的观点:
        • 开发人员把数据库当黑盒看待,开发出数据库独立的应用程序--必然是会产生大量性能很差或者可扩展性很差的程序.
        • 一个例子:
           

sys@ORCL> create table t(processed_flag varchar2(1) );

Table created.

 

sys@ORCL> create bitmap index t_idx on t(processed_flag);

Index created.

 

sys@ORCL> insert into t values ( 'N' );

1 row created.
如果另外一个会话也执行该insert语句,则会话会给挂起hang,因为锁等待. 而这造成了该表操作的可扩展性并发性很差.而开发人员却依然自以为是的认为开发的程序没问题啊. 这就是对于bitmap索引理解不深的问题.

  • 如何开发一个应用程序呢
    • 理解oracle的架构
      • 如果从sql server迁移到oracle,并发锁定机制等完全不同.所以,不可能有一个只需要最小的改变就能做到的.
        • 使用单独的连接.-sql server使用的是多连接,即使在一个page,oracle使用一个.而且windows的内存限制,解决方案:重构应用;升级os到大内存支持win;迁移os到其他os.
        • 使用绑定变量:可扩展性最大的影响在这儿.
          简单来说,绑定变量减少了sql语句的重新解析编译生成执行计划的时间.share pool中被cache,可以直接拿出来再次使用. 只是在执行时动态赋予变量值(绑定).
          Hard parse/soft parse.
        • 使用绑定变量,不但少用了很多资源,而且会保持latch更少的时间而且更不频繁的使用它们.
          看一个试验
           

sys@ORCL> create table t ( x int );

 

Table created.

 

sys@ORCL> create or replace procedure proc1

  2  as

  3  begin

  4      for i in 1 .. 10000

  5      loop

  6          execute immediate

  7          'insert into t values ( :x )' using i;

  8      end loop;

  9  end;

 10  /

 

Procedure created.

 

sys@ORCL> create or replace procedure proc2

  2  as

  3  begin

  4      for i in 1 .. 10000

  5      loop

  6          execute immediate

  7          'insert into t values ( '||i||')';

  8      end loop;

  9  end;

 10  /

 

Procedure created.

 

sys@ORCL> exec runstats_pkg.rs_start

 

PL/SQL procedure successfully completed.

 

sys@ORCL> exec proc1

 

PL/SQL procedure successfully completed.

 

sys@ORCL> exec runstats_pkg.rs_middle

 

PL/SQL procedure successfully completed.

 

sys@ORCL> exec proc2

 

PL/SQL procedure successfully completed.

 

sys@ORCL> exec runstats_pkg.rs_stop(1000)

Run1 ran in 295 hsecs

Run2 ran in 1115 hsecs

run 1 ran in 26.46% of the time

       

Name                                  Run1        Run2        Diff

LATCH.simulator lru latch            1,642         194      -1,448

LATCH.simulator hash latch           1,759         207      -1,552

STAT...redo size                 2,457,440   2,454,208      -3,232

LATCH.session allocation             7,226         909      -6,317

STAT...enqueue requests                 31      10,025       9,994

STAT...enqueue releases                 29      10,025       9,996

STAT...parse count (hard)                4      10,014      10,010

STAT...parse count (total)              25      10,074      10,049

STAT...session logical reads        10,666      20,754      10,088

STAT...consistent gets                 244      10,378      10,134

STAT...consistent gets from ca         244      10,378      10,134

STAT...recursive calls              10,326      20,887      10,561

LATCH.cache buffers chains          57,087      72,142      15,055

LATCH.enqueue hash chains              287      20,241      19,954

LATCH.enqueues                         271      20,243      19,972

LATCH.library cache pin             40,958      70,486      29,528

LATCH.kks stats                         46      39,815      39,769

STAT...physical read total byt     868,352     811,008     -57,344

STAT...physical read bytes         868,352     811,008     -57,344

LATCH.library cache lock               658      60,283      59,625

STAT...session uga memory                0      65,464      65,464

LATCH.row cache objects              2,880      90,651      87,771

STAT...session pga memory          -65,536      65,536     131,072

LATCH.library cache                 41,777     220,762     178,985

LATCH.shared pool                   22,059     207,732     185,673

STAT...session uga memory max      261,964           0    -261,964

       

Run1 latches total versus runs -- difference and pct

Run1        Run2        Diff       Pct

178,657     805,392     626,735     22.18%

可以大致看到差别上,hard parsesoft parse的巨大差距, 但这只是表面现象-本质在于,没有使用绑定变量时使用了大量的latch资源. 因为对于共享的内存资源,必须保证同时只能有一个进程修改,所以oracle采用了轻量级的latch机制(栓锁),来序列化这种访问(access). 这种串行化的设备,只允许一次一个而且短周期的访问那些数据结构.
Share pool
library cachebig-time latch,是用户最频繁竞争使用的资源. 那意味着实际上增加了更多的用户同时进行硬解析,则更多的用户同时争用共享池的latch,就会产生更长的队列,等待时间也随之变长,系统性能自然变差.
所以,很多项目的重构重点之一,就是重写sql为绑定变量方式.

  • 理解并行控制
    • 不同数据库的主要区别之一就在于并行控制的实现.如果我们理解不深入的话,坏处
      • 损坏数据的完整性(integrity)
      • 应用运行变慢
      • 很困难扩展到更多用户
    • 一个例子来说明并发性(backup at a tollbooth)-收费口阻塞.
    • 并发是很难跟踪的.
    • 1 :一个事务用锁来控制同一时刻只有该事务来修改一片给定的数据(比如同一行). 但是过度或者不恰当的使用锁会约束并发性.
      oracle
      中锁的机制参见前述章节.
      2
      阻止丢失更新.
      因为oracle的读是不堵塞的,所以很容易这样的一个类似预订会议室的程序会出现问题,该程序先查询空闲会议室,找到后再进行修改. -->而这有可能多个用户都同时查到,造成同时修改.
      这就需要有一个机制,在修改前的查询中也实现锁定. Select … for update.
  • 多版本(multi-versioning):并发操作中多用户得到的相同数据在oracle中可存在多个版本,称为多版本.
    • 多版本的实现是靠oracle的如下两个主要特征实现的:
      • 读一致性
      • 非阻塞的查询
    • 例子
      •  

drop table t;

 

create table t

as

select *

  from all_users;

 

variable x refcursor

 

begin

   open :x for select * from t;--->该句只open cursor而没有fetch.所以oracle此时并没有读数据,但该时间点的版本数据仍然'存在'.

end;

/

 

delete from t;

 

commit;

 

print x---->此时看到的数据应该是delete之前的数据.

 

  • 但是这个例子中并不是open动作oracle拷贝的数据,而是delete的时候保留了原来的数据(undo或者称为rollback回滚数据). 虽然我们无法确认数据是什么,只有在打开游标的时候(print x--fetch),但实际上从游标的角度来看,结果应该始终是固定不变的(与我们在一个查询中得到的结果类似,这个结果在open的时候就已经确认了,虽然只有到fetch的时候才开始取数据).
  • 多版本和闪回
    • oracle能够保证我们打开的数据的时间戳在以下两个时间点做选择(7章会详细说明隔离模式-isolation mode)
      • 游标open的点.这是read commited隔离模式的默认属性,也是默认的事务模式.
      • 查询所归属的事务的开始点(事务的开始点).这是READ ONLYSERIALIZABLE隔离模式的默认属性,但不是事务中默认的模式.
    • 9i开始,有了更多的灵活特性-比如flashback query.
      • 闪回的一个例子,
        • select count(*) from emp AS OF SCN :scn
    • 读一致性和非阻塞读
      • 看个例子,一个account,4条记录.
        • Select sum(account_balance) from accounts, ->这个操作在运算的时候,比如分解到11条记录取数值,当取到1的时候,如果第4条记录改变了,那么总的sum数值是否也该改变呢...当然不行,oracle独特的读一致性来保证.
          其他数据库的实现要么锁定表,要不在读的时候锁定行.
        • 不理解这个的话,开发者就会莫名其妙开发一些""或者同步的程序,读一致性开始于语句执行开始的时间点.
          Begin

for x in (select * from t)

loop

insert into t values (x.username, x.user_id, x.created);

end loop;

end;
没有读一致性这个程序就是一个死循环了
.
insert into t select * from t;-->很多数据库不支持这种recursive(自循环)语句

  • 数据库的独立性?
    • 从前面的例子中可以看到,这个想法是很困难实现的,除非你真的对所有的数据库都非常了解,才能开发出一个数据库独立的程序.
    • 重新拿起本章开头的预订会议室的例子来说,那个例子中select for update.如果设计是基于一个read lock的数据库,操作是序列化的-也就是说一个人预定完毕后才能下一个人定,从来不会并行. 该程序拿到oracle,oracle的锁是行级别的,而且不用读一致性锁,就会出现错误的结果,所以需要用for update来实现.
      各种不同的数据库很难用好坏来区分,只是工作机制不同.
      在举一个例子:
      对于NULL的实现,oracle是按照标准SQL的规定实现的,sybasesqlserver则不同,所以,如下oracle的语句在其他数据库中工作结果当然不同:
       

ops$tkyte@ORA10G> select * from dual where null=null;

no rows selected

ops$tkyte@ORA10G> select * from dual where null <> null;

no rows selected

ops$tkyte@ORA10G> select * from dual where null is null;

D

-

X
对于
NULL参与的运算,oracle比较操作符(=,<>)的结果既不是true,也不是false,而是未知. null is null可以, 所以sql server中的如下语句:
for C in ( select * from T where x = l_some_variable )
应该在
oracle中修改为
select *

from t

where ( x = l_some_variable OR (x is null and l_some_variable is NULL ))

但是带来新的问题--sql server中该语句可以使用索引,oraclenull是不能用的,,workaroud,
create index t_idx on t( nvl(x,-1) );
select * from t where nvl(x,-1) = nvl(l_some_variable,-1)
;

  • 有时候有人想要在plsql程序中建立一个临时表(比如sql server中的程序),但是如果想在oracle中做的话
    • ddl会阻止扩展
    • 常运行ddl会变慢?(doing ddl constantly is not fast)
    • ddlcommit现有的事务
    • 如果有在存储过程中执行ddl只能使用动态sql,而不是静态sql
    • 动态sql的运行是没有优化的静态sql执行快的
  • 标准化的影响(standards)
    • Sql89-->sql92-->sql99.
    • sql92的四个level
      • Entry level:入门级.大多数的数据库供应商都遵循该级别.但是没有数据库供应商获得比该级别更高级别的认证.
      • Transactional:介于entry levelintermediate之间
      • Intermediate: 增加了很多新功能
        • 动态sql
        • 级联delete对于引用完整性
        • Datetime的时间类型
        • Domains
        • 变长字符串
        • CASE定义
        • CAST函数用于在不同的数据类型间
      • Full
        • 连接管理
        • BIT数据类型
        • 可延迟的数据完整性deferable integrity constrains
        • From子句中导出表
        • CHECK子句中支持子查询
        • 临时表
    • 对于外连接和内连接,entry leveltransactional level都没有定义.但如果应用用了Intermediate或者更高级别的feature,则应用有一定的风险.
    • sql99定义只两个级别:coreenhanced.引入了面向对象的数据库的概念:arrays,collections,等等. 但是没有数据库遵循或者认证了该级别,无论core还是enhanced.
    • 各种数据库除了语法的差异外,实现上也有差异,从而相同的查询在不同的数据库中性能也不同.还有并行控制,数据隔离,查询一致性等问题.
  • 保守性编程(defensive programming)
    • 不刻意追求通用,尽可能多的使用你可用的资源,然后如果需要迁到其他数据库,case-by-case改变。
    • 当然,oracle本身就是portableapplication,但是在不同的os上,其内部实现仍然不同,比如windowsunixmultithreadmulti process.
    • 不刻意追求通用性的一个例子:如果要获得一个唯一增长的key值,不同数据库有不同的方法,oraclesequenceinformixSERIAL数据类型,SQL ServerIDENTIFY类型。 所以开发人员面临的选择刻意1)完全自行开发一个数据库通用的方法 2)使用现有的数据库的功能-不通用。
      • 如果自行开发:建立一个表,插入一个记录,然后update key数值。但是,问题在于:
        • 只有一个用户同时可以访问
        • SERILIZABLE隔离模式(J2EE下常使用)下,等待的资源会收到ORA-08177错误。-开始block然后醒来后又出错了,该是多么糟糕的结果!
      • 所以更好的解决方案应该用oracle自己的sequence.这是一个非阻塞,高并发的解决方案.
    • 另外一个例子,如果写程序,可以直接在编程中调用sql,也可以开发一个抽象层,在各个数据库中写存储过程,当然各个数据库的内部实现也不一样。
      • 一个争论是作者建议写中间层,而且各自采用数据库不同的好的特性
      • 另外一个争论就是是否在开发组中找出这样一个人,这个人非常熟悉各种数据库之间的差异,从而能够写出"通用"的代码. 但是这样比登天还难. tom做了oracle11年之后,还在每天继续学习和应用oracle一些新的特性,仍然怀疑自己开发的时候是否最高效和精确.但是,比较可行的方案是找到一个熟悉一种数据库的,然后开发这段中间层代码-存储过程,再找到熟悉另外一个数据库的,开发另外一层代码,这个相对比较容易实现.
      • (待续)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值