面试面试常问之数据库索引
Posted 黑黑白白君
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面试面试常问之数据库索引相关的知识,希望对你有一定的参考价值。
1)什么是索引?
索引在mysql中也叫做键(key),是存储引擎用于快速找到记录的一种数据结构。
- 索引是对数据库表中一个或多个列的值进行排序的结构。关键点是索引包含一个表中列的值,并且这些值存储在一个数据结构中。
- 例如对employee 表的姓名 (name) 列进行排序,如果想按特定职员的姓来查找ta,则与在表中搜索所有的行相比,索引有助于更快地获取信息。
例如这样一个查询:select * from table1 where id=10000。
- 如果没有索引,必须遍历整个表,直到ID等于10000的这一行被找到为止。
- 有了索引之后(必须是在ID这一列上建立的索引),即可在索引中查找。
- 由于索引是经过某种算法优化过的,因而查找次数要少得多。可见,索引是用来定位的。
1.0 为什么需要索引?
- 一个没加主键的表,它的数据无序的放置在磁盘存储器上,一行一行的排列的很整齐, 跟通常意义上的「表」很接近。
- 如果给表上了主键,那么表在磁盘上的存储结构就由整齐排列的结构转变成了树状结构(「平衡树」结构),换句话说,就是整个表就变成了一个索引。
下面是带有主键的表(聚集索引)的结构图:
- 其中树的所有结点(底部除外)的数据都是由主键字段中的数据构成,也就是通常我们指定主键的id字段。
- 最下面部分是真正表中的数据。
假如我们执行一个SQL语句:select * from table where id = 1256;
- 首先根据索引定位到1256这个值所在的叶结点,然后再通过叶结点取到id等于1256的数据行。
- 树一共有三层, 从根节点至叶节点只需要经过三次查找就能得到结果。
假如一张表有一亿条数据 ,需要查找其中某一条数据:
- 按照常规逻辑, 一条一条的去匹配的话, 最坏的情况下需要匹配一亿次才能得到结果,用大O标记法就是O(n),最坏时间复杂度。
- 这是无法接受的,而且这一亿条数据显然不能一次性读入内存供程序使用, 因此, 这一亿次匹配在不经缓存优化的情况下就是一亿次IO开销。
- 如果把这张表转换成平衡树结构(一棵非常茂盛和节点非常多的树), 则查找速度以指数级别提升,用大O标记法就是O(log n),n是记录总数,底数是树的分叉数,结果就是树的层次数。
- 因此,利用索引会使数据库查询有惊人的性能提升。
1.1 特点
- 索引可以加快数据库的检索速度
- 索引降低了数据库插入、修改、删除等维护任务的速度
- 索引创建在表上,不能创建在视图上
- 索引既可以直接创建,也可以间接创建
- 索引可以覆盖多个数据列,如像INDEX(columnA, columnB)索引,这就是联合索引
- 可以在优化隐藏中,使用索引
- 使用查询处理器执行SQL语句,在一个表上,一次只能使用一个索引
1.2 优缺点
-
优点:
-
通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。
-
可以大大加快数据的检索速度,这也是创建索引的最主要的原因。
-
可以加速表和表之间的连接,特别是在实现数据的参考完整性方面特别有意义。
-
在使用分组和排序子句进行数据检索时,同样可以显著减少查询中分组和排序的时间。
-
通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能。
-
-
缺点:
-
增加了数据库的存储空间。
- 索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间。
- 如果要建立聚簇索引,那么需要的空间就会更大。
-
创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加。
-
在插入和修改数据时要花费较多的时间(因为索引也要随之变动)。
-
1.3 索引的分类
SQL Sever索引类型有:唯一索引,主键索引,聚集索引,非聚集索引。
MySQL 索引类型有:唯一索引,主键(聚集)索引,非聚集索引,全文索引。
功能类型:
-
普通索引(INDEX):
普通索引允许被索引的数据列包含重复的值。
CREATE INDEX index_name ON table_name (column_list);
-
唯一索引(UNIQUE INDEX):
mysql数据库索引列的值必须唯一,但允许有空置(null)。如果是组合索引,列值的组合必须唯一。
CREATE UNIQUE INDEX index_name ON table_name (column_list);
-
主键索引(PRIMARY KEY):
是一种特殊的唯一索引,不允许有空置(null),一般在建表时创建。
CREATE TABLE table_name ( ID INT NOT NULL, [column] VARCHAR(16) NOT NULL, PRIMARY KEY(ID) );
- 数据库表经常有一列或多列组合,其值唯一标识表中的每一行,该列称为表的主键。每个表只能有一个主键(PRIMARY KEY)。
- 在数据库关系图中为表定义主键,将自动创建主键索引。
- 该索引要求主键中的每个值都唯一。
-
全文索引(FULLTEXT):
只可以用在MyISAM引擎,对大数据文本进行索引,在建立的索引中对要查找的单词进行搜索,定位哪些文本数据包括要搜索的单词。
1,建立索引 2,在索引中搜索定位 //针对content做了全文索引: CREATE TABLE `table` ( `id` int(11) NOT NULL AUTO_INCREMENT , `title` char(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL , `content` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL , PRIMARY KEY (`id`), FULLTEXT (content) ); //查找时: SELECT * FROM article WHERE MATCH( content) AGAINST('想查询的字符串')
*主键与唯一索引的区别
- 主键是一种约束,唯一索引是一种索引,两者在本质上是不同的。
- 主键创建后一定包含一个唯一性索引,唯一性索引并不一定就是主键。
- 唯一性索引列允许空值,而主键列不允许为空值。 主键列在创建时,已经默认为空值 + 唯一索引了。
- 主键可以被其他表引用为外键,而唯一索引不能。
- 一个表最多只能创建一个主键,但可以创建多个唯一索引。
- 主键更适合那些不容易更改的唯一标识,如自动递增列、身份证号等。 在 RBO 模式下,主键的执行计划优先级要高于唯一索引。
- 两者可以提高查询的速度。
-
聚集索引(CLUSTERED):
也叫聚簇索引,数据行的物理顺序与键值的逻辑(索引)顺序相同,即表数据按照索引的顺序来存储的。- 在一张表上只能创建一个聚集索引,因为真实数据的物理顺序只可能是一种。
- 对于聚集索引,叶子结点即存储了真实的数据行,不再有另外单独的数据页。
- MySQL的MyISAM除外,此存储引擎的聚集索引和非聚集索引只多了个唯一约束,其他没什么区别。
- 与非聚集索引相比,聚集索引通常提供更快的数据访问速度。
-
非聚集索引(NONCLUSTERED):
该索引中索引的逻辑顺序与磁盘上行的物理存储顺序不同,一个表中可以拥有多个非聚集索引。- 其实按照定义,除了聚集索引以外的索引都是非聚集索引,只是人们想细分一下非聚集索引,分成普通索引,唯一索引,全文索引。
- 非聚集索引叶节点仍然是索引节点,只是有一个指针指向对应的数据块。
- 此如果使用非聚集索引查询,而查询列中包含了其他该索引没有覆盖的列,那么还要进行第二次的查询,查询节点上对应的数据行的数据。
*如何解决非聚集索引的二次查询问题:
复合索引(覆盖索引):建立两列以上的索引,即可查询复合索引里的列的数据而不需要进行回表二次查询。
- 如index(col1, col2),执行语句:select col1, col2 from t1 where col1 = ‘213’;
- 要注意使用复合索引需要满足最左侧索引的原则,也就是查询的时候如果where条件里面没有最左边的一到多列,索引就不会起作用。
- 在SQL Server中还有include的用法,可以把非聚集索引里包含的列包含进来,而不一定需要建立复合索引。
*聚集索引和非聚集索引的区别
- 聚集索引一个表只能有一个,而非聚集索引一个表可以存在多个。
- 聚集索引存储记录是物理上连续存在,而非聚集索引是逻辑上的连续,物理存储并不连续。
- 聚集索引中物理存储按照索引排序,非聚集索引中物理存储不按照索引排序。
- 聚簇索引的索引叶节点就是数据节点,而非聚簇索引的叶节点仍然是索引节点,只不过有一个指针指向对应的数据块。
*何时使用聚集索引或非聚集索引
其他:
1、直接创建索引和间接创建索引
- 直接创建索引:
CREATE INDEX mycolumn_index ON mytable (myclumn)
- 间接创建索引:定义主键约束或者唯一性键约束,可以间接创建索引。
2、普通索引和唯一性索引
- 普通索引:
CREATE INDEX mycolumn_index ON mytable (myclumn)
- 唯一性索引:保证在索引列中的全部数据是唯一的,对聚簇索引和非聚簇索引都可以使用
CREATE UNIQUE COUSTERED INDEX myclumn_cindex ON mytable(mycolumn)
3、单个索引和复合索引
- 单个索引:即非复合索引,一个表可以多个单值索引
- 复合索引:又叫组合索引,在索引建立语句中同时包含多个字段名,最多16个字段,
CREATE INDEX name_index ON username(firstname,lastname)
4、聚簇索引和非聚簇索引
- 聚簇索引:物理索引与基表的物理顺序相同,数据值的顺序总是按照顺序排列
CREATE CLUSTERED INDEX mycolumn_cindex ON mytable(mycolumn) WITH ALLOW_DUP_ROW
(允许有重复记录的聚簇索引) - 非聚簇索引:
CREATE UNCLUSTERED INDEX mycolumn_cindex ON mytable(mycolumn)
1.4 索引的使用
-
创建索引:
- 方式一:
create 索引类型 索引名 on 表(字段)
- 单值:create index dept_index on tb(dept);
- 唯一:create unique index name_index on tb(name);
- 复合索引:create index dept_name_index on tb(dept,name);
- 方式二:
alter table 表名 add 索引类型 索引名(字段)
- 单值:alter table tb add index dept_index(dept);
- 唯一:alter table tb add unique index name_index(name);
- 复合索引:alter table tb add index dept_name_index(dept,name);
- 注意:如果一个字段是primary key,则该字段默认就是主键索引。
- 方式一:
2)索引结构类型
- MyISAM存储引擎的表的数据和索引是自动分开存储的,各自是独立的一个文件。
- InnoDB存储引擎的表的数据和索引是存储在同一个表空间里面,但可以有多个文件组成。
MySQL 中索引的存储类型目前只有两种(BTREE 和 HASH),具体和表的存储引擎相关:
- MyISAM 和 InnoDB 存储引擎都只支持 BTREE 索引。
- MEMORY/HEAP 存储引擎可以支持 HASH和 BTREE 索引。
存储引擎相关可参考《【数据库】数据库之存储引擎》。
2.1 B-Tree索引
B-Tree(Balanced Tree)是最常用的用于索引的数据结构,大多数mysql引擎都支持这种索引,也是oracle默认的索引类型。
- 因为时间复杂度低, 查找、删除、插入操作都可以可以在对数时间内完成。
- 另外一个重要原因存储在B-Tree中的数据是有序的。
Btree索引的数据结构:
特点:
- 树形结构:由根节(root)、分支(branches)、叶(leaves)三级节点组成,其中分支节点可以有多层。
- 多分支结构:与binary tree不相同的是,btree索引中单root/branch可以有多个子节点(超过2个)。
- 双向链表:整个叶子节点部分是一个双向链表。
- 单个数据块中包括多条索引记录。
2.2 哈希索引
哈希索引(hash index)基于哈希表实现,只有精确匹配索引所有列的查询才有效。在mysql中,只有memory引擎显示支持哈希索引。
-
工作原理:
- 对于每一行数据,存储引擎会对所有的索引列计算一个哈希码,不同的键值行计算出的哈希码不一样。
- 哈希索引将所有的哈希码存储在索引中,同时在哈希表中保存指向每个数据行的指针。
- 如果多个列的哈希值相同(哈希冲突),索引会以链表的方式存放多个记录指针到同一个哈希条目中去。
-
优点:
- 索引只存储哈希码及行指针,所以索引的数据结构非常的紧凑,这也让哈希索引查找速度非常快。
- 所以,如果使用哈希索引,对于比较字符串是否相等的查询能够极快的检索出的值。
- 索引只存储哈希码及行指针,所以索引的数据结构非常的紧凑,这也让哈希索引查找速度非常快。
-
缺点:
-
哈希索引只支持等值比较查询,包括=、IN()、<=>,不支持范围查询,如where price > 100。
-
哈希索引只保存哈希码和指针,而不存储字段值,所以不能使用索引中的值来避免读取行。(不过访问内存中的行速度非常快,因为是MEMORY引擎,所以对性能影响并不大)
-
哈希索引数据并不是按照索引值顺序存储的,所以无法用于排序。
-
哈希索引不支持部分索引列查找,因为哈希索引始终是使用索引列的全部内容来计算哈希码。
- 如,在数据列(A,B)上建立哈希索引,如果查询只有数据列A,则无法使用该哈希索引。
-
哈希冲突(不同索引列会用相同的哈希码)会影响查询速度,此时需遍历索引中的行指针,逐行进行比较。
-
如果哈希冲突很多,一些索引维护操作的代价会很高。
- 如果从表中删除一行,需要遍历链表中的每一行,找到并删除对应行的引用,冲突越多,代价越大。
-
*自定义哈希索引
存储引擎不支持哈希索引时,可以建立自定义哈希:
- 如:建表pseudohash:id,url,url_crc;
- 建立触发器:
- insert数据时 set new.url_crc = crc32(new.url);
- Update数据时 set new.url_crc = crc32(new.url);
- 查找:select url , url_crc from pseudohash where url_crc = crc32(“www.baidu.com”) and url = “www.baidu.com”;
- 在where语句中带入hash值和对应列值。
在InnoDB中,某些索引值被使用的非常频繁的时候,它会在内存中基于B+Tree的基础上再创建一个哈希索引,使其不必要在从根节点进行查找。
- 完全自动的内部行为,用户无法配置或更改。
2.3 其他类型的索引
- 使用R-Tree作为数据结构的索引通常用来为空间问题提供帮助。
- 例如,一个查询要求“查询出所有距离我两公里之内的星巴克”,如果数据库表使用R- Tree索引,这类查询的效率将会提高。
- 另一种索引是位图索引(bitmap index), 这类索引适合放在包含布尔值(true 和 false)的列上,但是这些值(表示true或false的值)的许多实例-基本上都是选择性(selectivity)低的列。
3)索引的优化
3.1 前缀索引
- 问题:有时候索引需要存储很长的字符串,会让索引变得大且慢。
- 解决方法:
- 1、hash索引
- 2、前缀索引
- 计算适合的前缀长度,使前缀的选择性接近于完整列的选择性。
- Mysql无法使用前缀索引做order by和group by,也无法使用前缀索引做覆盖扫描。
3.2 选择合适的索引列顺序
将选择性最高的列放在索引最前列。
【部分内容参考自】
- MYSQL(一)数据库索引类型,索引优点:https://blog.51cto.com/u_13580976/2103750
- 数据库索引 - 详解:http://blog.itpub.net/25969737/viewspace-1051502/
- 数据库索引分类及操作:https://www.cnblogs.com/tiankx/p/13898445.html
- Btree索引详解:https://blog.csdn.net/xu_flash/article/details/62216969
- mysql: 哈希索引,虽不常用,但威力巨大:https://baijiahao.baidu.com/s?id=1647079867833965110&wfr=spider&for=pc
- 聚集索引与非聚集索引的总结:https://www.cnblogs.com/s-b-b/p/8334593.html
- 深入浅出数据库索引原理:https://zhuanlan.zhihu.com/p/23624390
- 聚集索引和非聚集索引的区别:https://blog.csdn.net/riemann_/article/details/90324846
以上是关于面试面试常问之数据库索引的主要内容,如果未能解决你的问题,请参考以下文章