Greedy algorithm for picking files to compact.
Summary: It is best if we pick the largest file to compact in a level. This reduces the write amplification factor for compactions. Each level has an auxiliary data structure called files_by_size_ that sorts all files by their size. This data structure is updated when a new version is created. Test Plan: make check Differential Revision: https://reviews.facebook.net/D6195
This commit is contained in:
parent
8fb5f40468
commit
5b0fe6c73b
@ -268,6 +268,7 @@ static bool NewestFirst(FileMetaData* a, FileMetaData* b) {
|
|||||||
|
|
||||||
Version::Version(VersionSet* vset, uint64_t version_number)
|
Version::Version(VersionSet* vset, uint64_t version_number)
|
||||||
: vset_(vset), next_(this), prev_(this), refs_(0),
|
: vset_(vset), next_(this), prev_(this), refs_(0),
|
||||||
|
files_by_size_(vset->NumberLevels()),
|
||||||
file_to_compact_(NULL),
|
file_to_compact_(NULL),
|
||||||
file_to_compact_level_(-1),
|
file_to_compact_level_(-1),
|
||||||
compaction_score_(vset->NumberLevels()),
|
compaction_score_(vset->NumberLevels()),
|
||||||
@ -564,6 +565,11 @@ class VersionSet::Builder {
|
|||||||
Version* base_;
|
Version* base_;
|
||||||
LevelState* levels_;
|
LevelState* levels_;
|
||||||
|
|
||||||
|
typedef struct fsize {
|
||||||
|
int index;
|
||||||
|
FileMetaData* file;
|
||||||
|
} Fsize;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Initialize a builder with the files from *base and other info from *vset
|
// Initialize a builder with the files from *base and other info from *vset
|
||||||
Builder(VersionSet* vset, Version* base)
|
Builder(VersionSet* vset, Version* base)
|
||||||
@ -616,6 +622,7 @@ class VersionSet::Builder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
assert(v->files_[level].size() == v->files_by_size_[level].size());
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@ -712,8 +719,9 @@ class VersionSet::Builder {
|
|||||||
for (; base_iter != base_end; ++base_iter) {
|
for (; base_iter != base_end; ++base_iter) {
|
||||||
MaybeAddFile(v, level, *base_iter);
|
MaybeAddFile(v, level, *base_iter);
|
||||||
}
|
}
|
||||||
CheckConsistency(v);
|
|
||||||
}
|
}
|
||||||
|
UpdateFilesBySize(v);
|
||||||
|
CheckConsistency(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MaybeAddFile(Version* v, int level, FileMetaData* f) {
|
void MaybeAddFile(Version* v, int level, FileMetaData* f) {
|
||||||
@ -730,6 +738,34 @@ class VersionSet::Builder {
|
|||||||
files->push_back(f);
|
files->push_back(f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool compareSize(const Fsize& first, const Fsize& second) {
|
||||||
|
return (first.file->file_size > second.file->file_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateFilesBySize(Version* v) {
|
||||||
|
|
||||||
|
for (int level = 0; level < vset_->NumberLevels(); level++) {
|
||||||
|
// populate a temp vector for sorting based on size
|
||||||
|
const std::vector<FileMetaData*>& files = v->files_[level];
|
||||||
|
std::vector<Fsize> temp(files.size());
|
||||||
|
for (unsigned int i = 0; i < files.size(); i++) {
|
||||||
|
temp[i].index = i;
|
||||||
|
temp[i].file = files[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// do the sort based on file size
|
||||||
|
std::sort(temp.begin(), temp.end(), compareSize);
|
||||||
|
assert(temp.size() == files.size());
|
||||||
|
|
||||||
|
// initialize files_by_size_
|
||||||
|
std::vector<int>& files_by_size = v->files_by_size_[level];
|
||||||
|
for (unsigned int i = 0; i < temp.size(); i++) {
|
||||||
|
files_by_size.push_back(temp[i].index);
|
||||||
|
}
|
||||||
|
assert(v->files_[level].size() == v->files_by_size_[level].size());
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
VersionSet::VersionSet(const std::string& dbname,
|
VersionSet::VersionSet(const std::string& dbname,
|
||||||
@ -1575,7 +1611,7 @@ Compaction* VersionSet::PickCompactionBySize(int level) {
|
|||||||
|
|
||||||
// level 0 files are overlapping. So we cannot pick more
|
// level 0 files are overlapping. So we cannot pick more
|
||||||
// than one concurrent compactions at this level. This
|
// than one concurrent compactions at this level. This
|
||||||
// cold be made better by looking at key-ranges that are
|
// could be made better by looking at key-ranges that are
|
||||||
// being compacted at level 0.
|
// being compacted at level 0.
|
||||||
if (level == 0 && compactions_in_progress_[level].size() == 1) {
|
if (level == 0 && compactions_in_progress_[level].size() == 1) {
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -1586,25 +1622,22 @@ Compaction* VersionSet::PickCompactionBySize(int level) {
|
|||||||
c = new Compaction(level, MaxFileSizeForLevel(level),
|
c = new Compaction(level, MaxFileSizeForLevel(level),
|
||||||
MaxGrandParentOverlapBytes(level), NumberLevels());
|
MaxGrandParentOverlapBytes(level), NumberLevels());
|
||||||
|
|
||||||
// remember the first file that is not being compacted
|
// Pick the largest file in this level that is not already
|
||||||
int firstIndex = -1;
|
// being compacted
|
||||||
|
std::vector<int>& file_size = current_->files_by_size_[level];
|
||||||
|
for (unsigned int i = 0; i < file_size.size(); i++) {
|
||||||
|
int index = file_size[i];
|
||||||
|
FileMetaData* f = current_->files_[level][index];
|
||||||
|
|
||||||
|
// check to verify files are arranged in descending size
|
||||||
|
assert((i == file_size.size() - 1) ||
|
||||||
|
(f->file_size >= current_->files_[level][file_size[i+1]]->file_size));
|
||||||
|
|
||||||
// Pick the first file that comes after compact_pointer_[level]
|
|
||||||
for (size_t i = 0; i < current_->files_[level].size(); i++) {
|
|
||||||
FileMetaData* f = current_->files_[level][i];
|
|
||||||
// do not pick a file to compact if it is being compacted
|
// do not pick a file to compact if it is being compacted
|
||||||
// from n-1 level.
|
// from n-1 level.
|
||||||
if (f->being_compacted) {
|
if (f->being_compacted) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (firstIndex == -1) {
|
|
||||||
firstIndex = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pick a file that has a key-range larger than the range
|
|
||||||
// we picked in the previous call to PickCompaction.
|
|
||||||
if (compact_pointer_[level].empty() ||
|
|
||||||
icmp_.Compare(f->largest.Encode(), compact_pointer_[level]) > 0) {
|
|
||||||
// Do not pick this file if its parents at level+1 are being compacted.
|
// Do not pick this file if its parents at level+1 are being compacted.
|
||||||
// Maybe we can avoid redoing this work in SetupOtherInputs
|
// Maybe we can avoid redoing this work in SetupOtherInputs
|
||||||
if (ParentFilesInCompaction(f, level)) {
|
if (ParentFilesInCompaction(f, level)) {
|
||||||
@ -1613,14 +1646,7 @@ Compaction* VersionSet::PickCompactionBySize(int level) {
|
|||||||
c->inputs_[0].push_back(f);
|
c->inputs_[0].push_back(f);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (c->inputs_[0].empty() && firstIndex != -1) {
|
|
||||||
// Wrap-around to the beginning of the key space
|
|
||||||
FileMetaData* f = current_->files_[level][firstIndex];
|
|
||||||
if (!f->being_compacted && !ParentFilesInCompaction(f, level)) {
|
|
||||||
c->inputs_[0].push_back(f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (c->inputs_[0].empty()) {
|
if (c->inputs_[0].empty()) {
|
||||||
delete c;
|
delete c;
|
||||||
c = NULL;
|
c = NULL;
|
||||||
|
@ -124,9 +124,16 @@ class Version {
|
|||||||
Version* prev_; // Previous version in linked list
|
Version* prev_; // Previous version in linked list
|
||||||
int refs_; // Number of live refs to this version
|
int refs_; // Number of live refs to this version
|
||||||
|
|
||||||
// List of files per level
|
// List of files per level, files in each level are arranged
|
||||||
|
// in increasing order of keys
|
||||||
std::vector<FileMetaData*>* files_;
|
std::vector<FileMetaData*>* files_;
|
||||||
|
|
||||||
|
// A list for the same set of files that are stored in files_,
|
||||||
|
// but files in each level are now sorted based on file
|
||||||
|
// size. The file with the largest size is at the front.
|
||||||
|
// This vector stores the index of the file from files_.
|
||||||
|
std::vector< std::vector<int> > files_by_size_;
|
||||||
|
|
||||||
// Next file to compact based on seek stats.
|
// Next file to compact based on seek stats.
|
||||||
FileMetaData* file_to_compact_;
|
FileMetaData* file_to_compact_;
|
||||||
int file_to_compact_level_;
|
int file_to_compact_level_;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user