mysql索引理解实践

存储结构

b-tree


在这里插入图片描述

B-tree 索引特点
1.每个叶子节点都包含指向下一个叶子节点的指针,只有叶子节点包含数据。
2.所有数据是按顺序存储的,很适合范围查找
3.可以对 <,<=,=,>,>=,BETWEEN,IN,以及不以通配符开始的LIKE使用索引。
4.b+ 叶子节点存数据key-value,非叶子节点存储key
B-tree 索引限制
1.INSERT、UPDATE和DELETE影响性能,MySQL不仅要保存数据,还要保存索引文件

hash

hash索引限制
1.只存储hash 值,不存储实际的值,不能根据hash索引来进行避免读取行
2.hash索引数值并没有排序
3.hash索引不支持部分列匹配查找,哈希索引是索引列的全部值来计算哈希值
4.hash索引不支持等值比较和范围查询
5.hash冲突时,存储引擎需遍历链表中的所以指针进行比较,比较耗时,维护的代价比较高。
优势
1.hash索引只有精确匹配索引的列查询才有效,查询速度快。

索引建立的原则


1. 索引匹配原则,当遇到范围查询(>,<,between,like) 就停止匹配后面的索引列
2. =和in 中可以乱序,因为优化器可以优化索引可以识别的形式
3. 选择区分度高的索引列。count(disctint col)/count(*)
4. 扩展索引而不是新建,索引多会影响数据效率。
5. 索引列,不添加计算,min和max可以只走索引,索引是有序的
6. 索引字段尽量使用数字型

索引执行计划


CREATE TABLE IF NOT EXISTS `users`(
   `id` INT ,
   `name` VARCHAR(30) NOT NULL,
   `addr` VARCHAR(40) NOT NULL,
   `birth` DATE
);
mysql> select * from users;
+----+-------+----------+------------+
| id | name  | addr     | birth      |
+----+-------+----------+------------+
|  1 | name1 | beijing  | 2020-10-14 |
|  2 | name2 | shanghai | 2020-10-15 |
|  3 | name3 | hangzhou | 2020-10-15 |
|  4 | name4 | guanzhou | 2020-10-16 |
+----+-------+----------+------------+
4 rows in set (0.00 sec)
mysql>  explain select * from users where id=2 \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: users
   partitions: NULL
         type: ref  # 类型 range,all,const
possible_keys: id_name_addr #可能的索引列
          key: id_name_addr # 实际用的索引列
      key_len: 5  # 实际索引字段长度字节
          ref: const # 索引列
         rows: 1
     filtered: 100.00
        Extra: NULL # 是否下推、覆盖索引
1 row in set, 1 warning (0.00 sec)

索引类型和一些概念


索引分类

一个表只能有一个聚簇索引。
聚簇索引 就是以主键创建的索引,聚集索引在叶子节点存储的是表中的数据。
非聚簇索引就是以非主键创建的索引,非聚集索引在叶子节点存储的是主键和索引列
回表拿到主键再查找这个过程
覆盖索引-Using index假设所查询的列,刚好都是索引对应的列,不用再回表查,那么这叫覆盖索引
索引下推-Using index condition,在存储引擎进行数据过滤操作

普通索引允许被索引的数据列包含重复的值。而唯一索引列的值必须唯一,但允许有空值。
主键索引特殊的唯一索引,不允许有空值。

普通索引-》唯一索引-》主键索引 条件越来越严格

组合索引

列值的组合必须唯一
匹配原则

- 最左匹配原则
没有id,不走索引
mysql>  explain select * from users where  name='name2' and addr='shanghai';
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | users | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    4 |    25.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
- 匹配列前缀 
id和name都走索引
mysql>  explain select * from users where id=2 and name like 'name%';
+----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+-----------------------+
| id | select_type | table | partitions | type  | possible_keys | key          | key_len | ref  | rows | filtered | Extra                 |
+----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+-----------------------+
|  1 | SIMPLE      | users | NULL       | range | id_name_addr  | id_name_addr | 127     | NULL |    1 |   100.00 | Using index condition |
+----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)
id走索引
mysql>  explain select * from users where id=2 and name like '%name%';
+----+-------------+-------+------------+------+---------------+--------------+---------+-------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key          | key_len | ref   | rows | filtered | Extra                 |
+----+-------------+-------+------------+------+---------------+--------------+---------+-------+------+----------+-----------------------+
|  1 | SIMPLE      | users | NULL       | ref  | id_name_addr  | id_name_addr | 5       | const |    1 |    25.00 | Using index condition |
+----+-------------+-------+------------+------+---------------+--------------+---------+-------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)
- 匹配范围值
- or组合索引不走索引
mysql>  explain select * from users where id=2 or  name = 'name2';
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | users | NULL       | ALL  | id_name_addr  | NULL | NULL    | NULL |    4 |    43.75 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
- 匹配范围
mysql>  explain select * from users where id>2 and name like 'name%' and addr='shanghai';
+----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+-----------------------+
| id | select_type | table | partitions | type  | possible_keys | key          | key_len | ref  | rows | filtered | Extra                 |
+----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+-----------------------+
|  1 | SIMPLE      | users | NULL       | range | id_name_addr  | id_name_addr | 5       | NULL |    2 |    25.00 | Using index condition |
+----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)
-覆盖索引
mysql> explain select name,addr  from users  where name='name2' and addr='shanghai';
+----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+--------------------------+
| id | select_type | table | partitions | type  | possible_keys | key          | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | users | NULL       | index | NULL          | id_name_addr | 289     | NULL |    4 |    25.00 | Using where; Using index |
+----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)

索引排序

- 索引顺序扫描
mysql> explain select id,name,addr  from users  order by id,name,addr;
+----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type  | possible_keys | key          | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | users | NULL       | index | NULL          | id_name_addr | 289     | NULL |    4 |   100.00 | Using index |
+----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
- filesort 不能进行索引排序,就利用排序算法在内存中对数据进行排序。
mysql> explain select id,name,addr  from users  order by id,name,addr;
+----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type  | possible_keys | key          | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | users | NULL       | index | NULL          | id_name_addr | 289     | NULL |    4 |   100.00 | Using index |
+----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

特殊情况,查询数据和条件都走索引
or 也走索引

mysql> explain select name,addr  from users  where name='name2' or addr='shanghai';
+----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+--------------------------+
| id | select_type | table | partitions | type  | possible_keys | key          | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | users | NULL       | index | NULL          | id_name_addr | 289     | NULL |    4 |    43.75 | Using where; Using index |
+----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)

索引优化总结

  • 索引进行查询的时候尽量不要使用表达式,把计算放在业务层而不是数据库层
    select * from id users where id+1=3;
  • 尽量用主键索引,不用其他索引,为了防止回表
  • union all,in,or 都能过使用索引,推荐用in
  • 不要让字段的默认值为NULL,null值不会包含在索引中,组合索引有一列为null,那这一列对符合索引是无效。
  • 前缀索引、索引选择性问题
    对串列进行索引,如果可能应该指定一个前缀长度。
  • 用组合索引代替多列索引
  • 尽量不要使用NOT IN、<>、!= 操作
  • 当需要进行表连接的时候,join的字段数据类型必须一致

索引情况查看

  • 查看整体
show index from table_name;# 查看表的索引
optimize table table_name;#
mysql> show status like 'Handler_read%';
+-----------------------+-------+
| Variable_name         | Value |
+-----------------------+-------+
| Handler_read_first    | 25    |
| Handler_read_key      | 441   | #Handler_read_key 的值将很高,这个值代表了一个行被索引值读的次数
| Handler_read_last     | 0     |
| Handler_read_next     | 390   |
| Handler_read_prev     | 0     |
| Handler_read_rnd      | 41    |
| Handler_read_rnd_next | 93    |#数据文件中读下一行的请求数
+-----------------------+-------+
7 rows in set (0.03 sec)
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值