面试必备-数据库索引

1. Mybatis缓存

Mybatis中有一级缓存和二级缓存,默认情况下一级缓存是开启的,而且是不能关闭的。一级缓存是指 SqlSession 级别的缓存,当在同一个 SqlSession 中进行相同的 SQL 语句查询时,第二次以后的查询不会从数据库查询,而是直接从缓存中获取,一级缓存最多缓存 1024 条 SQL二级缓存是指可以跨 SqlSession 的缓存。是 mapper 级别的缓存,对于 mapper 级别的缓存不同的sqlsession 是可以共享的

1.1 Mybatis 的一级缓存原理 ( sqlsession 级别 )

第一次发出一个查询 sql,sql 查询结果写入 sqlsession 的一级缓存中,缓存使用的数据结构是一个 map

  • key:MapperID+offset+limit+Sql+所有的入参
  • value:用户信息

同一个 sqlsession 再次发出相同的 sql,就从缓存中取出数据。如果两次中间出现 commit 操作(修改、添加、删除),本 sqlsession 中的一级缓存区域全部清空,下次再去缓存中查询不到所以要从数据库查询,从数据库查询到再写入缓存。

1.2 二级缓存原理 ( mapper 级别 )

二级缓存的范围是 mapper 级别(mapper同一个命名空间),mapper 以命名空间为单位创建缓存数据结构,结构是 map。mybatis 的二级缓存是通过 CacheExecutor 实现的。cacheExecutor其实是 Executor 的代理对象所有的查询操作,在cacheExecutor 中都会先匹配缓存中是否存在,不存在则查询数据库。

key:MapperID+offset+limit+Sql+所有的入参

具体使用需要配置:

  1. Mybatis 全局配置中启用二级缓存配置
  2. 在对应的 Mapper.xml 中配置 cache 节点
  3. 在对应的 select 查询节点中添加 useCache=true

MyBatis 全局配置文件:



POJO 实现序列化接口:

package com.rgh.mybatis.bean;

import java.io.Serializable;

public class Department implements Serializable{
    private Integer id;
    private String departmentName;
    //省略get、set 与toString 方法
}

mapper 接口:

 Department getDeptById(Integer id);

SQL映射文件




1.3 与缓存相关的设置

  1. 可以在MyBatis 全局配置文件中的setting标签中配置cacheEnabled属性,表示是否启用二级缓存机制,不过不影响一级缓存。

  2. select查询标签中可以设置useCache属性,配置是否使用二级缓存,默认一级缓存是一直生效的。

  3. sqlSession.clearCache();方法默认只清除一级缓存中的数据

  4. 可以在每个增删改的标签中设置flushCache属性,设置为true后,如果执行了该增删改操作,则一级缓存与二级缓存中的数据都会清空

1.4 总结

在实际开发的过程中,MyBatis 提供的二级缓存机制好像并没有获得开发人员的青睐。原因是二级缓存是一个namespace级别的缓存,如果在不同的namespace下操作同一SQL 语句,可能导致缓存中的数据不正确。在进行多表联查的时候,也可能会导致二级缓存中的数据不正确。所以在实际的开发过程中要慎用二级缓存。

参考:CSDN博主「留兰香丶」
原文链接

2. 如何打破双亲委派模型

  1. 自己写一个类加载器
  2. 重写loadclass方法
  3. 重写findclass方法

这里主要重写的是loadclass方法,因为双亲委派机制的实现都是通过这个方法实现的,先找父类进行加载,如果父类加载器无法加载再由自己来进行加载,源码里会直接找到根加载器,重写了这个方法以后就能自定义加载的方式了。

3. 数据库存储引擎

3.1 概念

数据库存储引擎是数据库底层软件组织,数据库管理系统(DBMS)使用数据引擎进行创建、查询、更新和删除数据。不同的存储引擎提供不同的存储机制、索引技巧、锁定水平等功能,使用不同的存储引擎,还可以 获得特定的功能。现在许多不同的数据库管理系统都支持多种不同的数据引擎。存储引擎主要有: 1. MyIsam , 2. InnoDB, 3. Memory, 4. Archive, 5. Federated

3.1.1 InnoDB ( B+树 )

InnoDB 底层存储结构为B+树, B树的每个节点对应innodb的一个page,page大小是固定的,一般设为 16k。其中非叶子节点只有键值,叶子节点包含完成数据

适用场景:

  1. 经常更新的表,适合处理多重并发的更新请求。
  2. 支持事务
  3. 可以从灾难中恢复(通过bin-log日志等)。
  4. 外键约束。只有他支持外键。
  5. 支持自动增加列属性auto_increment。

3.1.2 TokuDB ( Fractal Tree-节点带数据 )

TokuDB 底层存储结构为 Fractal Tree,Fractal Tree 的结构与 B+树有些类似, 在 Fractal Tree中,每一个 child 指针除了需要指向一个 child 节点外,还会带有一个 Message Buffer ,这个Message Buffer 是一个 FIFO 的队列,用来缓存更新操作

例如,一次插入操作只需要落在某节点的 Message Buffer 就可以马上返回了,并不需要搜索到叶子节点。这些缓存的更新会在查询时或后台异步合并应用到对应的节点中。


TokuDB 在线添加索引,不影响读写操作, 非常快的写入性能, Fractal-tree 在事务实现上有优势。 他主要适用于访问频率不高的数据或历史数据归档

3.1.3 MyISAM

MyIASM是MySQL默认的引擎,但是它没有提供对数据库事务的支持,也不支持行级锁和外键,因此当 INSERT(插入)或 UPDATE(更新)数据时即写操作需要锁定整个表效率便会低一些。

ISAM 执行读取操作的速度很快,而且不占用大量的内存和存储资源。在设计之初就预想数据组织成有固定长度的记录,按顺序存储的。---ISAM 是一种静态索引结构

3.1.4 Memory

Memory(也叫 HEAP)堆内存:使用存在内存中的内容来创建表。每个 MEMORY 表只实际对应一个磁盘文件。MEMORY 类型的表访问非常得快,因为它的数据是放在内存中的,并且默认使用HASH 索引。但是一旦服务关闭,表中的数据就会丢失掉。 Memory 同时支持散列索引和 B 树索引,B树索引可以使用部分查询和通配查询,也可以使用<,>和>=等操作符方便数据挖掘,散列索引相等的比较快但是对于范围的比较慢很多。

4. 索引的工作原理及其种类

数据库索引,是数据库管理系统中一个排序的数据结构,以协助快速查询、更新数据库表中数据。索引的实现通常使用B树及其变种B+树

在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法。这种数据结构,就是索引。

为表设置索引要付出代价的:一是增加了数据库的存储空间,二是在插入和修改数据时要花费较多的时间(因为索引也要随之变动)。

上图展示了一种可能的索引方式。左边是数据表,一共有两列七条记录,最左边的是数据记录的物理地址(注意逻辑上相邻的记录在磁盘上也并不是一定物理相邻的)。为了加快Col2的查找,可以维护一个右边所示的二叉查找树,每个节点分别包含索引键值和一个指向对应数据记录物理地址的指针,这样就可以运用二叉查找在O(log2n)的复杂度内获取到相应数据。

4.1 索引优点

创建索引可以大大提高系统的性能。

  1. 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。
  2. 可以大大加快数据的检索速度,这也是创建索引的最主要的原因。
  3. 可以加速表和表之间的连接,特别是在实现数据的参考完整性方面特别有意义。
  4. 在使用分组和排序子句进行数据检索时,同样可以显著减少查询中分组和排序的时间
  5. 通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能。

4.2 索引缺点

也许会有人要问:增加索引有如此多的优点,为什么不对表中的每一个列创建一个索引呢?因为,增加索引也有许多不利的方面

  1. 创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加。
  2. 索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立聚簇索引,那么需要的空间就会更大。
  3. 当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,这样就降低了数据的维护速度

4.3 一般来说应该在这些列上建立索引:

  1. 经常需要搜索的列上,可以加快搜索的速度
  2. 在作为主键的列上,强制该列的唯一性和组织表中数据的排列结构
  3. 在经常用在连接的列上,这些列主要是一些外键,可以加快连接的速度
  4. 在经常需要根据范围进行搜索的列上创建索引,因为索引已经排序,其指定的范围是连续的
  5. 在经常需要排序的列上创建索引,因为索引已经排序,这样查询可以利用索引的排序,加快排序查询时间
  6. 在经常使用在WHERE子句中的列上面创建索引,加快条件的判断速度

4.4 一般来说,不应该创建索引的的这些列具有下列特点:

  1. 对于那些在查询中很少使用或者参考的列不应该创建索引
  2. 对于那些只有很少数据值的列也不应该增加索引
  3. 对于那些定义为text, image和bit数据类型的列不应该增加索引
  4. 修改性能远远大于检索性能时,不应该创建索引

4.5 索引类型

根据数据库的功能,可以在数据库设计器中创建三种索引:唯一索引、主键索引和聚集索引

唯一索引:

唯一索引是不允许其中任何两行具有相同索引值的索引。当现有数据中存在重复的键值时,大多数数据库不允许将新创建的唯一索引与表一起保存。数据库还可能防止添加将在表中创建重复键值的新数据。例如,如果在employee表中职员的姓(lname)上创建了唯一索引,则任何两个员工都不能同姓。

主键索引:

数据库表经常有一列或列组合,其值唯一标识表中的每一行。该列称为表的主键。 在数据库关系图中为表定义主键将自动创建主键索引,主键索引是唯一索引的特定类型。该索引要求主键中的每个值都唯一。当在查询中使用主键索引时,它还允许对数据的快速访问。

聚集索引:

在聚集索引中,表中行的物理顺序与键值的逻辑(索引)顺序相同。一个表只能包含一个聚集索引。如果某索引不是聚集索引,则表中行的物理顺序与键值的逻辑顺序不匹配。与非聚集索引相比,聚集索引通常提供更快的数据访问速度。

5. 数据库优化

  1. 应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。

  2. 应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描

    select id from t where num is null
    可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:
    select id from t where num=0

  3. 很多时候用 exists 代替 in 是一个好的选择

  4. 用Where子句替换HAVING 子句 因为HAVING 只会在检索出所有记录之后才对结果集进行过滤

  5. 在表中建立索引,优先考虑where、group by使用到的字段

  6. 尽量避免使用select *,返回无用的字段会降低查询效率。如下:

    SELECT * FROM t

    优化方式:使用具体的字段代替*,只返回使用到的字段。

  7. 尽量避免使用in 和not in,会导致数据库引擎放弃索引进行全表扫描。如下:

    SELECT * FROM t WHERE id IN (2,3)

    SELECT * FROM t1 WHERE username IN (SELECT username FROM t2)

    优化方式:如果是连续数值,可以用between代替。如下:

    SELECT * FROM t WHERE id BETWEEN 2 AND 3

    如果是子查询,可以用exists代替。如下:

    SELECT * FROM t1 WHERE EXISTS (SELECT * FROM t2 WHERE t1.username = t2.username)

  8. 尽量避免使用or,会导致数据库引擎放弃索引进行全表扫描。如下:

    SELECT * FROM t WHERE id = 1 OR id = 3

    优化方式:可以用union代替or。如下:

    SELECT * FROM t WHERE id = 1
    UNION
    SELECT * FROM t WHERE id = 3
    

    (PS:如果or两边的字段是同一个,如例子中这样。貌似两种方式效率差不多,即使union扫描的是索引,or扫描的是全表)

  9. 尽量避免在字段开头模糊查询,会导致数据库引擎放弃索引进行全表扫描。如下:

    SELECT * FROM t WHERE username LIKE '%li%'

    优化方式:尽量在字段后面使用模糊查询。如下:

    SELECT * FROM t WHERE username LIKE 'li%'

  10. 尽量避免在where条件中等号的左侧进行表达式、函数操作,会导致数据库引擎放弃索引进行全表扫描。如下:

    SELECT * FROM t2 WHERE score/10 = 9
    SELECT * FROM t2 WHERE SUBSTR(username,1,2) = 'li'
    

    优化方式:可以将表达式、函数操作移动到等号右侧。如下:

    SELECT * FROM t2 WHERE score = 10*9
    SELECT * FROM t2 WHERE username LIKE 'li%'
    
  11. 当数据量大时,避免使用where 1=1的条件。通常为了方便拼装查询条件,我们会默认使用该条件,数据库引擎会放弃索引进行全表扫描。如下:

    SELECT * FROM t WHERE 1=1

    优化方式:用代码拼装sql时进行判断,没where加where,有where加and。

6. 数据库索引相关面试

6.1 索引的底层实现原理和优化

B+树,经过优化的B+树,主要是在所有的叶子结点中增加了指向下一个叶子节点的指针,因此InnoDB建议为大部分表使用默认自增的主键作为主索引。

6.2 什么情况下设置了索引但无法使用

  • 以“%”开头的LIKE语句,模糊匹配
  • OR语句前后没有同时使用索引
  • 数据类型出现隐式转化(如varchar不加单引号的话可能会自动转换为int型)

6.3 简单描述mysql中,索引,主键,唯一索引,联合索引的区别,对数据库的性能有什么影响(从读写两方面)

  • 索引是一种特殊的文件(InnoDB数据表上的索引是表空间的一个组成部分),它们包含着对数据表里所有记录的引用指针
  • 普通索引(由关键字KEY或INDEX定义的索引)的唯一任务是加快对数据的访问速度
  • 普通索引允许被索引的数据列包含重复的值。如果能确定某个数据列将只包含彼此各不相同的值,在为这个数据列创建索引的时候就应该用关键字UNIQUE把它定义为一个唯一索引。也就是说, 唯一索引可以保证数据记录的唯一性
  • 主键,是一种特殊的唯一索引,在一张表中只能定义一个主键索引,主键用于唯一标识一条记录,使用关键字PRIMARY KEY 来创建。
  • 索引可以覆盖多个数据列,如像INDEX(columnA, columnB)索引,这就是联合索引
  • 索引可以极大的提高数据的查询速度,但是会降低插入、删除、更新表的速度,因为在执行这些写操作时,还要操作索引文件。

6.4 对于关系型数据库而言,索引是相当重要的概念,请回答有关索引的几个问题:

6.4.1 索引的目的是什么?

  • 快速访问数据表中的特定信息,提高检索速度
  • 创建唯一性索引,保证数据库表中每一行数据的唯一性。
  • 加速表和表之间的连接
  • 使用分组和排序子句进行数据检索时,可以显著减少查询中分组和排序的时间

6.4.2 索引对数据库系统的负面影响是什么?

  • 创建索引和维护索引需要耗费时间,这个时间随着数据量的增加而增加;
  • 索引需要占用物理空间,不光是表需要占用数据空间,每个索引也需要占用物理空间;
  • 当对表进行增、删、改、的时候索引也要动态维护,这样就降低了数据的维护速度

6.4.3 为数据表建立索引的原则有哪些?

  • 最频繁使用的、用以缩小查询范围的字段上建立索引。
  • 在频繁使用的、需要排序的字段上建立索引

6.4.4 什么情况下不宜建立索引?

对于查询中很少涉及的列或者重复值比较多的列,不宜建立索引。

对于一些特殊的数据类型,不宜建立索引,比如文本字段(text)等

6.5 你怎么看到为表格定义的所有索引?

SHOW INDEX FROM ;

6.6 实现索引的方式? 索引的原理? 索引的代价? 索引的类型?

  1. 实现索引的方式有两种:

    • 针对一张表的某些字段创建具体的索引,如对oracle:create index 索引名称 on 表名(字段名)
    • 创建表时为字段建立主键约束或者唯一约束,系统将自动为其建立索引。
  2. 索引的原理:

    根据建立索引的字段建立索引表存放字段值以及对应记录的物理地址,从而在搜索的时候根据字段值搜索索引表的到物理地址直接访问记录。

  3. 索引的代价:

    引入索引虽然提高了查询速度,但本身占用一定的系统存储容量和系统处理时间,需要根据实际情况进行具体的分析.

  4. 索引的类型有

    B树索引,位图索引,函数索引等。

6.7 怎样创建一个一个索引,索引使用的原则,有什么优点和缺点

6.7.1 创建索引

创建标准索引:

CREATE  INDEX 索引名 ON 表名 (列名)  TABLESPACE 表空间名; 

创建唯一索引:

CREATE unique INDEX 索引名 ON 表名 (列名)  TABLESPACE 表空间名; 

创建组合索引:

CREATE INDEX 索引名 ON 表名 (列名1,列名2)  TABLESPACE 表空间名; 

创建反向键索引:

CREATE INDEX 索引名 ON 表名 (列名) reverse TABLESPACE 表空间名; 

6.7.2 索引使用原则:

上面已经说过,这里不在赘述

6.7.3 优缺点

上面已经说过,这里不在赘述

6.8 索引的实现方式

都是 B+树索引, Innodb 是索引组织表, myisam 是堆表, 索引组织表和堆表的区别要熟悉

6.9 Oracle索引分为哪几类,说出唯一索引和位图索引的概念

  • Oracle索引有B树索引,位图索引,函数索引,簇索引等。
  • 唯一索引也是B树索引的一种,它要求被索引的字段值不可以重复。在创建的时候使用B树算法创建。
  • 位图索引并不是采用像唯一索引那样存储(索引字段值,记录ROWID)来创建索引段的,而是为每一个唯一的字段值创建一个位图,位图中使用位元来对应一个记录的ROWID。位元到ROWID是通过映射的到的。

6.10 MYsql 的索引原理,索引的类型有哪些,如何创建合理的索引,索引如何优化。

索引是通过复杂的算法,提高数据查询性能的手段。从磁盘io到内存io的转变

6.10.1 普通索引,主键,唯一,单列/多列索引建索引的几大原则

  1. 最左前缀匹配原则,非常重要的原则,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。
  2. =和in可以乱序,比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意顺序,mysql的查询优化器会帮你优化成索引可以识别的形式
  3. 尽量选择区分度高的列作为索引,区分度的公式是count(distinct col)/count(*),表示字段不重复的比例,比例越大我们扫描的记录数越少,唯一键的区分度是1,而一些状态、性别字段可能在大数据面前区分度就是0,那可能有人会问,这个比例有什么经验值吗?使用场景不同,这个值也很难确定,一般需要join的字段我们都要求是0.1以上,即平均1条扫描10条记录
  4. 索引列不能参与计算,保持列“干净”,比如from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,原因很简单,b+树中存的都是数据表中的字段值,但进行检索时,需要把所有元素都应用函数才能比较,显然成本太大。所以语句应该写成create_time = unix_timestamp(’2014-05-29’);
  5. 尽量的扩展索引,不要新建索引。比如表中已经有a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可

6.11 聚集索引和非聚集索引的区别。

“聚集”就是索引和记录紧密在一起

非聚集索引 索引文件和数据文件分开存放,索引文件的叶子页只保存了主键值,要定位记录还要去查找相应的数据块。

6.12 数据库中 BTREE 和 B+tree 区别。

B+是btree的变种,本质都是btree,btree+与B-Tree相比,B+Tree有以下不同点:

  • 每个节点的指针上限为2d而不是2d+1。
  • 内节点不存储data,只存储key;
  • 叶子节点不存储指针。

6.13 Btree 怎么分裂的,什么时候分裂,为什么是平衡的。

Key 超过1024才分裂B树为甚会分裂? 因为随着数据的增多,一个结点的key满了,为了保持B树的特性,就会产生分裂,就向红黑树和AVL树为了保持树的性质需要进行旋转一样!

参考文章:CSDN博主「专注Java面试整理」

你可能感兴趣的:(面试必备-数据库索引)