列族

介绍

在RocksDB 3.0中,我们添加了对列族的支持。

RocksDB中的每个键值对都与一个列族相关联。如果没有指定列族,则键值对与列族”default”关联。

列族提供了一种逻辑分区数据库的方法。一些有趣的性质:

  • 支持跨列族的原子写入。这意味着您可以原子地执行Write({cf1, key1, value1}, {cf2, key2, value2})。
  • 跨列族的数据库的一致视图。
  • 能够独立配置不同的列族。
  • 动态添加新列族并删除它们。这两种操作都相当快。

API

向后兼容性

尽管我们需要对API进行重大更改来支持列族,但我们仍然支持旧的API。 将应用程序升级到RocksDB 3.0不需要做任何更改。通过旧API插入的所有键值对都插入到列族”default”中。 升级后降级也是如此。如果您从未使用超过一个列族,我们不会更改任何磁盘格式,这意味着您可以安全地回滚到RocksDB 2.8。 这对Facebook内部的客户来说非常重要。

示例使用

https://github.com/facebook/rocksdb/blob/master/examples/column_families_example.cc

参考

  1. Options, ColumnFamilyOptions, DBOptions

include/rocksdb/options.h 定义,选项结构定义了RocksDB的行为和执行方式。 以前,每个选项都是在一个选项结构体中定义的。接下来,针对单个列族的选项将在ColumnFamilyOptions中定义,针对整个RocksDB实例的选项将在DBOptions中定义。 结构体继承了ColumnFamilyOptions和DBOptions,这意味着您仍然可以使用它来为一个具有单个(默认)列族的DB实例定义所有选项。

  1. ColumnFamilyHandle

使用ColumnFamilyHandle处理和引用列族。把它看作一个打开的文件描述符。在删除DB指针之前,需要删除所有columnfamilyhandle。 有一件有趣的事情:即使ColumnFamilyHandle指向一个被删除的列族,您也可以继续使用它。实际上,只有在删除所有未完成的columnfamilyhandle之后才会删除数据

  1. DB::Open(const DBOptions& db_options, const std::string& name, const std::vector<ColumnFamilyDescriptor>& column_families, std::vector<ColumnFamilyHandle*>* handles, DB** dbptr);

当以读写模式打开数据库时,需要指定数据库中当前存在的所有列族。如果不是这样,DB::Open调用将返回Status::InvalidArgument()。 使用ColumnFamilyDescriptors向量指定列族。ColumnFamilyDescriptor只是一个结构体,具有列姓和ColumnFamilyOptions。 Open call将返回一个状态和指向columnfamilyhandle的指针向量,然后可以使用这些指针引用列族。在删除DB指针之前,请确保删除所有columnfamilyhandle。

  1. DB::OpenForReadOnly(const DBOptions& db_options, const std::string& name, const std::vector<ColumnFamilyDescriptor>& column_families, std::vector<ColumnFamilyHandle*>* handles, DB** dbptr, bool error_if_log_file_exist = false)

该行为类似于DB::Open,只是它以只读模式打开DB。一个很大的区别是,当以只读方式打开数据库时,您不需要指定所有列族——您只能打开列族的一个子集。

  1. DB::ListColumnFamilies(const DBOptions& db_options, const std::string& name, std::vector<std::string>* column_families)

ListColumnFamilies是一个静态函数,它返回数据库中当前存在的所有列族的列表。

  1. DB::CreateColumnFamily(const ColumnFamilyOptions& options, const std::string& column_family_name, ColumnFamilyHandle** handle)

使用选项和名称创建指定的列族,并通过参数返回ColumnFamilyHandle。

  1. DropColumnFamily(ColumnFamilyHandle* column_family)

删除ColumnFamilyHandle指定的列族。注意,直到客户机调用delete column_family;,实际数据才会被删除。 如果您有一个出色的ColumnFamilyHandle指针,您仍然可以继续使用列族。

DB::NewIterators(const ReadOptions& options, const std::vector& column_families, std::vector* iterators)

这是一个新的调用,它使您能够在具有数据库一致视图的多个列族上创建迭代器。

WriteBatch

要以原子方式执行多个写操作,您需要构建一个WriteBatch。现在所有WriteBatch API调用都使用ColumnFamilyHandle*指定要写入的列族。

All other API calls 所有其他API调用

所有其他API调用都有一个新的参数ColumnFamilyHandle*,您可以通过它指定列族。

实现

列族背后的主要思想是它们共享写前日志,而不共享memtables和表文件。 通过共享写前日志,我们可以获得原子写的巨大好处。 通过分离memtables和表文件,我们能够独立配置列族并快速删除它们。

每次刷新单个列族时,我们都会创建一个新的WAL(写前日志)。所有给所有专栏家庭写的新文章都去了新墙。 但是,我们仍然不能删除旧的WAL,因为它包含来自其他列族的活动数据。只有当所有列族都被刷新并且包含在该WAL中的所有数据都保存在表文件中时,我们才能删除旧的WAL。 这创建了一些有趣的实现细节,并将创建有趣的调优需求。确保对RocksDB进行调优,以便定期刷新所有列族。此外,查看Options::max_total_wal_size,可以将其配置为自动刷新陈旧的列族。