采用uniqueidentifier (以下用guid代替)做主键有它的不可替代的优势,因其不重复性,可以用于数据合并、分布、交换等各种场合,可以由客户端生成并存储,避免了像identity一样需要多一次读取操作,但是它也有着固有的缺点,如不易读,存储和性能都不如int型。

既然我们用了guid做主键,而且我们一般情况下主键即聚集索引,如果这样的话,那么问题就来了:

1、guid的分散性会导致新增记录时可能产生大量的split page操作,这种操作对插入时的性能影响是巨大的,我们希望插入和更新操作不会随着数据量的增加而线性增加。

2、分散性同样导致读取批量记录集时可能会产生多次I/O读操作,因为需要的数据可能分散在各个page中,这种情况在list查询以及master-detail操作中经常会遇到,和上面一样,我们不希望读取操作的cost随时间而大大增长。

而我们的要求是:
1、写条件 -- 新插入的数据不会去影响以前page的存储
2、读条件 -- 读取批量数据时然后在最靠近的pages中获取

那么如果我们要做到这样该怎么办呢?

举个例子吧,有Order和OrderLine两个表(我不喜欢用复数命名,因为如果要mapping到class的话,还是单数最贴切):
Order (OrderID, OrderDate, OrderNo, CustomerCode, CustomerName, CreatedDate, CreatedBy)
OrderLine (LineID, OrderID, ProductID, ProductName, Price, Quantity, Amount)

一、对于Order表,首先我们将聚集索引从OrderID主键上移出,放到CreatedDate,这样最能满足写条件了,但是读条件不一定满足,因为可能我现在做了一票orderdate为上上个月的单据,于是我们把聚集索引放到OrderDate上去,因为一般来说,批量取的都是OrderDate在一个日期范围的数据。

那么这种方案是不是最好的呢?未必,因为如果OrderNo是根据OrderDate生成的而且也呈有序状态,那么把聚集索引放在它上面,会对大量的单数据读取(OrderNo = 'xxxx')节省一个bookmark lookup的时间,而且同样可以满足条件1和2。

二、对于OrderLine,这个就麻烦了,最先的LineID上放聚集索引是最糟糕的,因为条件1和2都满足不了,那么我们先把它移到OrderID上,这次满足读条件了,因为绝大多数时间是(where OrderID = 'xxxx')去取一个Order下面的Items。那么如果要满足条件1,该如何放置聚集索引呢?很不幸,在这个表结构中没办法满足到条件1(这也就是我伟大的progame犯的一个伟大的错误),如果要满足,可以这样处理:
1、添加CreatedDate或OrderDate列,这样导致数据存储增大,程序处理增加
2、所有索引(包括主键索引)都不做为clustered,这样写条件在任何情况下满足。(不得不感叹一下,伟大啊!)此时读条件基本满足

我们都是精打细算的程序员,当然是选百利而无一害的方法2,至于一个表如果没有聚集索引会发生什么问题,我暂时还不知道。
posted on 2008-08-20 10:10  errr  阅读(1279)  评论(0编辑  收藏  举报