关于数据库选择索引
什么是索引? 类比我们要查询一本很厚的书时,怎么快速定位到需要的部分(或者快速缩小查询范围),索引就类似于书中的目录。业务的抽象落地就是数据是怎么存储【数据结构】,怎么根据存储的数据结构获取想要的数据【算法】。可能(反正我是)刚开始理解索引的时候是从数据库索引开始的,其实只要像Mysql数据库内部那样可以通过B+树快速定位、查询想要的数据就是索引。Redis中不论使用哪种数据节点,都需要使用key快速定位,全局的索引就是散列表的数据结构。
如果我们想自己设计一个业务系统的索引时?怎么选择底层的数据结构,可能两眼一抹黑。其实可以基于业务梳理,业务特点,想干什么,需要提供什么功能或者方法。 从候选的数据结构中进行赛选,能够做索引的数据结构本身其实并不多,可供选择的有:散列表、有序数组、红黑树、跳表、B+树(B树)、位图(布隆过滤器)等。
个人理解:需求梳理 -> 功能(需要支持的方法)+ 性能 -> 算法 + 数据结构
比如:Mysql为什么选择B+树? 再比如:Redis中的有序集合为什么选择跳表?(PS: 有序集合中所有的数据都小于24字节,并且集合元素个数小于128个时,为了平衡时间空间复杂度,折中使用了压缩列表)
1、梳理需求,Redis需要支持5种数据类型,提供数据的多样性选择。有序集合(Sorted Sets)就是其中一种数据类型。
2、上面的需求,要实现有序数组功能,再梳理一下需要支持的方法
1)、新增、删除一个数据; 查询数据一个数据;
2)、按照区间查找数据,比如查询 [12-45]的数据(与mysql 的Between and一样);
3)、迭代输出有序序列。
3、基于上面的方法和一些限制(比如mysql是单线程,io型业务等,再进行权衡)
1)、数据结构本身读写性能要高,散列表(读写时间复杂度近似O(1))、有序数组只适用于静态数据。 读写时间复杂度都是O(logN)的红黑树、跳表、B+树都可行。但是数据是存储在内存中的,B+树比较适合的是外部存储(磁盘)。
2)、支持区间查询,散列表、红黑树都不适合去检查。 B+树适合区间查询,但是上面已经pass掉了。 到现在其实剩下的就是跳表了。
梳理完Mysql选择B+树的过程,Redis的有序集合选择跳表的过程。梳理需求功能点和要支持的方法、可供选择索引的数据结构的特点,是选择索引的关键,下面进行专门梳理,让自己选择索引、数据结构时不那么茫然。
1、需求梳理
1)、功能性需求
数据是结构化还是非结构化
结构化数据:可以类似表一样存储到Mysql等关系型数据库中的数据;数据库底层使用的是B树、B+树等。
非结构化数据:比如爬虫抓的html页面,比如几篇文章。这样的数据一般需要进行预处理,比如存储到Es中需要预先使用 ik等分词器进行分词,倒排索引。
数据是静态还是动态的
静态数据:不考虑新增删除的性能,有序数组就可以考虑;散列表等依赖数组实现的树结构,本身动态扩容时对性能本身是有影响的,当然也可以使用Redis那样的 渐进式rehash。
动态数据:需要数据结构本身新增、删除的时维护数据特点的代价不能太高。
索引是存储在内存还是磁盘中,还是一部分在磁盘一部分在内存
如果数据存储在磁盘中,我们就要考虑,内存计算寻址的速度是纳秒级别的,而磁盘寻址的效率是毫秒级别的,差了上万到几十万倍。开率该性能可能已经远超数据结构本身。B+树就是为了考虑数据库存储的数据将大量落在磁盘上,为了降低树的高度,多路平衡查找树,Mysql的B+树默认1200路,将树基本控制在4层内。
单只查询,还是区间查询
基本待选择作为索引的数据结构,单值查询效率都是非常高的。只是需要支持快速区间查询,基本就只有 跳表和B+树(B树)了。
一个关键词查询还是组合(mysql的and、or等查询数据的并集、交集)查询
可能需要对数据本身预先做处理等
2)、非功能性需求
其他非功能性需求,还需要考虑数据结构本身的空间复杂度不能过高,或高到不能接受;索引维护的成本问题,主要就是新增、删除之后,还需要维持原数据结构的特点。
2、可供索引的数据结构特点梳理
可供索引选择的数据结构一般时间复杂度都比较低,或者至少查询的时间复杂度比较低: 散列表、跳表、红黑树、B+树(B树)、有序数组,位图和布隆过滤器可以辅助。
散列表 新增、删除、查询的时间复杂度都接近O(1), 但是不支持区间查询。新增、删除操作时,需要考虑某一次进行动态扩容(缩容)的耗时较高,不能接受的话需要像Redis一样同时维护两个数组,渐进式rehash。 Redis、Mencache等 Key Value内存数据库会使用散列表做索引。
红黑树 新增、删除、查询的时间复杂度为O(logN),也不适合区间查询。非常适合用于内存中构建索引。 Ext文件系统中,使用红黑树对磁盘块进行索引。
B+树 新增、删除、查询的时间复杂度为O(logN),支持区间查询、比较适合在磁盘中构建索引。 Mysql中使用B+树,Oracle、MongoDB总使用B树构建索引。
跳表 新增、删除、查询的时间复杂度为O(logN),支持区间查询,比较适合在内存中构建索引,空间复杂度可以控制在一定范围内。 Redis的有序集合使用跳表构建索引。
有序数组 利用二分查询的查询的时间为O(logN),支持区间查询。如果是一次构建多次查询,或者基本就是静态数据,可以选择有序数组作为索引。
布隆过滤器 在大数据量比较大的情况下,判断数据是否存在可以使用布隆过滤器, 判断其可能存在的数据,大概率是存在(小概率误判不存在),判断不存在的数据一点不存在。数据量大时内存占用率非常低。