【数据库系统原理】第22篇:索引的神经:哈希索引、位图索引与全文索引的原理及应用场景
目录一、索引选型的多元性没有银弹只有场景适配二、哈希索引O(1)的等值查询承诺三、位图索引比特向量的集合运算四、全文索引倒排表与文本搜索五、索引选择的决策框架六、结语专业化的力量与代价一、索引选型的多元性没有银弹只有场景适配上一篇我们深入解析了B树——关系数据库索引结构的绝对主力。B树以其高扇出、高度平衡和叶子节点链表的精巧设计在等值查询、范围查询和排序输出等混合负载中提供了全面均衡的性能表现。然而B树并非万能的。在某些特定场景下B树的性能会被其他索引结构远远甩开——这些场景不要求“面面俱到”而是要求某一维度的极致优化。等值查询在B树下需要O(log N)次IO——这对绝大多数场景已经足够高效。但有些系统——缓存服务器、会话存储、分布式键值数据库——对等值查询的延迟要求是微秒级的O(log N)与O(1)之间的差距在此类场景中就是能否上线的分界线。低基数字段上的多条件组合查询——例如在数亿条用户记录中筛选“性别女 AND 城市北京 AND 会员等级黄金”——B树索引需要分别扫描多个索引再取交集而有一种结构能将这种多条件组合转化为硬件级的位运算。海量文本的语义搜索——例如在数千万篇文档中查找包含“数据库”和“索引”且两者相距不超过5个词的文档——B树的等值和范围比较模型在此完全失效需要一种完全不同原理的索引结构。这三种场景分别对应了三类专用索引哈希索引、位图索引和全文索引。它们不追求B树那样的普适性而是在各自的专长领域内实现B树无法企及的极致性能。理解它们的原理、优势和局限是数据库物理设计中索引选型能力的关键组成部分。二、哈希索引O(1)的等值查询承诺哈希索引的原理极为简洁——对索引键值应用哈希函数将结果映射到桶号桶内存储指向实际记录的指针。当查询WHERE 键值 K时系统对K计算哈希值定位目标桶在桶内进行小范围的顺序匹配找到目标记录。整个过程的核心路径与数据量无关——无论表中有一百万条还是一百亿条记录等值查询的IO次数不随数据量增长。哈希索引的理论查询复杂度为O(1)优于B树的O(log N)。但在实践中这一理论优势是否能兑现为可感知的性能差距取决于多个条件。哈希函数必须将键值均匀分布到各个桶中——如果哈希函数设计不佳大量键值被映射到少数桶中桶内链表过长查询退化为小范围线性扫描。桶的数量和大小需要合理配置——桶太少会导致桶内记录过多桶太多则浪费空间。这些设计参数在静态哈希中需要在创建索引时确定难以应对数据量的持续增长。动态哈希技术——可扩展哈希与线性哈希——解决了桶数量的动态扩展问题。可扩展哈希将哈希值视为二进制位串初始使用哈希值的前缀来索引桶目录随着数据增长逐步增加使用的前缀位数桶目录的规模以2的幂次扩展每个桶在溢出时分裂为两个。线性哈希则以更平滑的方式逐步增加桶每次只分裂一个桶桶数量的增长与数据量增长保持线性关系。这些技术让哈希索引能够在数据动态增长的场景中维持O(1)的近似性能但在工程实现上比B树复杂得多。哈希索引的根本性局限是无法支持范围查询和排序。哈希函数的设计目标恰恰是破坏键值之间的顺序关系——两个键值越接近哈希后的值应当越不相关。因此WHERE 键值 BETWEEN A AND B在哈希索引下无法利用索引只能退化为全表扫描。ORDER BY 键值同样无计可施。这意味着哈希索引只适用于纯等值查询的工作负载。在MySQL中MEMORY存储引擎默认使用哈希索引InnoDB也支持在特定条件下创建自适应哈希索引作为B树的补充加速结构。在键值数据库领域——如Redis、Memcached——哈希索引是核心存储结构。三、位图索引比特向量的集合运算位图索引的底层数据结构与B树和哈希索引截然不同——它不为每个键值存储记录指针而是为键值的每个可能取值维护一个位图。位图是一个比特序列序列长度等于表的行数第i位为1表示第i条记录在该列上的取值为该键值。例如在“性别”列上取值“男”的位图为10110...第1、3、4条记录为男性“女”的位图为01001...第2、5条记录为女性。位图索引的威力在多条件组合查询中集中爆发。查询WHERE 性别 女 AND 城市 北京 AND 会员等级 黄金时系统不需要分别扫描三个索引再取交集——它只需取出三个位图“女”位图、“北京”位图、“黄金”位图对它们执行按位AND操作。按位AND是CPU的单周期指令三个包含数十亿比特的位图在硬件层面可以在极短时间内完成交集运算。查询速度几乎与数据量和组合条件的复杂度无关。位图索引对低基数列——取值种类远小于行数的列——最为高效。性别2种取值、婚姻状况几种取值、省份数十种取值是位图索引的理想应用场景。当列基数较高时——例如用户ID几乎每行一个独立取值——位图将退化为极其稀疏的长序列存储效率极低查询时需要扫描大量全零比特性能不如B树。因此位图索引在实践中通常用于数据仓库和OLAP系统在这些系统中维度表的外码列基数较低且查询模式以多维度交叉过滤为主。位图索引的一项工程挑战是更新代价。当插入或删除一行时所有位图都需要在对应位置插入或删除一个比特。这种位图长度的全局变更对于频繁更新的事务型系统而言代价过高。因此位图索引在OLTP系统中较少使用而在以批量加载和只读查询为主的数据仓库环境中大放异彩。Oracle的位图索引是这一领域的典型实现。四、全文索引倒排表与文本搜索当查询条件从结构化字段的对等比较转向非结构化文本的语义搜索时B树、哈希索引和位图索引全部失效。WHERE 文章内容 CONTAINS 数据库 AND 文章内容 CONTAINS 索引这样的查询无法通过键值排序或哈希映射来高效处理。全文索引正是为这一场景设计的专用索引结构。全文索引的核心数据结构是倒排表。倒排表为文档集合中出现的每一个词维护一个倒排列表——记录该词出现在哪些文档中以及在每个文档中的位置信息。当用户搜索“数据库 AND 索引”时系统分别取出“数据库”和“索引”的倒排列表计算两个列表的交集哪些文档同时包含两个词。如果搜索“数据库 NEAR 索引”两者相距不超过N个词系统在位置信息的基础上进一步过滤。整个过程中系统仅访问包含搜索词的文档完全不触碰不包含这些词的文档——这与全表扫描逐篇检查相比效率提升了数个数量级。全文索引的构建需要对原始文本进行语言学预处理。第一步是分词——将连续文本切割为独立的词条。对于英文等以空格分隔的语言分词相对直接对于中文等无天然分隔的语言分词本身就是一个复杂的自然语言处理任务。第二步是去除停用词——过滤掉“的”、“是”、“the”、“a”等对检索意义不大的高频词以缩减索引体积。第三步是词干提取——将词的不同形态还原为词干使得“running”和“ran”都能匹配搜索词“run”。这些预处理步骤的质量直接影响检索的召回率和精确率。全文索引的查询能力远超简单的存在性检查。现代全文搜索引擎支持布尔检索AND、OR、NOT、短语检索用引号包围精确匹配短语、邻近检索词A在词B的N个词范围内、模糊检索容忍拼写错误、以及加权检索标题匹配的权重高于正文匹配。结果排序通常采用向量空间模型或BM25算法根据词频、逆文档频率和文档长度归一化等因素计算查询与文档的相关性评分将最相关的结果排在最前。MySQL的FULLTEXT索引、PostgreSQL的GIN索引配合tsvector、以及独立的搜索引擎Elasticsearch和Solr都建立在倒排表的原理基础之上。它们的区别在于分布式扩展能力、实时更新机制和分析器生态的丰富程度但底层的倒排表逻辑是一致的。五、索引选择的决策框架在面对一个具体的表和工作负载时设计者需要在B树、哈希索引、位图索引和全文索引之间做出选择。这个选择不应是出于对某种技术的好恶而应是基于对查询模式和数据特征的客观分析。第一步分析该表上的查询模式。查询是等值查询还是范围查询是单列过滤还是多列组合过滤是结构化字段匹配还是文本内容搜索查询的延迟要求是毫秒级还是秒级写入负载是每秒数千行还是每天批量导入第二步评估索引列的数据特征。列的基数高低如何列值的分布是否均匀列的长度如何长文本列不适合B树索引因为键值过大导致扇出降低数据量级和增长速度如何第三步根据前两步的分析结果匹配索引类型。等值查询为主、范围查询缺失的场景考虑哈希索引或B树自适应哈希。低基数列的多条件组合查询为主、写入更新稀少的分析场景考虑位图索引。文本内容的语义搜索使用全文索引。所有其他混合场景——包括等值查询与范围查询并存、需要排序输出、写入频繁——B树是默认且最安全的选择。在大多数实际系统中同一张表上可能同时存在多种索引。在用户表上主码使用B树聚簇索引性别和城市列上可能建位图索引如果是数据仓库用户昵称列上建全文索引以支持模糊搜索手机号列上建唯一B树索引以保证唯一性和快速登录查询。索引选型不是单选题而是根据多维度需求进行组合设计。六、结语专业化的力量与代价B树的普适性容易让人产生一种错觉——只要掌握了B树就掌握了索引的全部。本文展现的三种专用索引结构有力地反驳了这一错觉。哈希索引在等值查询上以O(1)碾压B树的O(log N)位图索引在多条件组合查询中以硬件级位运算取代B树的索引扫描与交集全文索引以倒排表支撑了B树完全无能为力的文本搜索场景。然而专业性伴随而来的是场景的局限性。哈希索引不支持范围查询位图索引在写入密集场景下代价高昂全文索引需要复杂的分词和排序基础设施。它们各自只能在特定的工作负载下发挥优势在不匹配的场景中表现甚至远不如B树。这正是索引选型需要深度知识的原因——不知道每种索引的适用边界就无法做出正确选择。下一篇我们将从索引的静态结构转向查询执行的动态过程——查询编译与优化。一条SQL语句从文本提交到结果返回经历了语法解析、语义检查、代数优化和物理计划生成等多道工序。理解这条流水线才能真正理解索引为什么能加速某些查询而对另一些查询无能为力以及查询优化器在背后所做的智能选择。