索引千万条,性能第一条:深入浅出数据库索引原理
索引虐我千百遍,我待索引如初恋
当有人问到索引是什么时,大家都喜欢用“书的目录”来做类比,没有索引的数据库,就像是没有目录的书,想找第3章的第7小节,就要一页一页翻过去,最可怕的是翻到了还要把这本书翻完才算完;那反之有索引的数据库,就是有目录的书了,直接按目录找到就可以了。
书放个目录,人来翻找,那数据库是怎么“翻找”呢?“目录”又是怎么放的?
先来看看“目录”是怎么放的。
数据库在安装完成之后,安装程序会自动创建master、model、tempdb等几个特殊的”系统数据库“,其中master是数据库的主数据库,用于保存和管理其它系统数据库、用户数据库以及数据库的系统信息。
master中有一个名为sysindexes的系统表,专门管理索引。数据库查询数据表的操作都必须用到它,毫无疑义,它是本文主角之一。
PS:查看一张表的索引属性,可以在查询分析器中使用以下命令:select * from sysindexes where id=object_id('tablename') 。参数tablename为被索引的表名。
所以,我们现在有了一个认知:索引是专门放在一张表里的,且放的位置和其他数据表不一样。
那数据库”翻找“就是先去找索引再去找数据不就行了?没那么简单。
先来介绍两个概念:聚集索引、非聚集索引
- 聚集索引,是索引表的顺序和对应数据表顺序相同,如索引表按照ID这个字段排序,则对应数据表也一致。
- 非聚集索引,是索引表顺序和对应数据表不一致,它指定的是逻辑顺序,那数据表呢,就是按照插入顺序排序的。
根据两者的特性,也很容易总结出他们的优缺点,即聚集索引查找速度更快,缺点则是对表进行修改速度较慢,因为要保持数据顺序一致,非聚集索引则反之。他们的适用场景如下:
接下来介绍一下平衡树
再回归到“目录”,在我们知道知道第3章第7节在236页时,你可能会随便翻,但更科学的方法的:先翻到书大概二分之一的地方,再在二分之一的书里找下一个二分之一,以此类推,直到找到正确的页数,熟悉算法的同学会发现,这是常见的”二分法“,微软在官方教程MOC里另有一种说法:叫B树(Balance Tree),即平衡树。
索引的实现就使用了B+树的数据结构,B+树内把真实的数据又放在了叶子节点中,非叶子节点中只存放了索引的数据,保证了数据项尽可能的多。
(B和B+树的区别在于,B+树的非叶子结点只包含导航信息,不包含实际的值,所有的叶子结点和相连的节点使用链表相连,便于区间查找和遍历。)
在对这两个概念有认知的基础下,我们来聊聊 数据库是怎么“翻找”的。
通过一个非聚集索引的访问案例来阐述。
假定在 name 这一参数上建立了非聚集索引,则执行如下语句时,查询过程是:
Select * From Member Where name='张三'
- 数据库查询INDID值为2,意味着表中存在非聚集索引页;
- 立即从根出发,在非叶级节点中定位最接近”张三“的值“李四”,并查到其位于叶级页面的第61页;
- 仅在叶级页面的第61页的”李四“下搜寻”张三“的RID,其RID显示为N∶706∶4,表示name字段中名为”张三“的记录位于堆的第707页的第4行
- 根据上述信息,数据库立马在堆的第 707页第4行将该记录“揪”出来并显示于前台(客户端)。视表的数据量大小,整个查询过程费时从百分之几毫秒到数毫秒不等。
最后我们再来聊聊索引的优缺点吧
索引有一些先天不足:
- 建立索引,系统要占用大约为表的1.2倍的硬盘和内存空间来保存索引。
- 更新数据的时候,系统必须要有额外的时间来同时对索引进行更新,以维持数据和索引的一致性——这就如同图书馆要有专门的位置来摆放索引柜,并且每当库存图书发生变化时都需要有人将索引卡片重整以保持索引与库存的一致。
当然建立索引的优点也是显而易见的:在海量数据的情况下,如果合理的建立了索引,则会大大加强数据库执行查询、对结果进行排序、分组的操作效率。
实践表明,不恰当的索引不但于事无补,反而会降低系统性能。因为大量的索引在进行插入、修改和删除操作时比没有索引花费更多的系统时间。比如在如下字段建立索引应该是不恰当的:1、很少或从不引用的字段;2、逻辑型的字段,如男或女(是或否)等。
综上所述,提高查询效率是以消耗一定的系统资源为代价的,索引不能盲目的建立,必须要有统筹的规划,一定要在“加快查询速度”与“降低修改速度”之间做好平衡,有得必有失,此消则彼长。这是考验一个DBA是否优秀的很重要的指标。