实例分析ORACLE数据库性能优化
oracle数据库的优化方式和mysql等很大的区别,今天通过一个oracle数据库实例从表格、数据等各个方便分析了如何进行oracle数据库的优化。
tsfree.sql视图
这个sql语句迅速的对每一个表空间中的空间总量与每一个表空间中可用的空间的总量进行比较
表空间是数据库的逻辑划分,一个表空间只能属于一个数据库。所有的数据库对象都存放在指定的表空间中。但主要存放的是表, 所以称作表空间。
select fs.tablespace_name "talbspace", (df.totalspace - fs.freespace) "userd mb", fs.freespace "free mb", df.totalspace "total mb", round(100 * (fs.freespace / df.totalspace)) "pct free" from (select tablespace_name, round(sum(bytes) / 1048576) totalspace from dba_data_files group by tablespace_name) df, (select tablespace_name, round(sum(bytes) / 1048576) freespace from dba_free_space group by tablespace_name) fs where df.tablespace_name = fs.tablespace_name;
varray 表的使用
create or replace type employer_name as object (e_name varchar(40)); create or replace type prior_employer_name_arr as varray(10) of employer_name; create or replace type full_mailling_adress_type as object(street varchar2(80), city varchar2(80), state char(2), zip varchar2(10)); create or replace type employee as object(last_name varchar(40), full_address full_mailling_adress_type, prior_employers prior_employer_name_arr); create table emp of employee; insert into emp values('jim', full_mailling_adress_type('airplan ave', 'rocky', 'nc', '2343'), prior_employer_name_arr(employer_name('ibm'), employer_name('apple'), employer_name('cnn')));
-- 回滚
drop type prior_employer_name_arr force;
drop type full_mailling_adress_type force;
drop type employee force;
drop type employer_name force;
drop table emp;
commit;
select p.last_name, prior_employers.*
from emp p, table(p.prior_employers) prior_employers
where p.last_name = 'jim';
sql 执行过程
1,检查安全性,确保sql数据执行者有权限执行
2,检查sql语法
3,可能发生的查询重新书写
4,执行
创建执行计划 生产器接受经过解析的sql 捆绑执行计划 执行执行计划 读取结果记录 排序结果集
数据访问方式:
1,全表扫描 db_file_multiblock_read_count = 128 一次性最大读取block的数量 oracle开启并行: alter table employee parallel degree 35; 顺序读取,直到结尾 1,当表中不存在索引 2,查询中不包含where字句 3,内置函数中的索引无效 4,like操作 %开头 5,使用基于成本优化器 数据量少时 6,当初始化文件中存在optimizer_mode = all_rows 7,负向条件查询不能使用索引 例如 status != 0, not in, not exists 可以优化为 in (2,3);
下列情况的sql语句会导致全表扫:
1,使用null条件查询导致全表扫,因为索引不能为空 为了绕过全表扫这个问题,可以采取这样的方法 update emp set name = 'n/a' where name is null; select name from emp where name = 'n/a'; 2,对没有索引的字段查询,找到where条件后面的查询不带索引的字段,加索引可以 大大提高查询性能。 3,带有like条件的查询 like '%x%' 全表扫描,like 'x%' 不会全表扫,因为like 以字符开始。 4,内置的函数使索引无效,对于date类型的数据来说非常的严重 内置函数 (to_date,to_char) 如果没有创建与内置函数匹配的基于函数的索引,那么这些函数通常会导致sql优化器全表扫描 select name from emp where date < sysdate -8; 检查where子句脚本是否含有 substr to_char decode select sql_text, disk_reads, executions, parse_calls from v$sqlarea where lower(sql_text) like '%substr%' or lower(sql_text) like '%to_char%' or lower(sql_text) like '%decode%' order by disk_reads desc; 使用函数索引解决这个问题 5,all_rows 优化器目标是提高吞吐量而且倾向于使用全表扫描,因此 对于任何一 个要求sql快速查询返回部分结果集而言,optimizer_mode 应该设置为first_rows 6,经验上,能过滤80%数据时就可以使用索引,对于订单状态,如果状态很少,不宜 使用索引,如果状态值很多可以使用索引。 7,如果查询字段大部分是单条数据查询,使用hash索引性能更好 原因:b-tree 索引的时间复杂度是o(log(n)) hash 索引的时间复杂度是o(1) 8,符合索引最左前缀,例如建立符合索引(password,username) select * from user u where u.pass_word = ? and u.user_name = ? 可以命中索引 select * from user u where u.user_name = ? and u.pass_word= ? 可以命中索引 select * from user u where u.pass_word = ? 可以命中索引 select * from user u where u.user_name = ? 不可以命中索引
如何找出影响力高的sql语句
视图 v$sqlarea ,下列参数按照重要性从高到低排序 executions :越经常执行的sql语句就应当越早的调整,因为它会对整体的性能产生巨大的影响。 disk_reads: 磁盘读取,高的磁盘读取量可能表明查询导致过多的输入输出量。 rows_processed:记录处理,处理大量的记录的查询会导致较大的输入输出量,同时在排序的时候对temp表空间产生影响。 buffer_gets:缓冲区读取,高的缓冲读取量可能表明了一个高资源使用量的查询。 sorts:排序会导致速度的明显减低,尤其是在temp表空间中进行的排序。
2.赛列获取
oracle对单表簇和多表簇进行散列存储,用来在连接操作中减低输入 输出
3,rowid 访问
通过rowid访问单条数据最快的方式,在实际的引用中,首先从索引中收集rowid,然后通过rowid进行数据读取
索引访问方式
索引都可以看做一组符合主键和rowid的组合,索引访问的目的是收集对目标快速读取时所需要的rowid
b树索引,位图索引 基于函数的索引.
索引范围扫描:读取一个或者多个rowid 索引数值升序排列
eg:select * from table where a = 'a';
快速全索引扫描
eg: select distinct color,count(*) from table group by color;
单个索引扫描:读取一个单独的rowid
降序索引范围扫描:读取一个或者多个rowid 索引数值降序排列
and - equals: select * from table where a = 'a' and b > 34; 从where字句中收集多个rowid
连接操作
嵌套循环连接
散列连接
散列连接通常快于嵌套循环连接,特别是在驱动表以及在查询的where子句中过滤,只剩下少量的记录的情况下
排序合并连接
连接提示:
表反向连接提示,例如,not in, not exists 尽量避免使用 not in 子句(它将调用子查询),而应该使用not exists 子句(它将调用相关联的子查询), 因为如果子查询返回的任何一条记录中包含空值,那么该查询将不会返回记录,如果允许not in 子句查询为空,那么 这种查询的性能非常的低,子查询会在外层查询块中对每一条记录重新执行一次。
排序大小 sort_area_size_init.ora 参数,在控制台查看 sort_area_size;
查询语句:show parameter sort_area_size;
磁盘排序的执行速度要比内存排序的的执行速度慢14000倍
磁盘排序之所以昂贵,有以下几个原因:
1,同在内存中进行排序比较,速度太慢
2,磁盘排序耗费临时表空间的资源
数据库分配2个临时表空间:
select default_tablespace, temporary_tablespace from dba_users where username='sys';
select * from dba_temp_free_space;
oracle临时表空间主要充当两个主要作用:临时表数据段分配和排序汇总溢出段。
排序汇总溢出的范围比较广泛。我们在sql语句中进行order by/group by等操作,
首先是选择pga的内存sort area、hash area和bitmap area。
如果sql使用排序空间很高,单个server process对应的pga不足以支撑排序要求的时候,临时表空间会充当排序段的数据写入。
而磁盘排序会降低单个任务的速度,同时还会影响oracle实例中正在执行的其他任务,而且过多的磁盘排序将导致过多的空闲缓冲等待
以及将其他任务的数据块从缓冲池中分页出去的昂贵代价。
oracle首先尝试在sort_area_size 分配的内存区中进行排序,oracle只有不能再内存中排序时,才会调用磁盘排序
并将内存框架迁移到temp表空间,继续进行排序。
使用索引范围扫描的总体原则
-- 对于原始排序的表, 仅读取少于40%的表记录查询就应该使用索引范围扫描,反之,多余40%,使用全表扫。 -- 对于未排序的表, 仅读取少于7%的表记录查询就应该使用索引范围扫描,反之,多余7%,使用全表扫。
表的访问方式
sql优化器
对于任何一个sql语句来说,存在唯一的优化表访问方式,而你的工作就是找到这种方式,并且长期使用它。
db_file_multiblock_read_count
目的是为sql语句生成最快 并且好资源最少的执行计划
1,基于规则的优化器
步骤 对于在where子句中的每一个表 -- 生成一个可行的执行计划列表,这个列表中列出所有可以用来访问表的路径 -- 为每一个执行计划指定级别数值 -- 选择级别数值最低的计划 -- 对结果集的选择级别最低 连接方法进行评估 基于规则优化器(pbo)特征 - 总是使用索引,使用索引永远比使用全表扫描或使用排序合并连接(排序合并连接不需要索引)更加可取 - 总是从驱动表开始 在from字句的最后一个表是驱动表,在这个表中选择的记录数应该是最少(查询返回值最少),rbo在执行嵌套循环连接 操作时,将这个驱动表作为第一个操作表。 - 只有在不可避免的情况下才使用全表扫描 -任何索引都可以 - 有时越简单越好
2,基于成本的优化器(cbo)
基于规则优化提供更加复杂的优化替代方案 analyze table tt_tcas_hk_qty compute statistics; analyze table tt_tcas_hk_qty estimate statistics sample 5000 rows; analyze table tt_tcas_hk_qty estimate statistics sample 5000 rows for all indexed columns; cbo在以下情况会选择错误的全表扫描 1,最高峰值过高 2,错误的optimizer_mode,如果optimizer_mode设置为all_rows,choose,那么sql优化器会倾向于使用全表扫描。 3,多表连接,存在多余3张表连接时,即使连接中存在索引,cbo仍然会对这些表进行全表扫描。 4,不平衡的索引分布,比如 color = 'blue' color字段上有索引,但是只有1%的记录属于blue,
sql 的sga统计资料
select name,value from v$sysstat where name like 'table%'
table scans(short table) -- 对小表全表扫描的次数
table scans(long table) -- 对大表全表扫描的次数,评估是否通过加索引减少大表的扫描次数 或者通过调用oracle并行(opq)来提高查询的执行速度。
table scans rows gotten -- 这个数目说明全表扫描扫描记录条数
table scans blocks gotten -- 扫描获取数据库的数目
table fetch by rowid -- 通过索引访问记录的数目,这里的索引通常是嵌套循环连接
table fetch by continued row -- 这个数目说明与其他数据块连接在一起的记录数目
程序库缓存中可以多次使用的sql
oracle在辨认"相同的"sql语句是存在问题
例如:select from customer; select from customer; 尽管区别字母的大小写,oracle会对第二个sql语句进行重新编译执行;