面试必问的数据库-3.2:索引-相关

1:数据库索引和选择性的关系

在讨论数据库索引的时候,经常会提到“选择性”(selectivity)这个概念。“选择性”是描述列值数据分布情况的一个重要属性。“选择性”和“基数”(cardinality)是两个密不可分的概念。“基数”就是一列中唯一值的数量,对于有唯一约束的列,“基数”等于表的总行数。

怎么计算索引的选择性?

选择性 =  基数/总行数 * 100%

选择性是什么意思?

上面的公式应该怎么理解?假设表中有Sex(性别)这一列,列值只有两种可能—`Male`(男性)和`Female`(女性),那么,Sex列的基数(cardinality)就是2。如果这张表中有10000条行记录,那么Sex列的索引的选择性就是2/10000 * 100% = 0.02%。

为什么“选择性”对索引很重要,数据库怎么利用“选择性”的?

“选择性”的高低可以衡量列值的可能性,换句话说,在给定的样本集里有多少个不同的值。

我们思考一下,低选择性意味着什么?低选择性意味着列值没有太多变化(或没有太多的可能性)。例如,Sex列的选择性,只有非常低的0.02%,这就意味着,Sex列的列值有很少的不同值。

选择性对数据库索引有什么影响?

数据库的查询优化器会根据索引的“选择性”来判断是否使用索引执行查询。也就是说,你在某列上创建了索引,这不意味着数据库就一定会使用这个索引,因为有时全表扫描是更高效的选择。

什么时候不应该使用数据库索引?

什么时候不应该使用数据库索引?当“选择性”比较低的时候!为什么低选择性的时候不适合使用索引呢?设想一下,现在我们要查询所有女性的名字,由于性别只有男性和女性两种情况,所以女性占比是50%的可能性很大。那我们就假设确实有50%(5000)是女性。如果查找索引的话,数据库为了查找出所有女性就需要访问索引5000次。要知道访问索引也是需要消耗时间和资源的。所以这种情况,直接去做全表扫描可能会更快一些。可以看到,数据库的查询优化器会根据“选择性”的高低来决定使用索引还是直接全表扫描。

“选择性”等于多少才会使用索引?

这个问题很难回答,它因数据库而异。

当然,“选择性”很高时,应该使用索引。例如,我们要查询的某列,其“选择性”是100%,说明该列的列值都是唯一的。这时,如果只查询其中的一行,使用索引是最高效的。同时,这也是执行全表扫描最坏的情形。:

 

2:哪些情况可创建索引:

索引是建立在数据库表中的某些列的上面。在创建索引的时候,应该考虑在哪些列上可以创建索引,在哪些列上不能创建索引。一般来说,应该在这些列上创建索引:

在经常需要搜索的列上,可以加快搜索的速度;

在作为主键的列上,强制该列的唯一性和组织表中数据的排列结构;

在经常用在连接(join)的列上,这些列主要是一些外键,可以加快连接的速度;

在经常需要根据范围进行搜索的列上创建索引,因为索引已经排序,其指定的范围是连续的;

在经常需要排序的列上创建索引,因为索引已经排序,这样查询可以利用索引的排序,加快排序查询时间;

在经常使用在where子句中的列上面创建索引,加快条件的判断速度。

同样,对于有些列不应该创建索引。一般来说,不应该创建索引的的这些列具有下列特点:

第一,对于那些在查询中很少使用或者参考的列不应该创建索引。这是因为,既然这些列很少使用到,因此有索引或者无索引,并不能提高查询速度。相反,由于增加了索引,反而降低了系统的维护速度和增大了空间需求。

第二,对于那些只有很少数据值的列也不应该增加索引。也就是上面说的选择性低,这是因为,由于这些列的取值很少,例如人事表的性别列,在查询的结果中,结果集的数据行占了表中数据行的很大比例,即需要在表中搜索的数据行的比例很大。增加索引,并不能明显加快检索速度。

第三,对于那些定义为text, image和bit数据类型的列不应该增加索引。这是因为,这些列的数据量要么相当大,要么取值很少。

第四,当修改性能远远大于检索性能时,不应该创建索引。这是因为,修改性能和检索性能是互相矛盾的。当增加索引时,会提高检索性能,但是会降低修改性能。当减少索引时,会提高修改性能,降低检索性能。因此,当修改性能远远大于检索性能时,不应该创建索引。也就是更新操作比较多的业务,建立索引不一定效率更好。

3:我们常用的btree索引和hash索引有区别吗?

一般我们都会用btree索引。

从索引原理角度考虑。B-Tree索引,是一般性的索引,适用范围更广。但是B-Tree索引不适合大范围数据扫描。B-Tree索引,至少需要至少两次查询,第一次扫描索引,拿到ROWID后,然后从数据块中去获取数据行。而Hash索引,只需要根据查询的值进行一次Hash函数计算,就可以得到ROWID的位置,也就是说肯定只需要计算一次就能拿到ROWID。因此,Hash所以你更适合=值查找。

哈希支持随机读取操作,但不支持顺序扫描;B-Tree支持顺序扫描!

4:全表扫描就有点垃圾了啊。

可以使用索引有效的避免全表扫描。

那哪些情况会造成全表扫描呢?

1:如果查询中没有where子句,会导致全表扫描。

2:有些情况,及时创建了索引,还是会导致全表扫描。

2.1:使用不等于操作(!= 或 <>)。

例如: WHERE NAME <> 'Jesus'

因为索引只能用于查找表中有什么,而不能用于查找表中没有什么。

2.2:使用`NOT`操作符。

例如:WHERE NOT NAME 'Jesus' 。原因同上

2.3:通配符出现在字符串比较的开始位置。

例如:WHERE NAME LIKE '%programmer%' 。

以哪个字母开始都不清楚,索引也无能为力了。

2.4:最左前缀匹配原则

对于多列的混合索引:,只有符合最左前缀的索引才能被查询优化器使用。

比如,某个3列的混合索引(col1,col2,col3),

只有下面三种情况可以使用到索引: (col1), (col1, col2),和 (col1, col2, col3)。

对于下面的查询语句:

SELECT * FROM tbl_name WHERE col1=val1;
SELECT * FROM tbl_name WHERE col1=val1 AND col2=val2;

SELECT * FROM tbl_name WHERE col2=val2;
SELECT * FROM tbl_name WHERE col2=val2 AND col3=val3;

只有前两个SELECT语句使用到了索引,

第3个和第4个查询虽然使用了索引列,但是(col2)和(col2,col3)不是混合索引(col1, col2, col3)的最左前缀。

3:如果条件中有or,即使其中有条件带索引也不会使用(这也是为什么尽量少用or的原因)

注意:要想使用or,又想让索引生效,只能将or条件中的每个列都加上索引

4:like查询是以%开头,将不会使用索引

5:如果列类型是字符串,那一定要在条件中将数据使用引号引用起来,否则不使用索引。

(要特别注意这个,这一点还是比较坑的)

5:索引区别

普通索引:最基本的索引,没有任何限制

唯一索引:与"普通索引"类似,不同的就是:索引列的值必须唯一,但允许有空值。

主键索引:它是一种特殊的唯一索引,不允许有空值。 

全文索引:针对较大的数据,生成全文索引很耗时好空间。

组合索引:为了更多的提高mysql效率可建立组合索引,遵循”最左前缀“原则。

其中还有一种索引叫覆盖索引:

覆盖索引就是从索引中直接获取查询结果,要使用覆盖索引需要注意select查询列中包含在索引列中;where条件包含索引列或者复合索引的前导列;查询结果的字段长度尽可能少。

覆盖索引:覆盖索引是指索引的叶子节点已包含所有要查询的列,因此不需要访问表数据(回表~~又学个名词)

覆盖索引是指索引的叶子节点已包含所有要查询的列,因此不需要访问表数据,能极大地提高性能。

其实理解覆盖索引很简单:

索引既数据

个人理解,覆盖索引指的是索引中已包含需要查询的数据,执行查询时无需扫描数据文件,扫描索引文件即可。联合索引,在多个属性,建立为一个索引,加快多条件查询。

比如我在user_info表中为name字段建立了索引

我这样查询:

Select * from user_info where name="zhangsan"

这种是正常查询,并没有使用覆盖索引。

那么如何使用覆盖索引呢?

Select name user_info where name="zhangsna"

这种是覆盖索引,我的查询结果的字段,可以直接从索引中获取。

注意:

1、覆盖索引也并不适用于任意的索引类型,索引必须存储列的值

2、Hash 和full-text索引不存储值,因此MySQL只能使用B-TREE

3、并且不同的存储引擎实现覆盖索引都是不同的

4、并不是所有的存储引擎都支持它们

5、如果要使用覆盖索引,一定要注意SELECT 列表值取出需要的列,不可以是SELECT *,因为如果将所有字段一起做索引会导致索引文件过大,查询性能下降,不能为了利用覆盖索引而这么做

所以,知道有这么个东西就行了,一般实际生产中都用户到覆盖索引的。

注意:数据量特别大的时候,最好不要用联合查询(join),即使你做了索引。

6:十万个为什么?

6.1:为什么加索引后会使查询变快?

因为加索引之后,会使数据库的所有数据变成平衡树的结构,查一次数据,只需要很少的IO次数就可以查到。

所以,查询效率变快。

 

6.2:为什么加索引后会使写入、修改、删除变慢?

因为平衡树这个结构必须一致维持在一个正确的状态,增删改数据都会改变平衡树各个节点中的索引的数据内容,

破坏树结构,

因此,每次数据改变时,都必须重新梳理索引的结构,以确保它的正确,

这会带来不小的性能开销,所以会变慢。

 

6.3:使用索引有什么代价?

首先,占用空间。你的表越大,索引需要的空间就越大。

另外会影响数据库性能,你对数据表进行增删改操作,同样的操作还要在索引上执行一次。

6.4:B+树索引和哈希索引的明显区别是?

1:如果是等值查询,那么哈希索引明显有绝对优势,因为只需要经过一次算法即可找到相应的键值;

当然了,这个前提是,键值都是唯一的。

如果键值不是唯一的,就需要先找到该键所在位置,然后再根据链表往后扫描,直到找到相应的数据;

2:如果是范围查询检索,这时候哈希索引就毫无用武之地了,

因为原先是有序的键值,经过哈希算法后,有可能变成不连续的了,就没办法再利用索引完成范围查询检索;

3:同理,哈希索引也没办法利用索引完成排序,

以及like ‘xxx%’ 这样的部分模糊查询(这种部分模糊查询,其实本质上也是范围查询);

4:哈希索引也不支持多列联合索引的最左匹配规则;

5:在有大量重复键值情况下,哈希索引的效率也是极低的,因为存在所谓的哈希碰撞问题。

6.5:NULL 的问题?

NULL会导致索引形同虚设,所以在设计表结构时应避免NULL 的存在,一般都要设置not null(用其他方式表达你想表达的NULL,比如 -1?)

比如:查询用is nullis not null是不走索引的。

如果列值有null,应该是可以走索引的,只是sql中不能用is null 或者 is not null 这种写法而已。

其实:

官方一点的说法应该是:

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

不建议列上允许为空。最好限制not null,并设置一个默认值,比如0''空字符串等,如果是datetime类型,可以设置成'1970-01-01 00:00:00'这样的特殊值。

但我本地试了:

is null 或者 is not null 在任何情况下都不会用索引。

如果某一列默认为null,也就是null列,是可以用到索引的,只要正常的查询。

http://www.cnblogs.com/DaBing0806/p/4876228.html

猜你喜欢

转载自blog.csdn.net/u010953880/article/details/88303928