CppGuide社区 CppGuide社区
首页
  • 🔥最新谷歌C++风格指南(含C++17/20)
  • 🔥C++17详解
  • 🔥C++20完全指南
  • 🔥C++23快速入门
  • C++语言面试问题集锦
  • 🔥交易系统开发岗位求职与面试指南 (opens new window)
  • 第1章 高频C++11重难点知识解析
  • 第2章 Linux GDB高级调试指南
  • 第3章 C++多线程编程从入门到进阶
  • 第4章 C++网络编程重难点解析
  • 第5章 网络通信故障排查常用命令
  • 第6章 高性能网络通信协议设计精要
  • 第7章 高性能服务结构设计
  • 第8章 Redis网络通信模块源码分析
  • 第9章 后端服务重要模块设计探索
  • 🚀 全部章节.pdf 下载 (opens new window)
  • 源码分析系列

    • leveldb源码分析
    • libevent源码分析
    • Memcached源码分析
    • TeamTalk源码分析
    • 优质源码分享 (opens new window)
    • 🔥远程控制软件gh0st源码分析
  • 从零手写C++项目系列

    • 🔥C++游戏编程入门(零基础学C++)
    • 🔥使用C++17从零开发一个调试器 (opens new window)
    • 🔥使用C++20从零构建一个完整的低延迟交易系统 (opens new window)
    • 🔥使用C++从零写一个C语言编译器 (opens new window)
    • 🔥从零用C语言写一个Redis
  • 🔥Windows 10系统编程
  • 🔥Linux 5.x内核开发与调试 完全指南 (opens new window)
  • TCP源码实现超详细注释版.pdf (opens new window)
  • Go语言特性

    • Go系统接口编程
    • 高效Go并发编程
    • Go性能调优
    • Go项目架构设计
  • Go项目实战

    • 🔥使用Go从零开发一个数据库
    • 🔥使用Go从零开发一个编译器 (opens new window)
    • 🔥使用Go从零开发一个解释器 (opens new window)
    • 🔥使用Go从零开发一个解释器 (opens new window)
    • 🔥用Go从零写一个编排器(类Kubernetes) (opens new window)
Rust编程指南
  • SQL零基础指南
  • MySQL开发与调试指南
GitHub (opens new window)
首页
  • 🔥最新谷歌C++风格指南(含C++17/20)
  • 🔥C++17详解
  • 🔥C++20完全指南
  • 🔥C++23快速入门
  • C++语言面试问题集锦
  • 🔥交易系统开发岗位求职与面试指南 (opens new window)
  • 第1章 高频C++11重难点知识解析
  • 第2章 Linux GDB高级调试指南
  • 第3章 C++多线程编程从入门到进阶
  • 第4章 C++网络编程重难点解析
  • 第5章 网络通信故障排查常用命令
  • 第6章 高性能网络通信协议设计精要
  • 第7章 高性能服务结构设计
  • 第8章 Redis网络通信模块源码分析
  • 第9章 后端服务重要模块设计探索
  • 🚀 全部章节.pdf 下载 (opens new window)
  • 源码分析系列

    • leveldb源码分析
    • libevent源码分析
    • Memcached源码分析
    • TeamTalk源码分析
    • 优质源码分享 (opens new window)
    • 🔥远程控制软件gh0st源码分析
  • 从零手写C++项目系列

    • 🔥C++游戏编程入门(零基础学C++)
    • 🔥使用C++17从零开发一个调试器 (opens new window)
    • 🔥使用C++20从零构建一个完整的低延迟交易系统 (opens new window)
    • 🔥使用C++从零写一个C语言编译器 (opens new window)
    • 🔥从零用C语言写一个Redis
  • 🔥Windows 10系统编程
  • 🔥Linux 5.x内核开发与调试 完全指南 (opens new window)
  • TCP源码实现超详细注释版.pdf (opens new window)
  • Go语言特性

    • Go系统接口编程
    • 高效Go并发编程
    • Go性能调优
    • Go项目架构设计
  • Go项目实战

    • 🔥使用Go从零开发一个数据库
    • 🔥使用Go从零开发一个编译器 (opens new window)
    • 🔥使用Go从零开发一个解释器 (opens new window)
    • 🔥使用Go从零开发一个解释器 (opens new window)
    • 🔥用Go从零写一个编排器(类Kubernetes) (opens new window)
Rust编程指南
  • SQL零基础指南
  • MySQL开发与调试指南
GitHub (opens new window)
  • leveldb源码分析1
  • leveldb源码分析2
  • leveldb源码分析3
  • leveldb源码分析4
  • leveldb源码分析5
  • leveldb源码分析6
  • leveldb源码分析7
  • leveldb源码分析8
  • leveldb源码分析9
  • leveldb源码分析10
  • leveldb源码分析11
    • 7.TableCache
      • 7.1 TableCache简介
      • 7.2 TableCache::Get()
      • 7.3 TableCache遍历
      • S1 初始化tableptr,调用FindTable,返回cache对象
      • S2 从cache对象中取出Table对象指针,调用其NewIterator返回Iterator对象,并为Iterator注册一个cleanup函数。
      • 7.4 TableCache::FindTable()
      • S1 首先根据file number从cache中查找table,找到就直接返回成功。
      • S2 如果没有找到,说明table不在cache中,则根据file number和db name打开一个RadomAccessFile。Table文件格式为:..sst。如果文件打开成功,则调用Table::Open读取sstable文件。
      • S3 如果Table::Open成功则,插入到Cache中。
      • 7.5 辅助函数
  • leveldb源码分析12
  • leveldb源码分析13
  • leveldb源码分析14
  • leveldb源码分析15
  • leveldb源码分析16
  • leveldb源码分析17
  • leveldb源码分析18
  • leveldb源码分析19
  • leveldb源码分析20
  • leveldb源码分析21
  • leveldb源码分析22
  • leveldb源码分析
zhangxf
2023-04-02
目录

leveldb源码分析11

# leveldb源码分析11

本系列《leveldb源码分析》共有22篇文章,这是第十一篇

# 7.TableCache

这章的内容比较简单,篇幅也不长。

# 7.1 TableCache简介

TableCache缓存的是Table对象,每个DB一个,它内部使用一个LRUCache缓存所有的table对象,实际上其内容是文件编号{file number, TableAndFile}*。TableAndFile是一个拥有2个变量的结构体:RandomAccessFile和Table;

TableCache类的主要成员变量有:

Env* const env_;            // 用来操作文件  
const std::string dbname_;  // db名  
Cache* cache_;              // LRUCache
1
2
3

三个函数接口,其中的参数**@file_number是文件编号,@file_size**是文件大小:

void Evict(uint64_tfile_number);  
// 该函数用以清除指定文件所有cache的entry,
//函数实现很简单,就是根据file number清除cache对象。  
EncodeFixed64(buf,file_number); cache_->Erase(Slice(buf, sizeof(buf)));  
Iterator* NewIterator(constReadOptions& options, uint64_t file_number,  
                      uint64_t file_size, Table**tableptr = NULL);  
//该函数为指定的file返回一个iterator(对应的文件长度必须是"file_size"字节). 
//如果tableptr不是NULL,那么tableptr保存的是底层的Table指针。
//返回的tableptr是cache拥有的,不能被删除,生命周期同返回的iterator

Status Get(constReadOptions& options,  
           uint64_t file_number,uint64_t file_size,  
           const Slice& k,void* arg,  
           void(*handle_result)(void*, const Slice&, const Slice&)); 

// 这是一个查找函数,如果在指定文件中seek 到internal key "k" 找到一个entry,
//就调用 (*handle_result)(arg,found_key, found_value).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 7.2 TableCache::Get()

先来看看**Get接口,**只有几行代码:

Cache::Handle* handle = NULL;  
Status s =FindTable(file_number, file_size, &handle);  
if (s.ok()) 
{  
    Table* t =reinterpret_cast<TableAndFile*>(cache_->Value(handle))->table;  
    s = t->InternalGet(options,k, arg, saver);  
    cache_->Release(handle);  
}  
return s;
1
2
3
4
5
6
7
8
9

首先根据file_number找到Table的cache对象,如果找到了就调用Table::InternalGet,对查找结果的处理在调用者传入的saver回调函数中。 Cache在Lookup找到cache对象后,如果不再使用需要调用Release减引用计数。这个见Cache的接口说明。

# 7.3 TableCache遍历

函数NewIterator(),返回一个可以遍历Table对象的Iterator指针,函数逻辑:

# S1 初始化tableptr,调用FindTable,返回cache对象

if (tableptr != NULL) *tableptr =NULL;  
Cache::Handle* handle = NULL;  
Status s =FindTable(file_number, file_size, &handle);  
if (!s.ok()) returnNewErrorIterator(s);
1
2
3
4

# S2 从cache对象中取出Table对象指针,调用其NewIterator返回Iterator对象,并为Iterator注册一个cleanup函数。

Table* table =reinterpret_cast<TableAndFile*>(cache_->Value(handle))->table;  
Iterator* result =table->NewIterator(options);  
result->RegisterCleanup(&UnrefEntry, cache_, handle);  
if (tableptr != NULL) *tableptr= table;  
return result;
1
2
3
4
5

# 7.4 TableCache::FindTable()

前面的遍历和Get函数都依赖于FindTable这个私有函数完成对cache的查找,下面就来看看该函数的逻辑。函数声明为:

Status FindTable(uint64_t file_number, uint64_t file_size,
                 Cache::Handle** handle)
1
2

函数流程为:

# S1 首先根据file number从cache中查找table,找到就直接返回成功。

char buf[sizeof(file_number)];  
EncodeFixed64(buf, file_number);  
Slice key(buf, sizeof(buf));  
*handle = cache_->Lookup(key);
1
2
3
4

# S2 如果没有找到,说明table不在cache中,则根据file number和db name打开一个RadomAccessFile。Table文件格式为:..sst。如果文件打开成功,则调用Table::Open读取sstable文件。

std::string fname =TableFileName(dbname_, file_number);  
RandomAccessFile* file = NULL;  
Table* table = NULL;  
s =env_->NewRandomAccessFile(fname, &file);  
if (s.ok()) s =Table::Open(*options_, file, file_size, &table);
1
2
3
4
5

# S3 如果Table::Open成功则,插入到Cache中。

TableAndFile* tf = newTableAndFile(table, file);  
*handle = cache_->Insert(key,tf, 1, &DeleteEntry); 
1
2

如果失败,则删除file,直接返回失败,失败的结果是不会cache的。

# 7.5 辅助函数

有点啰嗦,不过还是写一下吧。其中一个是为LRUCache注册的删除函数DeleteEntry。

static void DeleteEntry(const Slice& key, void* value) 
{  
    TableAndFile* tf =reinterpret_cast<TableAndFile*>(value);  
    delete tf->table;  
    delete tf->file;  
    delete tf;  
} 
1
2
3
4
5
6
7

另外一个是为Iterator注册的清除函数UnrefEntry。

static void UnrefEntry(void* arg1, void* arg2) 
{  
    Cache* cache =reinterpret_cast<Cache*>(arg1);  
    Cache::Handle* h =reinterpret_cast<Cache::Handle*>(arg2);  
    cache->Release(h);  
}
1
2
3
4
5
6
编辑 (opens new window)
上次更新: 2023/12/11, 22:32:09
leveldb源码分析10
leveldb源码分析12

← leveldb源码分析10 leveldb源码分析12→

最近更新
01
第二章 关键字static及其不同用法
03-27
02
第一章 auto与类型推导
03-27
03
C++语言面试问题集锦 目录与说明
03-27
更多文章>
Copyright © 2024-2025 沪ICP备2023015129号 张小方 版权所有
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式