为什么需要索引?
数据库中的索引,就好比一本书的目录,可以快速进行特定值的定位与查找,从而加快数据查询的效率。
索引是帮助数据库管理系统高效获取数据的数据结构。
索引有哪些需求?
功能性需求:
-
格式化数据 or 非格式化数据
要构建索引的原始数据,可以分为两类,一类是结构化数据,比如MySQL 中的数据;另一类是非结构化数据,比如搜索引擎中网页。对于非结构化数据,一般需要做预处理,提取出查询关键词,对关键词构建索引。
-
静态数据 or 动态数据
如果原始数据是一组静态数据,在构建索引的时候,只需要考虑查询效率就可以了。不过大部分情况下,都是对动态数据构建索引,不仅要考虑到索引的查询效率,在原始数据更新的同时,还需要动态地更新索引。
-
内存 or 磁盘
如果索引存储在内存中,那查询的速度肯定要比存储在磁盘中的高。但在原始数据量很大的情况下,对应的索引可能也会很大,只能将索引存储在磁盘中了。
实际上,还有第三种情况,那就是一部分存储在内存,一部分存储在磁盘,这样就可以兼顾内存消耗和查询效率。
-
单值查找 or 区间查找
散列表和平衡查找二叉树(红黑树)不能支持按照区间快速查找数据
跳表支持按照区间快速查找数据
-
单关键词查找 or 多关键字组合查找
像 MySQL 这种结构化数据的查询需求,可以实现针对多个关键词的组合,建立索引;对于像搜索引擎这样的非结构数据的查询需求,可以针对单个关键词构建索引,然后通过集合操作,比如求并集、求交集等,计算出多个关键词组合的查询结果。
非功能性需求:
-
存储空间
如果存储在内存中,索引对占用存储空间的限制就会非常苛刻。
如果存储在硬盘中,那索引对占用存储空间的限制,稍微会放宽一些。但也不能掉以轻心,有时候索引对存储空间的消耗会超过原始数据。
-
维护成本
基于动态数据集合构建的索引,在原始数据动态增删改的同时,还需要动态地更新索引。而索引的更新势必会影响到增删改操作的性能。
构建索引常用的数据结构有哪些?
内存索引,操作数据的时间复杂度低:
-
有序数组
如果数据是静态的(不会有插入、删除、更新操作),可以把数据的关键词(可供查询使用)抽取出来,组织成有序数组,然后利用二分查找算法来快速查找数据。
-
跳表
跳表通过灵活调整索引结点个数和数据个数之间的比例,可以很好地平衡索引对内存的消耗及其查询效率。
Redis 中的有序集合,就是用跳表来构建的。
-
散列表
Redis 和 Memcache中,就是使用散列表来构建索引的。
-
红黑树
Ext 文件系统中,对磁盘块的索引,用的就是红黑树。
Ext文件系统是Linux主流的文件系统。
磁盘索引,尽量减少磁盘的I/O操作:
-
B+树
大部分关系型数据库的索引,比如 MySQL、Oracle,都是用 B+ 树来实现的。
-
LSM树
写多读少的非关系型数据库,比如Hbase、RocksDB。
辅助索引:
-
位图
-
布隆过滤器
可以针对数据,构建一个布隆过滤器,并且存储在内存中。当要查询数据的时候,我们可以先通过布隆过滤器,判定是否存在。如果通过布隆过滤器判定数据不存在,那我们就没有必要读取磁盘中的索引了。对于数据不存在的情况,数据查询就更加快速了。
什么情况下不适合创建索引?
索引的价值是帮我们从海量数据中找到想要的数据,如果数据量少,那么是否使用索引对结果的影响并不大。
-
-
当数据重复度大,比如高于 10% 的时候,也不需要对这个字段使用索引(例如性别)。
索引有哪些种类?
功能逻辑:
-
普通索引
基础的索引,没有任何约束,主要用于提高查询效率
-
唯一索引
普通索引的基础上增加了数据唯一性的约束,在一张数据表里可以有多个唯一索引
-
主键索引
在唯一索引的基础上增加了不为空的约束,在一张表里最多只有一个主键索引
物理实现方式:
-
聚集索引
聚集索引的叶子节点存储了数据记录,表中数据记录按索引的排序方式进行存储。每一个表只能有一个聚集索引,因为数据行本身只能按一个顺序存储。
读多写少情况下查询效率高。
-
非聚集索引
非聚集索引的叶子节点存储了数据位置,不影响数据表的物理存储顺序。每一个表可以有多个非聚集索引,也就是多个索引目录提供数据检索。
写多读少情况下插入删除效率高。
字段个数:
-
单一索引
索引列为一列时为单一索引
-
联合索引
多个列组合在一起创建的索引为联合索引,联合索引存在最左匹配原则,按照最左优先的方式进行索引的匹配。
参考:
https://time.geekbang.org/column/article/112023
https://time.geekbang.org/column/article/78449
浙公网安备 33010602011771号