哈希索引的介绍和应用
哈希索引
-
-
一.哈希表
-
- 1.概念
-
二.哈希索引
-
- 1.概念
- 2.举例
- 3.哈希索引的限制
-
三.自定义哈希索引
-
- 1.创建思路
- 2.示例
- 3.触发器维护
- 4.截取MD5()函数返回值
-
一.哈希表
1.概念
哈希表又被称为散列表(Hash table),它是通过将键值(Key value)映射到表中特定位置来实现快速数据存取的一种数据结构。其基本原理是利用键值的唯一性及对应关系实现高效的插入、查找和删除操作。其核心机制在于利用键值映射至表中特定位置以实现快速定位记录的过程。这种对应关系通常称为哈希函数(Hash function),而对应的存储区域则被称为哈希表(Hash table)。
给定表M被定义为一个哈希(Hash)表,并且存在一个称为f的哈希(Hash)函数。对于任意给定的关键字key,在应用该函数后若能够得到包含该关键字的记录在表中的地址,则称此过程满足哈希(Hash)性质。
二.哈希索引
1.概念
该方法中的哈希索引为哈希表提供了基础。该方法仅当查询涉及所有指定列时才能生效。存储引擎会针对每一行数据计算其所有指定列对应的Hash Code(Hash Code),这个数值较小,并且不同键值会产生不同的或独特的Hash Code。这些Hash Code信息将被组织到相应的索引结构中,并且同时生成指向相应数据记录的位置信息。
MySQL 中仅指定 MySQL 的 Memory 引擎明确指定支持哈希索引;同时,默认情况下 MySQL 的 Memory 引擎也会建立该类型的索引;此外,在 MySQL 的数据库体系中,默认情况下 B-Tree 索引也被配置为常见的存储结构;值得注意的是,在 MySQL 的某些配置下,默认情况下可能会允许非唯一键的存在;这种特性在数据库领域内是非常独特的;如果多个列的哈希值相同,则该系统会以链表的方式存储多个记录指针到同一个哈希条目中
2.举例
-- 给列fname 创建索引 基于memory引擎
create table testhash(
fname varchar(50) not null,
lname varchar(50) not null,
key using hash(fname)
) engine = memory;
-- 插入数据
insert into testhase('aaa','AAA'),('bbb','BBB'),('ccc','CCC'),('ddd','DDD');
-- 查询数据
select * from testhash;
-- 数据列表
fname lname
aaa AAA
bbb BBB
ccc CCC
ddd DDD
-- 假设哈希函数f(),它返回的哈希码如下
f('aaa')= 2323
f('bbb')= 7437
f('ccc')= 8785
f('ddd')= 5565
-- 则哈希索引的数据结构如下(每个槽的编号是顺序的,但是数据记录行不是)
槽(slot) 值(vaule)
2323 指向第一行的指针
5565 指向第四行的指针
7437 指向第二行的指针
8785 指向第三行的指针
-- 数据查询示例
select * from testhash where fname = 'aaa'
查询流程:
计算 'aaa' 的哈希值 2323
使用该哈希值找到指针第一行的指针
找到该行比较值是否是'aaa',以确保就是要查找的行
返回记录
AI写代码
3.哈希索引的限制
a.只包含哈希值和指针,不存储字段值,不能使用索引中的值来读取行
b. 哈希索引槽不遵循基于索引值的顺序存储方式, 因此导致无法实现排序功能(因信息不明确)
c. 哈希索引无法基于单个索引字段执行部分匹配查询; 其哈希值计算基于每个索引字段的完整信息; 若配置有两个索引列,则需依靠这两个索引起进行数据检索
d.哈希索引只支持等值查询,不支持范围查询(支持 = , in() , <=> )
e. 在哈希表结构下实现快速数据访问;当出现冲突情况(即不同索引列值具有相同的哈希码 slot 值)时,则必须将引擎表中的链表节点全部存储起来,并依次比较各节点对应的索引列值, 最终返回符合条件的记录。
f.随着哈希冲突频率的增加,系统的后续维护工作也会相应加重。不同索引列的值可能会映射到同一个哈希地址上从而导致哈希冲突。在移除相关记录时必须逐一核对并删除对应的链表项以避免数据丢失的问题。
由于这些约束条件的存在,仅在特定条件下适用该方案,其性能预计会有显著提升.
三.自定义哈希索引
InnoDB引擎拥有一个独特的功能称为"自适应哈希表(adaptive hash table)".当InnoDB检测到某些键值被频繁使用时,它会在内存中基于B-Tree树状结构的基础上额外创建一个哈希表.这种设计使得B-Tree树状结构也具备了像哈希表一样的优势特性,例如快速的键查找.值得注意的是这一机制是完全自主运行的内部实现细节,用户无法通过显式配置来干预或关闭该功能.
1.创建思路
基于B-Tree构建一个伪哈希索引后,则可模仿InnoDB的方式建立相应的哈希索引。其主要优势在于仅需较小规模的索引导致对超长键的支持。
2.示例
-- B-Tree 来存储url(本身数据很长,创建索引查询速度不高)
select * from test_hash_demo where url = "http://www.baidu.com";
-- 删除原来的url索引,新增一个索引列 url_crc,使用CRC32做哈希,查询如下
select * from test_hash_demo where url = "http://www.baidu.com" and url_crc = CRC32("http://www.baidu.com");
AI写代码
该方案具有很高的实用价值。该方案中的MySQL优化器会利用一种高效且占用存储空间小的基于url_crc列的索引机制来实现数据快速定位。即使存在多个记录具有相同索引值的情况,在哈希值匹配的情况下,系统会逐一对应行进行比对,并最终返回所有匹配记录。
缺陷是需要维护哈希值.可以手动维护,也可以触发器维护
3.触发器维护
-- 创建表
create table pseudohash(
id int unsigned not null auto_increment,
url varchar(255) not null,
url_crl int unsigned not null default 0,
primary key (id)
);
-- 创建出发器
delimiter //
create trigger pseudohash_crc_ins before insert into pseudohash for each row begin
set new.url_crc = crc32(new.url);
end ;
//
create trigger pseudohash_crc_upd before update on pseudohash for each row begin
set new.url_crc= crc32(new.url);
end;
//
-- 插入数据
insert into pseudohash(url) values ('http://www.mysql.com');
select * from pesudohash;
-- 数据列表
id url url_crc
i http://www.mysql.com 1560514994
-- 修改数据
update pseudohash set url = 'http://www.mysql.com/' where id = 1;
select * from pesudohash;
-- 数据列表
id url url_crc
i http://www.mysql.com/ 158250469
AI写代码
不建议使用传统的SHA1和MD5算法来生成哈希值。它们会导致大量的冗余数据存储。这将占据过多存储空间资源,并进而影响整体系统的性能。
4.截取MD5()函数返回值
当数据量极大时,在处理大量数据时, crc32算法可能导致较多的哈希碰撞。一种解决方案是利用MD5算法的部分输出结果来构建自定义的散列函数。
select conv(right(md5('http://www.mysql.com/'),16),16,10) as HASH64;
-- 数据列表
HASH64
976117372318281581
AI写代码
In the execution of hash indexing queries, it is imperative to ensure that the WHERE clause contains fixed constant values to prevent hash collisions;
crc32()返回的是32位的整数,当索引有9300条记录时出现的概率是1%
-- 错误写法
select word,crc from words where crc = crc32('gnu');
-- 数据列表
word crc
codding 1774765869
gnu 1774765869
-- 正确写法
select word,crc from words where crc = crc32('gnu') and word = 'gnu';
AI写代码
要避免冲突,必须再where条件中带入哈希值和对应列值;
AI写代码
另外一种选择是采用FNV64()函数作为哈希函数。该算法可通过插件形式在所有MySQL版本中实现,并采用64位散列值。其计算速度较快,并且其冲突率低于crc32()。
