我在看一篇关于hash indexing的文章,好像md5
和php的函数很像,都是取一个字符串值,返回一个表示该字符串的整数,而且这个表示是一致的。这种相似性真的存在吗,还是我错过了什么?另外,有人对 MySQL 用于基于哈希的索引结构的哈希算法有所了解吗?
1 回答
我不是假装对 MySQL 算法给出完整的描述,但有一些事情可能会被猜到。
首先,Hash table wiki是必读的。然后我们从MySQL 文档中得到一个通知:
- 它们仅用于使用 = 或 <=> 运算符的相等比较(但速度非常快)。它们不用于
查找值范围的比较运算符,例如 <。依赖这种类型的单值查找的系统被称为“键值存储”;要将
MySQL 用于此类应用程序,请尽可能使用哈希索引。- 优化器不能使用哈希索引来加速 ORDER BY 操作。(这种类型的索引不能用于按顺序搜索下一个条目。)
- MySQL 无法确定两个值之间大约有多少行(范围优化器使用它来决定使用哪个索引)。如果您将 MyISAM 表更改为哈希索引的 MEMORY 表,这可能会影响某些查询。
- 只能使用整个键来搜索行。(使用 B 树索引,键的任何最左边的前缀都可用于查找行。)
这指向以下(相当常见的)属性:
MySQL 哈希函数对固定长度的“全键”记录进行操作(尽管这是一个问题,如何处理 varchar,例如,它们可能会用零填充到最大长度)
在猜测散列函数的上限行数时,引擎可能会使用一个
max_heap_table_size
全局值和一个参数。MAX_ROWS
MySQL允许非唯一键,但会警告按比例减速。至少这可以说明没有第二个散列函数,而只是在冲突解决中使用的链表。
至于实际使用的功能,我认为没什么好说的。MySQL 甚至可以根据一些关键启发式方法使用不同的函数(例如,一个用于大部分顺序数据,例如 ID,但另一个用于 CHAR),当然它的输出会根据估计的行数而改变。但是,只有当 BTREE 无法为您提供足够好的性能时,或者您永远不会使用它的任何优势时,您才应该考虑哈希索引,我想这是一种罕见的情况。
更新
一点来源:/storage/heap/hp_hash.c
包含哈希函数的一些实现。至少,对于 TEXT 和 VARCHAR,他们对不同的类型使用不同的技术是一个正确的假设:
/*
* Fowler/Noll/Vo hash
*
* The basis of the hash algorithm was taken from an idea sent by email to the
* IEEE Posix P1003.2 mailing list from Phong Vo (kpv@research.att.com) and
* Glenn Fowler (gsf@research.att.com). Landon Curt Noll (chongo@toad.com)
* later improved on their algorithm.
*
* The magic is in the interesting relationship between the special prime
* 16777619 (2^24 + 403) and 2^32 and 2^8.
*
* This hash produces the fewest collisions of any function that we've seen so
* far, and works well on both numbers and strings.
*/
我将尝试给出一个简化的解释。
ulong nr= 1, nr2= 4;
for (seg=keydef->seg,endseg=seg+keydef->keysegs ; seg < endseg ; seg++)
复合键的每一部分都单独处理,结果累加在nr
.
if (seg->null_bit)
{
if (rec[seg->null_pos] & seg->null_bit)
{
nr^= (nr << 1) | 1;
continue;
}
}
NULL 值单独处理。
if (seg->type == HA_KEYTYPE_TEXT)
{
uint char_length= seg->length; /* TODO: fix to use my_charpos() */
seg->charset->coll->hash_sort(seg->charset, pos, char_length,
&nr, &nr2);
}
else if (seg->type == HA_KEYTYPE_VARTEXT1) /* Any VARCHAR segments */
{
uint pack_length= seg->bit_start;
uint length= (pack_length == 1 ? (uint) *(uchar*) pos : uint2korr(pos));
seg->charset->coll->hash_sort(seg->charset, pos+pack_length,
length, &nr, &nr2);
}
TEXT 和 VARCHAR 也是如此。hash_sort
大概是其他一些考虑排序规则的功能。VARCHAR 的前缀长度为 1 或 2 字节。
else
{
uchar *end= pos+seg->length;
for ( ; pos < end ; pos++)
{
nr *=16777619;
nr ^=(uint) *pos;
}
}
并且所有其他类型都通过乘法和xor
.