网络模型。cli
内存结构
文件结构。写入的buf,flush,sync。页保证完整性
内存池
并发。网络,写入并发(批量写入,写log并发,写文件落盘并发,compact并发)
ACID 版本控制,日志
cache
复制
备份,快照
崩溃恢复
崩溃恢复
先从manifest恢复version.version_set,logid,seqid等,先合并删除再manifest再改current和versionset。再获取所有日志,比较log>manifest中的log的进行恢复。到mem可能还要compact。manifest若是未写成功则就当没有此次压缩。如果log未写成功,则mem不会有此数据。
并发
只有个批量写入log和mem
版本控制
见其他两篇文章,一个是读取内存时候比较seq。
一个是文件中compact后维护不同version。对不同version如果有引用则不删除,manifest中维护所有open以来的版本。详见另外两篇
若不带snapshot。赋值为 snapshot = versions_->LastSequence();
文件的 Version* current = versions_->current();
根据snapshot的seq构造key比较查找。每个的引用次数++,结束后--。每次compact结束后决定是否删除。
cache
http://www.pandademo.com/2016...
上面这个说的不错,两层,一个是文件索引的cache,调用Table::Open读取footer,index等,block_cache分片的,找到文件后定位到哪个block_cache,cache中是块信息。
内存结构
跳表,两个mem切换
文件结构
内存维护buf。4k一次调用write写入一个块。64k补一个sync。
页完整的保证:mysql的二次写,mongodb的固定块大小。
leveldb
1.log写入:若Block已经小于header,填充0下一个Block。否则写入block剩余和left小的,加入first等标识。一段调一次加入头信息/crc32c校验和和数据,走append和flush。每段日志都会write。block为32k一个大小
2.log读取:每32k读一次。校验和,读类型等,若未到结尾继续读,若校验和等出错,抛弃32k,继续读。考虑每次出问题的时候有一个块可能部分写。抛弃读取部分,但每次恢复遇到问题会指向下一个块或者读取部分就好。
3.数据写入:4k加一个index。调用一次write。一个table一次fsync
提供的file->append每64K一个buffer.但是数据和日志都分别在外面调用flush保证4k一次write,和一条日志一次write。
数据4k一个校验和写入,其他filter_block,meta_index_block,index_block,rooter算好最后在写入
每4看写入所有重启点(16个一个,重启点是指向重启点的offset),压缩,写入文件,写入检验和
4.数据读取:mm->imm->current->get
table_cache_->Get. sst文件放入table_cache
Iterator* iiter = rep_->index_block->NewIterator(rep_->options.comparator);
iiter->Seek(k);
Iterator* block_iter = BlockReader(this, options, iiter->value());
block_iter->Seek(k);
到 BlockReader:
Cache* block_cache = table->rep_->options.block_cache;
block_cache没有,ReadBlock
Status s = file->Read(handle.offset(), n + kBlockTrailerSize, &contents, buf);
校验,解压缩,校验不对直接返回错误。在一个块里搜索通过二分重启点,后线性找记录。数据校验有问题的直接错误并不处理。(看看recover会处理吗)
内存池:
glibc malloc和C++标准库的new返回的地址均是内存对齐的,这里由于是定制内存分配,指针预分配的大块内存(page页面大小),当连续分配小块内存时就需要做额外对齐。内存对齐有利于提高访存速度,因为内存按字节编址,根据CPU字长即sizeof(void *)大小做对齐,有利于减少访存指令次数,防止跨对齐边界访存,也能充分利用硬件体系,提高CPU性能。
char* alloc_ptr_;
size_t alloc_bytes_remaining_;
std::vector<char*> blocks_;
inline char* Arena::Allocate(size_t bytes) {
assert(bytes > 0);
if (bytes <= alloc_bytes_remaining_) {
char* result = alloc_ptr_;
alloc_ptr_ += bytes;
alloc_bytes_remaining_ -= bytes;
return result;
}
return AllocateFallback(bytes);
}
char* Arena::AllocateFallback(size_t bytes) {
if (bytes > kBlockSize / 4) {
char* result = AllocateNewBlock(bytes);
return result;
}
alloc_ptr_ = AllocateNewBlock(kBlockSize);
alloc_bytes_remaining_ = kBlockSize;
char* result = alloc_ptr_;
alloc_ptr_ += bytes;
alloc_bytes_remaining_ -= bytes;
return result;
}
char* Arena::AllocateNewBlock(size_t block_bytes) {
char* result = new char[block_bytes];
blocks_.push_back(result);
memory_usage_.fetch_add(block_bytes + sizeof(char*),
std::memory_order_relaxed);
return result;
}
对其版本:每次从8申请
char* Arena::AllocateAligned(size_t bytes) {
const int align = (sizeof(void*) > 8) ? sizeof(void*) : 8;
size_t current_mod = reinterpret_cast<uintptr_t>(alloc_ptr_) & (align - 1);
size_t slop = (current_mod == 0 ? 0 : align - current_mod);
size_t needed = bytes + slop;
char* result;
if (needed <= alloc_bytes_remaining_) {
result = alloc_ptr_ + slop;
alloc_ptr_ += needed;
alloc_bytes_remaining_ -= needed;
} else {
// AllocateFallback always returned aligned memory
result = AllocateFallback(bytes);
}
assert((reinterpret_cast<uintptr_t>(result) & (align - 1)) == 0);
return result;
}
- rocksdb的slice
不管是 it->key() 还是 it->value(),其值类型都是 rocksdb::Slice。 Slice 自身由一个长度字段[ sizet size ]以及一个指向外部一个内存区域的指针[ const char* data_ ]构成,但是在离开相应的 scope 之后,其值就会被释放,rocksdb v5.4.5 版本引入一个 PinnableSlice,增加引用计数
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。