Oracle 9i 10g编程艺术 —— 读书笔记(三)

来源:互联网 发布:面向对象数据库设计 编辑:程序博客网 时间:2024/04/27 14:52

        剩下的几章看得比较快了,有些看不懂的就直接过了。
        9.1redo与undo
        Oracle维护着两类重做日志文件:在线(online)重做日志文件和归档(archived)重做日志文件。若实例失败,就用在线重做日志文件恢复。若介质失败,就用归档重做日志文件恢复。

        9.4commit
        不论事务有多大,commit的响应时间一般都差不多。提交的过大花费的时间就越长。commit的开销存在两个因素:会增加与数据库的往返通信,如果每个记录都提交,生成的往返通信量就会大得多。每次提交时,必须等待redo写至磁盘。这会导致"等待",在这种情况下,等待称为"日志文件同步" 。

       11.索引
        Oracle包括如下索引:
        B*索引: 
        B*就是传统的索引,属于最常用的一种,其实现与二叉树查找很类似。其目标是尽可能减少Oracle查找数据所花费的时间,书中有个树型图画得相当清楚,树的最地层称为叶子节点,叶子节点之上的内部块称为分支块。B*索引只能是唯一条目,若存在非唯一的,Oracle会把rowid作为一个额外的列追加到键上,使得键唯一。在非唯一索引中,数据会先按索引键值排序,再按rowid升序排序,在唯一索引中,数据只按索引键值排序。
B*树的特点之一:所有的叶子节点都应该在同一层上,这一层称为索引的高度,这说明所有从索引的根块到叶子块的遍历都会访问同样数目的块,对于如"select indexed_col from T where indexed_col = :x", 要到达叶子块来获取第一行,不论使用的:x值是什么,都会执行同样数目的IO,换句话说,索引是高度平衡的。大多数B*树索引的高度都是2或者3,即使索引中有数百万行记录也是如此。这说明,一般来说,在索引中找到一个键
只需要执行2或3次IO,这倒不坏。还可以对索引键压缩,会相应的减少空间和高度。
B*索引的另一个特点是能够将索引键"反转",称为反向键索引。
B*索引还能扩展为降序索引,它允许索引中从大到小(降序)存储一列。
B*树索引的适用情况:
        1.通过索引来访问表中的很少的一部分行,如select * from T where owner = :user;  这里owner列上建立了B*索引。 这个比例在1%~20%之间,否则与全表扫描相比,通过B*树索引来访问这些数据通常要花更多的时间。
        2.索引结构提供了足够的信息,不用去访问堆组织表,在这种情况下,索引则用作一个"较瘦"版本的表,
 如select count(*) from T where owner = :user;  这扫描索引结构,并未访问表T。

        位图索引:
        位图索引对于相异基数低的数据最为合适,也就是说,与整个数据集的基数相比,这个数据只有很少几个不同的值。 位图索引的缺点,位图索引在读密集的环境中能很好的工作,但是对于写密集的环境则极不适用。原因在于,一个位图索引键条目指向多行,如果一个会话修改了所索引的数据,在大多数情况下,这个索引条目指向的所有行都会被锁定,这大大影响了并发性。

         位图联结索引:
         位图联结索引允许使用另外某个表的列对一个给定表建立索引。
 如:select emp.* from emp, dept where emp.deptno = dept.deptno and dept.dname = 'SALES';
 传统的方法必须访问emp和dept两张表,位图联合索引对dept.dname列建立索引,但这个索引不是指向dept表,而是指向emp表。 create bitmap index emp_bm_idx on emp(d.dname) from emp e, dept d where e.deptno = d.deptno /  再执行刚才的查询,会发现Oracle根本不会去访问dept表,使用emp上的索引就能从dept合并我们需要的数据,直接访问我们所需的行。 该索引有个先决条件,联结条件必须联结到另一个表中的主键或唯一键。

         基于函数的索引:
         这一节讲了很多问题,都是我项目中遇到,受益匪浅。 利用该索引能够对计算得出的列建立索引,并在查询中使用这些索引。 
        使用该索引常见的原因:1.这种索引很容易实现,并能立即提交一个值。2.可以加快现有应用的速度,而不用修改任何逻辑或查询。
        如:select * from emp where upper(ename) = 'KING';  执行这个查询,emp中的每一行都要扫描,改为大写并进行比较。 若加上索引:create index emp_upper_idx on emp(upper(ename));
 利用该索引,查询为索引提供了常量KING,然后只对少量数据执行区间扫描,并按rowid访问表来得到数据,这是相当快的。
        由于索引条目必须能在块大小的3/4中放得下,如果尝试对过大的返回值建索引,就会报错,如varchar2(4000)   这是就可以用substr处理一下,create index emp_idx on emp(substr(ename ,1 ,6))
 书中对有无该索引的情况说了些要点:
        有索引时,插入9999条记录需要大约两倍多的时间,多少影响性能,但是任何索引都会影响性能,由于大多数应用都只是插入和更新单个条目,而且插入每一行只会花不到1/10000秒的时间,所以这种影响并不明显,但查询运行的速度却快了几倍,所以有索引和无索引时查询的性能的差异相当显著。另外,表越大,全面扫描查询执行的时间就会越来越长,基于索引的查询则不同,随着表的增大,基于索引的查询总是有几乎相同的执行性能。
        这里书中举了个例子:有一个大表,其中一个not Null列,名为state,有两个值可取:Y或N,N代表未处理,处理完后把N改成Y,按一般来,会对state建位图索引,因为差异基数很低,但这个表会不断有新增的行,每行处理完后又会做修改,显然是高并发,所以位图索引并不合适,那就可以只对感兴趣的行建立索引,可以编写一个函数,如果不想对某个行加索引,则函数返回null,而对想加索引的行则返回一个非null值。
        建索引:create index state_idx on big_table(case temporary when 'N' then 'N' end);
        又一个例子:有一个项目表,项目有两种状态,active或inactive,要求是活动的项目名字必须唯一,不活动的项目无此要求。 常用的方法就是先查询表中有没有活动项目X,若没有则添加。这种方法在高并发的情况下很可能出问题(前面的多版本有说得)  通过一个唯一索引可以解决这个问题,create unique index active_unique on table(case when status = 'ACTIVE' then name end);   当status为ACTIVE时,name列将建立唯一索引。
 关于case关键字,在10.1.0.3中存在一个bug,即Oracle会重写case语句为得到一个更高效的语句,如前面的case when temporary = 'N' then 'N' end;    会被重写为case "temporary" when 'N' then 'N' end; 这样索引不仅不会工作,而且不可用。 这个可以通过视图user_idx_expressions来查看是如何重写的。 在10.1.0.4中,Oracle也会重写case语句,但同时也重写了查询本身使用的函数,因此二者是匹配的。 或可使用decode来代替case,因为decode不会被重写。

        应用域索引:
        这个索引即是自定义索引。可以根据自己的应用来构建索引,Oracle的文本索引就是如此。

        关于索引的常见问题:
       这里提到了一点,视图加索引,使用视图查询,优化器还是会对基表使用查询。使用视图时,完全可以考虑使用为基表编写的查询中所能用到的索引。
 
        外键加索引:
        外键未加索引是导致死锁的主要原因,这是因为,无论是更新父表住键,或者删除一个父记录,都会在子表上加一个表锁,这就会不必要的锁定更多的行。同时对性能也很有影响,例如:emp是dept的子表,delete dept where deptno = 10 会联级至emp ,如果emp中的deptno没有加索引,就会导致对emp执行一个全表扫描,而且如果从父表中删除多行,没次删除都会把子表扫描一次。还有若从父表查询子表时,在deptno上下文查询emp表相当常见,如select * from dept, emp where emp.deptno = dept.deptno and dept.dname = :x;
没有索引会使查询减慢。
       以下情况都满足就可以不加索引:
       未删除父表中的行
       未更新父表的唯一/主键
       不会从父表联结到子表,或者更一般的说,外键列不支持子表的一个重要的访问路径,而且你在谓词中没有使用这些外键列从子表中选择数据。

       B*索引(聚簇B*除外)对于完全为null的行没有相应的对应条目。
create table t(x int, y int);
create unique index t_idx on t(x, y);
但执行insert into t values(null, null); 索引中不会存储这个条目。
提交后再执行insert into t values(null, null);依然会成功,说明新的(null, null)和刚才的(null, null)相同,这是SQL标准要求的。
查询select * from T x where x is null; 这个查询不会使用索引,因为(null, null)并不在索引中,使用索引会得到错误的答案。只有当索引列中至少有一列时查询才会使用索引。
 
        书中列出了在哪些情况下Oracle不使用索引:
        情况1.现有表T,在T(x, y)上建立索引,做如下查询select * from T where y = '1'; 此时优化器就不打算使用T(x, y)索引,而是倾向执行全表扫描,因为谓词中不涉及x列,如果查询是select x, y from T where y = '1'; 优化器就不会执行全表扫描,而是使用索引。
        情况2.执行select count(*) from T; 表T上有个B*数索引,优化器有可能并不统计索引条目,而是统计表,这可能是索引建立在一些允许有null值的列上,上面也说过索引键完全为null的行不会建立相应的索引条目,所以索引中的行数可能并不是表中的行数,这里优化器选择是对的。所以索引必须建立not null=true的列上。
        情况3.在数值列上建立索引,select * from T where column = 5; 这里也不会执行索引,因为这个语句等价于select * from T to_number(column) = 5;  Oracle对这个列隐式的应用了一个函数,索引只是建在column上而不是to_number(column),所以这里不会执行索引。
        情况4.当表很小的时候,优化器会选择不执行索引。

       14.并行
        Oracle通常可以将某个大"任务"划分为较小的部分,并且并发地执行各个部分。一个过程原本需要执行数个小时或者数天,通过使用并行可能几分钟就可以完成。但在许多情况下,应用并行处理后,可能只会带来资源占有的增加,因为并行执行试图使用所有可用的资源。
        在应用并行执行之前,需要保证一下两点:
        必须有一个非常大的任务,如对50G数据进行全面扫描。
        必须有足够的可用资源,在并行全面扫描50G数据之前,你要确保有足够的空闲CPU,还要有足够的IO通道。

        因为有些章节不太适合现在的我,其中我漏过了很多,确实有不少的收获,但花的时间也太长,到后期耐心都消磨得差不多了,不过总算看完了,舒坦!!!^_^