author:skate
time:2010-12-08
前不久和同事分享了,针对oracle高效开发的交流,希望可以提高大家的编码质量。因为原文档是ppt,我是把ppt转换成word,看起来就不是舒服
oracle开发技术交流
一. 系统篇
高可用系统架构
数据库选型
如何估算系统设计规模
公司系统现状与扩展
二. 高性能数据库设计与pl/sql高级编程
表/索引的创建
B*树索引集群表与散列集群表
索引组织表
Sql部分
Pl/sql部分
索引表
嵌套表
变长数组
批绑定
集合的异常处理
pl/sql优化总结
管理事务和锁定
动态SQL与静态sql
具体内容:
系统篇
1. 高可用系统架构
2. 数据库选型
3. 如何估算系统设计规模
4. 公司系统现状简介
1.高可用系统架构概述
一个高可用系统是由多个子系统组合的,其中每个子系统的可用性一定会高于整个系统的可用性
A. 高可用的应用设计
分布式技术
cache技术
search技术
B. 高可用的数据库设计
好的数据库设计,可以保证数据库的可扩充性和良好的性能
1. oracle的高可用产品,如:RAC,data guard,stream,advance replication,
server HA,,还有MV/rman/oracle log miner/oracle flashback query等
2. 数据库的应用设计(范式设计,反范式设计,数据库分布式设计,索引表和簇表的
设计等)
C. 高可用的网络设计
可靠性,包括链路与硬件的冗余
高速性,包括响应速度和吞吐量
健壮性,比如在Dos攻击的表现
安全性,有很强的安全策略,防止入侵
D. 高可用的主机设计
主机的HA(双机互备/双机热备)
oracle的RAC
在因主机失败或主机网络失败,可以保证系统的可用性。HA和RAC最大的区别在于,HA是os提供的active/standby解决方案,RAC是 oracle提供的active/standby解决方案
E. 高可用存储设计
存储的选择(存储架构/存储的cache技术/存储主要性能指标)
选择合理的raid的级别
选择合理的存储容灾方案(oracle高可用产品stream,logical standby;非oracle技术,如复
制软件 share plex)
2.数据库选型
在数据库设计中,要确定数据库的类型(OLAP/OLTP),使用合适优化设计方法,要
熟悉所操作数据的特点(连续操作,随机操作),更重要的是要熟悉自己的业务类型
A.
OLTP系统是事务性非常多的系统,都是以小型查询为主,评估这样的系统的时候,一般看秒事
务数与执行的sql数量。典型的OLTP系统有电子商务,银行,证券等;OLTP系统最容易出现瓶颈
的地方是CPU和磁盘子系统
CPU出现瓶颈需要调整(调整应用一般可以解决80%以上的性能问题)
1. 调整应用(表现在高逻辑读总量与计算性函数或者过程)
2. 整理表的碎片,重建索引,添加适当索引,优化sql
3. 减少sql的分析(session_cached_cursors缓存游标,使用绑定变量)
4. 合理调整共享池
磁盘I/O出现瓶颈
1. 存储性能瓶颈:控制器不足,cache偏小,cache设置不合理,io通道容量不足等
2. 磁盘性能瓶颈:磁盘数量过少,使用了速度比较慢的磁盘等
3. 使用了不合理的raid模式
4. 在使用raid的情况下,存在io热点,多个热点文件使用同一个磁盘
5. 异步io配置不正确
6. 数据库各种缓冲区设置不合理,缓冲命中率很低
2.数据库选型
7. PGA的各种缓冲设置过小(自动PGA管理时,PGA设置过小)
8. 重做日志存在性能瓶颈
9. 重做缓冲设置不合理
10. 存在热点数据
11. 表空间碎片严重
12. 表和索引的存储参数不合理
13. 行迁移比较严重
14. 存在大量大表扫描的情况
15. sql选择了不好的执行计划
在当今的IT技术发展中,磁盘IO是相对发展最慢的,为了弥补磁盘io在整个系统的的不足,可以
使用大量cache(减少动态pv/增加cache命中率)和B-tree索引,在OLTP系统尽量参考如下规则
1. 语句越简单越好,这样执行计划比较稳定
2. 一定要使用绑定变量,减少语句解析
3. 减少表关联,减少分布式事务,不要使用mv技术,并行技术,位图索引
4. 使用批量操作快速提交,尽量把大事务转化小事务,避免阻塞的发生
B
OLAP系统,也称DSS系统,就是我们说的数据仓库,评估这样系统的时候,一般看磁盘系统的
吞吐量(带宽)
3.如何估算系统设计规模
如果要为新系统选择硬件,就要明确一些参数指标概念,下面是样例系统的相关指标参数:
pv/天, 动态pv率,逻辑读/DPV,高峰系数,cache命中率,读写的比例
1000000 1/10 2000 5 98% (15:1)
不同厂家评测,1GHZ的CPU,在满载的情况下对oracle数据库的平均处理能力
逻辑读/每秒 物理读/每秒
100000 25000
通过这些参数可以估算需要多少磁盘iops?或者说根据现有硬件可以预测系统所能承载的压力
高峰时期每秒物理读个数:
1000000/(24*3600)*1/10*2000*5*2/100*15/16
高峰时期每秒物理写个数
1000000/(24*3600)*1/10*2000*5*2/100*1/16
4. 公司系统现状简介
这部分信息隐去
二. 高性能数据库设计与pl/sql高级编程
表/索引的创建
B*树索引集群表与散列集群表
索引组织表
索引的创建与使用(*)
Sql部分(*)
Pl/sql部分
索引表
嵌套表
变长数组
批绑定
集合的异常处理
pl/sql优化总结
管理事务和锁定
动态SQL与静态sql
表/索引的创建
在我们的数据库结构中主要用了两种存储结构,普通堆表和B*索引;为了充分考虑数据存储优化
,我们还要引入IOT和聚簇表;针对表使用频繁度、并发度,数据增长量等因素为表和索引设计
pctfree、inittrans等存储参数;还有如何创建合适索引,这些都是我们在数据库设计阶段要考虑的。
表的类型:
堆组织表
B×树索引集群表
散列集群表
索引组织表
外部表
因为堆组织表我常用,外部表很少用到,所以我们这里主要极少集群表和IOT表
1. B*树索引集群表与散列集群表
创建B×树索引集群:
create cluster user_objects_cluster_btree(username varchar2(30)) size 1024;
create index user_object_idx on cluster user_objects_cluster_btree;
创建散列群:
create cluster user_objects_cluster_hash(username varchar2(30)) hashkeys 100 size 3168;
表/索引的创建
B*集群表使用样例:
create table user_info (
username,user_id,account_status,lock_date)
cluster user_objects_cluster_btree(username)
as
select username,user_id,account_status,lock_date from dba_users where 1=0;
散列集群表使用样例:
create table user_objects(
owner ,object_name,object_id,object_type)
cluster user_objects_cluster_btree(owner)
as
select owner,object_name,object_id,object_type from dba_objects where 1=0
集群表可以降低IO并提高缓冲区高速缓存的效率,B* 群表适合数据仓库,也适合OLTP系统的父
子表(要同时更新的)。
单表散列群表
顾名思义就是集群表里只有一个表
定义与使用样例:
create cluster object_id_lookup(object_id number)
single table
hashkeys 50000
size 100;
测试性能对比:
1.创建单表散列群表
create table single_table_hash_cluster
(
owner,object_name,object_id,object_type,created,last_ddl_time,timestamp,status
)
cluster object_id_lookup(object_id)
as
select owner,object_name,rownum,object_type,created,last_ddl_time,timestamp,status
from
(select * from dba_objects
union all
select * from dba_objects )
where rownum<=50000
2. 创建普通堆表
create table heap_table
(
owner,object_name,object_id,object_type,created,last_ddl_time,timestamp,status,
constraint heap_table_pk primary key(object_id)
)
as
select owner,object_name,rownum,object_type,created,last_ddl_time,timestamp,status
from
(select * from dba_objects
union all
select * from dba_objects )
where rownum<=50000
比较运行时间特性
alter session set events '10046 trace name context forever ,level 12';
declare
l_rec single_table_hash_cluster%rowtype;
begin
for x in 1..3
loop
for i in 1..5000
loop
select * into l_rec
from single_table_hash_cluster
where object_id=i;
select * into l_rec
from heap_table
where object_id=i;
end loop;
end loop;
end;
alter session set events '10046 trace name context off';
用tkprof或runstats查看运行结果对比。
单表散列群表主要用于只读表或者是按键值读取一个表的数量远大于修改的应用程序
集群表的优点是:物理集中放置数据,提高高速缓冲的效率,减少逻辑io,减少索引的需求;
缺点是:仔细考虑size的大小,控制插入增加集群效果,比堆表插入慢
索引组织表(IOT)
索引组织表基本是一个存储在索引中表,和集群表类似,但也有它的不同,如下:
他存在一个数据结构和一个索引结构中,而B树群有一个索引和数据段
IOT存储的数据按主键值排序,而B树群存储的键值本身是不排序的
IOT对于关联表和数据需要集中放置非常有用,不像集群表那样要考虑插入顺序
IOT表同样有集群表的优点,但也有传统表插入慢的缺点,比较适合瘦长表,即行短表
定义与使用样例
创建IOT测试表:
reate table iot_table (
username varchar2(80),
nickname varchar2(80),
memo char(100),
constraint iot_pk primary key(username,nickname)
)
organization index;
创建普通堆表:
create table heap_table (
username varchar2(80),
nickname varchar2(80),
memo char(100),
constraint heap_pk primary key(username,nickname)
)
装载测试数据:
declare
begin
for i in 1..100
loop
for x in (select username from all_users)
loop
insert into heap_table
values(x.username,x.username||'_'||i,'X');
insert into iot_table
values(x.username,x.username||'_'||i,'X');
end loop;
end loop;
commit;
end;
模拟查询并查看性能效果:
alter session set events ' 10046 trace name context forever ,level 12';
declare
type array_type is table of varchar2(100);
l_array1 array_type;
l_array2 array_type;
l_array3 array_type;
begin
for i in 1 .. 10 loop
for x in (select username from all_users) loop
for y in (select *
from heap_table single_row
where username = x.username) loop
null;
end loop;
for y in (select *
from iot_table single_row
where username = x.username) loop
null;
end loop;
select * bulk collect
into l_array1, l_array2, l_array3
from heap_table bulk_collect
where username = x.username;
select * bulk collect
into l_array1, l_array2, l_array3
from iot_table bulk_collect
where username = x.username;
end loop;
end loop;
end;
/
alter session set events ' 10046 trace name context off';
Sql部分
在上一次培训的内容介绍的很详细,可以参考
Pl/sql部分
1. 索引表
2. 嵌套表
3. 变长数组
4. 批绑定
5. 集合的异常处理
6 pl/sql优化总结
为什么使用pl/sql?
1.pl/sql是数据操作的最高效的语言(oracle自己的产品也在用pl/sql,如:高级复制,application suite,工作流引擎等)
2.pl/sql具有可移植性和可重复性
在实际代码中使用程序包,那程序包有哪些优点?
1. 程序包减少或消除了级联失效
2. 增加了名字空间
3. 支持重载和封装
4. 允许把相关功能组织在一起
当第一次调用打包子程序时,整个包会被加载到共享内存池。所以,以后调用包内相关子程序
时,就不再需要读取磁盘了,这样会加快我们的代码会执行速度,因此要把相关功能的代码放到
一个包里。
独立的过程和函数什么时候可以用?
在不被其他代码调用,有独立的功能时,可以使用它们
函数和过程的区别:
存储过程:
用于数据库中完成特定的操作或任务(如插入,删除等)
程序包头部声明为proceudre
程序包头部声明时不需要返回类型
可以使用in/out/in out 三种模式的参数
可以作为一个独立的pl/sql语句执行
可以通过out/in out返回零个或多个值
sql语句中不可调用存储过程
函数:
用户特定的数据(选择)
程序包头部声明为function
程序包头部声明时要有返回类型,而且pl/sql块中至少要包括一个有效的return语句
可以使用in/out/in out 三种模式的参数
不能独立的执行,必须作为表达式的一部分
通过return语句返回一个值,切该值要与声明部分一致,也可以通过out类型的参数带出变量
sql语句中可以调用函数
集合类型:
在处理单行单列数据时,可以使用标量变量(pl/sql变量)
在处理单行多列数据时,可以使用pl/sql记录
在处理单列多行数据时,可以使用pl/sql集合
在处理多行多列数据时,可以pl/sql记录表(集合pl/sql记录和pl/sql集合的优点)
pl/sql记录
定义:
declare
type a_login_record_type is record(
username a_login.user_name%type,
userid a_login.user_id%type
);
a_login_record a_login_record_type;
...
或
a_login_record a_login%rowtype;
简单实用(可以使用记录变量或记录元素)
set serverputput on;
declare
type a_login_record_type is record(
username a_login.user_name%type,
userid a_login.user_id%type
);
a_login_record a_login_record_type;
begin
select user_name,user_id into a_login_record
from a_login
where user_id=&v_userid;
dbms_output.put_line(a_login_record.username);
end;
/
pl/sql集合
pl/sql集合包括索引表(pl/sql表),嵌套表,变长数组
索引表
用于处理pl/sql数组的数据类型,他的元素个数没有限制,并且元素下标可以为负值(下标类型可以为binary_integer,pls_integer,varchar2)
定义与使用样例:
set serverputput on;
declare
type a_login_table_type is table of a_login.user_name%type index by binary_integer;
a_login_table a_login_table_type;
begin
select user_name into a_login_table(1)
from a_login
where user_id=&v_userid;
dbms_output.put_line('用户名:'|| a_login_table(1));
end;
/
嵌套表
用于处理pl/sql数组的数据类型,元素下标从1开始,并且元素的个数没有限制,元素值可以稀疏
的不需要排序;嵌套表类型可以作为表列的类型,但索引表类型不能作为表列的类型;在使用嵌
套表的时候,必须使用其构造方法初始化嵌套表
定义与使用样例:
set serverputput on;
decalre
type a_login_table_type is table of a_login.user_name%type;
a_login_table a_login_table_type;
begin
a_login_table:=a_login_table_type('username1','username2');
select user_name into a_login_table(1)
from a_login
where user_id=&v_userid;
dbms_output.put_line('用户名:'|| a_login_table(1));
end;
/
作为表列中类型的定义与使用样例
create type username_type is table of varchar2(50);
create table table_nest(
id number(4),
name varchar2(50),
username username_type
) nested table username store as username_table;
插入数据:
insert into table_nest values(1,'22',username_type('qwewq','qwewqe','12312') );
检索数据:
declare
username_tables username_type;
begin
select username into username_tables from table_nest where id=1;
for i in 1..username_tables.count loop
dbms_output.put_line('用户名:'|| username_tables(i));
end loop;
end;
/
更新数据:
update table_nest t set t.username= username_type('xxxx','aaaaaa','ccccc')
变长数组(varry)
用于处理pl/sql数组的数据类型,他也可以作为表列的类型,其元素下标从1开始,并且元素的最
大个数是有限制的,变长数组在使用时,也必须要用构造函数初始化的。
定义与使用样例
declare
type a_login_table_type is varry(20) of a_login.user_name%type;
a_login_table a_login_table_type:=a_login_table_type('初始化');
begin
select user_name into a_login_table(1)
from a_login
where user_id=&v_userid;
dbms_output.put_line('用户名:'|| a_login_table(1));
end;
/
作为表列中类型的定义与简单实用(和嵌套类似)
pl/sql记录表
pl/sql记录表集合pl/sql记录和pl/sql集合的优点,从而可以方便有效的处理多行多列数据
定义与使用样例
declare
type a_login_table_type is table of a_login%rowtype index by binary_integer;
a_login_table a_login_table_type
begin
select * into a_login_table(1) from a_login where user_id =&userid;
dbms_output.put_line('用户名:'|| a_login_table(1).user_name);
end;
/
多级集合
多级集合是指嵌套了集合类型的集合类型,通过多级集合,可以在pl/sql中实现类似多维数组的功
能。比如定义二维数组,如下:
Pl/sql部分
declare
type a1_varray_type is varray(10) of int;
type a2_varray_type is varray(10) of a1_varray_type;
na a2_varray_type:=a2_varray_type(
a1_varray_type(1,2,3,4),
a1_varray_type(2,3,4),
a1_varray_type(4,6)
);
begin
dbms_output.put_line('显示二维数组:');
for i in 1..na.count loop
for j in 1..na(i).count loop
dbms_output.put_line('na('||i||','||j||')='||na(i)(j));
end loop;
end loop;
end;
/
下表中列出oracle中集合的方法
COUNT 返回集合中元素的个数
DELETE 删除集合中所有元素
DELETE(x) 删除元素下标为x的元素,如果x为null,则集合保持不变 对VARRAY非法
DELETE(x,y) 删除元素下标从X到Y的元素,如果X>Y集合保持不变 对VARRAY非法
EXIST(x) 如果集合元素x已经初始化,则返回TRUE, 否则返回FALSE
EXTEND 在集合末尾添加一个元素 对Index_by非法
EXTEND(x) 在集合末尾添加x个元素 对Index_by非法
EXTEND(x,n) 在集合末尾添加元素n的x个副本 对Index_by非法
FIRST 返回集合中的第一个元素的下标号,对于VARRAY集合始终返回1。
LAST 返回集合中最后一个元素的下标号, 对于VARRAY返回值始终等于COUNT。
LIMIT 返回VARRY集合的最大的元素个数,对于嵌套表和Index_by集合无用。
NEXT(x) 返回在元素x之后及紧挨着它的元素的值,如果该元素是最后一个元素,则返回null。
PRIOR(x) 返回集合中在元素x之前紧挨着它的元素的值,如果该元素是第一个元素,则返回null。
TRIM 从集合末端开始删除一个元素 对index_by不合法
TRIM(x) 从集合末端开始删除x个元素 对index_by不合法
集合的赋值
当使用嵌套表和变长数组的时候,可以将一个集合赋值给另一个集合,从oracle10g开始,嵌套表
赋值,还可以使用set,multiset union,multiset intersect ,multiset except等集合操作符。
集合比较
从oracle10g开始
1. 判断集合是否为null:
“is null”,“is empty”
2. 判断两个嵌套表是否相等:
“=/!=”
3. 嵌套表还可以使用一些集合操作符:
cardinality(返回集合的元素个数),submultiset of(一个嵌套表是否为另一个嵌套表子集)
4.判断嵌套表是否包含重复的元素值:
is a set
批绑定
批量绑定是oracle9i提出的,是指执行单次sql操作能传递所有集合元素的数据,当在select,update,delete,insert
语句上处理批量数据时,通过批量绑定可以极大的加快数据处理速度,提高应用程序的性能。批量绑定使用bulk collect和forall
bulk collect:用于取得批量数据,该语句只能用于select,fetch和dml返回子句中
Forall:只适合执行批量DML语句
性能对比样例
创建测试表t1,t2 ,t
create table t1 tablespace tbs_arena
as select * from user_objects where 1=0;
create table t2 tablespace tbs_arena
as select * from user_objects where 1=0;
create table t2 tablespace tbs_arena
as select * from user_objects ;
装载数据:
insert into t select * from user_objects;
未批处理:
create or replace procedure row_at_a_time as
begin
for x in (select * from t) loop
insert into t1 values x;
end loop;
end;
/
批处理:
create or replace procedure nrow_at_a_time(limit_n in number) as
type array_table_type is table of t%rowtype;
data_array array_table_type;
cursor c is select * from t;
begin
open c;
loop
fetch c bulk collect
into data_array limit limit_n;
forall i in 1 .. data_array.count
insert into t2 values data_array (i);
exit when c%notfound;
end loop;
end;
测试:
exec runstats.rs_start;
exec row_at_a_time;
exec runstats.rs_middle;
exec nrow_at_a_time(100);
exec runstats.rs_stop(5000);
集合的异常处理
COLLECTION_IS_NULL: 在构造函数初始化集合之前试图使用它
NO_DATA_FOUND : 试图访问集合中不存在的条目
SUBCRIPT_BEYOND_COUNT: 使用的下标超过了集合当前的元素数目
SUBCRIPT_OUTSIDE_LIMIT: 变长数组中使用的下标大于该变长数组声明中规定的最大值
VALUE_ERROR: 使用一个不能转换为整形的下标
pl/sql优化总结
当基于PL/SQL的应用程序执行效率低下时,通常是由于糟糕的SQL语句、编程方法,对PL/SQL
基础掌握不好或是滥用共享内存造成的。
PL/SQL性能低效原因:
PL/SQL中糟糕的SQL语句
不好的编程习惯
内置函数的重复
低效的流程控制语句
隐式的数据类型转换
不适当的数字类型声明
不必要的NOT NULL约束
PL/SQL性能优化:
使用本地动态SQL优化PL/SQL
使用批量绑定优化PL/SQL
使用NOCOPY编译器提示优化PL/SQL
使用RETURNING子句优化PL/SQL
使用外部程序优化PL/SQL(比如用C代码编写底层受计算量限制的程序)
使用对象类型和集合优化PL/SQL
PL/SQL中糟糕的SQL语句
PL/SQL 编写比较简单主要是因为SQL语句分担了大量工作,而SQL语句的低效直接影响PL/SQL程序的性能,所以我们要尽可能优化SQL
不好的编程习惯
编写质量较差的子程序(例如,一个很慢的分类或检索函数)可能毁掉整个性能。假如有一个需要
被应用程序频繁调用的查询函数,在数据量比较大时,如果这个函数不是使用哈希或二分法,而
是直接使用遍历查找,就会大大影响效率。不好的程序指的是那些含有从未使用过的变量,传递
没有必要的参数的,把初始化或计算放到不必要的循环中执行的程序等等 。
内置函数的重复
PL/SQL提供了许多高度优化过的函数,如REPLACE、TRANSLATE、SUBSTR、INSTR、RPAD和LTRIM等,还有很强的分析函数,所以尽量不要自己编写相似功能的函数 。
低效的流程控制语句
在计算逻辑表达式值的时候,PL/SQL使用短路的计算方式。也就是说,一旦结果可以被确定下
来,PL/SQL就会停止剩余的表达式计算 。看如下的例子的不同
1. if fun(parameter) and (money < 5000) then
2. if fun(parameter) and (money < 5000) then
隐式的数据类型转换
运行时,PL/SQL能把结构化不同的数据类型进行隐式的转换 ,例如
DECLARE
n NUMBER;
c CHAR(5);
BEGIN
n := n + 15; -- 需要转换
n := n + 15.0; -- 不需要转换
c:=25; -- 需要转换
c:=‘25’ -- 不需要转换
...
END;
不必要的NOT NULL约束
PL/SQL中,使用NOT NULL约束也能导致性能损耗
PROCEDURE calc_m IS
m NUMBER NOT NULL := 0;
a NUMBER;
b NUMBER;
BEGIN
m := a + b;
...
END;
因为m是受NOT NULL约束的,表达式a + b的值就会赋给临时变量,然后PL/SQL会对这个临时变量作判空测
试。如果变量不是空,它的值就能赋给m,否则就会出现异常。但是,如果m不是有约束限制的话,结果值就会
直接赋给m 。
使用本地动态SQL优化PL/SQL
有些程序必须在运行时才能确定下来的SQL语句,这些语句被称为动态SQL语句。以前,要执行
动态SQL语句就必须使用包DBMS_SQL。现在,可以在PL/SQL中直接使用被称为本地动态SQL
的接口来执行各种动态SQL语句。 (后面会详细讲到)
本地动态SQL更容易使用,并且执行速度也要比DBMS_SQL包快。在下面的例子中,我们声明一
个游标变量,然后把它与一个能返回数据表emp记录的动态的SELECT语句关联起来:
DECLARE
TYPE logincurtyp IS REF CURSOR;
login_cv logincurtyp;
v_username VARCHAR2(50);
v_userid NUMBER := 1000;
BEGIN
OPEN login_cv FOR 'SELECT user_name, user_id FROM a_login
WHERE user_id > :s' USING v_userid;
...
END;
使用批量绑定优化PL/SQL
这个前面已经讲的很详细了!
使用NOCOPY编译器提示优化PL/SQL
默认情况,OUT和IN OUT模式的参数都是按值传递的。也就是说,一个IN OUT实参会把它的副
本拷贝到对应的形参中。然后,如果程序执行正确的话,这个值又会重新赋给OUT和IN OUT的实
参,但实参是集合、记录和对象实例这样的大的数据结构时,生成一个副本会极大地降低执行效
率并消耗大量内存的。为了解决这个问题,我们可以使用编译器提示NOCOPY,它能让编译器把
OUT和IN OUT参数按引用传递。下例中,我们就能让编译器按引用传递IN OUT参数my_unit
DECLARE
TYPE varry_type IS VARRAY(200) OF varchar2(50);
PROCEDURE test(a IN OUT NOCOPY varry_type)
IS
…
BEGIN
...
END;
END;
使用RETURNING子句优化PL/SQL
在实际业务中,我们往往需要知道DML语句所影响的行信息,我们可以通过returning子句来完成,避免在dml语句之后select操作,节省了CPU,内存,游标的资源,提高了性能。
使用外部程序优化PL/SQL(比如用C代码编写底层受计算量限制的程序)
PL/SQL是专门用来进行SQL事务处理的。有些任务在像C这样的低阶语言中处理起来会更加有
效 ,oracle在为了增强高级复制的性能,用C改写了部分底层代码。
使用对象类型和集合优化PL/SQL
对象和集合在存储和检索方面更加高效,因为它们是作为一个整体进行操作的,在前面已经介绍
了,这里就不说了
在我们遇到PL/SQL性能的问题时,我们可以用如下两个跟踪功能帮助我们优化
Profiler API:DBMS_PROFILER包
Trace API: DBMS_TRACE 包
这个以前也发邮件和大家详细演示过!!!
管理事务和锁定
事务是一个工作逻辑单元,由一条或多条数据操纵语句(DML)或数据定义语句(DDL)组成。Oracle
提供了两种通用的事务:只读事务和读写事务。只读事务规定,查询到的数据以及该事务中的查
询将不受发生在数据库中的任何其他事务的影响。而读写事务保证查询返回的数据与查询开始的
数据一致。
只读事务实现事务级读取的一致性,这种事务只能包含查询语句,而不能包含任何DML 语句。在
这种情况下,只能查询到事务开始之前提交的数据。因此,查询可以执行多次,并且每次返回的
结果都相同。
读写事务提供语句级读取的一致性,这种事务将看不到这查询执行期间提交的事务所做的修改。
事务处理步骤
A.开始事务
B.结束事务
C.撤销事务
D.两步提交
E.创建保存点
F.release
oracle锁
1.应用级锁:应用中对表等资源进行锁定,保证业务逻辑的正确性
2.数据字典锁:oracle RDBMS内核程序员使用的用来保证数据字典访问逻辑正确性的锁
3.内存控制锁:用户保护oracle内部数据结构的锁(latch,mutex)
开发中主要考虑应用级的锁
表锁(tm)/记录锁(tx)
表锁定可以5种不同的模式执行:
ROW SHARE:是限制性最小的表锁定,它允许其他并发事务查询、插入、更新、删除或锁定同一个表中的记
录。但不允许对同一个表进行独占式写访问
ROW EXCLUSIVE:当一个表中的多条记录被更新时发生这种表锁定。它允许其他并发事务查询、插入、更
新、删除或锁定同一个表中的记录。不能防止任何对同一个表的手工锁定或独占式读写访
问
SHARE LOCK: 只允许其他用户查询或锁定特定的记录。这种锁定防止对同一个表的任何更新、插入和删除
操作
SHARE ROW EXCLUSIVE: 这种锁定只允许用于UPDATE 语句的查询和选择
EXCLUSIVE :这种锁定允许事务写一个表,其他事务只能查询
动态SQL与静态sql
执行动态SQL有两种方式:
1. 是DBMS_SQL 程序包
2. 是本机动态 SQL
DBMS_SQL 程序包:
功能强大,函数众多,很少使用。
本机动态SQL:
本地动态SQL更容易使用,并且执行速度也要比DBMS_SQL包快
使用样例:
执行 DDL 语句
BEGIN
EXECUTE IMMEDIATE 'CREATE TABLE skate_table (id VARCHAR2(30) PRIMARY KEY)';
EXECUTE IMMEDIATE 'ALTER TABLE skate_table ADD (Photo CLOB)';
EXECUTE IMMEDIATE 'ALTER TABLE skate_table MODIFY (id NUMBER)';
--EXECUTE IMMEDIATE 'DROP TABLE skate_table';
END;
/
使用绑定变量
DECLARE
l_sql VARCHAR2(200);
l_photo skate_table.Photo%TYPE;
l_id skate_table.id%TYPE;
BEGIN
l_sql := 'INSERT INTO skate_table VALUES(:1, :2)';
EXECUTE IMMEDIATE l_sql USING 12, 'welcome to test';
COMMIT;
l_sql := 'SELECT Photo, id FROM skate_table WHERE id = :1';
EXECUTE IMMEDIATE l_sql INTO l_photo, l_id USING 12;
DBMS_OUTPUT.PUT_LINE(l_id);
DBMS_OUTPUT.PUT_LINE(l_photo);
END;
/
可以执行 PL/SQL 块:
DECLARE
l_id NUMBER;
l_sql VARCHAR2(200) := 'BEGIN SELECT id INTO :1 FROM skate_table; END;';
BEGIN
EXECUTE IMMEDIATE l_sql INTO l_id;
DBMS_OUTPUT.PUT_LINE(TO_CHAR(l_id));
END;
/
--------end-------------