面试面试常问之数据库索引

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,则该字段默认就是主键索引。

  • 删除索引:

    drop index 索引名 on 表名 ;
    • drop index name_index on tb ;

  • 查询索引:

    show index from 表名 ;


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

以上是关于面试面试常问之数据库索引的主要内容,如果未能解决你的问题,请参考以下文章

面试面试常问之数据库事务

面试面试常问之堆栈的区别

面试面试常问之堆栈的区别

面试面试常问之python修饰器

面试必问之spring 面试题

面试数据库常问之char与varchar的区别