除了b-tree 索引,mysql还提供了如下索引:
- hash索引
只有memory引擎支持,场景简单
- r-tree索引
myisam的一个特殊索引类型,主要用于地理空间数据类型
- full-text
myisam的一个特殊索引,主要用于全文索引,从mysql 5.6开始innodb支持全文索引
索引 / 存储引擎myisaminnodbmemoryb-tree索引支持支持支持hash索引不支持不支持支持r-tree索引支持支持不支持full-text索引支持支持不支持
最常用的索引也就是b-tree索引和hash索引,且只有memory, ndb两种引擎支持hash索引。 hash索引适于key-value查询,通过hash索引比b-tree索引查询更加迅速。但hash索引不支持范围查找例如<><==,>==等。 memory只有在”=”的条件下才会使用hash索引
mysql在 8.0才支持函数索引,在此之前只能对列的前面某一部分进行索引,例如标题title字段,可以只取title的前10个字符索引,这样的特性大大缩小了索引文件的大小,但前缀索引也有缺点,在order by和group by操作时失效。
create index idx_title on film(title(10));
1 特点
只存在数组,用一个hash函数把key转换成一个确定的内存位置,然后把value放在数组的该位置。使用 hash 自然会有哈希冲突可能,mysql 采取拉链法解决。
hash索引基于hash表实现,只有查询条件精确匹配hash索引中的列时,才能够使用到hash索引。对于hash索引中的所有列,存储引擎会为每行计算一个hashcode,hash索引中存储的就是hashcode。
- 例如一个维护了身份证号和姓名的表,根据身份证号查找对应名字,其hash索引如下:
比如我们想查id_card_n4对应username:
- 将id_card_n4通过hash函数算出a
- 按顺序遍历,找到user4
四个id_card_n值并不一定递增,这样即使增加新的user,速度也快,只需在后追加。 当然缺点也很明显,不是有序,所以hash索引做区间查询速度很慢。比如要找身份证号在[id_card_x, id_card_y]区间的所有用户,就须全表扫描。
2 hash索引的缺陷
- 必须二次查找
- 不支持部分索引查找、范围查找
- 哈希码可能存在哈希冲突,如果hash 算法设计不好,碰撞过多,性能也会变差
- 索引存放的是hash值,所以仅支持 < = > 以及 in
- 无法通过操作索引来排序,因为存放的时候会经过hash计算,但是计算的hash值和存放的不一定相等,所以无法排序
- 不能避免全表扫描,只是由于在memory表里支持非唯一值hash索引,即不同的索引键,可能存在相同hash值
- 因为哈希表是一种根据关键字直接访问内存存储位置的数据结构 ,所以利用其原理的hash 索引,也就需要将所有数据文件添加到内存,这就很耗内存
- 如果所有的查询都是等值查询,那么hash确实快,但实际上范围查找数据更多
- 智能处理键值得全值匹配
- 查询hash函数决定着索引键的大小
要使innodb或myisam支持哈希索引,可以通过伪哈希索引来实现,叫自适应哈希索引。
可通过增加一个字段,存储hash值,将hash值建立索引,在插入和更新的时候,建立触发器,自动添加计算后的hash到表里。
哈希表这种结构适用于只有等值查询的场景,比如memcached。
3 案例应用
假如有一个非常非常大的表,比如用户登录时需要通过email检索出用户,如果直接在email列建索引,除了索引区间匹配,还要进行字符串匹配比对,email短还好,如果长的话这个查询代价就比较大。 若此时,在email建立哈希索引,查询以int查询,性能就比字符串比对查询快多了。
hash 算法
建立哈希索引,首先就要选定哈希算法,《高性能mysql》说到的crc32算法。
insert update select 操作
在表中添加hash值的字段:
alter table `user` add column email_hash int unsigned not null default 0;
接下来就是在update和insert时,自动更新 email_hash 字段,通过触发器实现:
delimiter | create trigger user_hash_insert before insert on `user` for each row begin set new.email_hash=crc32(new.email); end; | create trigger user_hash_update before update on `user` for each row begin set new.email_hash=crc32(new.email); end; | delimiter ;
这样select请求就会变成:
select `email`, `email_hash` from `user` where email_hash = crc32(“xxoo@gmail.com”) and `email`= “xxoo@gmail.com”;
+—————————-+————+
| email | email_hash |
+—————————-+————+
| xxoo@gmail.com | 2765311122 |
+—————————-+————+
and email = “xxoo@gmail.com” 是为了防止哈希碰撞时数据不准确。
到此这篇关于mysql面试题讲解之如何设置hash索引的文章就介绍到这了,更多相关mysql 设置hash索引内容请搜索www.887551.com以前的文章或继续浏览下面的相关文章希望大家以后多多支持www.887551.com!