美文网首页
WALLog 文件的写入

WALLog 文件的写入

作者: wayyyy | 来源:发表于2024-02-07 19:40 被阅读0次

代码详细见 log_writer.h / log_writer.cc。

成员
WritableFile *dest_;
int block_offset_; // Current offset in block

// crc32c values for all supported record types.  These are
// pre-computed to reduce the overhead of computing the crc of the
// record type stored in the header.
uint32_t type_crc_[kMaxRecordType + 1];
AddRecord

该方法将记录写入一个Slice结构,调用 AddRecord 就会写入 WALLog 文件,AddRecord 根据写入的记录大小确定是否需要跨块,并据此得出相应的头部类型,然后将记录写入并且刷新到磁盘。

先来几个例子:
假设现在当前 WALLog 文件已经写入了 1000 字节,当前的待写入的 record 记录为500字节,一个块大小为32768字节,当前已经写入1000字节,那么该条500字节的记录可以完整的写入这个块中。

image.png

假设现在当前 WALLog 文件已经写入了 1000 字节,当前的待写入的 record 记录为 31755 字节,当前块还剩下:32768 - 1000 = 31768,那么写入该 record + header 则 剩下 32768 - 1000 - 31755 - 7 = 6 字节,而一个 record header 需要7字节,这时,这块剩余的6字节将被填充为 \x00\x00\x00\x00\x00\x00。

image.png

假设现在当前 WALLog 文件已经写入了 1000 字节,当前的待写入的 record 记录为 50000 字节,那么最终会为:

image.png
Status Writer::AddRecord(const Slice &slice)
{
    const char *ptr = slice.data();
    size_t left = slice.size();

    // Fragment the record if necessary and emit it.  Note that if slice
    // is empty, we still want to iterate once to emit a single
    // zero-length record
    Status s;
    bool begin = true;
    do
    {
        const int leftover = kBlockSize - block_offset_;
        assert(leftover >= 0);
        if (leftover < kHeaderSize)
        {
            // Switch to a new block
            if (leftover > 0)
            {
                // Fill the trailer (literal below relies on kHeaderSize being 7)
                static_assert(kHeaderSize == 7, "");
                dest_->Append(Slice("\x00\x00\x00\x00\x00\x00", leftover));
            }
            block_offset_ = 0;
        }

        // Invariant: we never leave < kHeaderSize bytes in a block.
        assert(kBlockSize - block_offset_ - kHeaderSize >= 0);

        const size_t avail = kBlockSize - block_offset_ - kHeaderSize;
        const size_t fragment_length = (left < avail) ? left : avail;

        RecordType type;
        const bool end = (left == fragment_length);
        if (begin && end)
        {
            type = kFullType;
        }
        else if (begin)
        {
            type = kFirstType;
        }
        else if (end)
        {
            type = kLastType;
        }
        else
        {
            type = kMiddleType;
        }

        s = EmitPhysicalRecord(type, ptr, fragment_length);
        ptr += fragment_length;
        left -= fragment_length;
        begin = false;
    } while (s.ok() && left > 0);
    return s;
}
const char *ptr = slice.data();
size_t left = slice.size();

left 表示此次写入数据的长度。随后进入do-while循环。

查看当前 block 剩下的是否 <7,如果 <7 则补位,并重置 block偏移。这样做的目的是保证:header部分不会跨block。

const int leftover = kBlockSize - block_offset_;    // 
assert(leftover >= 0);
if (leftover < kHeaderSize)
{
    // Switch to a new block
    if (leftover > 0)
    {
        // Fill the trailer (literal below relies on kHeaderSize being 7)
        static_assert(kHeaderSize == 7, "");
        dest_->Append(Slice("\x00\x00\x00\x00\x00\x00", leftover));
     }
     block_offset_ = 0;
}

计算 block 剩余大小(除开header长度)avail ,以及本次 log record 可写入数据长度 fragment_length 。

const size_t avail = kBlockSize - block_offset_ - kHeaderSize;
const size_t fragment_length = (left < avail) ? left : avail;

下面分为几种情况,决定此次的 RecordType

const bool end = (left == fragment_length);
if (begin && end)  {  type = kFullType;  }
else if (begin)  {  type = kFirstType;  }
else if (end)  {  type = kLastType;  }
else  {  type = kMiddleType;  }

s = EmitPhysicalRecord(type, ptr, fragment_length);
ptr += fragment_length;
left -= fragment_length;
begin = false;

1、假设 left = 36,avail = 18,fragment_length = avail = 18,显然当前的Record不够,需要2个。所以第一次(begin && end) 成立,为
kFirstType,
2、假设left = 18,avail=36,fragment_length = avail = 18,显然,当前的Record就够了,只需要一次,(begin && end)成立,为kFullType

Status Writer::EmitPhysicalRecord(RecordType t, const char *ptr, size_t length)
{
    assert(length <= 0xffff); // Must fit in two bytes
    assert(block_offset_ + kHeaderSize + length <= kBlockSize);

    // Format the header
    char buf[kHeaderSize];
    buf[4] = static_cast<char>(length & 0xff);
    buf[5] = static_cast<char>(length >> 8);
    buf[6] = static_cast<char>(t);

    // Compute the crc of the record type and the payload.
    uint32_t crc = crc32c::Extend(type_crc_[t], ptr, length);
    crc = crc32c::Mask(crc); // Adjust for storage
    EncodeFixed32(buf, crc);

    // Write the header and the payload
    Status s = dest_->Append(Slice(buf, kHeaderSize));
    if (s.ok())
    {
        s = dest_->Append(Slice(ptr, length));
        if (s.ok())
        {
            s = dest_->Flush();
        }
    }
    block_offset_ += kHeaderSize + length;
    return s;
}
WALLog 文件

LevelDB 每次进行写操作时,都需要调用AddRecord方法向 WALLog 文件写入此次的增加的键值对,并且根据WriteOptions来决定是否需要进行刷新磁盘操作。调用AddRecord的代码位于中的 write 方法

DBImpl::Write(const WriteOptions &options, WriteBatch *updates) 
{
    ...
    status = log_->AddRecord(WriteBatchInternal::Contents(write_batch));
    bool sync_error = false;
    if (status.ok() && options.sync)
    {
        status = logfile_->Sync();  // 调用刷盘函数
        if (!status.ok())
        {
            sync_error = true;
        }
    }
    ...
}

最终插入WALLog文件的数据格式可能如下:


image.png
  • 序列号:8个字节,表示表该批次写入的序列号
  • 个数:占4个字节,表示该批次有多少key-value对
  • 类型:0x0 表示删除该键值对,0x1表示新增该键值对

相关文章

  • fs文件系统操作

    基础写入文件 简单写入文件 流式文件写入 简单文件读取 流式文件读取 流式文件拷贝(读取 + 写入) 复制文件 f...

  • Golang文件写入的四种方式

    Golang 中关于文件写入的方法很多简单覆盖式文件写入常规文件写入带有缓冲区的文件写入复制操作的文件写入 1. ...

  • 第52课:写入文件

    预习: 10.2 写入文件 10.2.1 写入空文件 练习:(没有权限写入文件)

  • txt读写

    文件打开 读文件 读取字符串 按行读取整个文件 写文件 字符串写入txt 列表写入文件 双层列表写入文件 数组写入文件

  • IOS plist 文件写入与读取

    数组写入plist文件(文件储存到cache路径下) 字典写入plist文件 将字典数组写入plist文件

  • python 文件操作

    fp=open("文件路径","方式") 文件读取 文件写入 文件关闭 文件读取写入方式

  • 文件操作

    文件操作 w 写入(文件不存在,则新建文件) a 追加写入(文件不存在,则新建文件) x 写入 (文件...

  • pList文件

    pList文件特点: 一,数据写入 写入pList文件的两种方式:手动写入&调方法写入 1,手动写入 2,调用方法...

  • 阅读《Python编程从入门到实践》Day13

    第十章(二) 2、写入文件 保存数据的最简单的方式之一是将其写入到文件中。 (1)写入空文件 要将文本写入文件,需...

  • 文件写入内容

    /** * 写入文件 * @param content 写入文本内容 * @param f 文件...

网友评论

      本文标题:WALLog 文件的写入

      本文链接:https://www.haomeiwen.com/subject/yeunkltx.html