每天一个知识点:基于 Redis 的思考,一个键值数据库的实现。

可以存哪些数据?

  • Redis 能够在实际业务场景中得到广泛的应用,就是得益于支持多样化类型的 value。
  • 不同 value 类型的实现,不仅可以支撑不同业务的数据需求,而且也隐含着不同数据结构在性能、空间效率等方面的差异,从而导致不同的 value 操作之间存在着差异。

可以对数据做什么操作?

  • PUT:新写入或更新一个 key-value 对;
  • GET:根据一个 key 读取相应的 value 值;
  • DELETE:根据一个 key 删除整个 key-value 对。
  • 根据一段 key 的范围返回相应的 value 值。因此,PUT/GET/DELETE/SCAN 是一个键值数据库的基本操作集合。
  • 增加 EXISTS 操作接口,用于判断某个 key 是否存在。

键值对保存在内存还是外存?

  • 保存在内存的好处是读写很快,毕竟内存的访问速度一般都在百 ns 级别。但是,潜在的风险是一旦掉电,所有的数据都会丢失。
  • 保存在外存,虽然可以避免数据丢失,但是受限于磁盘的慢速读写(通常在几 ms 级别),键值数据库的整体性能会被拉低。
  • 结论:如何进行设计选择,我们通常需要考虑键值数据库的主要应用场景。

采用什么访问模式?

  • 通过函数库调用的方式供外部应用使用。
  • 通过网络框架以 Socket 通信的形式对外提供键值对操作,这种形式可以提供广泛的键值存储服务,但也给键值数据库的性能、运行模型提供了不同的设计选择,带来了一些潜在的问题。

如何定位键值对的位置?

  • 索引的作用是让键值数据库根据 key 找到相应 value 的存储位置,进而执行操作。
  • 不同键值数据库采用的索引并不相同,例如,Memcached 和 Redis 采用哈希表作为 key-value 索引,而 RocksDB 则采用跳表作为内存中 key-value 的索引。
  • 内存键值数据库(例如 Redis)采用哈希表作为索引,很大一部分原因在于,其键值数据基本都是保存在内存中的,而内存的高性能随机访问特性可以很好地与哈希表 O(1) 的操作复杂度相匹配。
  • Redis 采用一些常见的高效索引结构作为某些 value 类型的底层数据结构,这一技术路线为 Redis 实现高性能访问提供了良好的支撑。

不同操作的具体逻辑是怎样的?

  • 对于 GET/SCAN 操作而言,此时根据 value 的存储位置返回 value 值即可;
  • 对于 PUT 一个新的键值对数据而言,需要为该键值对分配内存空间;
  • 对于 DELETE 操作,需要删除键值对,并释放相应的内存空间,这个过程由分配器完成。

内存分配器

  • 可以采用常用的内存分配器 glibc 的 malloc 和 free,因此,并不需要特别考虑内存空间的管理问题。但是,键值数据库的键值对通常大小不一,glibc 的分配器在处理随机的大小内存块分配时,表现并不好。一旦保存的键值对数据规模过大,就可能会造成较严重的内存碎片问题。因此,分配器是键值数据库中的一个关键因素。对于以内存存储为主的 Redis 而言,这点尤为重要。Redis 的内存分配器提供了多种选择,分配效率也不一样。
  • memcached 相比 redis 为数不多的优点是由于使用预分配内存大小,mem只有内存浪费而没有内存碎片。

如何实现重启后快速提供服务?

鉴于磁盘管理要比内存管理复杂,直接采用了文件形式,将键值数据通过调用本地文件系统的操作接口保存在磁盘上。此时,只需要考虑何时将内存中的键值数据保存到文件中,就可以了。

  • 一种方式是,对于每一个键值对,都对其进行落盘保存,这虽然让数据更加可靠,但是,因为每次都要写盘,性能会受到很大影响。
  • 另一种方式是,只是周期性地把内存中的键值数据保存到文件中,这样可以避免频繁写盘操作的性能影响。但是,一个潜在的代价是数据仍然有丢失的风险。

最后,非常推荐极客时间的 Redis 核心技术与实战 这门课,受益良多。

你可能感兴趣的:(每天一个知识点:基于 Redis 的思考,一个键值数据库的实现。)