介绍¶
在 RocksDB3.0,我们增加了 Column Families 的支持。
RocksDB 的每个键值对都与唯一一个列族(column family)结合。如果没有指定 Column Family,键值对将会结合到“default” 列族。
列族提供了一种从逻辑上给数据库分片的方法。他的一些有趣的特性包括:
- 支持跨列族原子写。意味着你可以原子执行 Write({cf1, key1, value1}, {cf2, key2, value2})。
- 跨列族的一致性视图。
- 允许对不同的列族进行不同的配置
- 即时添加/删除列族。两个操作都是非常快的。
API¶
向后兼容¶
尽管我们需要做一些很极端的修改来支持列族,我们还是支持老的 API 的。你不需要对你的应用做任何改变就可以迁移到 RocksDB3.0。所有通过旧的 API 插入的键值对都会插入到“default”列族。升级之后降级也是同理。如果你从未使用超过一个列族,我们不会改变任何磁盘格式,也就是说你可以放心地回滚到 RocksDB2.8。这对我们那些 FaceBook 里的客户来说,是非常重要的。
使用例子¶
参考¶
Options, ColumnFamilyOptions, DBOptions¶
在 include/rocksdb/options.h 中定义,Options 结构定义 RocksDB 的行为和性能。以前,每个 option 都被定义在单独的 Options 结构体里。现在,对单个列族的配置,会被定义在 ColumnFamilyOptions,然后那些针对整个 RocksDB 实例的配置会被定义在 DBOptions。Options 结构体同时继承 ColumnFamilyOptions 和 DBOptions,你仍旧可以用它来设置所有针对只有一个类族的 DB 实例的配置。
ColumnFamilyHandle¶
列族通过 ColumnFamilyHandle 调用和引用。可以把它当成一个打开的文件描述符。在你删除 DB 指针前,你需要删除所有所有 ColumnFamilyHandle。一个有趣的事实:即时一个 ColumnFamilyHandle 指向一个已经删除的列族,你还是可以继续使用它。数据只有在你将所有存在的 ColumnFamilyHandle 都删除了,才会被清除。
DB::Open(const DBOptions& db_options, const std::string& name, const std::vector& column_families, std::vector handles, DB* dbptr);¶
当使用读写模式打开一个 DB 的时候,你需要声明所有已经存在于 DB 的列族。如果不是,DB::Open 会返回 Status::InvalidArgument(),你可以用一个 ColumnFamilyDescriptors 的 vector 来声明列族。ColumnFamilyDescriptors 是一个只有列族名和 ColumnFamilyOptions 的结构体。Open 会返回一个 Status 以及一个 ColumnFamilyHandle 指针的 vector,你可以用他们来引用这些列族。删除 DB 指针前请确保你已经删除了所有 ColumnFamilyHandle。
DB::OpenForReadOnly(const DBOptions& db_options, const std::string& name, const std::vector& column_families, std::vector handles, DB* dbptr, bool error_if_log_file_exist = false)¶
行为与 DB::Open 类似,只不过他用只读模式打开 DB。其中一个比较大的差别是,如果用只读模式打开 DB,你不需要声明所有列族——你可以只打开一个列族的子集。
DB::ListColumnFamilies(const DBOptions& db_options, const std::string& name, std::vector* column_families)¶
ListColumnFamilies 是一个静态方法,会返回当前 DB 里面存在的列族
DB::CreateColumnFamily(const ColumnFamilyOptions& options, const std::string& column_family_name, ColumnFamilyHandle** handle)¶
指定一个名字和配置,创建一个列族,然后在参数里面返回一个 ColumnFamilyHandle。
DropColumnFamily(ColumnFamilyHandle* column_family)¶
删除 ColumnFamilyHandle 指向的列族。注意,实际的数据在客户端调用 delete column_family 之前 并不会被删除。只要你还有 column_family,你就还可以继续使用这个列族。
DB::NewIterators(const ReadOptions& options, const std::vector& column_families, std::vector* iterators)¶
这是一个新的调用,允许你在 DB 上创建一个跨列族的一致性视图。
批量写¶
你需要构建一个 WriteBatch 来实现原子化的批量写操作。所有 WriteBatch API 现在可以额外携带一个 ColumnFamilyHandle 指针来声明你希望写到哪个列族。
所有其他 API 调用¶
所有其他 API 调用都有了一个新的参数 ColumnFamilyHandle*
,用于声明你想操作的列族。
实现¶
列族的主要实现思想是他们共享一个 WAL 日志,但是不共享 memtable 和 table 文件。通过共享 WAL 文件,我们实现了酷酷的原子写。通过隔离 memtable 和 table 文件,我们可以独立配置每个列族并且快速删除它们。
每当一个单独的列族刷盘,我们创建一个新的 WAL 文件。所有列族的所有新的写入都会去到新的 WAL 文件。但是,我们还不能删除旧的 WAL,因为他还有一些对其他列族有用的数据。我们只能在所有的列族都把这个 WAL 里的数据刷盘了,才能删除这个 WAL 文件。这带来了一些有趣的实现细节以及一些有趣的调优需求。确保你的所有列族都会有规律地刷盘。另外,看一下 Options::max_total_wal_size,通过配置他,过期的列族能自动被刷盘。