读写更新数据库

读写

数据库提供Put, Delete, Get这些方法来修改和查询数据库。比如,以下操作时将存储在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);

原子更新 - Atomic Updates

注意:上面的操作如果进程在Put key2Delete key1两个操作之间结束,那么这两个键将存储相同的值。因此,尽可能使用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对象保存对数据库进行的一系列操作,然后在这一批次中按照顺序应用这些操作。注意:这里先进行Delete操作,然后再进行Put操作,是因为在key1key2相同的情况下不会错误的将该值丢弃。

WriteBatch类除了原子性的优势外,也可以用于通过将大量个体变动放置在同一批次中而加速批量更新。

同步写入 - Synchronous Writes

在默认情况下,levedb中的每一次写操作都是异步的:它会在把写入操作从进程中推送到操作系统后返回,而从操作系统内存到底层持久化存储的传输是异步的。对于特定的写操作,是可以打开同步sync标志使写操作一直到数据被传输到底层存储器后再返回。(在基于Posix标准的操作系统系统中,这一步是通过在写操作返回之前调用fsync(…)fdatasync(…)nsync(…, MS_SYNC)实现的。)

leveldb::WriteOptions write_options;
write_options.sync = true;
db->Put(write_options, ...);

异步写操作一般比同步写操作快很多很多。但异步写入的缺点是,在机器宕机时有可能导致最后几步的更新丢失。请注意,如果是在写入过程中的宕机(而非重新启动),即使sync设置为false,更新操作也会认为已经将更新从内存中推送到了操作系统。

通常可以安全地使用异步写入。比如,当加载大量数据到数据库中时,可以通过在宕机后重新启动批量加载来处理丢失的更新。有一个可用的混合方案,将多次写入的第N次写入设置为同步的,并在宕机重启后的情况下,批量加载由前一次运行的最后一次同步写入之后重新开始。(同步写入时可以更新描述宕机后批量加载重新开始的标记。)

WriteBatch提供一个代替异步写操作的选择:将多个更新操作放置在同一个WriteBatch对象中然后使用同步写入一起应用(write_options.sync设置为ture);这时同步写入的额外成本开销将在这批次中所有的写入操作中摊销。