数据库核心:索引,你知道多少?

138 阅读4分钟

今天聊点面试中经常聊的话题 —— 索引!虽然网上已经有很多类似的文章啦,但是我们开启的方式却不同。


01. 体验很重要


首先打造一个世界上最简单的迷你版数据库,一起来围观体验。

#!/bin/bash

# 迷你版数据库对外提供的函数
method=$1
# 向迷你版数据库中存放的key
key=$2
# 向迷你版数据库中存储的value
value=$3

# key-value 数据存储
function db_set() {
    echo "$key,$value" >> database
}

# 根据 key 查询数据库存储的 value
function db_get() {
    grep "^$key," database | sed -e "s/^$key,//" | tail -n 1
}

case $method in
  (db_set)
     db_set
     ;;
  (db_get)
     db_get
     ;;
  (*)
     echo "Error method"
     ;;
esac

这款迷你版的数据库,实现了一个 key-value 的简易数据存储,主要由数据存储(db_set)、查询(db_get)两个函数构成。


当我们调用 db_set key value,它将在数据库中保存你所输入的 key 和 value。


其中 key 和 value 几乎可以是任何内容,例如,值可以是一个 JSON 文档。然后,当我们调用 db_get key,它会查找与输入 key 相关联的最新值并返回。


例如,向迷你数据库插入数据。

./minidb.sh db_set 123456 '{"name":"London","attractions":["Big Ben","London Eye"]}'


./minidb.sh db_set 42 '{"name":"San Francisco","attractions":["Godlen Gate Bridge"]}'

然后,从迷你数据库查询指定的数据。

./minidb.sh db_get 42

./minidb.sh db_get 123456



迷你数据库到这就算体验完了,原理超级简单,就是往 database 文件尾部追加数据就好了,其实许多数据库内部实现都和 db_set 类似,它们都使用日志文件,因为它只支持追加式的方式更新。


但是,大家有没有想过?


如果日志文件保存了大量的记录,那么数据库的查询(db_get)肯定会非常差。每次要查找一个 key 对应的值,就必须扫描整个数据库文件来查找 key 出现的位置,这样开销会很大,有没有高效的解决方案呢?


为了高效地查找数据库中特定的 key 的值,那么就需要引入新的数据结构 —— 索引!


02. 索引


哈希索引。

我们继续以 key-value 数据的索引为基础,假设数据存储全部采用追加式的文件组成,像上面的迷你数据库那样,数据都追加到 database 文件中。那么最简单的索引策略,莫过于把每个 key 对应文件中的字节偏移量(也就是在文件中的位置),保存到内存中的哈希表中(Hashtable 或 HashMap),这样就可以快速找到每个值的位置。


每当向数据库追加新的 key-value 时,也就需要更新内存中的 HashMap 来记录刚刚写入数据的偏移量;当查找某个值时,使用 HashMap 来找到文件中的存储位置,然后读取其内容。


上面的方式虽然很简单,但是的确是一个可行的方法。静下来思考,多数实现其实都是换汤不换药,变化无非也就是通过计算 key 的 hash 值,然后映射对应的 value。


但是哈希索引使用起来,存在一定的问题(注意,这块面试的时候经常会聊呦!)。

  1. 哈希表必须全部放入内存,如果有大量的 key 的情况下,表现就不会那么好啦;

  2. 区间查询效率不太好,例如要查询 key0000 和 key9999 区间的所有键,只能采用逐个查找的方式查询每一个键;

  3. 由于哈希索引数据没有按照索引值顺序存储,所以无法用来进行排序。

  4. 。。。。。。


面对哈希索引的一些限制,在 LSM-Tree、B-Tree 等索引结构上得到了很大的解决,时间关系,今天就不深入展开啦。


03. 常见面试题


  1. B-Tree 与 哈希索引的比较?

    参见官方答案:

    https://dev.mysql.com/doc/refman/8.0/en/index-btree-hash.html

  2. 索引能提升查询效率,是不是越多越好呢?

    索引是额外的数据结构,但是维护这个额外的数据结构肯定也会引入开销,特别是在新数据写入的时候。由于每次写数据时,需要更新索引,所以索引也会降低写的速度,设计系统的时候一定要进行权衡。

  3. 聊聊哈希表、二叉搜索树、B-Tree、B+Tree 常见数据结构?学习算法的好网址,建议收藏。

    https://www.cs.usfca.edu/~galles/visualization/OpenHash.html

    https://www.cs.usfca.edu/~galles/visualization/BST.html

    https://www.cs.usfca.edu/~galles/visualization/BTree.html

    https://www.cs.usfca.edu/~galles/visualization/BPlusTree.html


文中有些观点来自于近期看的一本书《数据密集型应用系统设计》,开卷有益,希望大家抽时间多读书、多看报、少吃零食多睡觉。最后,希望通过本次简短的分享,都有所启发。

山再高,往上攀,总能登顶! 

路再长,走下去,定能到达!

欢迎关注微信公众号“一猿小讲”了解更多精彩分享。