对于MS [主键默认是聚集索引] 的质疑1.ms sqlserver的默认特性:主键默认是聚集索引2.我听到的一些与我的理
对于MS [主键默认是聚集索引] 的质疑
1.ms sqlserver的默认特性:主键默认是聚集索引
2.我听到的一些与我的理解明显不同的言论:
..聚集索引所在的列或列的组合最好是唯一的
..最好使用自增列作为聚集索引列
上面这些我一直理解不能,而且持上述看法的不在少数,这与我的认知严重违背,有谁能有办法说服我,告诉我是我错了?
================================以下是我的看法================================
1.在大多数情况下我们的性能问题是读取而不是写入,客户反映我们的系统慢,通常说的是某个功能或报表的查询慢,而不是保存慢,即使对于一个写操作非常频繁的系统来说,读写的比例至少在10:1以上,普通的信息系统这个比例就更高了,所以"从性能的角度考虑"这句话,我认为是要重点考虑读取性能,而不是写入的性能。
基于上述原因,我认为我们在做设计的时候,如果能通过聚集显著的提升读取性能,那么我们就可以接受聚集带来的负面效果(写性能下降,以及表上其它非聚集索引的读性能下降)
2.聚集的优势在于读取批量且连续的记录,这是由聚集的物理形态决定的。比如读取一个月内的销售数据,如果按日期聚集,则这一个月内的数据所在的页面在物理上是连续的,可以极大避免最昂贵的磁头随机移动的操作,这是非聚集索引无法做到的。就好比你查字典,查找所有读音为 [ta] 的字效率很高,但是查找所有偏旁部首为 [亻]的字效率非常低。
因此基于上述分析:我认为聚集索引应该要选择一个离散度适中的列,以保证能够命中适当范围的记录,注意我的表述是[适当范围]:
如果命中范围太大则说明该列没有索引的价值(比如性别更,平均每次命中50%的记录)
如果命中范围太小则说明没有聚集的价值(比如主键,平均只能命中1条记录)
3.表上的聚集索引只能有一个,而且聚集还有负面影响,因此其宝贵程度不言而喻。而自长列通常在业务逻辑上毫无意义(用户不可能按自增长列来进行查询或分析等业务操作),这样的列用做主键没有问题,但用作聚集索引只能说是暴殄天物,原因与上面第2条同理:自增长列的离散度太低,导致选择性过高,每次只能命中一条记录,并不具备聚集的价值,这样的聚集还不如没有,因为我们承受了聚集带来的负面效果,却又没有体现出聚集的优势,因此我一向认为堆表的性能肯定要比那些设计得很糟糕的聚集表要高。
[解决办法]
聚集索引所在的列或列的组合最好是唯一的
--> 不一定,当表的主键约束不是表的聚集索引时,聚集索引的列或列组合可以不是唯一的.
最好使用自增列作为聚集索引列
--> 不一定,但需看具体的情况.
个人不建议用自增列作主键或聚集索引,因为:
1.自增列的数据(1,2,3,4..)没有意义,纯属为了不重复而存在,占用存储空间(每行4kb).
2.浪费了对于一个表唯一的一个聚集索引,查询时的访问顺序:非聚集索引->聚集索引->数据页.
3.当重复写入相同的记录时,无法检查数据的唯一性,造成数据重复.
[解决办法]
两位的精神这么好啊,都是半夜三更来提问回答问题的。
[解决办法]
如果已经有适合业务主键的话可以不用逻辑主键,但我大部分时候都逻辑主键。首先自增列的写入性能好,其次占用空间小。使用复杂的业务主键的时候建立额外的索引是非常痛苦的事情。
create table (Country char(10),City char(20),Offcie char(50),Staff char(20),Primary key(Country,City,Office,Staff),Name varchar(50).......)
如果使用业务主键的话经常会看到这样的表,不考虑元数据,这个表主键占用的空间是100byte,假设有100W的数据的时候,要创建额外的索引需要使用索引列使用的空间+100byte*100W=100MB的空间。
create table (ID int identity(1,1) primary key,Country char(10),City char(20),Offcie char(50),Staff char(20,Name varchar(50).......)
使用这样的结构的话,最初使用的空间是更多的,首先需要4byte*100W=4MB的聚集索引的空间,其次还需要一个业务主键替代的非重复的索引100byte+4byte=104MB,但这个优势慢慢会体现在建立索引上,因为建立一个索引只需要额外的4MB的空间了,一般这样的表都会建立非常多的索引,因为检索条件是非常灵活的。
另一个问题是建立外键的时候,如果有100byte的业务主键,有100W的数据量,要还创建一个使用这个的外键表数据上亿的时候内心会有什么东西奔腾而过的。
[解决办法]
学习,关注、支持
[解决办法]
虽然我谢绝教条主义,但我不明白你为什么将离散性和聚集性能挂钩。查询性能也不是只盯着聚集索引,就拿你举的例子[ta]和[亻],这是一个很形象的问题,你认为查找所有读音为 [ta] 的字效率很高,我认为查找所有偏旁部首为 [亻]的字更具效率,部首索引条目就有,多极几页,翻聚集索引的[ta],超10页没问题吧。每一个查询都走聚集,等于没索引设计,如将字典部首索引撕了。
[解决办法]你上面提到多次“聚集还有负面影响”,能否说说负面影响具体指哪些? 除了你说的“写性能下降”
[解决办法]LZ举得例子本身就是个大BUG,a和b都不是离散列。
你认同的a和别人认同的b是一个道理,跟是否是UNIQUE没关系
要实现业务日期高离散
1、用hash封装
2、要么让改装datetime,实现timestamp%60,按“秒”离散,或者更大的离散值
聚集索引到底因该被设计成单调递增还是离散式,这要根据业务来定论
就关系型数据库而言,大部分业务任然是轻量级的。流读业务多用单调递增,随机访问的业务多用离散。
sql server为什么将主键默认为聚集索引
首先要了解关系型数据库的三范式
第一范式就不提了
第二范式的定义:第二范式(2NF)要求数据库表中的每个实例或行必须可以被唯一地区分。为实现区分通常需要为表加上一个列,以存储各个实例的唯一标识。2NF要求实体的属性完全依赖于主关键字。所谓完全依赖是指不能存在仅依赖主关键字一部分的属性,如果存在,那么这个属性和主关键字的这一部分应该分离出来形成一个新的实体,新实体与原实体之间是一对多的关系。为实现区分通常需要为表加上一个列,以存储各个实例的唯一标识。简而言之,第二范式就是非主属性非部分依赖于主关键字。
第二范式中提到的唯一列和聚集索引合并设计的最大优势是:按唯一标识的主关键字,进行数据查、增、删、改时,性能最大化。这里指的查是指连接查询。
聚集索引往往被设计在一些有主外连接的数据字段上,也就是通常说业务连接体:比如订单流水号、Log日志流水号等。这些连接体加载着业务表于业务表之间的逻辑关系。逻辑关系就是业务关系,业务关系放到在三范式中就是主外键。
聚集索引的设计方式和实体业务挂钩,和批量或随机访问的方式无关。所以主键被默认加上聚集索引并没有问题。
至于到底是设计成离散还是单调递增这个由业务决定,上面已经说过了,不重复。
LZ说:
通常说的是某个功能或报表的查询慢,而不是保存慢因此我一向认为堆表的性能肯定要比那些设计得很糟糕的聚集表要高。在轻量级数据级别下,这个假设没错,但当一张表上到几亿甚至几十亿,这个时候写的性能更重要。
为什么?道理很简单,当数据都写不进DB了,还谈什么读?
这时候只能做一件事情删除所有的二级索引!!只保留聚集索引
在这种情况下,所有的三范式、ACID定理都是浮云。
假设你用的是随机插入式堆表,那不好意思,只能等死。
还有一种设计模式,就是分布式 SQL SERVER + 数据代理层。在这种设计模式下SQL SERVER的读写比是倒过来
一次读取缓存,多次写入。嘿嘿,没见过吧。这种设计模式在网游行业非常普遍,SQL SERVER只作为数据持久化的载体,不带任何逻辑。玩家的数据每分钟可能会回写好几次,但读取只有一次,就是在玩家登录游戏的时候。假设你用的是随机插入式堆表,那不好意思,也只能等死。
所以,归根结底,还是要根据业务类型和数据量来决定数据库的设计模式,不论怎么设计都有其存在的价值,不能简单用对错来判定。
[解决办法]数据库本身不是也有lazywrite这一特性吗?提交的数据也不是时时刻刻写入磁盘中的数据文件中的。
[解决办法]比较可以,不错
[解决办法]