ObjectiveRocks 0.11.0

ObjectiveRocks 0.11.0

Iskandar Abudiab 维护。



ObjectiveRocks

ObjectiveRocks 是 Facebook 的 RocksDB 的 Objective-C 封装 - 一种用于闪存和 RAM 存储的持久性键值存储。

Build Status Carthage Compatible License MIT


当前 RocksDB 版本:v6.2.4


快速概览

RocksDB 是一个键值存储,其中的键和值是任意大小的字节流。键根据指定的比较函数在键值存储中进行排序。RocksDB 支持原子读写、快照、迭代和许多配置选项。

ObjectiveRocks 提供了一个容易的接口到 RocksDB,以及一个面向 Objective-C 的 API,它抽象了底层的 C++ 实现,所以您不必处理它。虽然不需要了解 RocksDB 的细节来使用这个包装器,但建议您对内部结构有基本的了解,这可以解释背后的 API 设计决策。

如果您对 RocksDB 的内部结构感兴趣,请参阅 RocksDB Wiki

Swift

ObjectiveRocks提供了一个纯正的Objective-C接口,并且可以被用在Swift项目中。此外,ObjectRocks的所有测试也都转移到了Swift中。您可以查看包含OSX和iOS ObjectiveRocks实现的桥接头代码及其源代码的Tests目标。

您需要了解的最低限度的知识

  • 键和值是字节数组
  • 数据库中的所有数据都通过给定的Comparator逻辑上按顺序排列
  • RocksDB支持列族
    • 列族提供了一种逻辑分区数据库的方法,类似于MongoDB中的集合
    • 可以独立配置
    • 可以动态添加或删除
    • 键值对与数据库中确切的一个列族相关联。
    • 如果没有指定列族,则使用默认列族
  • RocksDB提供了三个基本操作
    • Get(key)
    • Put(key, value)
    • Delete(key)
  • 应用程序可以通过Merge Operator定义一个合并操作
    • 合并是一个原子性的读-修改-写操作
  • RocksDB提供了一个Iterator API来执行数据库上的RangeScan
  • RocksDB提供了一个Snapshot API,允许应用程序创建数据库的即时点视图
  • 有许多配置选项
    • DBOptions: 控制数据库的行为
    • ColumnFamilyOptions: 控制列族的行为
    • ReadOptions: 应用到单个读取操作
    • WriteOptions: 应用到单个写入操作

RocksDB Lite

ObjectiveRocks包含了两个目标,用于iOS和macOS。iOS目标构建了RocksDB Lite版本,该版本不包括完整的功能集。

这些功能仅适用于macOS

  • 列族元数据
  • 带有索引的写批处理
  • 普通和Cuckoo表工厂
  • 向量、哈希SkipList、哈希LinkList和哈希Cuckoo Memtable Rep工厂
  • 数据库备份
  • 数据库统计信息
  • 数据库属性
  • 线程状态

安装

Carthage

Carthage 是一种去中心化的依赖管理工具,它构建您的依赖并提供二进制框架。

如果您还没有安装 Carthage,可以使用以下命令通过 Homebrew 安装:

$ brew update
$ brew install carthage

要使用 Carthage 将 ObjectiveRocks 添加到您的项目中作为依赖,只需在您的 Cartfile 中添加以下行:

github "iabudiab/ObjectiveRocks"

然后运行以下命令以构建框架,并将构建的 ObjectiveRocks.framework 拖动到您的 Xcode 项目中。

$ carthage update

手动操作

1. 将 ObjectiveRocks 添加为 git 子模块

$ git submodule add https://github.com/iabudiab/ObjectiveRocks.git

2. 打开 ObjectiveRocks 文件夹,并将 ObjectiveRocks.xcodeproj 拖放到 Xcode 的 Project Navigator 中,将其添加为子项目。

3. 在您的目标 General 面板下,在 Embedded Binaries 下添加 ObjectiveRocks.framework

注意,ObjectiveRocks 依赖于 RocksDB 并将其作为 Git 子模块包含在内。

使用说明

为了简洁起见,README 将使用 NSString 表示法代替 NSData 中的密钥和值!

打开和关闭数据库实例

要打开数据库,您必须指定其位置

RocksDB *db = [RocksDB databaseAtPath:@"path/to/db"];
...
[db close];

RocksDB 提供许多配置设置,可以在打开数据库时指定。为此 ObjectiveRocks 提供了一个基于 blocks 的初始化器。您需要的最低配置是 createIfMissing,以便在数据库不存在的情况下创建一个新的数据库

RocksDB *db = [RocksDB databaseAtPath:@"path/to/db" andDBOptions:^(RocksDBOptions *options) {
	options.createIfMissing = YES;
}];

列出了所有当前可用的选项及其说明。

更健壮的设置可能看起来像这样

RocksDB *db = [RocksDB databaseAtPath:@"path/to/db" andDBOptions:^(RocksDBOptions *options) {
	options.createIfMissing = YES;

	options.maxOpenFiles = 50000;

	options.tableFacotry = [RocksDBTableFactory blockBasedTableFactoryWithOptions:^(RocksDBBlockBasedTableOptions *options) {
		options.filterPolicy = [RocksDBFilterPolicy bloomFilterPolicyWithBitsPerKey:10];
		options.blockCache = [RocksDBCache LRUCacheWithCapacity:1024 * 1024 * 1024];
		options.blockSize = 64 * 1024;
	}];

	options.writeBufferSize = 64 * 1024 * 1024;
	options.maxWriteBufferNumber = 7;
	options.targetFileSizeBase = 64 * 1024 * 1024;
	options.numLevels = 7;

	options.maxLogFileSize = 50 * 1024 * 1024;
	options.keepLogFileNum = 30;
}];

基本操作

数据库提供三项基本操作,即Put(存储)、Get(查询)和Delete(删除)以存储/查询数据。RocksDB中的键和值是任意字节数组

RocksDB *db = [RocksDB databaseAtPath:@"path/to/db" andDBOptions:^(RocksDBOptions *options) {
	options.createIfMissing = YES;
}];

NSData *data = [@"World" dataUsingEncoding:NSUTF8StringEncoding]
NSData *key = [@"Hello" dataUsingEncoding:NSUTF8StringEncoding]

[db storeData:data forKey:key];
NSData *get = [db getDataForKey:key];
[db deleteDataForKey:key];

读写错误

数据库操作可以通过传递一个NSError引用来检查是否发生了任何错误

NSError *error = nil;

[db setObject:object forKey:key error:&error];
NSMutableDictionary *dictionary = [db dataForKey:@"Hello" error:&error];
[db deleteDataForKey:@"Hello" error:&error];

读写选项

可以通过特定的选项来调整每个单个的读写操作

[db setObject:anObject forKey:aKey writeOptions:^(RocksDBWriteOptions *writeOptions) {
	writeOptions.syncWrites = YES;
	writeOptions.disableWriteAheadLog = YES;
	writeOptions.timeoutHint = 5;
	writeOptions.ignoreMissingColumnFamilies = NO;
}];

[db dataForKey:aKey readOptions:^(RocksDBReadOptions *readOptions) {
	readOptions.verifyChecksums = YES;
	readOptions.fillCache = NO;
}];

RocksDBRocksDBColumnFamily实例上也可以设置默认选项

[db setDefaultReadOptions:^(RocksDBReadOptions *readOptions) {
	readOptions.fillCache = YES;
	readOptions.verifyChecksums = YES;
} andWriteOptions:^(RocksDBWriteOptions *writeOptions) {
	writeOptions.syncWrites = YES;
	writeOptions.timeoutHint = 5;
}];

您可以在配置指南中了解更多关于读写选项的信息

迭代

迭代是通过RocksDBIterator类提供的。

您可以手动迭代

RocksDB *db = ...;
RocksDBColumnFamily *stuffColumnFamily = .../

RocksDBIterator *iterator = [db iterator];
RocksDBIterator *cfIterator = [stuffColumnFamily iterator];

// Alternatively, you can get an iterator with specific read options
iterator = [db iteratorWithReadOptions:^(RocksDBReadOptions *readOptions) {
	// Read options here
}];

for ([iterator seekToKey:@"start"]; [iterator isValid]; [iterator next]) {
	NSLog(@"%@: %@", [iterator key], [iterator value]);
	// Iterates all keys starting from key "start" 
}

或者使用提供的一个枚举块

[db setData:@"Value 1" forKey:@"A"];
[db setData:@"Value 2" forKey:@"B"];
[db setData:@"Value 3" forKey:@"C"];
[db setData:@"Value 3" forKey:@"D"];

RocksDBIterator *iterator = [db iterator];

/* Keys Enumeration */
[db enumerateKeysUsingBlock:^(NSData *key, BOOL *stop) {
	NSLog(@"%@", key);
	// A, B, C, D
}];

// reverse enumeration
[db enumerateKeysInReverse:YES usingBlock:^(NSData *key, BOOL *stop) {
	NSLog(@"%@", key);
	// D, C, B, A
}];

// Enumeration in a given key-range [start, end)
RocksDBIteratorKeyRange range = RocksDBMakeKeyRange(@"A", @"C");

[db enumerateKeysInRange:range reverse:NO usingBlock:^(NSData *key, BOOL *stop) {
	NSLog(@"%@", key, [db dataForKey:key]);
	// B, C
}];

/* Key-Value Enumeration */
[db enumerateKeysAndValuesUsingBlock:^(NSData *key, NSData *value BOOL *stop) {
	NSLog(@"%@:%@", key, value);
	// A:1, B:2, C:3, D:4
}];

[db enumerateKeysAndValuesInReverse:YES usingBlock:^(NSData *key, BOOL *stop) {
	NSLog(@"%@: %@", key, [db dataForKey:key]);
	// D:4, C:3, B:2, A:1
}];

// Enumeration in a given key-range [start, end)
RocksDBIteratorKeyRange range = RocksDBMakeKeyRange(@"A", @"C");

[db enumerateKeysAndValuesInRange:range reverse:YES usingBlock:^(NSData *key, BOOL *stop) {
	NSLog(@"%@:%@", key, [db dataForKey:key]);
	// B:2, C:3
}];

前缀遍历迭代

RocksDBIterator可以通过提供RocksDBPrefixExtractor来支持在键前缀内进行迭代。这样一个提取器是内置的,并且为每个键提取固定长度的前缀

RocksDB *db = [RocksDB databaseAtPath:_path andDBOptions:^(RocksDBOptions *options) {
	options.createIfMissing = YES;
	options.prefixExtractor = [RocksDBPrefixExtractor prefixExtractorWithType:RocksDBPrefixFixedLength length:2];
}];

[db setData:@"a" forKey:@"10.1"];
[db setData:@"b" forKey:@"10.2"];
[db setData:@"b" forKey:@"10.3"];
[db setData:@"c" forKey:@"11.1"];
[db setData:@"d" forKey:@"11.2"];
[db setData:@"d" forKey:@"11.3"];

RocksDBIterator *iterator = [db iterator];

// Enumeration starts with the key that is Greater-Than-Or-Equal to a key
// with the given "prefix" parameter
[iterator enumerateKeysWithPrefix:@"10" usingBlock:^(NSData *key, BOOL *stop) {
	NSLog(@"%@", key);
	// 10.1, 10.2, 10.3
}];

// .. so in this case the enumeration starts at key "10.2", even if "10.1" 
// has the same prefix
[iterator enumerateKeysWithPrefix:@"10.2" usingBlock:^(NSData *key, BOOL *stop) {
	NSLog(@"%@", key);
	// 10.2, 10.3
}];

您还可以定义自己的前缀提取器

RocksDBPrefixExtractor *extractor = [[RocksDBPrefixExtractor alloc] initWithName:@"custom_prefix"
	transformBlock:^id (NSData *key) {
		// Apply your key transformation to extract the prefix part
		id prefix = extractPrefixFromKey(key);
		return prefix;
	}
	prefixCandidateBlock:^BOOL (NSData *key) {
		// You can filter out keys that are not viable candidates for
		// your custom prefix format, e.g. key length is smaller than
		// the target prefix length
		BOOL isCandidate = canExtractPrefixFromKey(key);
		return isCandidate;
	}
	validPrefixBlock:^BOOL (NSData *prefix) {
		// After a prefix is extracted you can perform extra
		// checks here to verify that the prefix is valid
		BOOL isValid = isExtractedPrefixValid(prefix);
		return isValid;
	}
];

列族

一旦您有了RocksDB实例,您可以动态地创建和删除列族

RocksDB *db = [RocksDB databaseAtPath:@"path/to/db" andDBOptions:...];

RocksDBColumnFamily *columnFamily = [db createColumnFamilyWithName:@"new_column_family" andOptions:^(RocksDBColumnFamilyOptions *options) {
	// Options for the new column family
};
// Do stuff with columnFamily and close it when you're done
[columnFamily close];

// To drop it
[columnFamily drop];

注意,RocksDBColumnFamilyRocksDB 的一个子类。

如果数据库已存在除默认以外的列族,则在打开它时需要指定数据库中目前存在的所有列族,包括默认列族。您使用 RocksDBColumnFamiliesDescriptor 对象来指定列族。

RocksDBColumnFamilyDescriptor *descriptor = [RocksDBColumnFamilyDescriptor new];

[descriptor addColumnFamilyWithName:@"default" andOptions:^(RocksDBColumnFamilyOptions *options) {
	// Options for the default column family
}];
[descriptor addColumnFamilyWithName:@"stuff_column_family" andOptions:^(RocksDBColumnFamilyOptions *options) {
	// Options for the stuff_column_family
}];

RocksDB *db = [RocksDB databaseAtPath:@"path/to/db" columnFamilies:descriptor andDatabaseOptions:^(RocksDBDatabaseOptions *options) {
	// Database options here
}];

NSArray *columnFamilies = db.columnFamilies;
RocksDBColumnFamily *defaultColumnFamily = columnFamilies[0];
RocksDBColumnFamily *stuffColumnFamily = columnFamilies[1];
// At this point you can either use the db instance or 
// the defaultColumnFamily instance to access the default column family

原子更新

可以使用 WriteBatch 在数据库上原子地应用一组更新。使用 WriteBatch 有两种方式:

  • 一个基于内部块的方法
[db setData:@"Value 1" forKey:@"Key 1"];

[db performWriteBatch:^(RocksDBWriteBatch *batch, RocksDBWriteOptions *writeOptions) {
	[batch setData:@"Value 2" forKey:@"Key 2"];
	[batch setData:@"Value 3" forKey:@"Key 3"];
	[batch deleteDataForKey:@"Key 1"];
}];
  • 通过 WriteBatch 实例,这可能在“零散”逻辑中更灵活
[db setData:@"Value 1" forKey:@"Key 1"];

RocksDBWriteBatch *batch = [db writeBatch];
[batch setData:@"Value 2" forKey:@"Key 2"];
[batch setData:@"Value 3" forKey:@"Key 3"];
[batch deleteDataForKey:@"Key 1"];
...
[db applyWriteBatch:batch withWriteOptions:^(RocksDBWriteOptions *writeOptions) {
	// Write options here
}];

默认情况下,Write Batch 对象会在与 DB 实例关联的列族上操作,该实例用于创建它。但是,您也可以指定列族,以实现跨多个列族的原子写入。在这种情况下,应用批处理的具体实例无关紧要

RocksDB *db = ...;
RocksDBColumnFamily *stuffColumnFamily = .../

// Write Batch for default column family
RocksDBWriteBatch *batch = [db writeBatch];

// Write Batch for stuffColumnFamily
RocksDBWriteBatch *cfBatch = [stuffColumnFamily writeBatch];

[batch setData:@"Value 1" forKey:@"Key 1"];
[batch setData:@"Value 2" forKey:@"Key 2" inColumnFamily:stuffColumnFamily];

// You can apply the Write Batch object either on the DB instance
// or the stuffColumnFamily instance.
// The following two calls have the same effect:
/**
	[db applyWriteBatch:batch withWriteOptions:^(RocksDBWriteOptions *writeOptions) {
		// Write options here
	}];
	[stuffColumnFamily applyWriteBatch:batch withWriteOptions:^(RocksDBWriteOptions *writeOptions) {
		// Write options here
	}];
*/

快照

快照提供了一个关于键值存储状态的只读一致视图。当不再需要时,别忘了关闭快照。

[db setData:@"Value 1" forKey:@"A"];

RocksDBSnapshot *snapshot = [db snapshot];
// Alternatively, you can get a snapshot with specific read options
snapshot = [db snapshotWithReadOptions:^(RocksDBReadOptions *readOptions) {
	// Read options here
}];

[db deleteDataForKey:@"A"];
[db setData:@"Value 2" forKey:@"B"];

NSString *value1 = [snapshot dataForKey:@"A"];
// value1 == @"Value 1"
NSString *value2 = [snapshot dataForKey:@"B"];
// value2 == nil
...
[snapshot close];

检查点

检查点是在时间点上的一个可打开的数据库快照。

[db setData:@"Value 1" forKey:@"A"];
[db setData:@"Value 2" forKey:@"B"];

RocksDBCheckpoint *checkpoint = [[RocksDBCheckpoint alloc] initWithDatabase:db];
NSError *error = nil;
[checkpoint createCheckpointAtPath:@"path/to/checkpoint" error:&error];

RocksDB *db2 = [RocksDB databaseAtPath:@"path/to/checkpoint"];
...
[db close];
[db2 close];

键比较器

键值存储中的键是按照指定的比较函数顺序排列的。默认的键排序函数按字典顺序排序字节。

通过在打开数据库时提供自定义比较器,可以更改此行为,使用 RocksDBComparator

假设你有 NSString 键,并且想要它们使用不区分大小写的、基于区域的比较来排序

RocksDBComparator *localizedKeys = [[RocksDBComparator alloc] initWithName:@"LocalizedKeys" andBlock:^int (NSData *key1, NSData *key2) {
	return [key1 localizedCaseInsensitiveCompare:key2];
];
	
RocksDB *db = [RocksDB databaseAtPath:_path andDBOptions:^(RocksDBOptions *options) {
	options.comparator = localizedKeys;
}];

比较器的名称在创建数据库时附加到数据库上,并且在每次后续打开数据库时都会检查。如果名称更改,则打开调用将失败。因此,如果新的键格式和比较函数与现有数据库不兼容,则需要更改名称,并且可以安全地丢弃现有数据库的内容。

内置比较器

ObjectiveRocks具有一些内置比较器,可以使用如下方式

RocksDB *db = [RocksDB databaseAtPath:@"path/to/db" andDBOptions:^(RocksDBOptions *options) {
	options.comparator = [RocksDBComparator comaparatorWithType:RocksDBComparatorNumberAscending];
}];
  • RocksDBComparatorBytewiseAscending: 以字典序升序对键进行排序。
    • 如果没有指定,这将是默认行为。
  • RocksDBComparatorBytewiseDescending 以字典序降序对键进行排序。
  • RocksDBComparatorStringCompareAscending 通过compare选择器以升序对NSString键进行排序。
    • 该比较器假设NSString键,并通过initWithData:encoding:使用UTF-8对关联的NSData进行编码
  • RocksDBComparatorStringCompareDescending 通过compare选择器以降序对NSString键进行排序。
    • 该比较器假设NSString键,并通过initWithData:encoding:使用UTF-8对关联的NSData进行编码

合并操作符

合并操作符是RocksDB中的一种原子性读取-修改-写入操作。有关详细描述,请访问RocksDB项目的合并操作符维基页面。

comparator类似,使用一个merge operator创建的数据库无法用另一个打开。

关联合并操作符

当您有关联数据时可以使用此合并操作符

  • 您的合并操作数格式与您的Put值相同,并且
  • 可以将多个操作数合并为一个(只要它们是同一顺序的)

例如,我们可以使用合并操作符将条目追加到现有数组中,而不是完全读取它、更新它并写回

RocksDBMergeOperator *arrayAppend = [RocksDBMergeOperator operatorWithName:@"ArrayAppend" andBlock:^id (NSData *key, NSData *existingValue, NSData *mergeValue) {
	if (existingValue == nil) {
		return mergeValue;
	} else {
		[existingValue addObjectsFromArray:mergeValue];
		return existingValue;
	}
}];

RocksDB *db = [RocksDB databaseAtPath:@"path/to/db" andDBOptions:^(RocksDBOptions *options) {
	options.mergeOperator = arrayAppend;
}];

NSMutableArray *letters = [NSMutableArray arrayWithObjects:@"A", @"B", nil];

[db setData:letters forKey:@"Key"];
[db mergeData:@[@"C", @"D"] forKey:@"Key"];
[db mergeData:@[@"E"] forKey:@"Key"];

NSMutableArray *merged = [db dataForKey:@"Key"];
// merged = @[@"A", @"B", @"C", @"D", @"E"];

通用合并操作符

如果两个关联约束中的任何一个都不成立,则使用通用合并操作符。

通用合并操作符有两个方法,PartialMerge,FullMerge

  • PartialMerge:用于组合两个合并操作数(如果可能)。如果客户端指定的操作符可以逻辑上处理将两个合并操作数组合成一个单一操作数,则应在此方法中提供执行的语义,然后它应返回一个非nil对象。如果返回nil,则表示两个合并操作数不能组合成一个。

  • FullMerge:此函数接收一个现有值和一个已入栈的操作数列表。客户端指定的合并操作符应逐个应用操作数,并返回结果对象。如果返回值为nil,则表示失败,即损坏的数据、错误等。

ObjectiveRocks引入了一个新的mergeOperation方法,用于与通用合并操作符一起使用。这个假设的例子展示了如何使用客户端定义的合并操作与通用合并操作符一起使用。

RocksDBMergeOperator *mergeOp = [RocksDBMergeOperator operatorWithName:@"operator"
	partialMergeBlock:^id(NSData *key, NSData *leftOperand, NSData *rightOperand) {
		NSString *left = [leftOperand componentsSeparatedByString:@":"][0];
		NSString *right = [rightOperand componentsSeparatedByString:@":"][0];
		if ([left isEqualToString:right]) {
			return rightOperand;
		}
		return nil;
	} fullMergeBlock:^id(id key, id *existing, NSArray *operands) {
		for (NSString *operand in operands) {
			NSArray *components = [operand componentsSeparatedByString:@":"];
			NSString *action = components[1];
			if	([action isEqualToString:@"DELETE"]) {
				[existing removeObjectForKey:components[0]];
			} else {
				existing[comp[0]] = components[2];
			}
		}
		return existing;
	}
];
	
RocksDB *db = [RocksDB databaseAtPath:_path andDBOptions:^(RocksDBOptions *options) {
	options.createIfMissing = YES;
	options.mergeOperator = mergeOp;
}];

NSDictionary *object = @{@"Key 1" : @"Value 1",
						 @"Key 2" : @"Value 2",
						 @"Key 3" : @"Value 3"};

[db setObject:object forKey:@"Dict Key"];

[db mergeData:@"Key 1:UPDATE:Value X" forKey:@"Dict Key"];
[db mergeData:@"Key 4:INSERT:Value 4" forKey:@"Dict Key"];
[db mergeData:@"Key 2:DELETE" forKey:@"Dict Key"];
[db mergeData:@"Key 1:UPDATE:Value 1 New" forKey:@"Dict Key"];

id result = [db dataForKey:@"Dict Key"];
/**
result = @{@"Key 1" : @"Value 1 New",
		   @"Key 3" : @"Value 3",
		   @"Key 4" : @"Value 4"};
*/

环境 & 线程状态

RocksDBEnv允许修改后台作业的线程池。RocksDB使用此线程池进行压缩和内存表刷新。

RocksDBEnv *dbEnv = [RocksDBEnv envWithLowPriorityThreadCount:12 andHighPriorityThreadCount:4];
RocksDB *db = [RocksDB databaseAtPath:@"path/to/db" andDBOptions:^(RocksDBOptions *options) {
	options.env = dbEnv;
}];

// To get a list of all threads
NSArray *threads = dbEnv.threadList;

// "threads" array contains objects of type RocksDBThreadStatus
RocksDBThreadStatus *status = threads[0];

备份 & 还原

使用RocksDBBackupEngine备份数据库

RocksDB *db = ...

RocksDBBackupEngine *backupEngine = [[RocksDBBackupEngine alloc] initWithPath:@"path/to/backup"];
NSError *error = nil;
[backupEngine createBackupForDatabase:db error:&error];
...
[backupEngine close];

还原数据库备份

RocksDBBackupEngine *backupEngine = [[RocksDBBackupEngine alloc] initWithPath:@"path/to/backup"];
backupEngine restoreBackupToDestinationPath:@"path/to/db" error:nil];
backupEngine close];

RocksDB *db = [RocksDB databaseAtPath:@"path/to/db"];
...
[db2 close];

备份是增量备份,只将新数据复制到备份目录,因此您可以

  • 检索所有可用的备份信息
  • 还原特定增量备份而不是最后一个备份
  • 删除特定的备份
  • 保留最后N个备份并清除所有其他备份
RocksDB *db = ...

RocksDBBackupEngine *backupEngine = [[RocksDBBackupEngine alloc] initWithPath:@"path/to/backup"];

[db setData:@"Value 1" forKey:@"A"];
[backupEngine createBackupForDatabase:db error:nil];

[db setData:@"Value 2" forKey:@"B"];
[backupEngine createBackupForDatabase:db error:nil];

[db setData:@"Value 3" forKey:@"C"];
[backupEngine createBackupForDatabase:db error:nil];

// An array containing RocksDBBackupInfo objects
NSArray *backupInfo = backupEngine.backupInfo;

// Restore second backup
[backupEngine restoreBackupWithId:2 toDestinationPath:@"path/to/db" error:nil];

// Delete first backup
[backupEngine deleteBackupWithId:1 error:nil];

// Purge all except the last two
[backupEngine purgeOldBackupsKeepingLast:2 error:nil];

统计信息

您可以通过在数据库选项中创建和设置RocksDBStatistics对象来收集这些统计信息

RocksDBStatistics *dbStatistics = [RocksDBStatistics new];

RocksDB *db = [RocksDB databaseAtPath:@"path/to/db" andDBOptions:^(RocksDBOptions *options) {
	options.statistics = dbStatistics;
}];
...

[dbStatistics countForTicker:RocksDBTickerBytesRead];
RocksDBStatisticsHistogram *dbGetHistogram = [dbStatistics histogramDataForType:RocksDBHistogramDBGet];

RocksDBStatistics.h中定义了可用的计时器和直方图

属性

数据库通过列族级别的属性导出有关其状态的一些信息。在RocksDBProperties.h中定义了可用的属性

RocksDB *db = ...

NSString *dbStats = [db valueForProperty:RocksDBPropertyDBStats];
uint64_t sizeActiveMemTable = [db valueForIntProperty:RocksDBIntPropertyCurSizeActiveMemTable];

配置

目前只封装/提供RocksDB所有选项的一个子集。

稍后将添加其他选项(大多是在我有时间试验并且了解它们的功能时)。

ObjectiveRocks 数据库选项

选项 描述 默认值
createIfMissing 如果数据库缺失,将创建数据库 false
createMissingColumnFamilies 自动创建缺失的列族 false
errorIfExists 如果数据库已存在,则抛出错误 false
paranoidChecks RocksDB 會積極檢查數據的一致性 true
infoLogLevel 日志级别 INFO
maxOpenFiles DB可以使用的打开文件的数量 5000
maxWriteAheadLogSize 寫前日志寫入強制刷新前的最大大小 0 (=動態選擇)
statistics 如果非nil,則會收集數據庫操作的度量 nil
disableDataSync manifest和數據文件的內容不會同步到穩定存儲 false
useFSync 每次將存儲發到穩定存儲時都會發起fsync false
maxLogFileSize 資訊日誌文件的最大大小,超出則會旋轉 0 (=所有日志都寫入一個文件)
logFileTimeToRoll 資訊日志文件旋轉的時間(單位:秒) 0 (禁用)
keepLogFileNum 要保留的資訊日誌文件的最大數量 1000
bytesPerSync 在寫入時逐漸將文件同步到磁盤 0 (禁用)

ObjectiveRocks 列族选项

选项 描述 默认值
comparator 用於定義表中鍵的順序 字節卻序的字典順序
mergeOperator 對合併操作必須提供 nil
writeBufferSize 在寫入磁盤之前在記憶體中建築的數據量 4 * 1048576 (4MB)
maxWriteBufferNumber 在記憶體中建築的寫入緩冲板數量的最大值 2
minWriteBufferNumberToMerge 寫入存儲之前合併的寫入緩冲板的最小數量 1
compressionType 使用指定的壓縮算法進行塊壓縮 Snappy 壓縮
prefixExtractor 如果非nil,則使用指定的函數來確定鍵的前綴 nil
numLevels DB的層數 7
level0FileNumCompactionTrigger 使用指定的壓縮算法進行塊壓縮 4
level0SlowdownWritesTrigger level-0文件數量的軟限制 20
level0StopWritesTrigger level-0文件數量的最大值 24
targetFileSizeBase 収縮的目標文件大小 2 * 1048576 (2MB)
targetFileSizeMultiplier 不同層次文件大小的倍數器 1 (不同層次的文件將具有相似的大小)
maxBytesForLevelBase 控制某個層次的最大總數據大小 10 * 1048576 (10MB)
maxBytesForLevelMultiplier 文件大小/層次的倍數器 10
expandedCompactionFactor 所有収縮文件中的最大字節數 25
sourceCompactionFactor 在單次收縮運行中要収縮的所有源文件的 最大字節數 1
maxGrandparentOverlapFactor 控制父代(即level+2)中最大重叠字節數 10
softRateLimit 當任何層次的收縮分數超過此限制時,將延遲0-1毫秒的PUT操作 0 (禁用)
hardRateLimit 当任何级别的压缩得分超过此限制时,put操作将延迟1ms。 0 (禁用)
arenaBlockSize 区域能够分配内存的一个块的大小。 0
disableAutoCompactions 禁用自动压缩。 false
purgeRedundantKvsWhileFlush 在将内存表刷新到存储时清除重复键或删除的键。 true
verifyChecksumsInCompaction 如果设置为true,压缩时将验证每个读取操作的校验和(作为压缩的一部分)。 true
filterDeletes 当此选项为true时,使用KeyMayExist API过滤删除操作。 false
maxSequentialSkipInIterations 在迭代中如果不设置此选项,跳过连续的相同用户键。 8
memTableRepFactory 提供一个MemTableRep对象的工厂。 nil。内部,RocksDB将使用一个提供一个基于跳表实现的MemTableRep工厂的工厂。
tableFacotry 提供一个TableFactory对象的工厂。 nil。内部,RocksDB将使用一个提供默认TableBuilder和TableReader实现(使用默认的BlockBasedTableOptions)的基于块的表工厂。
memtablePrefixBloomBits 如果设置了prefixExtractor且bloom_bits不为0,对内存表创建前缀布隆。 0
memtablePrefixBloomProbes 每个键的哈希探测数。 6
memtablePrefixBloomHugePageTlbSize 用于内存表中bloom的巨大页面TLB的页面大小。 0
bloomLocality 控制布隆过滤器探测的局部性,以改善缓存未命中率。 0
maxSuccessiveMerges 在内存表中键上的最大连续合并操作数。 0
minPartialMergeOperands 积累的局部合并操作数,达到此数量后执行局部合并。 2

读取选项

选项 描述 默认值
verifyChecksums 读取的数据将与相应的校验和进行验证。 true
fillCache 是否将此迭代的读取操作缓存到内存中。 true

写入选项

选项 描述 默认值
syncWrites 在被视为完成之前,将从缓冲区刷新写入。 false
disableWriteAheadLog 写入将不会首先写入写入日志。 true
ignoreMissingColumnFamilies 忽略写入不存在的列族。 false

表格式

  • BlockBasedTable:是RocksDB的默认SST表格式。
  • PlainTable:是RocksDB的SST文件格式,针对降低纯内存或低延迟媒体的查询延迟进行了优化。
  • CuckooTable:专为需要快速点查找但不需要快速范围扫描的应用而设计。

Memtable Formats

  • SkipList:使用跳表存储键。这是默认选项。
  • Vector:创建由 std::vector 支持的 MemTableReps。在迭代时,向量会被排序。这对于迭代很少而写操作一般不会在读取开始后执行的工作负载非常有用。
  • HashSkipList:包含一个指向跳表的固定桶数组。
  • HashLinkedList:基于散列表创建内存表:它包含一个指向固定桶数组的指针,每个桶内部包含一个或多个超过预定义阈值的链表或跳表。
  • Cuckoo:创建基于布隆哈希的内存表示。布隆哈希是一种闭哈希策略,其中所有键/值对都存储在桶数组本身中,而不是存储在桶数组以外的某些数据结构中。

更多详细信息,请访问维基百科 基于哈希的内存表实现