摘要:
第1章 开发成功的Oracle应用程序
位图索引只适用于低基数(low-cardinality)值,但对频繁更新的列不适用。
(所谓低基数列就是指这个列只有很少的可取值。)
原因:采用位图索引,一个键指向多行,可能数以百计甚至更多,如果更新一个位图索引键,那么这个键指向的数百条记录会与你实际更新的哪一样一同被有效地锁定。
如 PROCESSED_FLAG 列只有两个值:Y和N,对于插入到表中的记录,该列值为N(表示未处理)。其他进程读取和处理这个记录时,就会把该列值从N更新为Y。如果有人插入一条新纪录(PROCESSED_FLAG列值为N)。此时,想要读取这个表并处理记录的进程就无法将N记录修改为Y记录。原因是,要想把这个列从N更新为Y,需要锁定同一个位图索引键。实际上,想在这个表中插入新纪录的其他会话也会阻塞,因为他们同样想对这个位图索引键锁定。简单的讲,开发人员实现了这样一组结构,它最多只允许一个人插入或更新!
例:使用两个会话来展示阻塞的发生
SQL> create table t (processed_flag varchar2(2));
Tbale created.
SQL> create bitmap index t_idx on t(processed_flag) ;
Index created.
SQL> insert into t value('N');
1 row created.
在另一个sql*plus会话中执行以下命令:
SQL> insert into t values('N');
这条语句就会“挂起”,直到第一个阻塞会话发出commit位置。
=====================================
1.3 开发数据库应用的正确(和不正确)方法
1.3.1 了解Oracle体系结构
SQL Server中有一种很普通的做法,就是对想要执行的每条并发语句都打开一个数据库连接。如果想执行5个查询,可能就会在SQL Server中看到5个连接。就好像Windows是针对多线程而不是多进程设计的一样。 在Oracle中,不论你想执行5个查询还是500个查询,都希望最多打开一个连接。Oracle是本着这样的理念设计的。所以,SQL Server中常用的做法在Oracle中却不提倡:你可能不想维护多个数据库连接。
1.3.2 使用绑定变量
Oracle将已解析、已编译的SQL连通其他内容存储在共享池(shared pool)中,这是系统全局区(System Global Area,SGA)中一个非常重要的共享内存结构。这个结构能完成“平滑”操作,前提是开发人员大多数情况下会使用绑定。如果你确实想让Oracle缓慢地运行,甚至几近停顿,只要根本不使用绑定变量就可以办到。
绑定变量(bind variable)是查询中的一个占位符。例如:
select * from emp where empno=123;
或者,也可以将绑定变量 :empno设置为123,并执行以下查询:
select * from emp where empno = :empno;
如果查询中使用直接量(常量),那么每个查询都是一个区全年新的查询,在数据库看来从未见过,必须对查询进行解析、限定(2解析)、安全检查、优化等。简单的讲,就是你执行的每条不同语句在执行时进行编译。
第二个查询使用了一个绑定变量 :empno,变量在查询执行时提供。这个查询只编译一次,随后会吧查询计划存储在一个共享池(库缓存)中,以便以后获取和重用这个查询计划。
1.3.2 理解并发控制
1.实现锁定
Oracle只在修改时才对数据加行级锁。
2.防止丢失更新
select * from resources where resource_name = :room_name FOR UPDATE;
在调度资源之前先锁定资源(这里指房间),换句话说,就是在resources 表中查询该资源的预定情况之前先锁定资源。通过锁定所要调度的资源,开发人员可以保证别人不会同时修改对 资源的调度。其他人都必须等待,直到他提交了事物为止。如果别人已经锁定了这一行,我们就会阻塞并等待。
例:
(1)、进入sql*plus(会话1)
SQL> select * from TMP_DEST where DEST_SYS_ID='IC' FOR UPDATE;
DEST_SYS_ID SYS_NAME IP_ADDR PORT USR_NAME PASSWD SUBS_MODE TRAN_MODE PUBLISH_MODE UPD_FLAG
----------- ------------ ------------- ---- -------- ---------------- --------- ------------------------------------- ------------ --------
IC 报表平台3新线 21.123.22.146 23 dcdsftp 56864857DC705A33 0 com.bocsoft.dcds.dataproces.FTPUpload 1 A
(2)、新开窗口进入另一个sql*plus会话(会话2):
SQL> update TMP_DEST set port='24'where DEST_SYS_ID='IC';
执行后光标闪烁,处于等待(阻塞)状态。
(3)、在会话1的sql*plus进行提交:
SQL> commit;
Commit complete.
(4)、会话2的sql*plus屏幕:
1 row updated.
第2章 体系结构概述
2.1定义数据库和实例
数据库(database):物理操作系统文件或磁盘(disk)的集合。
实例(instance):一组Oracle后台进程/线程以及一个共享内存区,这些内存由同一个计算机上运行的线程/进程所共享。
实例就是一组操作系统进程(或者是一个多线程的进程)以及一些内存。这些进程可以操作数据库;而数据库只是一个文件集合(包括数据文件、临时文件、重做日志文件和控制文件)。在任何时刻,一个实例只能有一组相关的文件(与一个数据库关联)。大多数情况下,反过来也成立:一个数据库上只有一个实例对其进行操作。不过,Oracle的真正应用集群(Real application clusters,RAC)是一个例外,这是Oracle提供的一个选项,允许在集群环境中的多台计算机上操作,这样就可以有多台实例同时装载并打开一个数据库(位于一组共享物理磁盘上)。
第6章 锁
Oracle中对表的update不会阻塞对这个表的查询;行级锁没有相关的开销。
6.2.2 悲观锁定
悲观锁定(pessimistic locking)仅用于有状态(stateful)或有链接(connected)环境,也就是说,你的应用于数据库有一条连续的连接,而且至少在事物生存其中只有你一个人使用这条连接。
6.2.3 乐观锁定
乐观锁定(optimistic locking),即把所有锁定都延迟到即将执行更新之前才做。即认为数据不会被其他用户修改。这种方法,执行更新的用户“失败”的可能性会加大。这个用户要更新他的数据行时,发现数据已经修改过,所以必须从头再来。
可以在应用中同时保留旧值和新值,然后再更新数据时使用如下的更新语句,这是乐观锁定的一种流行实现:
Update table
set column1 = :new_column1, column2 = :new_column2, ......
where primary_key = :primary_key
and column1 =
ld_column1 and column2 =
ld_column2
如果更新了零行,表示更新失败。
1.使用版本列的乐观锁定
对每个要保护的表增加一列。这一列一般是 NUMBER或 DATA/TIMESTAMP列,通常通过表上的一个行触发器来维护。每次修改时,这个触发器要负责递增NUMBER列中的值,或者更新DATA/TIMESTAMP/列。
如果应用要实现乐观并发控制,只需要保存整个附加列的值,而需要保存其他列的所有“前”映象。应用只需验证请求更新那一刻,数据库中这一列的值与最初读出的值是否匹配。如果两个值相等,就说明这一行未被更新过。
create table dept
(deptno number(2),
dname varchar2(14),
loc varchar2(13),
last_mod timestamp with time zone
default systimestamp
not null,
constraint dept_pk primary key(deptno)
)
不能总是依赖各个应用来维护这个字段。建议放到存储过程中。存储过程可以取以上更新中使用的绑定变量作为输入,执行同样的更新。当检测更新了0行时,存储过程会向客户返回一个异常,让客户知道更新失败了。
不建议使用触发器,会引入大量的开销,而且这么简单的工作也没必要使用它们。
2.使用校验和的乐观锁定
3.使用 ORA_ROWSCN的乐观锁定
6.2.5 阻塞
如果一个会话持有某个资源的锁,而另一个会话在请求这个资源,就会出现阻塞(blocking)。这样,请求的会话会被阻塞,它会“挂起”,只是持有会话放弃锁定的资源。
数据库中有5条常见的DML语句可能会阻塞: INSERT、UPDATE、DELETE、MERGE 和 SELECT FOR UPDATE。对于一个阻塞的SELECT FOR UPDATE的,只需增加 NOWAIT子句,它就不会阻塞了。
6.2.6 死锁
如果有两个会话,每个会话都持有另一个会话想要的资源,此时就会出现死锁(deadlock)。
6.3 锁类型
Oracle中主要有3类锁:
DML锁:DML代表数据操纵语言(Data Manipulation Language)。一般来讲,表示SELECT,INSERT,UPDATE,MERGEE和DELETE语句。DML锁机制允许并发执行数据修改。例如,DML锁可能是特定数据行上的锁,或者锁定表中所有行的表级锁。
DDL锁:DDL代表数据定义语言(Data Definition Language),如CREATE 和 ALTER语句等。DDL锁可以保护对象结构定义。
内部锁和闩:...
第7章 并发与多版本
7.1什么是并发控制
并发控制(concurrency control)是数据库提供的函数集合,允许多个人同时访问和修改数据。
锁是Oracle管理共享数据库资源并发访问并防止并发数据库事务之间“相互干涉”的核心机制之一
7.2 事务隔离级别
ANSI(American National Standards Institute美国国家标准学会)/ISO(International Standardization Organization国际标准化组织) SQL标准定义了4种隔离级别,对于相同的事务,采用不同的隔离级别分别有不同的结果。也就是说,即使输入相同,而且采用同样的方式来完成同样的工作,也可能得到完全不同的答案,中取决于事务的隔离级别。这些隔离级别是根据3个“现象”来定义的,一下就是给定隔离级别可能允许或不允许的3中现象:
脏读(dirty read):读取未提交的数据就是脏读。只要打开别人正在读写的一个0S文件,就可以达到脏读效果。
不可重复读(nonrepeatable read):如果你在T1时间读取某一行,在T2重新读取这一行是,这一行可能已经有所修改。也许它已经消失,有可能被更新了,等等。
幻象读(phantom read):这说明,如果你在T1时间执行一个查询,而在T2时间再执行这个查询,此时可能已经向数据中增加了另外的行,这会影响你的结果。与不可重复读的区别在于:在幻象读中,已经读取的数据不会改变,只是与之前相比,会有更多的数据满足你的查询条件。
ANSI隔离级别
隔离级别 脏读 不可重复 幻象读
READ UNCOMMITTED(读未提交) 允许 允许 允许
READ COMMITTED(读已提交) 允许 允许
REPEATABLE READ(可重复读) 允许
SERIALIZABLE(可串行化)