数据库 | 索引
目录
概念
作用
适用范围
使用
1.查看索引
2.创建索引
3.删除索引
⭐索引背后的数据结构【经典面试题】
概念
索引是一种特殊的文件,包含着对数据表里所有记录的引用指针。可以对表中的一列或多列创建索引,并指定索引的类型,各类索引有各自的数据结构实现。
类似于目录。打个比方,数据库表就像是图书架上的书,数据就是书籍里面的内容,而索引就是书籍的目录。
作用
特点:
适用于查询频繁,增删较少的场景。
可以快速定位,检索数据;提高数据库性能。
缺点:
引入索引,能够提高查询速度,但目录本身占据存储空间;能提高查询速度,但可能会拖慢增删改的速度(不仅要修改数据,还要调整索引)。
适用范围
对数据库表的某列或某几列创建索引,需要考虑几点:
a数据量较大,且经常对这些列进行条件查询;
b该数据库表的插入操作,及对这些列的修改操作频率较低;
c索引会占用额外的磁盘空间。
(满足以上条件时可以考虑对表中的这些字段创建索引,以提高查询效率。
使用
创建主键约束(PRIMARY KEY),唯一约束(UNIQUE),外键约束(FOREIGN KEY)时,会自动创建对应列的索引。
主要有以下三种使用方式:
1)查看索引:show index from 表名;
2)创建索引:create index 索引名 on 表名(列名);
3)删除索引:drop index 索引名 on 表名;
1.查看索引
show index from 表名;
2.创建索引
create index 索引名 on 表名(列名);
需要注意的是:
创建索引的过程中,表数据少影响不大;但如果表很大,此时创建索引就会引起大量的cpu/硬盘IO的消耗,也是可能会把数据库搞挂了的。
创建索引会创建出一些相关的数据结构。(索引的实现通常使用B树及其变种B+树)
对于非(主键,唯一约束,外键)的字段,可以创建普通索引。
3.删除索引
drop index 索引名 on 表名;
删除索引和创建索引类似,都是比较低效的操作(对于生产环境上比较大的表,一般都是建表初始就把索引规划好的)。删除索引也是比较危险的操作,大量的IO操作也会使得数据库服务器挂掉。
⭐索引背后的数据结构【经典面试题】
索引的内部结构是怎样的?涉及到底层的数据结构(构建B+树)。
索引,引入了额外的一些数据结构(B树及其变种B+树),来增加查询的速度。默认情况下,进行条件查询操作就是在遍历表,一条一条数据都代入条件。引入索引,就是要通过其他的数据结构加快查询的速度,减少遍历表的可能性。
为什么选择B+树?
(一)首先,谈谈ArrayList和LinkedList的区别:
- ArrayList底层是顺序表,LinkedList底层是链表;
- ArrayList查找较快,LinkedList增删较快(这是错误的❌)
ArrayList查找并不快,只是具备随机访问能力。(随机访问 是通过下标获取元素;查找 是根据值来获取元素位置indexOf。而ArrayList查找的时候是要遍历的,时间复杂度为O(N)并没有优势)
LinkedList增删也不快,首位增删是复杂度是O(1),但是中间位置进行增删还是O(N),增加add(index,value)要遍历找index位置,所以还是O(N)。
这里的中间位置插入删除之所以是O(N),不是链表不行而是LinkedList不行。
(二)哈希表和二叉搜索树都不太适合左数据库的索引:
- 哈希表增删查改都快,但是只能查询值相等的情况。对于“>,<,between...and..”这类比较大小的范围查询是不太适合的。
- 二叉搜索树最坏查询速度是O(N),像AVL树/红黑树这些比较平衡的二叉搜索树是O(LogN)。如果数据库数据特别多,则二叉搜索树高度O(LogN)就会比较高,这又和查询次数相关。
如果比较实在内存中进行的,那么多比较几次少比较都影响不大;但如果是每次都要从硬盘上读取,这个就可能会产生影响:IO次数越少越好。
因此,结合上述B+树更适合用来做索引。
先认识B树:
(1)B树是一个N叉搜索树,每个节点上可能会包含N-1个值,当然也可能更少;N-1个值就把区间划分成N份;这样,在同样的数据集合下,N叉树就会比二叉树高度小很多,IO次数也降低了。
(2)B树代码的实现是非常复杂的,复杂主要是在 分裂和合并 上。
B树和B+树的区别:
- B树每个节点N-1个值,就区分了N个区间;B+树N个值就分出N个区间。
- B树中的值不会重复出现,B+树是可能重复出现的(况且在B+树中,父元素的值会在子元素中以最大值/最小值的姿态出现)。
- 在叶子节点这里,B+树会把所有的叶子节点以链表的形式首位相连,此时会非常便于范围查找。(叶子节点这里已经是完整的数据集合了,前面的非叶子节点都会在这里体现出来,这也就是元素重复的意义)
- 正因为叶子节点是全集数据,只需要把每一行(每一条记录的完整的所有列 关联到叶子节点上即可),非叶子节点只需要保存索引列(其实就是一个id)。
所以非叶子节点占用的空间相比于完整的数据集合非常小,就可以在内存中缓存;因此此时的查询又进一步减少了硬盘IO。