知识卡片

二级索引的本质:再建一个Map,用空间换时间解决O(N)遍历效率问题

普通读书笔记卡

内容

关系模型底层可以用KV映射(Map)来实现:以主键(PK)作为Map的key,把这一行的其他字段值打包作为value,通过map.get(主键值)就能高效地拿到一整行数据。但当查询条件不是主键、而是其他字段(比如按user_ID查询)时,这个天然按主键组织的Map就没办法直接用了——最直接的想法是遍历Map里的每一个条目,逐个检查user_ID是否匹配,符合就保留、不符合就丢弃,但这种方式的效率是O(N),如果有一亿条记录,就要做上亿次这样的判断,明显太慢。解决办法是引入空间换时间的思路:再建一个新的Map,这次以user_ID作为key、以对应的主键值列表作为value——有了这个新Map,只需要一次get操作就能快速拿到所有符合user_ID条件的主键列表,再拿这些主键去查最初那个Map,取出完整的行数据即可。这正是关系数据库里”二级索引”这个概念在最底层的真实机制:不是什么神秘的黑科技,本质就是针对某个高频被用作查询条件、但又不是主键的字段,额外建立一个”字段值→主键”的映射关系,用多存一份数据(空间)的代价,换来查询效率从O(N)线性扫描降低到接近O(1)的直接查找(时间)。这个案例给出了一条理解和设计索引类结构的通用心智模型:任何”按某个非主键字段高效查找数据”的需求,本质上都可以归结为”需不需要为这个字段单独维护一份’字段值到主键’的映射”——这个思路不局限于关系数据库的二级索引,也适用于任何需要为某种非天然的访问模式(原本组织方式之外的检索维度)提供快速查找能力的场景,核心权衡永远是”额外的存储和维护成本”和”查询效率的提升幅度”之间的取舍。

参考来源

- 位置:《高可用架构(第1卷)》第6章《大数据与数据库》"6.7 从NoSQL历史看未来"节,"6.7.3 1980年:Know SQL"(源文件:_epub-src/OEBPS/Text/Chapter6_7_4.xhtml) - 结论依据:原文说明"如果我有1亿条记录,就要做次这件事。明显的O(N)效率太慢了。那要怎么加快一下?有需求就会有人响应,我们可以用一个空间换时间的法子……上图里增加了一个新的Map:Map:key->user_ID,value->[ID]……刚才介绍的其实就是关系模型如何映射到Map(也就是KV模型)的关键方法了",直接支撑本卡片结论。 - 原始内容:如果我有1亿条记录,就要做次这件事。明显的O(N)效率太慢了。那要怎么加快一下?……我们可以用一个空间换时间的法子……Map:key->user_ID,value->[ID]……刚才介绍的其实就是关系模型如何映射到Map(也就是KV模型)的关键方法了。