状态少的字段不能建索引

我们在学sqlserver的时候,大多教科书和前辈们都说状态少的字段不要建索引,由此带来的开销还不如不建索引,但是这句话有多少人真的知道,

或者说有多少人真的对此有比较深刻的理解,而不是听别人道听途说。。。这样记得快,忘记的也不慢。。。这篇我来分析一下这句话到底有几个意思。

一:现象

  首先我们还是用测试数据来发现问题,我先建立一个Person,有5个字段,建表sql如下:

DROP TABLE dbo.Person

CREATE TABLE Person(ID INT PRIMARY KEY IDENTITY,NAME VARCHAR(900),Age INT,Email VARCHAR(20),isMan INT )

-- 在isMan字段创建非聚集索引(0:女 1:男)
CREATE INDEX idx_isMan ON dbo.Person(isMan)

DECLARE @ch AS INT=0

WHILE @ch<=100000
BEGIN
    INSERT INTO dbo.Person(NAME,Age,Email,isMan)
    VALUES
    (
      REPLICATE(CHAR(@ch),50),
      @ch,
      CAST(CAST(RAND()*1000000000 AS INT) AS VARCHAR(10))+‘qq.com‘,
      @ch%2
    )
    SET @[email protected]+1
END

通过上面的sql可以发现表中有5个字段,ID为聚集索引,isMan为非聚集索引,isMan也就是两种状态(0,1),并且插入10w条记录,截图如下:

sql都做完了,接下来要做的事情就是查询下: isMan=1的记录,如下图:

麻蛋。。。。哥哥明明是在isMan上做数据检索的,怎么就变成 “聚集索引扫描”了???这他么的什么意思嘛,居然不走我的“idx_isMan”索引,

却走他么的“聚集索引(PK__Person__3214EC276EF57B66)”。。。。同时也看到上面的”逻辑读取”为521。。。说明在内存中走了521个数据页。

但是我不服呀。。。我一定要让执行计划走我的索引。。。办法就是强制指定。。。如下图。

看到上面的图,你是不是已经疯了。。。老子才捞5w的数据,你给我走了10w多次数据页。。。这么说1条记录要走两个数据页。。。而扫描聚集

索引才走521个数据页,相差200倍。。。难怪执行计划打死也不走“idx_isMan”这条索引。。。要是这样走了人家还不拿刀捅了sqlserver么???

二:分析原因

  现在很生气,整个人都不好了,为什么会这样???为了找出问题,我们还得看数据页。

1 DBCC TRACEON(3604,2588)
2 DBCC IND(Ctrip,Person,-1)

通过上面的三个图,大概可以看到,10w条数据用了697数据页,其中聚集索引有521个,非聚集索引为176个,这也说明了上面的”聚集索引扫描“走

遍了它自己所有的数据页来才捞出数据,同时还发现这两个索引都有一个共同特征就是,只有一个根节点(indexLevel=1)和无数个(indexLevel=0)

叶子节点,然后我脑子里面就有一幅图出来了。。。

上面就是我构思出来的图,这个专业一点的名字叫做书签查找。。。我们通过建立”idx_isMan“索引后,就会构建右半图的B树结构,其中索引记录

会存放两个值,一个是索引值isMan和一个聚集索引值ID,如果你不相信的话,可以通过DBCC Page去探索"idx_isMan"的索引页,你也可以通过

DBCC SHOW_STATISTICS 去查看,如图:

然后引擎通过“idx_isMan“扫描后,拿到了key值,但是非常可惜,我是select * 的,所以必须还要喷出记录中的Name,Emai等l字段,但是

”index_isMan"中并没有保存这几个字段,所以必须通过key去”聚集索引“的B树中去找。。。最后通过”聚集索引“的B树找到了目标记录,这也

就是所谓的执行计划中的”键查找“,然后喷出”Name,Email“等字段。。。。问题就在这里。。。因为我这样来回的蹦跶蹦跶。。。造成了找出

完整的一个记录,需要蹦跶2-3次数据页。。。具体的寻找记录,可参考图中的”紫色线条“,最后也就造成了10w多次蹦跶。。。

三:启示

那这个例子给我们什么启示呢???仔细想想你就知道。。。使用非聚集索引,千万不要捞取过多的数据。。。因为过多的数据会造成在多个

B树中来回的蹦跶。。。想要做到捞取数据较少,就必须在高唯一性的字段上建立索引,这样的话在非聚集索引B树中符合的数据相对较少,也就

减少了我蹦跶到”主键索引“的B树次数。。。这样的话来回蹦跶的次数远远比”聚集索引“扫描来的实惠,对不对。。。

所以结论出来了:必须在唯一性较高的字段上建立非聚集索引。

时间: 2024-10-29 19:09:55

状态少的字段不能建索引的相关文章

Sql Server之旅——第七站 为什么都说状态少的字段不能建索引

我们在学sqlserver的时候,大多教科书和前辈们都说状态少的字段不要建索引,由此带来的开销还不如不建索引,但是这句话有多少人真的知道, 或者说有多少人真的对此有比较深刻的理解,而不是听别人道听途说...这样记得快,忘记的也不慢...这篇我来分析一下这句话到底有几个意思. 一:现象 首先我们还是用测试数据来发现问题,我先建立一个Person,有5个字段,建表sql如下: DROP TABLE dbo.Person CREATE TABLE Person(ID INT PRIMARY KEY I

【数据库】- 一个值只有0和1的字段,到底要不要建索引?

关于数据库索引的所有文章,都会告诉你不要对这种字段建索引. 但这些文章不会告诉你的一个事实是: 如果表里面这个字段的值分布极度不均匀的情况下,而且你需要查询分布较少的记录的话,索引就非常有用了 举个例子,假设表中有一千万条记录,某个状态为0的记录总数大概会有100条,那么你想查询状态为0的记录时,有没有索引影响非常大,而查询状态为1的记录,则索引基本无用.如果两种状态的记录数相差无几的话,索引也基本无用. 所有的关于索引的文章,建议你不要为这种字段建索引的依据,都是以值分布是均匀为前提的.但如果

时间字段是否适合建索引

时间字段是否适合建索引 可以建立索引的:至于建立聚集索引或者是非聚集索引,那要看你这个时间字段的具体情况以及使用或变更频繁程度. 一般来说,适合建立聚集索引的要求:“既不能绝大多数都相同,又不能只有极少数相同”的规则. 先说说一个误区:有人认为:只要建立索引就能显著提高查询速度.这个想法是很错误的.建立非聚集索引,确实,一般情况下可以提高速度,但是一般并不会达到你想要的速度.只有在适当的列建立适当的(聚集)索引,才能达到满意的效果. 下面的表总结了何时使用聚集索引或非聚集索引(很重要). 动作描

建索引的原则-以innodb为例

一.写在前面       随着开发.测试任务进入尾声,大家都在整理一些项目发布前的一些准备工作,其中一个重要的工作就是为之前写的一些sql语句建立索引,这高并发.高访问量的环境下是非常有必要的,建立一个好的索引能够极大地提高sql语句的查询效率,那么问题来了,到底什么是索引,怎样才能建立一个好的索引呢?本文以mysql Innodb存储引擎为例,结合实际的项目来看一下,如何建立一个好的而索引. 二.索引定义 MySQL官方对索引的定义为:索引(Index)是帮助MySQL高效获取数据的数据结构.

(转)Mysql哪些字段适合建立索引

工作中处理数据时,发现某个表的数据达到亿条,所以要为表建索引提高查询性能,以下两篇文章总结的很好,记录一下,以备后用. 数据库建立索引常用的规则如下: 1.表的主键.外键必须有索引: 2.数据量超过300的表应该有索引: 3.经常与其他表进行连接的表,在连接字段上应该建立索引: 4.经常出现在Where子句中的字段,特别是大表的字段,应该建立索引: 5.索引应该建在选择性高的字段上: 6.索引应该建在小字段上,对于大的文本字段甚至超长字段,不要建索引: 7.复合索引的建立需要进行仔细分析:尽量考

MySQL 索引B+树原理,以及建索引的几大原则

MySQL事实上使用不同的存储引擎也是有很大区别的,下面猿友们可以了解一下. 一.存储引擎的比较 注:上面提到的B树索引并没有指出是B-Tree和B+Tree索引,但是B-树和B+树的定义是有区别的. 在?MySQL?中,主要有四种类型的索引,分别为:B-Tree 索引, Hash 索引, Fulltext 索引和 R-Tree 索引. B-Tree?索引是?MySQL?数据库中使用最为频繁的索引类型,除了 Archive 存储引擎之外的其他所有的存储引擎都支持 B-Tree 索引.Archiv

solr 的客户端调用solrj 建索引+分页查询

一.利用SolrJ操作solr API 使用SolrJ操作Solr会比利用httpClient来操作Solr要简单.SolrJ是封装了httpClient方法,来操作solr的API的.SolrJ底层还是通过使用httpClient中的方法来完成Solr的操作. 需要的包如下: 1. apache-solr-solrj-3.5.0.jar 2. commons-httpclient-3.1.jar 3.slf4j-api-1.6.0.jar 4.commons-logging-1.1.jar 在

建索引时优化的观察和思考

同事调整了IndexWriterConfig的maxThreadStates参数,发现性能有很大提升,原来之前一直没去注意这个东西. addDocument时默认会调用ThreadAffinityDocumentsWriterThreadPool来获取线程锁,而这个线程池默认是8个线程,如果同时addDocument的线程多于8个,则线程处在等待锁的状态(一般是等最小竞争的>锁),所以本质上要在indexwriterconfig中增加最大索引线程数. Lucene中还存在一个FlushStall

Mysql索引分析:适合建索引?不适合建索引?【转】

数据库建立索引常用的规则如下: 1.表的主键.外键必须有索引: 2.数据量超过300的表应该有索引: 3.经常与其他表进行连接的表,在连接字段上应该建立索引: 4.经常出现在Where子句中的字段,特别是大表的字段,应该建立索引: 5.索引应该建在选择性高的字段上: 6.索引应该建在小字段上,对于大的文本字段甚至超长字段,不要建索引: 7.复合索引的建立需要进行仔细分析:尽量考虑用单字段索引代替: A.正确选择复合索引中的主列字段,一般是选择性较好的字段: B.复合索引的几个字段是否经常同时以A