mysql(10)、索引

索引介绍

作用: 针对精确查询提升查询速度。

没有索引: 一般查询都是遵循逐行遍历,若数据量大的情况下,就会比较费时,查询效率低;

有索引: 类似与二分法查询,可以迅速缩小范围。


磁盘IO & 预读

每次读取数据花费的时间可以分为寻道时间、旋转延迟、传输时间三个部分:

  • 1、寻道时间: 指的是磁臂移动到指定磁道所需要的时间,主流磁盘一般在5ms以下;
  • 2、旋转延迟: 就是我们经常听说的磁盘转速,比如一个磁盘7200转,表示每分钟能转7200次,也就是说1秒钟能转120次,旋转延迟就是1/120/2 = 4.17ms
  • 3、传输时间:指的是从磁盘读出或将数据写入磁盘的时间,一般在零点几毫秒,相对于前两个时间可以忽略不计。

一次磁盘IO的时间约等于5+4.17 = 9ms左右,一台500 -MIPS(Million Instructions Per Second)的机器每秒可以执行5亿条指令,换句话说执行一次IO的时间可以执行约450万条指令,数据库动辄十万百万乃至千万级数据,每次9毫秒的时间,显然是个灾难.
考虑到磁盘IO成本是非常高昂的操作,计算机操作系统做了一些优化,当一次IO时,不光把当前磁盘地址的数据,而是把相邻的数据也都读取到内存缓冲区内,因为局部预读性原理告诉我们,当计算机访问一个地址的数据的时候,与其相邻的数据也会很快被访问到。每一次IO读取的数据我们称之为一页(page)。具体一页有多大数据跟操作系统有关,一般为4k或8k,也就是我们读取一页内的数据时候,实际上才发生了一次IO,这个理论对于索引的数据结构设计非常有帮助。


Btree索引数据结构

B-Tree 索引是 MySQL 数据库中使用最为频繁的索引类型,除了 Archive 存储引擎之外的其他所有的存储引擎都支持 B-Tree 索引。
不仅仅在 MySQL 中是如此,实际上在其他的很多数据库管理系统中B-Tree 索引也同样是作为最主要的索引类型,这主要是因为 B-Tree 索引的存储结构在数据库的数据检 索中有非常优异的表现。

InnoDB引擎的btree索引查找过程

浅蓝色的块我们称之为一个磁盘块,可以看到每个磁盘块包含几个数据项(深蓝色所示) 和 指针(黄色所示),如磁盘块1包含数据项1735,包含指针P1P2P3P1表示小于17的磁盘块,P2表示在1735之间的磁盘块,P3表示大于35的磁盘块。真实的数据存在于叶子节点即3、5、9、10、13、15、28、29、36、60、75、79、90、99。非叶子节点(根节点和树枝节点)不存储真实的数据,只存储指引搜索方向的数据项,如1735并不真实存在于数据表中。

查找过程

  1. 如果要查找数据项29,那么首先会把磁盘块1由磁盘加载到内存,此时发生一次IO;
  2. 在内存中用二分查找确定29在17和35之间,锁定磁盘块1的P2指针,内存时间因为非常短(相比磁盘的IO)可以忽略不计;
  3. 通过磁盘块1的P2指针的磁盘地址把磁盘块3由磁盘加载到内存,发生第二次IO;
  4. 29在26和30之间,锁定磁盘块3的P2指针,通过指针加载磁盘块8到内存,发生第三次IO;
  5. 在内存中做二分查找找到29,结束查询,总计三次IO。

真实的情况是,3层的b+树可以表示上百万的数据,如果上百万的数据查找只需要三次IO,性能提高将是巨大的;如果没有索引,每个数据项都要发生一次IO,那么总共需要百万次的IO,显然成本非常非常高。

MyIasm引擎的btree索引查找过程

过程类似InnoDB引擎的btree索引查找过程,只不过叶子节点存储的是行指针,需要再根据行指针定位对应的行,而InnoDB叶子节点存储的是完整的数据行。


hash索引

Hash 索引结构的特殊性,其检索效率非常高,索引的检索可以一次定位,不像B-Tree 索引需要从根节点到枝节点,最后才能访问到页节点这样多次的IO访问,所以 Hash 索引的查询效率要远高于 B-Tree 索引。

缺点

  • 1、Hash 索引仅仅能满足"=","IN"和"<=>"查询,不能使用范围查询。由于 Hash 索引比较的是进行 Hash 运算之后的 Hash 值,所以它只能用于等值的过滤,不能用于基于范围的过滤,因为经过相应的 Hash 算法处理之后的Hash值的大小关系,并不能保证和Hash运算前完全一样,就变成全表扫描。
  • 2、Hash 索引无法被用来避免数据的排序操作。
    由于 Hash 索引中存放的是经过 Hash 计算之后的 Hash 值,而且Hash值的大小关系并不一定和 Hash 运算前的键值完全一样,所以数据库无法利用索引的数据来避免任何排序运算;
  • 3、Hash 索引不能利用部分索引键查询。
    对于组合索引,Hash 索引在计算 Hash 值的时候是组合索引键合并后再一起计算 Hash 值,而不是单独计算 Hash 值,所以通过组合索引的前面一个或几个索引键进行查询的时候,Hash 索引也无法被利用。
  • 4、Hash 索引在任何时候都不能避免表扫描。
    前面已经知道,Hash 索引是将索引键通过 Hash 运算之后,将 Hash运算结果的 Hash 值和所对应的行指针信息存放于一个 Hash 表中,由于不同索引键存在相同 Hash 值,所以即使取满足某个 Hash 键值的数据的记录条数,也无法从 Hash 索引中直接完成查询,还是要通过访问表中的实际数据进行相应的比较,并得到相应的结果。
  • 5、Hash 索引遇到大量Hash值相等的情况后性能并不一定就会比B-Tree索引高。


索引种类

    1. 辅助索引index :加速查找;
    1. 唯一索引: 运用innoDB记得要创建主键;

      主键索引:primary key :加速查找+约束(不为空且唯一)
      唯一索引:unique:加速查找+约束 (唯一)

    1. 联合索引

      primary key(id,name):联合主键索引
      -unique(id,name):联合唯一索引
      -index(id,name):联合普通索引

    1. 全文索引fulltext: 用于搜索很长一篇文章的时候,效果最好。
    1. 空间索引spatial :了解就好,几乎不用

创建& 删除索引

# 方法一:创建表时;
    create table t1(
        id int,
        name char,
        age int,
        sex enum('male','female'),
        unique key uni_id(id),
        index ix_name(name) # index 索引名+字段名
    );


# 方法二:CREATE在已存在的表上创建索引;
    create index ix_age on t1(age);


# 方法三:ALTER TABLE在已存在的表上创建索引;
    alter table t1 add index ix_sex(sex);


# 删除索引:DROP INDEX 索引名 ON 表名字;
    alter table s1 drop index ix_sex;
    drop index 索引名称 on 表名(字段名)

# 查看表结构
    show create table t1;
    
    | t1    | CREATE TABLE `t1` (
      `id` int(11) DEFAULT NULL,
      `name` char(1) DEFAULT NULL,
      `age` int(11) DEFAULT NULL,
      `sex` enum('male','female') DEFAULT NULL,
      UNIQUE KEY `uni_id` (`id`),
      KEY `ix_name` (`name`),
      KEY `ix_age` (`age`),
      KEY `ix_sex` (`sex`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 |

测试索引

# 1.准备表
    create table s1(
        id int,
        name varchar(20),
        age int,
        email varchar(50)
    );


# 2.创建存储过程
    # 声明存储过程的结束符号为$$
    delimiter $$
    create procedure auto_insert()
    BEGIN
        declare i int default 1;
        while (i<3000000) do
            insert into s1 values(i,'lynn',10,concat('lynn',i,'@oldboy'));
            set i = i+1;
        end while;
    END$$
    delimiter ; 
    # 重新声明分号为结束符号

# 3.查看存储过程
    show create procedure auto_insert\G 

# 4.调用存储过程,可能需十来分钟!
    call auto_insert();

# 5. 查看结果
    select count(id) from s1;
    -------+
    | count(id) |
    +-----------+
    |   2999999 |
    +-----------+
    1 row in set (1.68 sec)


# 对比有无索引的查询速度======================================

# 1.没有索引
select * from s1 where id=3333333;
Empty set (2.16 sec)

# 2.有索引
# 给id字段增加索引
    alter table s1 modify id int primary key;
    Query OK, 0 rows affected (11.60 sec)

# 根据索引方式查找
select * from s1 where id=3333333;
Empty set (0.00 sec)


# 总结
对比可以看出,没有索引花了2.16秒,但是给id设置索引后,查询速度为0,因为直接排除了错误,最大的id为3000000;

索引选择

字段明确优先,若条件不明确,条件中出现这些符号或关键字:>、>=、<、<=、!= 、between...and...、like、大于号、小于号,那么索引作用就不明显.

# 文件扫描
    mysql> select count(id) from s1 where id > 1000;
    +-----------+
    | count(id) |
    +-----------+
    |   2998999 |
    +-----------+
    1 row in set (2.36 sec)

# 查询大范围扫描,查询速度没有提升, 查询范围小,速度就很快
    mysql> select count(*) from s1 where id > 1000;
    +----------+
    | count(*) |
    +----------+
    |  2998999 |
    +----------+
    1 row in set (2.30 sec)

# 范围准确,瞬间查到===========================
    mysql> select count(*) from s1 where id = 1000;
    +----------+
    | count(*) |
    +----------+
    |        1 |
    +----------+
    1 row in set (0.00 sec)
  1. 字段区分度高的优先,表示字段不重复的比例.
mysql> select count(*) from s1 where gender='malea';
+----------+
| count(*) |
+----------+
|        0 |
+----------+
1 row in set (0.00 sec)

# 所有行都符合条件, 区分度为0, 相当于全文扫描,但是速度还是比没有索引的快
mysql> select count(*) from s1 where gender='male';
+----------+
| count(*) |
+----------+
|  2999999 |
+----------+
1 row in set (1.10 sec)

# 当符合条件的项不存在btree中时, 那么瞬间查询出结果
mysql> select count(*) from s1 where gender='male1';
+----------+
| count(*) |
+----------+
|        0 |
+----------+
1 row in set (0.00 sec)

设计索引的原则

  • 1、搜索的索引列,不一定是所要选择的列。换句话说,最适合索引的列是出现在 WHERE
    子句中的列,或连接子句中指定的列,而不是出现在 SELECT 关键字后的选择列表中的列;
  • 合理的建立索引能够加速数据读取效率,不合理的建立索引反而会拖慢数据库的响应速度。
  • 2、使用唯一索引,即区分度大的列;
  • 3、使用短索引。如果对字符串列进行索引,应该指定一个前缀长度,只要有可能就应该这样做。例如,如果有一个 CHAR(200)列,如果在前10个或20个字符内,多数值是惟一的,那么就不要对整个列进行索引。对前10个或20个字符进行索引能够节省大量索引空间,也可能会使查询更快。较小的索引涉及的磁盘 IO较少,较短的值比较起来更快。更为重要的是,对于较短的键值,索引高速缓存中的块能容纳更多的键值;
  • 4、不要过度索引。不要以为索引“越多越好”,什么东西都用索引是错误的。每个额
    外的索引都要占用额外的磁盘空间,并降低写操作的性能。在修改表的内容时,索引必须进
    行更新,有时可能需要重构,因此,索引越多,所花的时间越长。
  • 5、当你的程序和数据库结构/SQL语句已经优化到无法优化的程度,而程序瓶颈并不能顺利解决,那就是应该考虑使用诸如memcached这样的分布式缓存系统的时候了。
  • 6、习惯和强迫自己用EXPLAIN来分析你SQL语句的性能。
  • 7、对于 InnoDB 存储引擎的表,记录默认会按照一定的顺序保存,如果有明确定义的主
    键,则按照主键顺序保存。如果没有主键,但是有唯一索引,那么就是按照唯一索引的顺序
    保存。如果既没有主键又没有唯一索引,那么表中会自动生成一个内部列,按照这个列的顺
    序保存。按照主键或者内部列进行的访问是最快的,所以 InnoDB 表尽量自己指定主键,当
    表中同时有几个列都是唯一的,都可以作为主键的时候,要选择最常作为访问条件的列作为
    主键,提高查询的效率。另外,还需要注意,InnoDB 表的普通索引都会保存主键的键值,
    所以主键要尽可能选择较短的数据类型,可以有效地减少索引的磁盘占用,提高索引的缓存
    效果。

猜你喜欢

转载自www.cnblogs.com/fqh202/p/9428039.html