今天面试问到一个东西——索引,我懵逼了,只知道这东西可以进行sql优化,但是根本不知道其中的原理。。。
原文:(https://www.cnblogs.com/hyd1213126/p/5828937.html)
实际上,索引可以理解成一种特殊的目录。微软的SQL Server提供了两种索引:聚集索引(也可称为聚类索引、簇集索引)和非聚集索引(当然也可以称为非聚类索引、非簇集索引)。下面,我们举例来说明一下聚集索引和非聚集索引的区别:
其实,我们的汉语字典正文本身就是一个聚集索引,我们要查“暗”字,就会很自然地翻开字典的前几页,因为对于“暗”的拼音来说“an”,而对于字典的排列顺序来说是按照拼音的“a-z”顺序排列的,那么“暗”这个字很明显就是从字典的前面部分了。如果你翻完了字典的“a”部分,还没有找到“暗”字,那么可以确定的认为字典中“暗”字不存在;那么再次举例来说,我们查找“炸”字,根据拼音来查询“zha”,直接从字典后半部分查找,如果找遍了“z”拼音部分,还是没有找到,那么就可以认为“炸”字不存在于词典中。所以可以认为,在字典的正文就是一种目录,我们可以不通过去查找其他目录来找到我们所需要查找的内容。所以对于此:正文内容,我们把其按照一定规则排序的目录称之为“聚集索引”。
那么如果说:我们知道某个字的写法,但是我们不知道它是怎么读了?这个时候我们就可以通过偏旁部首去进行一个查找了,然后直接通过页码直接找到我们要查找的字。但是对于通过“部首目录”和“检字表”而查询到的字的排序并不是正文排序的方式,比如说查询“张”字,可以知道“张”字的页码是672页,而在“检字表”中“张”字上面的“驰”字页码是63页,“张”字下面的“弩”字页码是390页。丝毫不连续。很显然,它们并不是真的存在于“张”字的上下方,现在您看到的连续的“驰、张、弩”三个字实际上就他们在非聚集索引中的排序方式,是字典证明中的字在非聚集索引中的映射。我们可以通过这种方式来找到我们所需要的字,但是它需要两个过程,先找到目录中的结果,然后再翻到我们所需要的页面。我们把这种目录纯粹称之为目录,正文纯粹是正文的排序方式称为“非聚集索引”。
通过一个字典的查询方式,可以简单的理解一下什么叫非聚集索引和聚集索引了。那么我们就可以很容易的理解每张表只能选择非聚集索引或聚集索引其一。
我们可以通过第一小节中的聚集索引和非聚集索引的介绍来理解记忆上表。比如:返回某一范围内的数据中的一项数据。比如:我们的一个表中有一个时间字段,恰好我们把聚合索引建立在了该字段上,这个时候我们查询2019年1月1日到2019年12月30日之间的全部数据时,这个速度将会是很快,因为我们的这个字典正文是通过日期来进行排序的,聚类索引只需要找到要检索的所有数据中的开头和结尾数据就行;如果我们使用非聚集索引,那么还要查到2019年1月2号对应的页码,然后再根据页码来查询数据,那将是很耗时的。
今天面试笔者就是犯了严重的错误,面试官咋眼看着我,我很无奈。通常来说,我们会在每个表中建立一个id字段,用来区分每一条数据,而且通常来说会使用id自增长配置,每次自增为1。在前面的讲解中,可以了解到聚集索引最大的好处就是建立查询数据的一个区间,降低查询数据的总条数,避免全表查询。在实际应用中,因为id是自动生成的,程序员并不知道每条记录所对应的id,所以我们很难在实际应用中进行一个id号作为查询条件。这也让id号作为聚集索引成为了一种资源浪费。其次,让每个id号都有不同的字段作为聚集索引页不符合“在大范围数量个不同的值作为非聚集索引”规则。所以说,这种错误操作是针对于用户经常修改记录内容,特别是索引项的时候还会起到负作用,但是对于查询速度并没有多大的影响。
对于erp系统中来说,不管是系统中用户所需要接收的文件、会议还是用户进行文件查询等任何情况下进行数据查询都离不开字段的是“日期”还有用户本身的“用户名”。
一般来说,对于erp系统的首页都会显示每个用户为签收的文件或者会议。虽然我们的where语句可以仅仅限制当前用户尚未签收的条件,但是如果您的系统已经建立了很长时间,并且数据量庞大,那么每个用户打开首页的时候都进行了一次全表扫描,这样做的意义不是很大,而且对于1月之前的文件信息,用户已经进行处理完成了。如果说还进行一个多余数据查询,只是增加了数据库的开销而已。所以说,我们完全可以让用户打开系统首页的时候,数据库仅仅查询了这个用户近3个月的未处理的文件就可以了。通过“时间日期”这个字段来限制表的扫描条数,提高查询速度。如果说我们所使用的办公自动化系统,已经存在了2年的时间,那么从理论上来讲,查询速度将是原来的8倍之上。
当然如果说不从应用场景来使用聚集索引和非聚集索引的使用字段,那么查询速度还是得不到提升的,即使在“时间日期”这个字段上建立的非聚集索引。那么索引还是得不到提升的。所以,归根揭底就是一句话:尽量避免在id自增长上建立索引。
2.【只要建立索引就可以提高查询速度】:
在实际应用中,我们每天都会产生一些数据(500条),那么这些数据的发送日期都是相同的,总共数据有5000万条,那么这就符合“既不能绝大多数相同的数据,有不能只是极少的相同数据”的规则,所以,对于聚合索引,适当创建是由提高查询速度的。
3.【把所有需要提高查询速度的字段都加进聚集索引,用于提高查询效率】
对于上面已经说到的数据查询需要使用到索引字段“日期”还有用户本身的“用户名”。既然这两个字段都是如此的重要,那么我们可以把他们合并起来,建立复合索引。
对于只使用聚集索引的起始字段作为查询条件和使用到复合聚集索引的全部字段的查询速度几乎一样,甚至比使用上全部的复合索引字段还要略快(在查询结果集数目一样的情况下);而如果仅仅使用了复合聚集索引的非起始字段(只使用用户名条件语句)作为查询条件的话,这个索引是起不到任何作用的。对于使用单纯的聚合索引和使用复合索引执行速度一样是因为查询的数目一样。如果复合索引将所有的字段都用上,而且查询的结果集少的话,这样就会形成“索引覆盖”,因而性能可以达到最优。同时,请记住:无论我们是否经常使用到聚合索引的其他字段,但其前导字段(第一个条件字段)一定要是使用最频繁的字段。
1.使用聚集索引比用不上聚集索引的主键速度要快。
2.使用聚集索引比用一般的主键作为order by时的执行速度要快,特别是在小数据量的情况下,(如果是数据量大,效果不上很明显)
3.使用聚集索引内的时间段,搜索时间会按该时间段的数据占整个数据表的百分比成比例减少(聚集索引时间段内的数据越少,搜索时间越快,效率越高),而且无论聚集索引使用了多少个。
4.日期字段不会因为有分秒的详细而减慢查询速度
关于索引的使用问题,如果使用不当,不仅不会提高检索效率,如果过多或不当的索引配置,会导致系统效率低下。因为用户在每张表汇总加入一个索引,那么数据库就要做更多的工作。过多的索引甚至会导致索引碎片效应。
所以说,对于索引的使用问题,应当注意聚合索引的创建。
1.select * from table1 where name = “wangji” and tid > 10000
2.select * from table1 where tid > 10000 and name = “wangji”
对于上面两条语句,分析感觉是由区别的。因为对于第一条,先查询出name为wangji的数据,然后再从这批数据查询出tid大于10000;而第二条,先查询出tid大于10000,然后再从这批数据查询出name为wangji的。
其实对于这种分析是错误的。因为对于数据库来说,判断sql语句的时候,会判断出where中查询的条件那个索引能够缩小表的扫描条数。也就是自动化判断优先级。
对于这种自动化操作的原理:在查询语句分析阶段,查询优化器查看的每个阶段并决定限制需要扫描的数据量是否有用。如果一个阶段可以被用作一个扫描参数(SARG),那么就可以认定为该sql是可以进行一个优化的。并且可以使用索引进行优化。
扫描参数:用于限制搜索的一个操作·,因为它通常是指定一个特定的匹配,一个值得范围内的匹配或者两个以上的条件的AND连接。例如:
如果说一个表达式不能满足扫描参数的格式,那么就无法实现限制范围搜索,那么就变成了全表判断是否满足。所以一个索引对于不满足参数扫描的格式是没有作用的。
那么说对于sql的编写,哪些会造成全表搜索呢?(以下错误示范)
Name like “%际”
Abs < 5000