【神经网络与深度学习】Leveldb的一些具体操作说明
本文转自 http://blog.csdn.net/poweruser5956/article/details/7727325
Leveldb概述
leveldb提供了持久的键值对的存储。key和value为任意的字节数组。键的存储是有序的,可以通过用户自定义的比较函数进行排序。
打开数据库
leveldb数据库的名字和文件系统目录是一致的。所有数据库的内容都存放在这个文件系统的目录下。下面的实例展示了如何打开leveldb数据库,如果没有则会自动创建。
#include <assert>
#include "leveldb/db.h"
leveldb::DB* db;
leveldb::Options options;
options.create_if_missing = true;
leveldb::Status status = leveldb::DB::Open(options, "/tmp/testdb", &db);
assert(status.ok());
...
如果数据库已经存在的情况下能够得到错误,则可以在leveldb::DB::Open前面增加如下内容:
options.error_if_exists = true;
Status
在leveldb中,大部分函数会返回上面实例中提到的leveldb::Status类型。如果函数出错,可以检查结果是否正确或者打印相关的错误信息:
leveldb::Status s = ...;
if (!s.ok()) cerr << s.ToString() << endl;
关闭数据库
当用完数据库的时候应当关闭数据库。例如:
... open the db as described above ...
... do something with db ...
delete db;
读和写
数据库提供了插入、删除和查询的方法来修改或查询数据库。例如,下面的代码展示了将值从key1下移动到key2下。
std::string value;
leveldb::Status s = db->Get(leveldb::ReadOptions(), key1, &value);
if (s.ok()) s = db->Put(leveldb::WriteOptions(), key2, value);
if (s.ok()) s = db->Delete(leveldb::WriteOptions(), key1);
原子更新操作
在将key2插入之后key1删除之前调用的进程挂掉,在不同的key下存在相同的value。WriteBatch类可以通过一系列的更新操作来避免这个问题。
#include "leveldb/write_batch.h"
...
std::string value;
leveldb::Status s = db->Get(leveldb::ReadOptions(), key1, &value);
if (s.ok()) {
leveldb::WriteBatch batch;
batch.Delete(key1);
batch.Put(key2, value);
s = db->Write(leveldb::WriteOptions(), &batch);
}
WriteBatch类顺序的执行一系列的操作。在例子中,要在插入之前调用删除操作。这样当key1和key2相同的时候,不需要结束错误的删除值的操作。
WriteBatch类除了原子性的好处之外,可以大大加快大批量更新操作的速度。
同步写
默认情况下,每一次写操作都是异步的:当把写操作提交之后函数就会返回。从内存到持久化存储的转化是异步的。同步标志可以控制写操作在数据已经执行了所有的持久化存储之后返回。(在Posix操作系统上,在写操作返回之前是通过调用fsync(...)、fdatasync(...)、msync(..., MS_SYNC)其中的一个方法实现)。
leveldb::WriteOptions write_options;
write_options.sync = true;
db->Put(write_options, ...);
异步操作通过比同步操作快1000多倍。异步写的负面影响是机器故障可能会引起最近的少部分更新操作丢失。即使当sync标志位false的时候,写进程的错误(不是重启机器)也不会引起任何丢失。在写操作完成之前,更新操作已经提交给了操作系统。
异步写通常情况下是安全的。例如,在故障发生之后,当加载大批量的数据到数据库时,可以通过重新启动bulk load来处理丢失的更新操作。一种可能的混合模式是每N次写是同步的,在故障发生的时候,在最后一次同步写完成之后bulkload会重启。(同步写可以更新用来描述故障时从哪个地方重启的标志。)
WriteBatch 类提供了一种异步写的替代方案。WriteBatch通过多个更新语句一块执行来完成同步操作。同步写入的开销将会合在一块。
Concurrency
一个数据库可能在同一时刻仅被一个进程打开。leveldb需要一个操作系统的锁来阻止这种情况。在一个单独的进程中,相同的level::DB对象可以被不同的线程安全的访问。比如,不同的线程可以写入或者迭代或者查询相同的数据库而不需要额外的同步(leveldb会自动执行必要的同步工作)。其他的一些对象如Iterator和WriteBatch也可能需要额外的同步。如果两个线程共享同一个对象,线程必须通过锁机制来访问。更多详细的内容可以通过头文件查看。
IIteration
下面的例子展示了如何打印数据库中的所有键值对。
leveldb::Iterator* it = db->NewIterator(leveldb::ReadOptions());
for (it->SeekToFirst(); it->Valid(); it->Next()) {
cout << it->key().ToString() << ": " << it->value().ToString() << endl;
}
assert(it->status().ok()); // Check for any errors found during the scan
delete it;
下面的例子展示了如何访问key在[start,limit)范围内的情况:
for (it->Seek(start);
it->Valid() && it->key().ToString() < limit;
it->Next()) {
...
}
下面展示了逆序访问(注意:逆序访问会比正序访问要慢)
for (it->SeekToLast(); it->Valid(); it->Prev()) { ... }
快照
快照提供了和整个键值对的存储一致的只读的视图。ReadOptions::snapshot如果不等于Null,可以用来指示读取一个特定的版本的数据库。如果ReadOptions::snapshot等于Null,隐含得读取当前版本的数据库。
快照通过DB::GetSnapshot()方法创建:
leveldb::ReadOptions options; options.snapshot= db->GetSnapshot(); ... apply some updates to db ... leveldb::Iterator*iter = db->NewIterator(options); ... read using iter to view the state whenthe snapshot was created ... delete iter;db->ReleaseSnapshot(options.snapshot);
当快照对象不再需要的时候,应该通过DB::ReleaseSnapshot接口释放。这样可以摆脱为了支持快照读取而维持的状态。
Slice
前面的it->key()和it->value()的返回值为leveldb::Slice类型的实例。Slice的结构非常简单,包含了长度和指向字节数组类型的指针字段。返回Slice的代价要比std::string类型小的多,因为不要拷贝键值对。另外,leveldb的方法不返回以'\0'结尾的C风格的字符串,因为leveldb允许键和值包含'\0'。
std::string和以'\0'结尾的C风格的字符串可以非常容易的转换为Slice类型:
leveldb::Slice s1 = "hello";std::string str("world"); leveldb::Slice s2 = str;
Slice可以非常容易的转换为std::string类型:
std::string str = s1.ToString();
assert(str ==std::string("hello"));
当在使用Slice的时候应该注意Slice指向的字节数组仍然有效。例如,下面的代码就有问题:
leveldb::Slice slice;
if (...) {
std::string str = ...;
slice = str;
}
Use(slice);
当超出了str的作用域之后,str将会被销毁,slice指向的内容将会失效。
比较器
前面的例子中key的排序按照系统默认的排序方式,即按照字典的排序方式。当打开数据库时可以提供定制的比较器。例如,数据库的键可以由两个数字组成,利用第一个数字进行比较,跟第二个数字无关。第一步,按照如下规则定义leveldb::Comparator的子类:
class TwoPartComparator :public leveldb::Comparator { public: // Three-way comparison function: // if a< b: negative result // if a > b: positive result // else: zero resultint Compare(const leveldb::Slice& a, const leveldb::Slice&
b) const {int a1, a2, b1, b2; ParseKey(a, &a1, &a2); ParseKey(b, &b1,&b2); if (a1 < b1) return -1; if (a1 > b1) return +1; if (a2 < b2)return -1; if (a2 > b2) return +1; return 0; } // Ignore the followingmethods for now: const char* Name() const { return
"TwoPartComparator";} void FindShortestSeparator(std::string*, const leveldb::Slice&) const { }void FindShortSuccessor(std::string*) const { } };
第二步,使用定制的比较器:
TwoPartComparator cmp;
leveldb::DB* db;
leveldb::Options options;
options.create_if_missing = true;
options.comparator = &cmp;
leveldb::Status status =leveldb::DB::Open(options, "/tmp/testdb", &db);
...
向下兼容性
当数据库创建时,比较器的Name()方法的返回值和数据库相关,在其后的数据库打开都会被检查。如果Name()的返回值改变,leveldb::DB::Open()方法将会失败。因此,只有当新的key的格式和比较函数不能和现有的数据库匹配时才能改变Name()方法的返回值。现有数据库中的数据会被抛弃。
但是随着时间的推移,仍然可以安装预先计划的改变几个比特。例如,在每个key的最后保留一个版本号(对多数应用一个字节已经足够)。当改变key的格式的时候,(例如在TwoPartComparator的key中增加第三部分 ),(a)保持相同的比较器名字(b)为每一个key增加版本号(c)改变比较器函数,确保可以用key中的版本数字来决定如何解释key。
性能
可以通过改变include/leveldb/options.h中的默认值来调整性能。
Block大小
leveldb相邻的key一块放到同一个block中,block是持久化存储的单元。默认block大小为4096字节。经常处理大块查询的应用应当提高block的大小。经常处理大量的小value的应用程序使用更小的block,性能测试有提高时可以采用。block的大小小于1kb或者大于几mb没有性能优势。越大的block大小对压缩更有优势。
压缩
在写入硬盘之前每一个block都是被单独的压缩。压缩默认是开启的,因为压缩方法非常快,并且非压缩的数据将不可用。罕见情况下,如果不采用压缩对性能有明显提升,可以采用非压缩。
leveldb::Options options;
options.compression = leveldb::kNoCompression;
... leveldb::DB::Open(options, name, ...) ....
缓存
数据库的内容被存储在文件系统的一系列文件中,每个文件包含多个压缩的block。如果options.cache非空,可以用来缓存使用频率非常高的未压缩的block内容。
#include "leveldb/cache.h"
leveldb::Options options;
options.cache = leveldb::NewLRUCache(100 * 1048576); // 100MB cache
leveldb::DB* db;
leveldb::DB::Open(options, name, &db);
... use the db ...
delete db
delete options.cache;
需要注意的是,缓存中保存的是未压缩的数据,因此大小和数据的大小相同。
当执行大批量的读取时,应用程序应该放弃缓存,只有这样才不会取代原先缓存中的内容。迭代器选项fill_cache可以实现这种方式:
leveldb::ReadOptions options;
options.fill_cache = false;
leveldb::Iterator* it = db->NewIterator(options);
for (it->SeekToFirst(); it->Valid(); it->Next()) {
...
}
Key layout
硬盘存储和传输的基本单元为block。通过数据库的key的排序方式相邻的key可能存放到同一个block中。因此应用程序可以通过将不常使用的key放到单独的区域来提高程序的性能。
假设基于leveldb实现的简单的文件系统,存储的键值对类型如下:
filename -> permission-bits, length, list of file_block_ids
file_block_id -> data
在filename加上一个字符的前缀,比如’/’,在file_block_id加上另外一个字符作为前缀,比如’0’。这样缓存就不会强制缓存大数据的filename部分。
Filters
由于leveldb在硬盘上的数据组织方式,在一次的读取操作中可能会独到多条数据。可选的FilterPolicy 机制可以用来减少读取硬盘的次数。
leveldb::Options options;
options.filter_policy = NewBloomFilter(10);
leveldb::DB* db;
leveldb::DB::Open(options, "/tmp/testdb", &db);
... use the database ...
delete db;
delete options.filter_policy;
上面的例子中用到了基于Bloom filter的过滤器代理。基于Bloom filter的过滤器依赖于每个key中相同比特的数据(在上述例子中为10个比特)。这种方法将减少Get()方法对磁盘的不必要的读写。增加key的比特数将会节省更多的内存。建议如果数据不适合在内存中存放或者做大量的随机读取操作设置过滤器代理。
当使用定制的比较器时,需要确认过滤器代理和比较器是兼容的。例如,当比较器在比较key的时候忽略了key后面的空间,NewBloomFilter一定不能用这种比较器。相反,应用程序应当提供一个定制的同样忽略了key后面空间的过滤器代理。例如:
class CustomFilterPolicy : public leveldb::FilterPolicy {
private:
FilterPolicy* builtin_policy_;
public:
CustomFilterPolicy() : builtin_policy_(NewBloomFilter(10)) { }
~CustomFilterPolicy() { delete builtin_policy_; }
const char* Name() const { return "IgnoreTrailingSpacesFilter"; }
void CreateFilter(const Slice* keys, int n, std::string* dst) const {
// Use builtin bloom filter code after removing trailing spaces
std::vector<Slice> trimmed(n);
for (int i = 0; i < n; i++) {
trimmed[i] = RemoveTrailingSpaces(keys[i]);
}
return builtin_policy_->CreateFilter(&trimmed[i], n, dst);
}
bool KeyMayMatch(const Slice& key, const Slice& filter) const {
// Use builtin bloom filter code after removing trailing spaces
return builtin_policy_->KeyMayMatch(RemoveTrailingSpaces(key), filter);
}
};
更高级的应用程序可以使用不是基于bloom filter的过滤器而是基于一系列key的概括的过滤器。详情参见leveldb/filter_policy.h。
校验和
Leveldb对所有存储在硬盘上的数据都会进行校验。对校验的控制有两个单独的参数:
l ReadOptions::verify_checksums强制对文件系统中读取的所有数据校验。默认情况下该校验不开启。
l Options::paranoid_checks在打开数据库之前应当设置为true,这样当内部出现错误的时候可以立刻产生出来。当数据库打开或者做其他数据库操作的时候有问题的数据库的错误就会产生。默认情况下该选项为关闭状态,即使数据库有错误也仍然可用。
近似大小
GetApproximateSizes方法可以获取一个或几个key范围内所使用的文件系统的近似大小。
leveldb::Range ranges[2];
ranges[0] = leveldb::Range("a", "c");
ranges[1] = leveldb::Range("x", "z");
uint64_t sizes[2];
leveldb::Status s = db->GetApproximateSizes(ranges, 2, sizes);
上述代码会将key的范围为[a,c)所占用文件系统大小返回给sizes[0],将key的范围为[x,z)所占用文件系统大小返回给sizes[1]。
环境
所有文件操作(包括其他操作系统调用)都和leveldb::Env对象相关。高级的客户端通过自己的环境变量来得到更好的控制。例如,应用程序可以通过增加文件系统的人为延迟来避免leveldb对系统的其他工作的影响。
class SlowEnv : public leveldb::Env {
.. implementation of the Env interface ...
};
SlowEnv env;
leveldb::Options options;
options.env = &env;
Status s = leveldb::DB::Open(options, ...);
移植
通过提供leveldb/port/port.h导出的类型/方法/函数的新平台的特殊实现,leveldb可以移植到新的平台。查看leveldb/port/port_example.h获取更多的详细信息。
另外,新平台需要实现新的默认的leveldb::Env,可以参考leveldb/util/env_posix.h。
【神经网络与深度学习】Leveldb的一些具体操作说明的更多相关文章
- 【神经网络与深度学习】【CUDA开发】caffe-windows win32下的编译尝试
[神经网络与深度学习][CUDA开发]caffe-windows win32下的编译尝试 标签:[神经网络与深度学习] [CUDA开发] 主要是在开发Qt的应用程序时,需要的是有一个使用的库文件也只是 ...
- (转)神经网络和深度学习简史(第一部分):从感知机到BP算法
深度|神经网络和深度学习简史(第一部分):从感知机到BP算法 2016-01-23 机器之心 来自Andrey Kurenkov 作者:Andrey Kurenkov 机器之心编译出品 参与:chen ...
- [DeeplearningAI笔记]神经网络与深度学习人工智能行业大师访谈
觉得有用的话,欢迎一起讨论相互学习~Follow Me 吴恩达采访Geoffrey Hinton NG:前几十年,你就已经发明了这么多神经网络和深度学习相关的概念,我其实很好奇,在这么多你发明的东西中 ...
- 【吴恩达课后测验】Course 1 - 神经网络和深度学习 - 第二周测验【中英】
[中英][吴恩达课后测验]Course 1 - 神经网络和深度学习 - 第二周测验 第2周测验 - 神经网络基础 神经元节点计算什么? [ ]神经元节点先计算激活函数,再计算线性函数(z = Wx + ...
- 【吴恩达课后测验】Course 1 - 神经网络和深度学习 - 第一周测验【中英】
[吴恩达课后测验]Course 1 - 神经网络和深度学习 - 第一周测验[中英] 第一周测验 - 深度学习简介 和“AI是新电力”相类似的说法是什么? [ ]AI为我们的家庭和办公室的个人设备供电 ...
- 对比《动手学深度学习》 PDF代码+《神经网络与深度学习 》PDF
随着AlphaGo与李世石大战的落幕,人工智能成为话题焦点.AlphaGo背后的工作原理"深度学习"也跳入大众的视野.什么是深度学习,什么是神经网络,为何一段程序在精密的围棋大赛中 ...
- 如何理解归一化(Normalization)对于神经网络(深度学习)的帮助?
如何理解归一化(Normalization)对于神经网络(深度学习)的帮助? 作者:知乎用户链接:https://www.zhihu.com/question/326034346/answer/730 ...
- 【神经网络与深度学习】卷积神经网络(CNN)
[神经网络与深度学习]卷积神经网络(CNN) 标签:[神经网络与深度学习] 实际上前面已经发布过一次,但是这次重新复习了一下,决定再发博一次. 说明:以后的总结,还应该以我的认识进行总结,这样比较符合 ...
- 【神经网络与深度学习】【Matlab开发】caffe-windows使能Matlab2015b接口
[神经网络与深度学习][Matlab开发]caffe-windows使能Matlab2015b接口 标签:[神经网络与深度学习] [Matlab开发] 主要是想全部来一次,所以使能了Matlab的接口 ...
- 【神经网络与深度学习】【python开发】caffe-windows使能python接口使用draw_net.py绘制网络结构图过程
[神经网络与深度学习][python开发]caffe-windows使能python接口使用draw_net.py绘制网络结构图过程 标签:[神经网络与深度学习] [python开发] 主要是想用py ...
随机推荐
- Android APK 手动签名
首先,如果没有签名密钥,先生成密钥: keytool -genkey -alias android.keystore -keyalg RSA -validity 20000 -keystore and ...
- qt5--定时器
定时器方式一:----定时器事件 需要 #include <QTimerEvent> #include "win.h" #include <QDebug& ...
- AngularJS基础语法
1.ng-app 决定了angularjs的作用域范围,你可以如下使用: <html ng-app> … </html> 来让angularjs渲染整个页面,也可以使用 < ...
- sh_12_字典的遍历
sh_12_字典的遍历 xiaoming_dict = {"name": "小明", ", "} # 迭代遍历字典 # 变量k是每一次循环中 ...
- sh_03_列表的数据统计
sh_03_列表的数据统计 name_list = ["张三", "李四", "王五", "王小二", "张三 ...
- R_Studio(学生成绩)对数据缺失值md.pattern()、异常值分析(箱线图)
我们发现这张Gary.csv表格存在学生成绩不完全的(五十三名学生,三名学生存在成绩不完整.共四个不完整成绩) 79号大学语文.高等数学 96号中国近代史纲要 65号大学体育 (1)NA表示数据集中的 ...
- Spring4 MVC json问题(406 Not Acceptable)
最近使用spring4.0的Mvc,json请求时,客户端报错,406 Not Acceptable 解决方法: 1.导入第三方的fastjson包,fastjson-1.1.34.jar 2.Spr ...
- uswgi
1.安装uwsgi注意: 1)在系统环境安装,非虚拟环境 2)使用对应python版本安装 3)要先安装python开发包 ###sudo apt-get install python3.6-dev ...
- python 生成随机数的几种方法
随机取一个: import random random.choice(string.digits)#从数字里随机选取一位数字: 随机取多位数: random.sample(string.dig ...
- servlet3.0无web.xml
大家应该都已经知道spring 3.1对无web.xml式基于代码配置的servlet3.0应用.通过spring的api或是网络上高手们的博文,也一定很快就学会并且加到自己的应用中去了.PS:如果还 ...