无名

长风破浪会有时,直挂云帆济沧海!

  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
  41 随笔 :: 0 文章 :: 9 评论 :: 0 Trackbacks

2008年7月14日 #

不论是 聚集索引,还是非聚集索引,都是用B+树来实现的。我们在了解这两种索引之前,需要先了解B+树。如果你对B树不了解的话,建议参看以下几篇文章:

BTree,B-Tree,B+Tree,B*Tree都是什么
http://blog.csdn.net/manesking/archive/2007/02/09/1505979.aspx

B+ 树的结构图:

B+ 树的特点:

  • 所有关键字都出现在叶子结点的链表中(稠密索引),且链表中的关键字恰好是有序的;
  • 不可能在非叶子结点命中;
  • 非叶子结点相当于是叶子结点的索引(稀疏索引),叶子结点相当于是存储(关键字)数据的数据层;

B+ 树中增加一个数据,或者删除一个数据,需要分多种情况处理,比较复杂,这里就不详述这个内容了。 

聚集索引(Clustered Index)

  • 聚集索引的叶节点就是实际的数据页
  • 在数据页中数据按照索引顺序存储
  • 行的物理位置和行在索引中的位置是相同的
  • 每个表只能有一个聚集索引
  • 聚集索引的平均大小大约为表大小的5%左右

下面是两副简单描述聚集索引的示意图: 

在聚集索引中执行下面语句的的过程:

select * from table where firstName = 'Ota'

 在聚集索引中搜索

一个比较抽象点的聚集索引图示:

聚集索引单个分区中的结构

 

非聚集索引 (Unclustered Index)  

  • 非聚集索引的页,不是数据,而是指向数据页的页。
  • 若未指定索引类型,则默认为非聚集索引
  • 叶节点页的次序和表的物理存储次序不同
  • 每个表最多可以有249个非聚集索引
  • 在非聚集索引创建之前创建聚集索引(否则会引发索引重建)

在非聚集索引中执行下面语句的的过程:

select * from employee where lname = 'Green'

Selecting rows using a nonclustered index

一个比较抽象点的非聚集索引图示:

非聚集索引的级别

 

什么是 Bookmark Lookup

虽然SQL 2005 中已经不在提  Bookmark Lookup 了(换汤不换药),但是我们的很多搜索都是用的这样的搜索过程,如下:
先在非聚集中找,然后再在聚集索引中找。

Bookmark Lookup 

http://www.sqlskills.com/ 提供的一个例子中,就给我们演示了 Bookmark Lookup  比 Table Scan 慢的情况,例子的脚本如下:

USE CREDIT
go
-- These samples use the Credit database. You can download and restore the
-- credit database from here:
-- http://www.sqlskills.com/resources/conferences/CreditBackup80.zip
-- NOTE: This is a SQL Server 2000 backup and MANY examples will work on 
-- SQL Server 2000 in addition to SQL Server 2005.
-------------------------------------------------------------------------------
-- (1) Create two tables which are copies of charge:
-------------------------------------------------------------------------------
-- Create the HEAP
SELECT * INTO ChargeHeap FROM Charge
go
-- Create the CL Table
SELECT * INTO ChargeCL FROM Charge
go
CREATE CLUSTERED INDEX ChargeCL_CLInd ON ChargeCL (member_no, charge_no)
go
-------------------------------------------------------------------------------
-- (2) Add the same non-clustered indexes to BOTH of these tables:
-------------------------------------------------------------------------------
-- Create the NC index on the HEAP
CREATE INDEX ChargeHeap_NCInd ON ChargeHeap (Charge_no)
go
-- Create the NC index on the CL Table
CREATE INDEX ChargeCL_NCInd ON ChargeCL (Charge_no)
go
-------------------------------------------------------------------------------
-- (3) Begin to query these tables and see what kind of access and I/O returns
-------------------------------------------------------------------------------
-- Get ready for a bit of analysis:
SET STATISTICS IO ON
-- Turn Graphical Showplan ON (Ctrl+K)
-- First, a point query (also, see how a bookmark lookup looks in 2005)
SELECT * FROM ChargeHeap WHERE Charge_no = 12345
go
SELECT * FROM ChargeCL WHERE Charge_no = 12345
go
-- What if our query is less selective?
-- 1000 is .0625% of our data... (1,600,000 million rows)
SELECT * FROM ChargeHeap WHERE Charge_no < 1000
go
SELECT * FROM ChargeCL WHERE Charge_no < 1000
go
-- What if our query is less selective?
-- 16000 is 1% of our data... (1,600,000 million rows)
SELECT * FROM ChargeHeap WHERE Charge_no < 16000
go
SELECT * FROM ChargeCL WHERE Charge_no < 16000
go
-------------------------------------------------------------------------------
-- (4) What's the EXACT percentage where the bookmark lookup isn't worth it?
-------------------------------------------------------------------------------
-- What happens here: Table Scan or Bookmark lookup?
SELECT * FROM ChargeHeap WHERE Charge_no < 4000
go
SELECT * FROM ChargeCL WHERE Charge_no < 4000
go
-- What happens here: Table Scan or Bookmark lookup?
SELECT * FROM ChargeHeap WHERE Charge_no < 3000
go
SELECT * FROM ChargeCL WHERE Charge_no < 3000
go
-- And - you can narrow it down by trying the middle ground:
-- What happens here: Table Scan or Bookmark lookup?
SELECT * FROM ChargeHeap WHERE Charge_no < 3500
go
SELECT * FROM ChargeCL WHERE Charge_no < 3500
go
-- And again:
SELECT * FROM ChargeHeap WHERE Charge_no < 3250
go
SELECT * FROM ChargeCL WHERE Charge_no < 3250
go
-- And again:
SELECT * FROM ChargeHeap WHERE Charge_no < 3375
go
SELECT * FROM ChargeCL WHERE Charge_no < 3375
go
-- Don't worry, I won't make you go through it all :)
-- For the Heap Table (in THIS case), the cutoff is: 0.21%
SELECT * FROM ChargeHeap  WHERE Charge_no < 3383
go
SELECT * FROM ChargeHeap WHERE Charge_no < 3384
go
-- For the Clustered Table (in THIS case), the cut-off is: 0.21%
SELECT * FROM ChargeCL WHERE Charge_no < 3438
SELECT * FROM ChargeCL WHERE Charge_no < 3439
go

这个例子也就是 吴家震 在Teched 2007 上的那个演示例子。

小结:

这篇博客只是简单的用几个图表来介绍索引的实现方法:B+数, 聚集索引,非聚集索引,Bookmark Lookup 的信息而已。

参考资料:

表组织和索引组织
http://technet.microsoft.com/zh-cn/library/ms189051.aspx
http://technet.microsoft.com/en-us/library/ms189051.aspx

How Indexes Work
http://manuals.sybase.com/onlinebooks/group-asarc/asg1200e/aseperf/@Generic__BookTextView/3358

Bookmark Lookup
http://blogs.msdn.com/craigfr/archive/2006/06/30/652639.aspx 

Logical and Physical Operators Reference
http://msdn2.microsoft.com/en-us/library/ms191158.aspx

转载:http://blog.joycode.com/ghj/archive/2008/01/02/113291.aspx

posted @ 2008-07-14 11:02 无名 阅读(28) | 评论 (0)编辑

B树、B-树、B+树、B*树都是什么

 

B

       即二叉搜索树:

       1.所有非叶子结点至多拥有两个儿子(LeftRight);

       2.所有结点存储一个关键字;

       3.非叶子结点的左指针指向小于其关键字的子树,右指针指向大于其关键字的子树;

       如:

       

       B树的搜索,从根结点开始,如果查询的关键字与结点的关键字相等,那么就命中;否则,如果查询关键字比结点关键字小,就进入左儿子;如果比结点关键字大,就进入右儿子;如果左儿子或右儿子的指针为空,则报告找不到相应的关键字;

       如果B树的所有非叶子结点的左右子树的结点数目均保持差不多(平衡),那么B树的搜索性能逼近二分查找;但它比连续内存空间的二分查找的优点是,改变B树结构(插入与删除结点)不需要移动大段的内存数据,甚至通常是常数开销;

       如:

      

   但B树在经过多次插入与删除后,有可能导致不同的结构:

   右边也是一个B树,但它的搜索性能已经是线性的了;同样的关键字集合有可能导致不同的树结构索引;所以,使用B树还要考虑尽可能让B树保持左图的结构,和避免右图的结构,也就是所谓的“平衡”问题;      

       实际使用的B树都是在原B树的基础上加上平衡算法,即“平衡二叉树”;如何保持B树结点分布均匀的平衡算法是平衡二叉树的关键;平衡算法是一种在B树中插入和删除结点的策略;

 

B-

       是一种多路搜索树(并不是二叉的):

       1.定义任意非叶子结点最多只有M个儿子;且M>2

       2.根结点的儿子数为[2, M]

       3.除根结点以外的非叶子结点的儿子数为[M/2, M]

       4.每个结点存放至少M/2-1(取上整)和至多M-1个关键字;(至少2个关键字)

       5.非叶子结点的关键字个数=指向儿子的指针个数-1

       6.非叶子结点的关键字:K[1], K[2], …, K[M-1];且K[i] < K[i+1]

       7.非叶子结点的指针:P[1], P[2], …, P[M];其中P[1]指向关键字小于K[1]的子树,P[M]指向关键字大于K[M-1]的子树,其它P[i]指向关键字属于(K[i-1], K[i])的子树;

       8.所有叶子结点位于同一层;

       如:(M=3

       B-树的搜索,从根结点开始,对结点内的关键字(有序)序列进行二分查找,如果命中则结束,否则进入查询关键字所属范围的儿子结点;重复,直到所对应的儿子指针为空,或已经是叶子结点;

B-树的特性:

       1.关键字集合分布在整颗树中;

       2.任何一个关键字出现且只出现在一个结点中;

       3.搜索有可能在非叶子结点结束;

       4.其搜索性能等价于在关键字全集内做一次二分查找;

       5.自动层次控制;

       由于限制了除根结点以外的非叶子结点,至少含有M/2个儿子,确保了结点的至少利用率,其最底搜索性能为:

       其中,M为设定的非叶子结点最多子树个数,N为关键字总数;

       所以B-树的性能总是等价于二分查找(与M值无关),也就没有B树平衡的问题;

       由于M/2的限制,在插入结点时,如果结点已满,需要将结点分裂为两个各占M/2的结点;删除结点时,需将两个不足M/2的兄弟结点合并;

 

B+

       B+树是B-树的变体,也是一种多路搜索树:

       1.其定义基本与B-树同,除了:

       2.非叶子结点的子树指针与关键字个数相同;

       3.非叶子结点的子树指针P[i],指向关键字值属于[K[i], K[i+1])的子树(B-树是开区间);

       5.为所有叶子结点增加一个链指针;

       6.所有关键字都在叶子结点出现;

       如:(M=3

   B+的搜索与B-树也基本相同,区别是B+树只有达到叶子结点才命中(B-树可以在非叶子结点命中),其性能也等价于在关键字全集做一次二分查找;

       B+的特性:

       1.所有关键字都出现在叶子结点的链表中(稠密索引),且链表中的关键字恰好是有序的;

       2.不可能在非叶子结点命中;

       3.非叶子结点相当于是叶子结点的索引(稀疏索引),叶子结点相当于是存储(关键字)数据的数据层;

       4.更适合文件索引系统;

  

B*

       B+树的变体,在B+树的非根和非叶子结点再增加指向兄弟的指针;

   B*树定义了非叶子结点关键字个数至少为(2/3)*M,即块的最低使用率为2/3(代替B+树的1/2);

       B+树的分裂:当一个结点满时,分配一个新的结点,并将原结点中1/2的数据复制到新结点,最后在父结点中增加新结点的指针;B+树的分裂只影响原结点和父结点,而不会影响兄弟结点,所以它不需要指向兄弟的指针;

       B*树的分裂:当一个结点满时,如果它的下一个兄弟结点未满,那么将一部分数据移到兄弟结点中,再在原结点插入关键字,最后修改父结点中兄弟结点的关键字(因为兄弟结点的关键字范围改变了);如果兄弟也满了,则在原结点与兄弟结点之间增加新结点,并各复制1/3的数据到新结点,最后在父结点增加新结点的指针;

       所以,B*树分配新结点的概率比B+树要低,空间使用率更高;

  

小结

       B树:二叉树,每个结点只存储一个关键字,等于则命中,小于走左结点,大于走右结点;

       B-树:多路搜索树,每个结点存储M/2M个关键字,非叶子结点存储指向关键字范围的子结点;

       所有关键字在整颗树中出现,且只出现一次,非叶子结点可以命中;

       B+树:在B-树基础上,为叶子结点增加链表指针,所有关键字都在叶子结点中出现,非叶子结点作为叶子结点的索引;B+树总是到叶子结点才命中;

       B*树:在B+树基础上,为非叶子结点也增加链表指针,将结点的最低利用率从1/2提高到2/3


转载:http://blog.csdn.net/manesking/archive/2007/02/09/1505979.aspx
posted @ 2008-07-14 10:57 无名 阅读(43) | 评论 (0)编辑

2008年7月9日 #


本文首先讨论了基于第三范式的数据库表的基本设计,着重论述了建立主键和索引的策略和方案,然后从数据库表的扩展设计和库表对象的放置等角度概述了数据库管理系统的优化方案。
关键词: 优化(Optimizing) 第三范式(3NF) 冗余数据(Redundant Data) 索引(Index) 数据分割(Data Partitioning) 对象放置(Object Placement)
1、 引言
     数据库优化的目标无非是避免磁盘I/O瓶颈、减少CPU利用率和减少资源竞争。为了便于读者阅读和理解,笔者参阅了Sybase、Informix和 Oracle等大型数据库系统参考资料,基于多年的工程实践经验,从基本表设计、扩展设计和数据库表对象放置等角度进行讨论,着重讨论了如何避免磁盘 I/O瓶颈和减少资源竞争,相信读者会一目了然。
2 、基于第三范式的基本表设计
      在基于表驱动的信息管理系统(MIS)中,基本表的设计规范是第三范式(3NF)。第三范式的基本特征是非主键属性只依赖于主键属性。基于第三范式的数据库表设计具有很多优点:
     (一)、是消除了冗余数据,节省了磁盘存储空间;
     (二)、是有良好的数据完整性限制,即基于主外键的参照完整限制和基于主键的实体完整性限制,这使得数据容易维护,也容易移植和更新;
     (三)、是数据的可逆性好,在做连接(Join)查询或者合并表时不遗漏、也不重复;
     (四)、是因消除了冗余数据(冗余列),在查询(Select)时每个数据页存的数据行就多,这样就有效地减少了逻辑I/O,每个Cash存的页面就多,也减少物理I/O;
     (五)、是对大多数事务(Transaction)而言,运行性能好;
     (六)、是物理设计(Physical Design)的机动性较大,能满足日益增长的用户需求。
      在基本表设计中,表的主键、外键、索引设计占有非常重要的地位,但系统设计人员往往只注重于满足用户要求,而没有从系统优化的高度来认识和重视它们。实际上,它们与系统的运行性能密切相关。现在从系统数据库优化角度讨论这些基本概念及其重要意义:
      (1)主键(Primary Key):主键被用于复杂的SQL语句时,频繁地在数据访问中被用到。一个表只有一个主键。主键应该有固定值(不能为Null或缺省值,要有相对稳定性),不含代码信息,易访问。把常用(众所周知)的列作为主键才有意义。短主键最佳(小于25bytes),主键的长短影响索引的大小,索引的大小影响索引页的大小,从而影响磁盘I/O。主键分为自然主键和人为主键。自然主键由实体的属性构成,自然主键可以是复合性的,在形成复合主键时,主键列不能太多,复合主键使得Join*作复杂化、也增加了外键表的大小。人为主键是,在没有合适的自然属性键、或自然属性复杂或灵敏度高时,人为形成的。人为主键一般是整型值(满足最小化要求),没有实际意义,也略微增加了表的大小;但减少了把它作为外键的表的大小。
      (2)外键(Foreign Key):外键的作用是建立关系型数据库中表之间的关系(参照完整性),主键只能从独立的实体迁移到非独立的实体,成为后者的一个属性,被称为外键。
      (3)索引(Index):利用索引优化系统性能是显而易见的,对所有常用于查询中的Where子句的列和所有用于排序的列创建索引,可以避免整表扫描或访问,在不改变表的物理结构的情况下,直接访问特定的数据列,这样减少数据存取时间;利用索引可以优化或排除耗时的分类*作;把数据分散到不同的页面上,就分散了插入的数据;主键自动建立了唯一索引,因此唯一索引也能确保数据的唯一性(即实体完整性);索引码越小,定位就越直接;新建的索引效能最好,因此定期更新索引非常必要。索引也有代价:有空间开销,建立它也要花费时间,在进行Insert、Delete和Update*作时,也有维护代价。索引有两种:聚族索引和非聚族索引。一个表只能有一个聚族索引,可有多个非聚族索引。使用聚族索引查询数据要比使用非聚族索引快。在建索引前,应利用数据库系统函数估算索引的大小。
        ① 聚族索引(Clustered Index):聚族索引的数据页按物理有序储存,占用空间小。选择策略是,被用于Where子句的列:包括范围查询、模糊查询或高度重复的列(连续磁盘扫描);被用于连接Join*作的列;被用于Order by和Group by子句的列。聚族索引不利于插入*作,另外没有必要用主键建聚族索引。
        ② 非聚族索引(Nonclustered Index):与聚族索引相比,占用空间大,而且效率低。选择策略是,被用于Where子句的列:包括范围查询、模糊查询(在没有聚族索引时)、主键或外键列、点(指针类)或小范围(返回的结果域小于整表数据的20%)查询;被用于连接Join*作的列、主键列(范围查询);被用于Order by和Group by子句的列;需要被覆盖的列。对只读表建多个非聚族索引有利。索引也有其弊端,一是创建索引要耗费时间,二是索引要占有大量磁盘空间,三是增加了维护代价(在修改带索引的数据列时索引会减缓修改速度)。那么,在哪种情况下不建索引呢?对于小表(数据小于5页)、小到中表(不直接访问单行数据或结果集不用排序)、单值域(返回值密集)、索引列值太长(大于20bitys)、容易变化的列、高度重复的列、Null值列,对没有被用于Where子语句和 Join查询的列都不能建索引。另外,对主要用于数据录入的,尽可能少建索引。当然,也要防止建立无效索引,当Where语句中多于5个条件时,维护索引的开销大于索引的效益,这时,建立临时表存储有关数据更有效。
批量导入数据时的注意事项:在实际应用中,大批量的计算(如电信话单计费)用C 语言程序做,这种基于主外键关系数据计算而得的批量数据(文本文件),可利用系统的自身功能函数(如Sybase的BCP命令)快速批量导入,在导入数据库表时,可先删除相应库表的索引,这有利于加快导入速度,减少导入时间。在导入后再重建索引以便优化查询。
      (4)锁:锁是并行处理的重要机制,能保持数据并发的一致性,即按事务进行处理;系统利用锁,保证数据完整性。因此,我们避免不了死锁,但在设计时可以充分考虑如何避免长事务,减少排它锁时间,减少在事务中与用户的交互,杜绝让用户控制事务的长短;要避免批量数据同时执行,尤其是耗时并用到相同的数据表。锁的征用:一个表同时只能有一个排它锁,一个用户用时,其它用户在等待。若用户数增加,则Server的性能下降,出现“假死”现象。如何避免死锁呢?从页级锁到行级锁,减少了锁征用;给小表增加无效记录,从页级锁到行级锁没有影响,若在同一页内竞争有影响,可选择合适的聚族索引把数据分配到不同的页面;创建冗余表;保持事务简短;同一批处理应该没有网络交互。
      (5)查询优化规则:在访问数据库表的数据(Access Data)时,要尽可能避免排序(Sort)、连接(Join)和相关子查询*作。经验告诉我们,在优化查询时,必须做到:
        ① 尽可能少的行;
        ② 避免排序或为尽可能少的行排序,若要做大量数据排序,最好将相关数据放在临时表中*作;用简单的键(列)排序,如整型或短字符串排序;
        ③ 避免表内的相关子查询;
        ④ 避免在Where子句中使用复杂的表达式或非起始的子字符串、用长字符串连接;
        ⑤ 在Where子句中多使用“与”(And)连接,少使用“或”(Or)连接;
        ⑥ 利用临时数据库。在查询多表、有多个连接、查询复杂、数据要过滤时,可以建临时表(索引)以减少I/O。但缺点是增加了空间开销。
     除非每个列都有索引支持,否则在有连接的查询时分别找出两个动态索引,放在工作表中重新排序。
3、 基本表扩展设计
     基于第三范式设计的库表虽然有其优越性(见本文第一部分),然而在实际应用中有时不利于系统运行性能的优化:如需要部分数据时而要扫描整表,许多过程同时竞争同一数据,反复用相同行计算相同的结果,过程从多表获取数据时引发大量的连接*作,当数据来源于多表时的连接*作;这都消耗了磁盘I/O和CPU时间。
尤其在遇到下列情形时,我们要对基本表进行扩展设计:许多过程要频繁访问一个表、子集数据访问、重复计算和冗余数据,有时用户要求一些过程优先或低的响应时间。
如何避免这些不利因素呢?根据访问的频繁程度对相关表进行分割处理、存储冗余数据、存储衍生列、合并相关表处理,这些都是克服这些不利因素和优化系统运行的有效途径。
3.1 分割表或储存冗余数据
    分割表分为水平分割表和垂直分割表两种。分割表增加了维护数据完整性的代价。
水平分割表:一种是当多个过程频繁访问数据表的不同行时,水平分割表,并消除新表中的冗余数据列;若个别过程要访问整个数据,则要用连接*作,这也无妨分割表;典型案例是电信话单按月分割存放。另一种是当主要过程要重复访问部分行时,最好将被重复访问的这些行单独形成子集表(冗余储存),这在不考虑磁盘空间开销时显得十分重要;但在分割表以后,增加了维护难度,要用触发器立即更新、或存储过程或应用代码批量更新,这也会增加额外的磁盘I/O开销。
垂直分割表(不破坏第三范式),一种是当多个过程频繁访问表的不同列时,可将表垂直分成几个表,减少磁盘I/O(每行的数据列少,每页存的数据行就多,相应占用的页就少),更新时不必考虑锁,没有冗余数据。缺点是要在插入或删除数据时要考虑数据的完整性,用存储过程维护。另一种是当主要过程反复访问部分列时,最好将这部分被频繁访问的列数据单独存为一个子集表(冗余储存),这在不考虑磁盘空间开销时显得十分重要;但这增加了重叠列的维护难度,要用触发器立即更新、或存储过程或应用代码批量更新,这也会增加额外的磁盘I/O开销。垂直分割表可以达到最大化利用Cache的目的。
总之,为主要过程分割表的方法适用于:各个过程需要表的不联结的子集,各个过程需要表的子集,访问频率高的主要过程不需要整表。在主要的、频繁访问的主表需要表的子集而其它主要频繁访问的过程需要整表时则产生冗余子集表。
注意,在分割表以后,要考虑重新建立索引。
3.2 存储衍生数据
     对一些要做大量重复性计算的过程而言,若重复计算过程得到的结果相同(源列数据稳定,因此计算结果也不变),或计算牵扯多行数据需额外的磁盘I/O开销,或计算复杂需要大量的CPU时间,就考虑存储计算结果(冗余储存)。现予以分类说明:
若在一行内重复计算,就在表内增加列存储结果。但若参与计算的列被更新时,必须要用触发器更新这个新列。
若对表按类进行重复计算,就增加新表(一般而言,存放类和结果两列就可以了)存储相关结果。但若参与计算的列被更新时,就必须要用触发器立即更新、或存储过程或应用代码批量更新这个新表。
若对多行进行重复性计算(如排名次),就在表内增加列存储结果。但若参与计算的列被更新时,必须要用触发器或存储过程更新这个新列。
     总之,存储冗余数据有利于加快访问速度;但违反了第三范式,这会增加维护数据完整性的代价,必须用触发器立即更新、或存储过程或应用代码批量更新,以维护数据的完整性。
3.3 消除昂贵结合
     对于频繁同时访问多表的一些主要过程,考虑在主表内存储冗余数据,即存储冗余列或衍生列(它不依赖于主键),但破坏了第三范式,也增加了维护难度。在源表的相关列发生变化时,必须要用触发器或存储过程更新这个冗余列。当主要过程总同时访问两个表时可以合并表,这样可以减少磁盘I/O*作,但破坏了第三范式,也增加了维护难度。对父子表和1:1关系表合并方法不同:合并父子表后,产生冗余表;合并1:1关系表后,在表内产生冗余数据。
4 、数据库对象的放置策略
      数据库对象的放置策略是均匀地把数据分布在系统的磁盘中,平衡I/O访问,避免I/O瓶颈。
        ⑴ 访问分散到不同的磁盘,即使用户数据尽可能跨越多个设备,多个I/O运转,避免I/O竞争,克服访问瓶颈;分别放置随机访问和连续访问数据。
        ⑵ 分离系统数据库I/O和应用数据库I/O。把系统审计表和临时库表放在不忙的磁盘上。
        ⑶ 把事务日志放在单独的磁盘上,减少磁盘I/O开销,这还有利于在障碍后恢复,提高了系统的安全性。
        ⑷ 把频繁访问的“活性”表放在不同的磁盘上;把频繁用的表、频繁做Join*作的表分别放在单独的磁盘上,甚至把把频繁访问的表的字段放在不同的磁盘上,把访问分散到不同的磁盘上,避免I/O争夺;
        ⑸ 利用段分离频繁访问的表及其索引(非聚族的)、分离文本和图像数据。段的目的是平衡I/O,避免瓶颈,增加吞吐量,实现并行扫描,提高并发度,最大化磁盘的吞吐量。利用逻辑段功能,分别放置“活性”表及其非聚族索引以平衡I/O。当然最好利用系统的默认段。另外,利用段可以使备份和恢复数据更加灵活,使系统授权更加灵活。

优化SQL Server数据库的经验总结

来自:http://www.enet.com.cn/article/2008/0108/A20080108997121.shtml

优化数据库的注意事项:

  1、关键字段建立索引。

  2、使用存储过程,它使SQL变得更加灵活和高效。

  3、备份数据库和清除垃圾数据。

  4、SQL语句语法的优化。(可以用Sybase的SQL Expert,可惜我没找到unexpired的序列号)

  5、清理删除日志。

  SQL语句优化的基本原则:

  1、使用索引来更快地遍历表。

  缺省情况下建立的索引是非群集索引,但有时它并不是最佳的。在非群集索引下,数据在物理上随机存放在数据页上。合理的索引设计要建立在对各种查询的分析和预测上。一般来说:①.有大量重复值、且经常有范围查询(between, >,< ,>=,< =)和order by、group by发生的列,可考虑建立群集索引;②.经常同时存取多列,且每列都含有重复值可考虑建立组合索引;③.组合索引要尽量使关键查询形成索引覆盖,其前导列一定是使用最频繁的列。

  2、IS NULL 与 IS NOT NULL

  不能用null作索引,任何包含null值的列都将不会被包含在索引中。即使索引有多列这样的情况下,只要这些列中有一列含有null,该列就会从索引中排除。也就是说如果某列存在空值,即使对该列建索引也不会提高性能。任何在where子句中使用is null或is not null的语句优化器是不允许使用索引的。

  3、IN和EXISTS

  EXISTS要远比IN的效率高。里面关系到full table scan和range scan。几乎将所有的IN操作符子查询改写为使用EXISTS的子查询。

  4、在海量查询时尽量少用格式转换。

  5、当在SQL SERVER 2000中,如果存储过程只有一个参数,并且是OUTPUT类型的,必须在调用这个存储过程的时候给这个参数一个初始的值,否则会出现调用错误。

  6、ORDER BY和GROPU BY

  使用ORDER BY和GROUP BY短语,任何一种索引都有助于SELECT的性能提高。注意如果索引列里面有NULL值,Optimizer将无法优化。

  7、任何对列的操作都将导致表扫描,它包括数据库函数、计算表达式等等,查询时要尽可能将操作移至等号右边。

  8、IN、OR子句常会使用工作表,使索引失效。如果不产生大量重复值,可以考虑把子句拆开。拆开的子句中应该包含索引。

  9、SET SHOWPLAN_ALL ON 查看执行方案。DBCC检查数据库数据完整性。DBCC(DataBase Consistency Checker)是一组用于验证SQL Server数据库完整性的程序。

  10、谨慎使用游标

  在某些必须使用游标的场合,可考虑将符合条件的数据行转入临时表中,再对临时表定义游标进行操作,这样可使性能得到明显提高。

  注释:所谓的优化就是WHERE子句利用了索引,不可优化即发生了表扫描或额外开销。经验显示,SQL Server性能的最大改进得益于逻辑的数据库设计、索引设计和查询设计方面。反过来说,最大的性能问题常常是由其中这些相同方面中的不足引起的。其实SQL优化的实质就是在结果正确的前提下,用优化器可以识别的语句,充份利用索引,减少表扫描的I/O次数,尽量避免表搜索的发生。其实SQL的性能优化是一个复杂的过程,上述这些只是在应用层次的一种体现,深入研究还会涉及数据库层的资源配置、网络层的流量控制以及操作系统层的总体设计。

数据库设计中的14个技巧

随着计算机技术越来越广泛地应用于国民经济的各个领域,在计算机硬件不断微型化的同时,应用系统向着复杂化、大型化的方向发展。数据库是整个系统的核心,它的设计直接关系系统执行的效率和系统的稳定性。因此在软件系统开发中,数据库设计应遵循必要的数据库范式理论,以减少冗余、保证数据的完整性与正确性。只有在合适的数据库产品上设计出合理的数据库模型,才能降低整个系统的编程和维护难度,提高系统的实际运行效率。虽然对于小项目或中等规模的项目开发人员可以很容易地利用范式理论设计出一套符合要求的数据库,但对于一个包含大型数据库的软件项目,就必须有一套完整的设计原则与技巧。

一、成立数据小组
 
大型数据库数据元素多,在设计上有必要成立专门的数据小组。由于数据库设计者不一定是使用者,对系统设计中的数据元素不可能考虑周全,数据库设计出来后,往往难以找到所需的库表,因此数据小组最好由熟悉业务的项目骨干组成。
 
数据小组的职能并非是设计数据库,而是通过需求分析,在参考其他相似系统的基础上,提取系统的基本数据元素,担负对数据库的审核。审核内容包括审核新的数据库元素是否完全、能否实现全部业务需求;对旧数据库(如果存在旧系统)的分析及数据转换;数据库设计的审核、控制及必要调整。

二、设计原则


1.规范命名。所有的库名、表名、域名必须遵循统一的命名规则,并进行必要说明,以方便设计、维护、查询。
 
2.控制字段的引用。在设计时,可以选择适当的数据库设计管理工具,以方便开发人员的分布式设计和数据小组的集中审核管理。采用统一的命名规则,如果设计的字段已经存在,可直接引用;否则,应重新设计。
 
3.库表重复控制。在设计过程中,如果发现大部分字段都已存在,开发人员应怀疑所设计的库表是否已存在。通过对字段所在库表及相应设计人员的查询,可以确认库表是否确实重复。
 
4.并发控制。设计中应进行并发控制,即对于同一个库表,在同一时间只有一个人有控制权,其他人只能进行查询。
 
5.必要的讨论。数据库设计完成后,数据小组应与相关人员进行讨论,通过讨论来熟悉数据库,从而对设计中存在的问题进行控制或从中获取数据库设计的必要信息。
 
6.数据小组的审核。库表的定版、修改最终都要通过数据小组的审核,以保证符合必要的要求。
 
7.头文件处理。每次数据修改后,数据小组要对相应的头文件进行修改(可由管理软件自动完成),并通知相关的开发人员,以便进行相应的程序修改。

三、设计技巧

1.分类拆分数据量大的表。对于经常使用的表(如某些参数表或代码对照表),由于其使用频率很高,要尽量减少表中的记录数量。例如,银行的户主账表原来设计成一张表,虽然可以方便程序的设计与维护,但经过分析发现,由于数据量太大,会影响数据的迅速定位。如果将户主账表分别设计为活期户主账、定期户主账及对公户主账等,则可以大大提高查询效率。
 
2.索引设计。对于大的数据库表,合理的索引能够提高整个数据库的操作效率。在索引设计中,索引字段应挑选重复值较少的字段;在对建有复合索引的字段进行检索时,应注意按照复合索引字段建立的顺序进行。例如,如果对一个5万多条记录的流水表以日期和流水号为序建立复合索引,由于在该表中日期的重复值接近整个表的记录数,用流水号进行查询所用的时间接近3秒;而如果以流水号为索引字段建立索引进行相同的查询,所用时间不到1秒。因此在大型数据库设计中,只有进行合理的索引字段选择,才能有效提高整个数据库的操作效率。
 
3.数据操作的优化。在大型数据库中,如何提高数据操作效率值得关注。例如,每在数据库流水表中增加一笔业务,就必须从流水控制表中取出流水号,并将其流水号的数值加一。正常情况下,单笔操作的反应速度尚属正常,但当用它进行批量业务处理时,速度会明显减慢。经过分析发现,每次对流水控制表中的流水号数值加一时都要锁定该表,而该表却是整个系统操作的核心,有可能在操作时被其他进程锁定,因而使整个事务操作速度变慢。对这一问题的解决的办法是,根据批量业务的总笔数批量申请流水号,并对流水控制表进行一次更新,即可提高批量业务处理的速度。另一个例子是对插表的优化。对于大批量的业务处理,如果在插入数据库表时用普通的Insert语句,速度会很慢。其原因在于,每次插表都要进行一次I/O操作,花费较长的时间。改进后,可以用Put语句等缓冲区形式等满页后再进行I/O操作,从而提高效率。对大的数据库表进行删除时,一般会直接用Delete语句,这个语句虽然可以进行小表操作,但对大表却会因带来大事务而导致删除速度很慢甚至失败。解决的方法是去掉事务,但更有效的办法是先进行Drop操作再进行重建。
 
4.数据库参数的调整。数据库参数的调整是一个经验不断积累的过程,应由有经验的系统管理员完成。以Informix数据库为例,记录锁的数目太少会造成锁表的失败;逻辑日志的文件数目太少会造成插入大表失败等,这些问题都应根据实际情况进行必要的调整。
 
5.必要的工具。在整个数据库的开发与设计过程中,可以先开发一些小的应用工具,如自动生成库表的头文件、插入数据的初始化、数据插入的函数封装、错误跟踪或自动显示等,以此提高数据库的设计与开发效率。
 
6.避免长事务。对单个大表的删除或插入操作会带来大事务,解决的办法是对参数进行调整,也可以在插入时对文件进行分割。对于一个由一系列小事务顺序操作共同构成的长事务(如银行交易系统的日终交易),可以由一系列操作完成整个事务,但其缺点是有可能因整个事务太大而使不能完成,或者,由于偶然的意外而使事务重做所需的时间太长。较好的解决方法是,把整个事务分解成几个较小的事务,再由应用程序控制整个系统的流程。这样,如果其中某个事务不成功,则只需重做该事务,因而既可节约时间,又可避免长事务。
 
7.适当超前。计算机技术发展日新月异,数据库的设计必须具有一定前瞻性,不但要满足当前的应用要求,还要考虑未来的业务发展,同时必须有利于扩展或增加应用系统的处理功能。

 相对于中小型数据库,大型数据库的设计与开发要复杂得多,因此在设计、开发过程中,除了要遵循数据库范式理论、增加系统的一致性和完整性外,还要在总体上根据具体情况进行分布式设计,紧紧把握集中控制、统一审核的基本原则,保证数据库设计结构紧凑、分布平衡、定位迅速。在数据库操作上,要采用一定的技巧提高整个应用系统的执行效率,并注意适当超前,以适应不断变化的应用及系统发展的要求。


 

posted @ 2008-07-09 14:49 无名 阅读(17) | 评论 (0)编辑

2008年6月24日 #

 
posted @ 2008-06-24 10:09 无名 阅读(16) | 评论 (0)编辑

2008年6月19日 #

需求: 选择A字段唯一值,并上B字段,如果A字段相同B字段不同,则取第一次出现的B字段的值。


A B Id
--------------------------------------
1 A 1
1 B 2
2 C 3
2 D 4
3 E 5
3 F 6

选择结果:
A B Id
--------------------------------------
1 A 1
2 C 3
3 E 5



SELECT *
FROM TEST
WHERE (ID IN
(SELECT MIN(id)
FROM TEST
GROUP BY A))

文章来源:http://jinweijie.blogspot.com/2006/10/codesql.html

获取字段的唯一性:
select Salary from TableName group by Salary having count(*)=1
posted @ 2008-06-19 16:16 无名 阅读(13) | 评论 (0)编辑

     摘要: Asp.Net 文件操作基类(读取,删除,批量拷贝,删除,写入,获取文件夹大小,文件属性,遍历目录)Submitted by 浪淘沙 on 2007, October 8, 4:12 AM. asp.net/############################################版权声明:文章内容为他人编辑,整理,创作.你可以任意转载、发布、使用但请务必以明文标注文章原始出处及本声... 阅读全文
posted @ 2008-06-19 13:45 无名 阅读(21) | 评论 (1)编辑

C# code
//该方法是按钮单击事件,完成上传文件到服务器
private void btnUpload_Click(object sender, System.EventArgs e)
  {
    if(fileUpload1.PostedFile!=null && fileUpload1.PostedFile.FileName!="" &&    fileUpload1.PostedFile.ContentLength!=0)
    {
      if(fileUpload1.PostedFile.ContentLength > 5 * 1024 )
      {
    lblError.Text = "上传文件过大";
    lblError.Visible = true;
    return;
      }
      string sFileName = Path.GetExtension(fileUpload1.PostedFile.FileName).ToUpper();
      if(! (strFileName == ".BMP" || strFileName == ".GIF" || strFileName == ".JPG") )
      {
    lblError.Text = "文件格式不正确";
    lblError.Visible = true;
    return;
      }

      Random ran = new Random();
      string sNewImg = DateTime.Now.ToString(@"yyyyMMddHHmmss") + ran.Next(100,999) + Path.GetExtension(fileUpload1.PostedFile.FileName) ;
      string sPath = Server.MapPath( "~/Picture/" + sNewImg);
      if( !Directory.Exists(Path.GetDirectoryName(sPath) ) )
      {
        Directory.CreateDirectory(Path.GetDirectoryName(strPath));
      }
      fileUpload1.PostedFile.SaveAs(sPath);
    }        
  }
posted @ 2008-06-19 11:09 无名 阅读(11) | 评论 (0)编辑

2008年6月16日 #

     摘要: namespaceConvenanceCompany.SQLServerDAL{publicclassCompany:ICompany{/**////<summary>///MethodtogetCompanyInformation///</summary>///<returns></returns>publicCompanyInfoGetCompa... 阅读全文
posted @ 2008-06-16 07:19 无名 阅读(26) | 评论 (0)编辑

2008年6月15日 #

     摘要: 泛型最常见的用途是泛型集合,命名空间System.Collections.Generic 中包含了一些基于泛型的集合类,使用泛型集合类可以提供更高的类型安全性,还有更高的性能,避免了非泛型集合的重复的装箱和拆箱。 很多非泛型集合类都有对应的泛型集合类,下面是常用的非泛型集合类以及对应的泛型集合类: 非泛型集合类 泛型集合类 ArrayList List<T> HashTable DI... 阅读全文
posted @ 2008-06-15 09:19 无名 阅读(20) | 评论 (0)编辑

2008年6月14日 #

tagprefix
与命名空间关联的别名。
tagname
与类关联的别名。
Namespace
与 tagprefix 关联的命名空间。
Src
与 tagprefix:tagname 对关联的声明性用户控件文件的位置(相对的或绝对的)。
Assembly
与 tagprefix 关联的命名空间所驻留的程序集。
注意 该程序集名称不包含文件扩展名。

posted @ 2008-06-14 22:32 无名 阅读(6) | 评论 (0)编辑