引起数据库性能问题的因素
Posted wx6323d657ef2b3
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了引起数据库性能问题的因素相关的知识,希望对你有一定的参考价值。
软件架构设计对数据库性能的影响
软件系统的架构对数据库的影响是非常直接的。例如一套系统,如果并发数非常大,比如是超过3000个并发,通常这种情况下,我们会考虑采用一套软件来搭建一个中间层,这就是通常讲到的3层或是多层结构。使用这一套软件的目的是用来构建一个缓冲池,在数据库之前对大量的并发进行处理,以便于每次只有少数的用户连接到数据库中,其他的用户在缓冲池的队列中等待。当然,这只是一个动态的过程,程序会尽可能快地去响应所有用户的请求,这种提前对大量并发用户进行处理的方式,会比让这3000个用户直接连接到数据库中效果要好得多,同时数据库也可以使用更多的资源来处理用户的操作请求而不是去开3000个进程来处理每个用户的请求,这个开销是非常大的 。所以对于大量并发的系统来讲,在数据库之前建一个缓冲用户请求的中间件服务,显得至关重要。 同时,很多这种中间件软件还提供了负载均衡的功能。
当然,Oracle数据库自身也提供了一种MTS的技术,作用和这种中间件服务是一样的,但目前看来,采用商业中间件软件或是开发商自己开发一套中间件服务的做法更多一些
软件代码的编写对数据库性能的影响
软件代码对数据库的影响,通常指的是应用程序代码中对数据库操作的代码部分对数据库产生的影响。具体来讲就是SQL语句或是PL/SQL包。SQL语句造成的影响,一种是SQL语句本身在逻辑上就是效率低下的,另一种就是SQL语句没有绑定变量(多出现在OLTP系统中)。
性能低下的SQL语句,比如使用Hint,不合适的外连接,谓词的隐含转换,优化器的选择等,会对SQL的执行产生非常大的影响,特别是多表关联的情况下,影响更是显著。 它主要体现为SQL语句的执行受到了人为的约束,比如数据的访问方式(索引还是全表扫描),以及表关联方式的选择上(Hah Join,Nested Loops)。
人为地在SQL代码中加入Hint来约束SQL的执行计划
曾有这样一个例子,开发人员为了要求每次对一个表做操作的时候都使用索引,于是在代码中强行加入了Hint约束SQL的执行计划,它的样子大概是这样:
Select /*+ index(t1 ind_t1) */ col1,col2 from t1 where col1>...... and col1<......
我猜测他们在系统上线之前测试的阶段,发现这条SQL选择索引比全表扫描效率高得多,为了保证以后执行计划能够始终选择索引,他们在代码中的SQL里加入了这个Hint。系统上线后,没有出现过问题,直到有一天用户抱怨查询非常慢,我从数据库里得到了用户端发出的SQL,才知道这个SQL在代码里加入了Hint。问题是,为什么之前操作都没有问题呢?登录这个数据库后,查看了一下这个表的信息,惊奇地发现,这个表每天仍然定时在做分析操作,这是一个奇怪的现象,人为地对表进行定时分析,却不允许数据库来选择执行计划,这显然是不合理的事情。但这种现象在开发人员当中又是比较普遍的,大家了解一些数据库的技术,却无法将这些知识整合起来运用,系统设计及开发阶段,没有DBA参与进来,直到系统进入运行维护阶段,才有DBA来充当救火队员的角色,这在当前中国软件开发中是一个很普遍的现象。
接着说这个案例。这是一个Oracle 10gr2的数据库,CBO(基于成本的优化器)的技术已经比较成熟,所以此时应该选择由Oracle数据库来决定SQL的执行计划。我分别执行了这条原始SQL和去掉了Hint的SQL,并获得了各自的执行计划,执行计划显示出,去掉Hint的SQL选择了全表扫描(Full Table Scan),执行中扫过的数据块远远小于通过索引访问数据的SQL,于是原因找到了。
可是为什么之前没有出现过这个问题?我对比了一下最近的数据和之前的数据,发现近期的数据在创建索引的列上的列值重复率要远远高于从前,因此Oracle在选择索引之后,比以前读取了更多的索引块和数据块,造成了大量的I/O操作。
因此,对于高版本的数据库(10g以上),我们还是应该让数据库自己根据表、索引的统计分析信息来决定SQL的执行计划,因为表中的数据是会变化的,这种人为的强行干预,必然会在某个时候出现问题。
不必要的外连接操作
外连接是一个代价非常昂贵的执行过程。如果业务需要,这种操作是必要的,但是有时候会出现人为地在SQL中使用不必要的外连接的情形,这实际上是因为有的开发人员担心遗漏一些数据而刻意使用它,这就非常有可能留下隐患,让数据库选择昂贵的执行计划而不是最优的那一个。
下面的这个例子说明了一个不必要的外连接使用。
我们创建两个简单的表,并插入一些数据,同时给其中的一个表T2的C列上插入一些空值:
SQL> create table t1 as select rownum a,rownum+100 b from dba_users where rownum<10;
表已创建。
SQL> create table t2 as select decode(mod(rownum,2),0,rownum) c,rownum+1000 d from dba_users where rownum<10;
表已创建。
SQL> select * from t1;
A B
---------- ----------
1 101
2 102
3 103
4 104
5 105
6 106
7 107
8 108
9 109
已选择9行。
SQL> select * from t2;
C D
---------- ----------
1001
2 1002
1003
4 1004
1005
6 1006
1007
8 1008
1009
已选择9行。
通过下面这条语句,通过使用A字段和T2表C字段关联,我们获取了T1表上所有的行,以及T2表上符合条件的行:
SQL> select a,b,c,d from t1,t2 where t1.a=t2.c(+) ;
A B C D
---------- ---------- ---------- ----------
2 102 2 1002
4 104 4 1004
6 106 6 1006
8 108 8 1008
1 101
3 103
5 105
7 107
9 109
请看下面这条SQL,它是什么意思呢?
SQL> select a,b,c,d from t1,t2 where t1.a=t2.c(+) and t2.d > 1000;
A B C D
---------- ---------- ---------- ----------
2 102 2 1002
4 104 4 1004
6 106 6 1006
8 108 8 1008
Elapsed: 00:00:00.03
这条SQL的意思是告诉数据库,我要得到T1表上所有的行,并且用A列和T2表C做关联,同时要求T2表C列的值大于1000。
让我们再看看另一条结果集完全一样的SQL:
SQL> select a,b,c,d from t1,t2 where t1.a=t2.c and t2.d > 1000;
A B C D
---------- ---------- ---------- ----------
2 102 2 1002
4 104 4 1004
6 106 6 1006
8 108 8 1008
Elapsed: 00:00:00.01
从结果集上来看,这是两条等价的SQL语句,就是说,在这种情况下,外连接其实是没有用的,是人为地在SQL里设定的限制!如果仔细看一下第一条语句,我们不难发现,条件中T2.C>1000已经明确地指出,在结果集中,T2表在任何一行,C列都应该有值的,也就是在这种情况下,根本就不需要使用外连接,业务逻辑上讲,外连接在这里是多余的。这种情况在开发人员的代码中有时候会遇到,对他们来讲,只要保证结果集正确就好,但对数据库来讲,在执行时可能会引起极大的性能差别。
CBO下优化器模式的选择
通常对于一种功能单一的数据库来讲,在实例级设置一个优化器模式就可以了,比如对于OLAP系统,绝大多数时候数据库上运行着的是报表作业,执行基本上是聚合类的SQL操作,比如GROUP BY,这时候,把优化器模式设置成all_rows是恰当的。
而对于一些分页操作比较多的网站类数据库,设置成first_rows会更好一些。
我却遇到了另外的一件事情。我们的数据库上运行着的基本上是一个OLAP系统,所以优化器模式设置为ALL_ROWS,这有利于报表SQL的快速完成。但数据库上还运行着一些用户查询的业务,查询的方式可以说成是分页的。有时候就会出现用户抱怨查询慢的问题,尽管我知道问题所在,却比较难解决,因为这些SQL已经被开发人员写到代码里面了。
针对这种情况,如果能在开发阶段就考虑到这个问题,针对需要分页操作的SQL,开发人员在SQL里通过Hint的方式来将优化模式转换成FIRST_ROWS,这样就可以大大地提高数据的处理速度。
比如这样一个每次取出10条记录的分页查询:
Select * from
(SELECT /*+ first_rows(10) */ a.*,rownum rnum from
(SELECT /*+ first_rows(10) */ id,name from t1 order by id) a
Where rownum<=10)
Where rnum>=1;
可以在每个子查询中重复使用FIRST_ROWS(n)来提高查询效率。
尽管说在SQL中人为地加入Hint操作不是一个好主意,但是有些时候,比如需要兼顾其他的用户操作时,可以考虑做这样的设定。但前期需要做一些测试工作,以确保这样的设定能够带来性能上的提高,同时不会对数据库造成其他方面的影响。这是系统设计阶段应该仔细考虑好的一个问题。
没有绑定变量的SQL
有时候没有使用绑定变量对性能的影响被夸大化了。实际上,至少对于OLAP系统(在线分析系统,通常指的是这样的一个系统,数据库存放着海量的数据,连接的用户少,SQL语句基本上都是用户产生报表的大查询)来说,未绑定变量对数据库的影响是很有限的,甚至是完全没有必要的,因为只有少量的用户和少量的SQL操作,数据库不需要花多少资源在SQL分析上面。
绑定变量的真正用途是在一个OLTP系统中,这个系统通常有这样的特点,用户并发数很大,用户的请求十分密集,并且这些请求的SQL大多数是可以重复使用的,我们试想,当这些成千上万的SQL被数据库一遍又一遍地进行语法分析、语义分析,生成执行计划时,这对数据库的压力该有多大?如果一条SQL执行一遍之后就被缓存到数据库的内存当中(实际上是在共享池里),以后的成百上千的用户请求都使用这个SQL解析后的结果,那效率将有多么大的提高!
所以,我的观点是,当你要考察绑定变量对你的数据库的影响有多大时,先确定你的系统是OLTP系统或是OLAP系统;当然,现在很多数据库同时担负这两种角色,那么你需要分析数据库的性能情况,比如,做一个Statspack Report来帮助你确定变量是否绑定,以及是否已经对系统的性能构成严重的影响。
PL/SQL包
如果你的程序里面有PL/SQL包,请考虑使用存储过程来代替它,存储过程是经过成功编译后存放在数据库中的代码,执行起来的效率要比程序代码中PL/SQL包的效率高很多,因为它不再需要做语法和语义的分析(语法的分析指的是数据库对代码进行检查,看它是否存在语法上的错误;而语义分析是查看语句执行的对象是否存在,比如需要操作的表、列等,以及是否有执行这些操作的权限)。
数据库设计
基本上看来,前期数据库设计的一个根基就是要弄清楚数据库的类型。通常来说,我们把业务分为两类,在线事务处理系统(OLTP)和在线分析系统(OLAP)或者DSS(决策支持系统),这两类系统在数据库的设计上是如此不同,甚至有些地方的设计是貌似相悖的。比如OLTP系统强调数据库的内存效率,强调内存各种指标的命中率,强调绑定变量,强调并发操作;而OLAP系统则强调数据分析,强调SQL执行时长,强调磁盘I/O,强调分区等。因为这些区别,在数据库设计的阶段,弄清楚数据库类型是至关重要的,只有在这个前提之下,才能够讨论数据库的具体设计,否则设计必然是盲目的,“皮之不存毛将焉附”。
OLTP数据库
OLAP和OLTP是两类完全不同的系统,对数据库的要求也截然不同。通常来讲,OLTP(在线事务处理系统)的用户并发数都很多,但他们只对数据库做很小的操作,数据库侧重于对用户操作的快速响应,这是对数据库最重要的性能要求。我清楚地记得在2008年的时候,某个门票在线销售系统允许人们通过网络购买门票,这是一个典型的OLTP系统。我当时还想尝试去买一张,结果是还没等到我去买,就听说系统瘫痪了。我想,应该是在线购票的用户数太多吧,导致数据库(我不太确定,也可能是中间件系统)没有办法处理大量的连接,从而导致了系统崩溃。这真是一个惨痛的教训,它用事实告诉我们,对于一个系统,特别是非常重要的系统,一些前瞻性的预测和系统的压力测试有多么的重要。
对于一个OLTP系统来说,数据库内存设计显得很重要,如果数据都可以在内存中处理,那么数据库的性能无疑会提高很多。我知道有些对处理速度要求很高的系统,已经采用了一些内存数据库,比如Oracle的Times Ten。
内存的设计通常是通过调整Oracle和内存相关的初始化参数来实现的,比较重要的几个是内存相关的参数,包括SGA的大小(Data Buffer,Shared Pool)、PGA大小(排序区,Hash区等)等,这些参数在一个OLTP系统里显得至关重要,OLTP系统是一个数据块变化非常频繁、SQL语句提交非常频繁的系统。对于数据块来说,应尽可能让数据块保存在内存当中,对于SQL来说,尽可能使用变量绑定技术来达到SQL的重用,减少物理I/O和重复的SQL解析,能极大地改善数据库的性能。
关于一些初始化参数的设定的问题,我认为,这里绝没有一个确定的标准,这和每个数据库上运行的业务直接相关,能够确定这些参数值的唯一方法就是测试,先给这些参数设定一个经验值,然后通过搭建测试环境对数据库进行测试,通过一些性能报告(比如AWR或者Staspack 报告)作为依据,不断地调整这些参数值,以达到最佳的性能。
除了内存、没有绑定变量的SQL会对OLTP数据库造成极大的性能影响之外,还有一些因素也会导致数据库的性能下降,比如热块(hot block)的问题,当一个块被多个用户同时读取的时候,Oracle为了维护数据的一致性,需要使用一种称为Latch的东西来串行化用户的操作。当一个用户获得了这个Latch后,其他的用户就只能被迫等待。获取这个数据块的用户越多,等待就越明显,就造成了这种热块问题。这种热块可能是数据块,也可能是回滚段块。对于数据块来讲,通常是数据块上的数据分布不均匀导致,如果是索引的数据块,可以考虑创建反向索引来达到重新分布数据的目的,对于回滚段数据块,可以适当多增加几个回滚段来避免这种争用
OLAP数据库
我一直认为OLAP数据库在内存上可优化的余地很小,甚至觉得增加CPU处理速度和磁盘I/O速度是最直接的提高数据库性能的方式,但这将意味着系统成本的增加。实际上,用户对OLAP系统性能的期望远远没有对OLTP性能的期望那么高。
内存的优化,对OLAP来讲影响很小,比如我曾经遇到的一个数据库,每天晚上运行的报表程序,基本上都是对几亿条或者几十亿条数据进行聚合处理,这种海量的数据,全部在内存中操作是很难的,同时也完全没有必要,因为这些数据块很少重用,缓存起来没有实际意义,倒是物理I/O相当大,这种系统的瓶颈往往是在磁盘I/O上面。
对于OLAP系统,SQL的优化显得非常重要,试想,如果一张表中只有几千条数据,无论执行全表扫描或是使用索引,对我们来说差异都很小,几乎感觉不出来,但是当数据量提升到几亿或者几十亿甚至更多的时候,全表扫描、索引可能导致极大的性能差异,因此SQL的优化显得重要起来。
看下面的一个例子,它对比了索引和全表扫描的效率:
**********************************************************************
select *
from
t where object_id<100
call count cpu elapsed disk query current rows
---- ------ ---- -------- ----- ----- ------- -----
Parse 1 0.01 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 8 0.00 0.00 0 17 0 98
---- ------ ---- -------- ----- ----- ------- -----
total 10 0.01 0.00 0 17 0 98
Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 55
Rows Row Source Operation
------- ---------------------------------------------------
98 TABLE ACCESS BY INDEX ROWID T (cr=17 pr=0 pw=0 time=95 us)
98 INDEX RANGE SCAN T_INX (cr=9 pr=0 pw=0 time=2383 us)(object id 51627)
**********************************************************************
select /*+ full(t) */ *
from
t where object_id<100
call count cpu elapsed disk query current rows
---- ------ ------ -------- ------ ----- ------- -----
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 8 0.01 0.00 0 695 0 98
---- ------ ------ -------- ------ ----- ------- -----
total 10 0.01 0.01 0 695 0 98
Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 55
Rows Row Source Operation
------- ---------------------------------------------------
98 TABLE ACCESS FULL T (cr=695 pr=0 pw=0 time=116 us)
**********************************************************************
OLAP数据库中的分区
我们看到,在这个只有几万条记录的表中,相同的SQL语句,全表扫描扫过的数据块(一致性读)是695个,而索引只扫过了17个,差别还是非常大的。
分区技术在OLAP数据库中很重要,这种重要主要体现在数据管理上,比如数据加载,可以通过分区交换的方式实现,备份可以通过备份分区表空间实现,删除数据可以通过分区进行删除;至于分区在性能上的影响,不能一概而论,认为分区的性能将始终好于非分区,这个结论是不成立的,至少是片面的 ,我们通过以下几种情况来分析它。
当查询的范围正好落在某个分区的时候
这时候分区的效率自然是高于没有分区的,因为SQL在有分区的表上只扫过一个分区的数据,而对于没有分区,需要扫描整个表,这也是大多数人认为分区会提高性能的一个原因吧,比如下面的例子:
**********************************************************************
select count(*)
from
t where x<1000
call count cpu elapsed disk query current rows
---- ------ ------- -------- ---- ----- ------- -----
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 2 0.00 0.00 0 23 0 1
---- ------ ------- -------- ---- ----- ------- -----
total 4 0.00 0.00 0 23 0 1
Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 55
Rows Row Source Operation
------- ---------------------------------------------------
1 SORT AGGREGATE (cr=23 pr=0 pw=0 time=2495 us)
999 PARTITION RANGE SINGLE PARTITION: 1 1 (cr=23 pr=0 pw=0 time=9085 us)
999 TABLE ACCESS FULL T PARTITION: 1 1 (cr=23 pr=0 pw=0 time=4077 us)
**********************************************************************
select count(*)
from
t1 where x<1000
call count cpu elapsed disk query current rows
---- ------ ------- -------- ---- ----- ------- -----
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 2 0.01 0.00 0 84 0 1
---- ------ ------- -------- ---- ----- ------- -----
total 4 0.01 0.01 0 84 0 1
Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 55
Rows Row Source Operation
------- ---------------------------------------------------
1 SORT AGGREGATE (cr=84 pr=0 pw=0 time=9015 us)
999 TABLE ACCESS FULL T1 (cr=84 pr=0 pw=0 time=4077 us)
第一个SQL只扫过了一个分区的数据,扫过的数据块为23个;第二个SQL做了全表扫描,扫过的数据块为84个,这种情况下肯定是分区表的效率要高一些。
当查询的范围跨越几个分区时
这时候分区可能并不绝对是最优的,比如下面的例子,我们把查询的范围扩大到分区表的13个分区,让CBO使用FAST INDEX FULL SCAN的方式扫描索引,另外我们创建另一张非分区表,表结果和数据同分区表完全一样,我们使用同一条SQL,并且也让CBO强制使用FAST INDEX FULL SCAN的方式访问非分区表上的全局索引。我们要验证的一个观点是,分区索引并不一定比全局索引在任何时候都快,有时候它反而会慢。下面是输入的结果:
Select /*+ index_ffs(t t_ind) */ count(*)
from
t where x<13000
call count cpu elapsed disk query current rows
---- ------ ------- -------- ---- ----- ------- -----
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 2 0.03 0.02 0 164 0 1
---- ------ ------- -------- ---- ----- ------- -----
total 4 0.03 0.03 0 164 0 1
Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 55
Rows Row Source Operation
------- ---------------------------------------------------
1 SORT AGGREGATE (cr=164 pr=0 pw=0 time=29234 us)
12999 PARTITION RANGE ALL PARTITION: 1 13 (cr=164 pr=0 pw=0 time=117074 us)
12999 INDEX FAST FULL SCAN T_IND PARTITION: 1 13 (cr=164 pr=0 pw=0 time=52408 us)(object id 51774)
select /*+ index_ffs(t1 t1_ind) */ count(*)
from
t1 where x<13000
call count cpu elapsed disk query current rows
---- ------ ------- -------- ---- ----- ------- -----
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 2 0.03 0.02 0 117 0 1
---- ------ ------- -------- ---- ----- ------- -----
total 4 0.03 0.02 0 117 0 1
Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 55
Rows Row Source Operation
------- ---------------------------------------------------
1 SORT AGGREGATE (cr=117 pr=0 pw=0 time=24755 us)
12999 INDEX FAST FULL SCAN T1_IND (cr=117 pr=0 pw=0 time=52082 us)(object id 51788)
**********************************************************************
在这个例子里面,分区索引之所以扫过了更多的数据块,是因为分区索引在做FFS(INDEX FAST FULL SCAN)的时候只能够在本地索引上进行,如果涉及其他的分区,还需要按照访问索引的方式去访问其他索引(比如先找到其他分区索引的根数据块,再找到最左边的叶块,然后执行FFS操作),这样,查询跨过的分区越多,这种额外的代价就越大;而在这种情况下,全局索引只需要定位到一个叶块,然后执行一次FFS就能够扫过所有的索引叶块,这样性能就会好于分区索引。
上面的例子是想说明,OLAP环境中,分区主要的功能是管理上的方便性,它并不能绝对保证查询性能的提高,有时候分区会带来性能上的提高,有时候甚至会降低,就像我们在例子中看到的一样。
数据库的硬件设计
存储容量
如果一个系统的生命周期可以确定,或者说数据库中的数据保存时间可以确定,那么我们就可以通过一个简单的计算,大致估算出数据库所存放的数据量的大小,以作为存储设备采购的一个依据。
可以通过估算占有存储空间的所有数据库对象(其实主要是估算业务用户下的所有对象)的容量,来计算数据的容量。
占用空间的对象都可以在DBA_SEGMENTS视图里面找到,数据库的空间的分配是以段的形式分配的,凡是段对象,都是要占用空间的,它包括表、索引、物化视图、其他的一些大对象(比如全文索引对象)。
如果在开发阶段能够预测每个表的记录数,然后我们取得这个表的字段总长度,于是表的容量=记录数*字段长度。
一个表中索引的大小和索引的类型,以及索引键值的重复率有很大的关系,开发人员可以通过模拟一些实际数据来估算出索引和表数据的一个比例,然后做出索引所占空间的估算。
一个计算容量的例子如下。
我们创建一个表,然后在表上创建索引,之后对表和索引进行分析,然后查询视图user_tables就可以得到表的大致容量。
SQL> create table t as select * from dba_objects;
Table created.
SQL> create index t_ind on t(object_id);
Index created.
SQL> exec dbms_stats.gather_table_stats(user,t,cascade=>true);
PL/SQL procedure successfully completed.
SQL> select avg_row_len from user_tables where table_name=T;
AVG_ROW_LEN
-----------
93
这个数值就是表的平均行长,如果我们能够估算出预期表的记录数N,那么最终表占用的空间就是 93 bytes*N:
SQL> select segment_name,segment_type,bytes from user_segments where segment_name in (T,T_IND);
SEGMENT_NAME SEGMENT_TYPE BYTES
------------- ------------------ ----------
T TABLE 6291456
T_IND INDEX 1048576
SQL> select trunc((1048576/6291456)*100) ind_pct from dual;
IND_PCT
----------
16
我们取得了测试表中表和索引的大小,计算出索引和表大小的百分比,这样,我们就获得了最终这个表的空间使用量为:
表的总使用量 = 93 bytes*N(1+16)
这就是预期这个表的容量,如果能够预测出未来数据库的数据量,我们就可以比较客观地估算出数据库预期的容量大小。另外一个容易被开发商忽略的问题是对系统备份数据占用空间的考虑
存储的物理设计
现在越来越多的大数据量数据库选择了SAN存储结构(参见图1-2),这是一个扩展性非常好的存储设计,它可以非常方便地将存储设备增加到存储网络当中,但成本和故障点相应地就会变多,对维护人员的技术要求就很高,它不但要求维护人员懂得磁盘阵列的技术,还要掌握SAN交换机的相关技术。
参考至:《让Oracle跑得更快》
以上是关于引起数据库性能问题的因素的主要内容,如果未能解决你的问题,请参考以下文章