├── .DS_Store ├── 00.Architecture ├── .DS_Store ├── MyRocks best practice at Alibaba - percona.pdf ├── RocksDB(MyRocks)源码学习—写.pdf ├── RocksDB(MyRocks)源码学习—读.pdf ├── class_diagram.md ├── images │ └── rocksdb类图.png └── myRocks-transactions.pdf ├── 01.Class_Relation ├── class_relation.md └── class_rocksdb.png ├── 02.Read ├── code_DBImpl_GetImpl.md ├── code_MemTable_Get.md ├── code_TableCache_Get.md ├── code_Version_Get.md ├── code_do_select.md ├── code_myrocks_ha_rocksdb_rnd_init.md ├── code_rocksdb_get.md ├── notes_Read │ ├── assets │ │ ├── 1599805914-06897fdc14ea93138b4a19e1b3cc5f0b.png │ │ ├── 1599805914-0a90e60de512338b578afc2adfe4ac52.png │ │ ├── 1599805914-0e0cf6338619205c20df532489e2ce0b.jpg │ │ ├── 1599805914-2af0d6a8616c66698a486e46a8384235.jpg │ │ ├── 1599805914-53418207f5137d4efce858bd1bea6168.jpg │ │ ├── 1599805914-59c30fbdcdd086529bfb5c52adaeca73.jpg │ │ ├── 1599805914-8b3dd79bf8afd7a11430ebf1130d3ca1.jpg │ │ ├── 1599805914-915e1d5ae3b19ca34d53586d92f43e84.jpg │ │ ├── 1599805914-91b156f25d95a9e328b0baacd7b85807.jpg │ │ ├── 1599805914-a1392f3898a550da5a612043db71cc88.jpg │ │ ├── 1599805914-a9fac8fee725dd09425da4a9ea30189b.jpg │ │ ├── 1599805914-c217cb89e646b43f26f4ea490a78d642.jpg │ │ ├── 1599805914-c4e3a5ed8a667c9a841587e16e7bf518.png │ │ ├── 1599805914-d0252f7da8480c3241f26682fbfb54ee.png │ │ ├── 1599805914-efc3e77847382f4f689a8248bfa0424d.png │ │ └── 1599805914-f00a283b92d885aa4be05f5e9828756a.png │ └── index.md ├── notes_Read_Flow │ ├── Read.md │ └── images │ │ ├── 1.png │ │ └── 2.png └── read.txt ├── 03.Write ├── code_DBImpl_WriteImpl.md ├── code_DBImpl_WriteImpl_2.md ├── code_Memtable_Add.md ├── code_WriteBatchInternal_InsertInto.md ├── code_insert.md ├── code_rocksdb_open.md ├── notes_Write │ ├── assets │ │ ├── 1599790983-1952bf94d857fbfee01a8622abff4aaa.jpg │ │ ├── 1599790983-2fff9db269164343308cb0b2bcd3a205.jpg │ │ ├── 1599790983-5883cea91e22f4cda4318d71a4749c12.jpg │ │ ├── 1599790983-5975086c9ff1b27b2385d22520849dfc.jpg │ │ ├── 1599790983-61f8a4250d7696c63f00974dc6493483.jpg │ │ ├── 1599790983-787a17debceb521f00cdba94def64519.png │ │ ├── 1599790983-8f6c9493a0946f5c439974e5f72d3d96.jpg │ │ ├── 1599790983-b3c3f34dcb7ec56aab554d76965f5919.jpg │ │ ├── 1599790983-b640bb893d51adb07a58fd58e2b9c91b.jpg │ │ ├── 1599790983-b81bfceaf78fa30e7148a3eeddd8563e.jpg │ │ ├── 1599790983-c07b72388716c152c487bf13a04d713b.jpg │ │ ├── 1599790983-ccbe426b51b41450f25eb5f3c365f7c0.jpg │ │ ├── 1599790983-d466f0bf2fc753abe549876527f64a6c.jpg │ │ ├── 1599790983-d8bb9c8341039f73ba8269ffafdd6543.jpg │ │ ├── 1599790983-e768b003699851b8035aee6a0cef7ca7.jpg │ │ ├── 1599790983-e7add1e3b77288c83c7958f577f8e650.jpg │ │ ├── 1599790983-f00a283b92d885aa4be05f5e9828756a.png │ │ └── 1599790983-f92eed5c356cfe7fc4affeddfb8c0ca2.jpg │ └── index.md └── write.txt ├── 04.Mainfest └── Mainfest.md ├── 05.VersionSet ├── 1.png ├── 2.png └── VersionSet.md ├── 06.Memtable_Flush_Read ├── 1.png ├── Flush_notes │ ├── assets │ │ ├── 1599735083-34cda6ae8edfa5c5a7e02414b7cd3633.png │ │ ├── 1599735083-605ef45264358719c2a6fef49fc5d479.png │ │ ├── 1599735083-a661d3a5ad30776e7b95a01a5335d799.png │ │ └── 1599735083-c49da31b58abf43f5f9356792b02b92e.png │ └── index.md ├── Memtable_Flush.md └── memtable.txt ├── 07.SSTable ├── SSTable.txt ├── notes_BlockBasedTable_Compress │ ├── assets │ │ ├── 1599809496-129d10647bd625a17d08c5beed7cb935.jpg │ │ ├── 1599809496-238e3b3f0214099cb6460b877c84eaaf.jpg │ │ ├── 1599809496-2b7642dbf9770ff944caea94f6969de8.jpg │ │ ├── 1599809496-2d47f8db799645507174403e2552ff4c.jpg │ │ ├── 1599809496-448f9751afeaff9818d21fa91d3f1e86.jpg │ │ ├── 1599809496-4f8f578093fb39fdc229f2cd8e956b5a.jpg │ │ ├── 1599809496-5b8ae05399d451de702b6e30f4c6d969.jpg │ │ ├── 1599809496-c69936d411ca842fdee77e7bd308e28d.jpg │ │ ├── 1599809496-d2def64aba145010465142c7f1d7d4e1.png │ │ ├── 1599809496-dfd540a17a3481088f4113e044a0ce4f.jpg │ │ ├── 1599809496-e114a7374b67035ccb6780088849fea3.jpg │ │ ├── 1599809496-e3269e0b73e22c2d9abc55f43c799b5f.jpg │ │ ├── 1599809496-e55c2da22b87c957e06672ffdadcb1d9.jpg │ │ ├── 1599809496-ea7ab4ef16e96a0b772ee45fa4a1a759.jpg │ │ ├── 1599809496-f00a283b92d885aa4be05f5e9828756a.png │ │ └── 1599809496-fd31e70d53272a46125468ed7945ccfe.jpg │ └── index.md └── notes_SSTable │ ├── SSTable.md │ └── images │ ├── SSTable.png │ └── sstable2.png ├── 08.Transaction ├── Lock_Transaction │ ├── assets │ │ ├── 1599734854-07954a83df2ed5dc425295a5c1877caa.png │ │ ├── 1599734854-0ed43024a0919cc9fa07884732981e9f.png │ │ ├── 1599734854-1b23341a0ea959e27f487265ba5d5708.png │ │ ├── 1599734854-2f9836f391212bcdbcc26adc40f65e36.png │ │ ├── 1599734854-32d93a478ad3e9dd7372f15fd88763ed.png │ │ ├── 1599734854-678504024bb2f7a16179588202d904d0.png │ │ ├── 1599734854-6d0552a02407e8e35223501ba3d6345e.png │ │ ├── 1599734854-7d0b001256239b0414d1db2d339a39e6.png │ │ ├── 1599734854-ab2d832e26f095bc9f3a2f2d166f6f26.png │ │ ├── 1599734854-bdc36cdd5f142f117c08227fd7fa7e7e.png │ │ ├── 1599734854-f00a283b92d885aa4be05f5e9828756a.png │ │ └── 1599734854-fdc1ca0cc049d7b1250ec0503aba58f2.png │ └── index.md └── transaction.txt ├── 09.Compaction ├── Compaction_Flush │ ├── assets │ │ ├── 1599735083-2d2424d8966e15cee6e2c83a77d3fe67.png │ │ ├── 1599735083-34cda6ae8edfa5c5a7e02414b7cd3633.png │ │ ├── 1599735083-3ff4fd416ddf4a888e7bcd46c91a862f.png │ │ ├── 1599735083-605ef45264358719c2a6fef49fc5d479.png │ │ ├── 1599735083-62c3719f0938ee9fa0e36d6199307509.png │ │ ├── 1599735083-879c181377d82488a9aace78ae77b2a7.png │ │ ├── 1599735083-8d2370cb4efeb3ff4221bd07a5782658.png │ │ ├── 1599735083-a55dce20a8cea75b77e00a816fda3e14.png │ │ ├── 1599735083-a60a6309f97eb05ce2760324e44d106a.png │ │ ├── 1599735083-a661d3a5ad30776e7b95a01a5335d799.png │ │ ├── 1599735083-aa14f863ecd211ed666d0e4bc8e658dd.png │ │ ├── 1599735083-c49da31b58abf43f5f9356792b02b92e.png │ │ ├── 1599735083-dc3ed654edc5d384fd5a47868463e2b7.png │ │ ├── 1599735083-e3d634e297618efecfac00c9e78393ae.png │ │ ├── 1599735083-f00a283b92d885aa4be05f5e9828756a.png │ │ └── 1599735083-fb459c1c4c89891b329f090e98aeb942.png │ └── index.md └── compaction.txt ├── 10.CF └── CF │ ├── assets │ ├── 1599739636-5cfa77592484edd684e065afefecc134.png │ ├── 1599739636-a14f3446821d50a8ce3f2b442c760fe6.png │ └── 1599739636-e79622b86376382f60a6d81702611c06.png │ └── index.md ├── 11.Dictionary ├── Dic_table.txt ├── enum_DATA_DICT_TYPE.md └── notes_Dictionary │ ├── assets │ ├── 1599739636-8ef3252da2a37e93f3359306d5368b12.png │ └── 1599739636-fc1c3061b56bd2f8326d0a98a39ad0c4.png │ └── index.md ├── 12.Record_Format ├── Record_Format.txt ├── Record_Format │ ├── assets │ │ ├── 1599739636-013e3948422f51f8b8bbb9ad105b080b.png │ │ ├── 1599739636-415fcbda855d2da975a98602829a4863.png │ │ ├── 1599739636-4b8aadf246b7854c260e07a4b63e46b0.png │ │ ├── 1599739636-684894f81be8e04ac63db190f8053e79.png │ │ ├── 1599739636-7588a070837408a85cc6c06a269cd5d7.png │ │ └── 1599739636-f00a283b92d885aa4be05f5e9828756a.png │ └── index.md └── enum_ValueType.md ├── 13.Backup_Restore ├── 1.png ├── Backup_Restore.md └── backupRestore.txt ├── 14.Compress └── Compress │ ├── assets │ ├── 1599812462-1fb3efa259235bef7af7e38e61f78027.jpg │ ├── 1599812462-6db59fc68e8468498b87fb5ab0acc9b8.jpg │ ├── 1599812462-ab21f743e6d0ad1af1fcee59ec5daabf.png │ ├── 1599812462-f00a283b92d885aa4be05f5e9828756a.png │ └── 1599812462-f329489c1e9b323316cbe0edc589b04e.png │ └── index.md ├── 15.Lock └── notes_Lock_Transaction │ ├── assets │ ├── 1601900301-07954a83df2ed5dc425295a5c1877caa.png │ ├── 1601900301-0ed43024a0919cc9fa07884732981e9f.png │ ├── 1601900301-1b23341a0ea959e27f487265ba5d5708.png │ ├── 1601900301-2f9836f391212bcdbcc26adc40f65e36.png │ ├── 1601900301-32d93a478ad3e9dd7372f15fd88763ed.png │ ├── 1601900301-678504024bb2f7a16179588202d904d0.png │ ├── 1601900301-6d0552a02407e8e35223501ba3d6345e.png │ ├── 1601900301-7d0b001256239b0414d1db2d339a39e6.png │ ├── 1601900301-ab2d832e26f095bc9f3a2f2d166f6f26.png │ ├── 1601900301-bdc36cdd5f142f117c08227fd7fa7e7e.png │ ├── 1601900301-f00a283b92d885aa4be05f5e9828756a.png │ └── 1601900301-fdc1ca0cc049d7b1250ec0503aba58f2.png │ └── index.md ├── 16.WAL ├── WAL.txt ├── code_WriteToWAL.md └── notes_WAL │ └── index.md ├── 17.WriteBatch └── writebatch.txt ├── 31.Env └── MockEnv.txt ├── 32.handler └── handler.txt ├── 80.TTL └── TTL.md ├── 90.Config └── Config.md ├── 91.Performance ├── 1.png ├── 2.png └── perf_data.md ├── 92.Limitation └── Limitation.md ├── 93.GTID ├── 1.png ├── 2.png └── Enable_GTID.md ├── 98.sample ├── code_c_simple_sample.md └── examples.txt └── 99.Research ├── Group_GC ├── Aerospike基础知识.pdf ├── Aerospike源码分析.pdf ├── Aerospike磁盘数据存储格式V0.2.pdf ├── Group_GC.txt └── aerospike.txt ├── L0_2_in_mem └── L0_2_in_memory.txt └── PebblesDB ├── Compaction.txt ├── Version.txt └── VersionSet.txt /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/.DS_Store -------------------------------------------------------------------------------- /00.Architecture/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/00.Architecture/.DS_Store -------------------------------------------------------------------------------- /00.Architecture/MyRocks best practice at Alibaba - percona.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/00.Architecture/MyRocks best practice at Alibaba - percona.pdf -------------------------------------------------------------------------------- /00.Architecture/RocksDB(MyRocks)源码学习—写.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/00.Architecture/RocksDB(MyRocks)源码学习—写.pdf -------------------------------------------------------------------------------- /00.Architecture/RocksDB(MyRocks)源码学习—读.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/00.Architecture/RocksDB(MyRocks)源码学习—读.pdf -------------------------------------------------------------------------------- /00.Architecture/class_diagram.md: -------------------------------------------------------------------------------- 1 | ## RocksDB简化版类关系图 2 | ![Rocksdb类图](/Architecture/images/rocksdb类图.png) 3 | 4 | -------------------------------------------------------------------------------- /00.Architecture/images/rocksdb类图.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/00.Architecture/images/rocksdb类图.png -------------------------------------------------------------------------------- /00.Architecture/myRocks-transactions.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/00.Architecture/myRocks-transactions.pdf -------------------------------------------------------------------------------- /01.Class_Relation/class_relation.md: -------------------------------------------------------------------------------- 1 | #1.Class Relation 2 | 3 | ![](class_rocksdb.png) -------------------------------------------------------------------------------- /01.Class_Relation/class_rocksdb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/01.Class_Relation/class_rocksdb.png -------------------------------------------------------------------------------- /02.Read/code_DBImpl_GetImpl.md: -------------------------------------------------------------------------------- 1 | #1.DBImpl::GetImpl 2 | 3 | ###数据库实现层逻辑 4 | Rocksdb::DB是个抽象基类,其中定义些纯虚函数,包括Get,Put操作等,具体实现时调用它的子类rocksdb::DBImpl实现这些接口功能。Get的功能就是油DBImpl::GetImpl完成。 5 | 其中通过SuperVersion可以找到当前的Memtable、Immutable Memtable队列、以及当前Version(由此可以找到当前版本的sstable结构中的所有文件),它是由columnfamily提供维护的。 6 | 而Version时由一个时间点所有有效SST文件组成。每次campact完成,将会创建新的version。 7 | 旧版本的super version和version在引用为零时会被清除。 8 | 9 | 10 | ```cpp 11 | //SuperVersion数据结构定义 12 | // holds references to memtable, all immutable memtables and version 13 | struct SuperVersion { 14 | // Accessing members of this class is not thread-safe and requires external 15 | // synchronization (ie db mutex held or on write thread). 16 | MemTable* mem; 17 | MemTableListVersion* imm; 18 | Version* current; 19 | 20 | 21 | DBImpl::GetImpl 22 | --GetAndRefSuperVersion 23 | --if (!skip_memtable) 24 | ----MemTable::Get//sv->mem->Get 25 | ----MemTableListVersion::Get//sv->imm->Get 26 | --else 27 | ----Version::Get//sv->current->Get 28 | --ReturnAndCleanupSuperVersion 29 | ``` -------------------------------------------------------------------------------- /02.Read/code_MemTable_Get.md: -------------------------------------------------------------------------------- 1 | #1.MemTable::Get 2 | 3 | 在内存中搜索 4 | 5 | 在Memtable和Immutable memtable中搜索调用的函数是一致的。其默认实现是基于跳表完成的。 6 | 跳表迭代器SkipListRep::Iterator iter通过FindGreaterOrEqual实现搜索定位,并根据获得目标对象的key决定是继续搜索还是返回结果。 7 | 8 | 值得注意的是,rocksdb引入了范围删除和Merge等运算操作,所以在值类型判断的时候要比leveldb复杂的多。 9 | 具体流程如下: 10 | 1.在Memtable中找到一个大于等于该值的节点 11 | 2.根据不同情况决定是否向下执行: 12 | --User Key不等,直接失败。-------------->查找失败,Merge Context被传到下一级查找。。 13 | --User Key相等,查找成功 14 | --类型为Merge,将Merge内容记入Merge Context,向下查找。 15 | 16 | 17 | ```cpp 18 | 19 | MemTable::Get 20 | --std::unique_ptr range_del_iter(NewRangeTombstoneIterator(read_opts)); 21 | --range_del_agg->AddTombstones(std::move(range_del_iter)); 22 | --prefix_bloom_->MayContain(prefix_extractor_->Transform(user_key)); 23 | --MemTableRep::Get//table_->Get 24 | ----MemTableRep::GetDynamicPrefixIterator 25 | ------GetIterator 26 | --------SkipListRep::Iterator(&skip_list_); 27 | ----SkipListRep::Iterator::Seek//iter->Seek(k.internal_key(), k.memtable_key().data()) 28 | ------InlineSkipList::Iterator::Seek 29 | --------InlineSkipList::FindGreaterOrEqual 30 | ----SaveValue//返回true代表还要next,false就是不用继续了。 31 | ------s->mem->GetInternalKeyComparator().user_comparator()->Equal//如果不相等,则返回,说明不在memtable 32 | --------DecodeFixed64(key_ptr + key_length - 8) 33 | --------UnPackSequenceAndType(tag, &seq, &type); 34 | --------if ((type == kTypeValue || type == kTypeMerge || type == kTypeBlobIndex) && range_del_agg->ShouldDelete(Slice(key_ptr, key_length))) { 35 | type = kTypeRangeDeletion;} 36 | ------switch (type) 37 | --------case kTypeBlobIndex: 38 | --------case kTypeValue: 39 | ----------s->value->assign(v.data(), v.size());//找到,value赋值 40 | ----------*(s->found_final_value) = true; 41 | --------case kTypeDeletion: 42 | ----------*(s->status) = Status::NotFound(); 43 | ----------*(s->found_final_value) = true; 44 | --------case kTypeMerge: 45 | ----------merge_context->PushOperand 46 | ----------if (merge_operator->ShouldMerge) 47 | ------------MergeHelper::TimedFullMerge 48 | ------------*(s->found_final_value) = true; 49 | ``` 50 | 51 | -------------------------------------------------------------------------------- /02.Read/code_TableCache_Get.md: -------------------------------------------------------------------------------- 1 | #1.TableCache::Get 2 | 3 | 4 | ```cpp 5 | TableCache::Get 6 | --//可能有优化 7 | --// Check row cache if enabled. Since row cache does not currently store 8 | // sequence numbers, we cannot use it if we need to fetch the sequence. 9 | if (ioptions_.row_cache && !get_context->NeedToReadSequence()) { 10 | --TableCache::FindTable 11 | ----number = fd.GetNumber(); 12 | ----key = GetSliceForFileNumber(&number); 13 | ----*handle = cache_->Lookup(key);//根据fd查找内存中是否有sst对应的table 14 | ----if (*handle == nullptr) 15 | ------GetTableReader 16 | --------//ioptions_.table_factory->NewTableReader 17 | --------BlockBasedTableFactory::NewTableReader 18 | ----------BlockBasedTable::Open 19 | ----cache_->Insert//将新建的table插入cache,以备下次读。 20 | ----return s;//返回table对象,table在cache中,通过handle拿 21 | 22 | --t = GetTableReaderFromHandle(handle) 23 | --std::unique_ptr range_del_iter(t->NewRangeTombstoneIterator(options)); 24 | --get_context->range_del_agg()->AddTombstones(std::move(range_del_iter)) 25 | --BlockBasedTable::Get//t->Get(options, k, get_context, skip_filters) 26 | ----filter_entry = GetFilter 27 | ----if (!FullFilterKeyMayMatch) 28 | return 29 | ----if (FullFilterKeyMayMatch) 30 | ------iiter = NewIndexIterator 31 | ------for (iiter->Seek(key); iiter->Valid() && !done; iiter->Next()) 32 | --------handle_value = iiter->value(); 33 | --------bool not_exist_in_filter = 34 | filter != nullptr && filter->IsBlockBased() == true && 35 | handle.DecodeFrom(&handle_value).ok() && 36 | !filter->KeyMayMatch(ExtractUserKey(key), handle.offset(), no_io); 37 | --------if (not_exist_in_filter) 38 | break; 39 | --------else exist 40 | ----------BlockBasedTable::NewDataBlockIterator//注意在这里把数据load到 41 | ------------MaybeLoadDataBlockToCache 42 | ------------ReadBlockFromFile 43 | ----------for (biter.Seek(key); biter.Valid(); biter.Next()) 44 | ------------get_context->SaveValue(parsed_key, biter.value(), &biter)) 45 | 46 | ``` 47 | 48 | 其中的FindTable实现从缓存中去拿选出的sstable文件的基本元信息,如果缓存里面没有就需要先创建(BlockBasedTable::Open)一个,同时还要插入缓存(cache_->Insert)。 49 | Open涉及的内容很多,主要是通过读footer在TableReader对象里面缓存meta block和index block里面的信息。 50 | 然后就是BlockBasedTable::Get去根据table拿到key-value了。首先考虑拿过滤器判断想搜索的内容是否可能存在。 51 | 接着用NewIndexIterator去seek值被包括的块,优先尝试从cache中读这个快,然后再考虑用过滤器判断值是否在块内, 52 | 最后用 NewDataBlockIterator去seek快内的值。最后SaveValue按照key-value的类型决定返回还是继续搜索。 53 | 54 | 55 | 读流程逻辑步骤: 56 | -- 找到一个可能含有Key的文件(sst fraction cascading) 57 | -- 从TableCache中查找Table Reader 58 | -- 使用FullFilter对文件进行过滤 59 | -- 如果包含,使用Index定位block位置 60 | -- 使用BlockFilter对Block进行过滤 61 | -- 在DataBlock中搜索记录。 -------------------------------------------------------------------------------- /02.Read/code_Version_Get.md: -------------------------------------------------------------------------------- 1 | #1.Version::Get 2 | 3 | 在sstable中搜索 4 | 如果在内存中没有得到结果,那么下一步就是从sstable中找寻了。但是这一步并不是马上会产生I/O,因为rocksdb/leveldb都做了一定程度的cache(table cache/block cache),只有cache中没有的时候才回去读磁盘。 5 | 6 | 7 | ```cpp 8 | Version::Get 9 | --GetContext get_context() 10 | --pinned_iters_mgr.StartPinning(); 11 | --FilePicker fp() 12 | --FdWithKeyRange* f = fp.GetNextFile() 13 | ----while (!search_ended_) { // Loops over different levels. 14 | while (curr_index_in_curr_level_ < curr_file_level_->num_files) { 15 | ------if (num_levels_ > 1 || curr_file_level_->num_files > 3) 16 | --------fractional cascading优化寻找SST的方法 FileIndexer. 17 | ------else 18 | --------层内顺序访问sst文件(已经排序) 19 | --------PrepareNextLevel(Binary Search决定候选文件) 20 | 21 | --while (f != nullptr) 22 | ----*status = table_cache_->Get 23 | ----switch (get_context.State()) 24 | ------case GetContext::kNotFound: 25 | break; 26 | ------case GetContext::kMerge: 27 | break; 28 | ------case GetContext::kFound: 29 | return//stop while 30 | ------case GetContext::kDeleted: 31 | // Use empty error message for speed 32 | *status = Status::NotFound(); 33 | return; 34 | case GetContext::kCorrupt: 35 | *status = Status::Corruption("corrupted key for ", user_key); 36 | return; 37 | case GetContext::kBlobIndex: 38 | ROCKS_LOG_ERROR(info_log_, "Encounter unexpected blob index."); 39 | *status = Status::NotSupported 40 | ----f = fp.GetNextFile(); 41 | 42 | --if (GetContext::kMerge == get_context.State())//begin the merge 43 | ----*status = MergeHelper::TimedFullMerge 44 | ----if (LIKELY(value != nullptr)) 45 | ------value->PinSelf() 46 | ----else 47 | ------*status = Status::NotFound(); // Use an empty error message for speed 48 | 49 | 50 | 51 | ``` 52 | 53 | 这里面有两个函数值得注意,一个是GetNextFile()明确了我们每一次去读哪个文件,按什么顺序读。另一个是TableCache::Get明确了每个文件我们怎么读里面的内容。 54 | 55 | 在GetNextFile中,RocksDB实现了一个基于FileIndexer得fractional cascading优化寻找sst算法。 56 | 常规的办法是通过两层循环先在每层内全局二分搜索,然后从0层循环到最高层。 57 | 通过维护FileIndexer,对于第1层以上的层且层文件数多于3个时,可以根据之前上一层读过的最后一个文件的key-range和要搜索的key值,对本层要读的sstable文件进行范围收紧。 58 | 这里有最多五种不同情况,每一种都可以至少确定一个right_bound或者left_bound。 59 | -------------------------------------------------------------------------------- /02.Read/code_do_select.md: -------------------------------------------------------------------------------- 1 | #1.do_select 2 | 3 | ```cpp 4 | do_select 5 | --sub_select 6 | ----join_init_read_record 7 | ------init_read_record 8 | --------handler::ha_rnd_init 9 | ----------myrocks::ha_rocksdb::rnd_init 10 | ----rr_sequential 11 | ------handler::ha_rnd_next 12 | --------myrocks::ha_rocksdb::rnd_next 13 | ----------myrocks::ha_rocksdb::rnd_next_with_direction 14 | ------------rocksdb::BaseDeltaIterator::Next 15 | --------------rocksdb::BaseDeltaIterator::Advance 16 | ----------------rocksdb::BaseDeltaIterator::AdvanceBase 17 | ----------------rocksdb::ArenaWrappedDBIter::Next 18 | ------------------rocksdb::ArenaWrappedDBIter::Next 19 | --------------------rocksdb::DBIter::Next 20 | ----------------------rocksdb::MemTableIterator::Next 21 | ------------------------rocksdb::(anonymous namespace)::SkipListRep::Iterator::Next 22 | --------------------------rocksdb::InlineSkipList::Node::Next 23 | ----------------------rocksdb::DBIter::FindNextUserEntry 24 | ------------------------rocksdb::DBIter::FindNextUserEntryInternal 25 | ----------------rocksdb::BaseDeltaIterator::UpdateCurrent 26 | ------------myrocks::ha_rocksdb::convert_record_from_storage_format 27 | --------------myrocks::ha_rocksdb::convert_field_from_storage_format 28 | --------------Field::move_field 29 | --------------myrocks::ha_rocksdb::convert_varchar_from_storage_format 30 | ----evaluate_join_record 31 | ------myrocks::ha_rocksdb::unlock_row 32 | 33 | ``` -------------------------------------------------------------------------------- /02.Read/code_myrocks_ha_rocksdb_rnd_init.md: -------------------------------------------------------------------------------- 1 | #1.myrocks::ha_rocksdb::rnd_init 2 | 3 | read流程图 4 | 5 | ```cpp 6 | 7 | mysql_execute_command 8 | --execute_sqlcom_select 9 | ----handle_select 10 | ------mysql_select 11 | --------mysql_execute_select 12 | ----------JOIN::exec 13 | ------------do_select 14 | --------------sub_select 15 | ----------------join_init_read_record 16 | ------------------init_read_record 17 | --------------------handler::ha_rnd_init 18 | ----------------------myrocks::ha_rocksdb::rnd_init 19 | 20 | myrocks::ha_rocksdb::rnd_init 21 | --myrocks::get_or_create_tx 22 | --myrocks::ha_rocksdb::setup_read_decoders 23 | --myrocks::ha_rocksdb::setup_iterator_for_rnd_scan 24 | ----myrocks::Rdb_key_def::get_first_key 25 | ------myrocks::Rdb_key_def::get_infimum_key 26 | --------myrocks::rdb_netbuf_store_index 27 | ----myrocks::ha_rocksdb::setup_scan_iterator 28 | ------myrocks::get_or_create_tx 29 | ------myrocks::ha_rocksdb::check_bloom_and_set_bounds 30 | --------myrocks::ha_rocksdb::can_use_bloom_filter 31 | --------myrocks::ha_rocksdb::setup_iterator_bounds 32 | ------myrocks::Rdb_transaction::get_iterator 33 | --------myrocks::Rdb_transaction_impl::acquire_snapshot 34 | ----------TransactionBaseImpl::SetSnapshot 35 | ------------rocksdb::DBImpl::GetSnapshotForWriteConflictBoundary 36 | --------------rocksdb::DBImpl::GetSnapshotImpl 37 | ----------------rocksdb::SnapshotList::New 38 | ------------rocksdb::TransactionBaseImpl::SetSnapshotInternal 39 | ----------rocksdb::TransactionBaseImpl::GetSnapshot 40 | ----------myrocks::Rdb_transaction::snapshot_created 41 | ----rocksdb::BaseDeltaIterator::Seek//setup_iterator_for_rnd_scan 42 | ------rocksdb::DBIter::Seek 43 | --------rocksdb::MemTableIterator::Seek 44 | ----------rocksdb::(anonymous namespace)::SkipListRep::Iterator::Seek 45 | ------------rocksdb::InlineSkipList::Iterator::Seek 46 | --------------rocksdb::InlineSkipList::FindGreaterOrEqual 47 | --------rocksdb::DBIter::FindNextUserEntry 48 | ----------rocksdb::DBIter::FindNextUserEntryInternal 49 | ------rocksdb::WBWIIteratorImpl::Seek 50 | --------rocksdb::SkipList::Iterator::Seek 51 | ----------rocksdb::SkipList::FindGreaterOrEqual 52 | ------rocksdb::BaseDeltaIterator::UpdateCurrent 53 | 54 | ``` -------------------------------------------------------------------------------- /02.Read/code_rocksdb_get.md: -------------------------------------------------------------------------------- 1 | #1.rocksdb_get 2 | 3 | ###对外接口 4 | RocksDB 对外提供了Get(key), Put(key), Delete(key) and NewIterator()等操作,我们可以直接从官方的文档里面找到测试程序,以此为入口开始代码分析(examples/c_simple_example.c)。 5 | 6 | 代码中的rocksdb_put、rocksdb_get作为全局函数调用DB::put/DB:get接口,本质是调用的数据库实现类DBImpl的操作。 7 | 8 | 写入过程中key和value被打包成slice对象传入put接口,写入WriteBatch,后续将批量写入Memtable。 9 | 10 | Slice由一个size变量和一个指向外部内存区域的指针构成。使用slice可以减少之后的key、value在传值过程中的拷贝操作。 11 | 12 | 而在读的过程中,引入了PinnabeSlice作为引用出参,同样是减少内存拷贝,并做到了生命周期控制,防止block_cache中的数据在返回前被释放,最终get不到数据的问题。(BlockIter退出前通过DelegateCleanUpsTo将cleanup交给PinnabeSlice) 13 | 14 | 15 | 16 | ```cpp 17 | main//c_simple_example 18 | --rocksdb_options_create 19 | --rocksdb_open 20 | --rocksdb_readoptions_create 21 | --rocksdb_get(db, readoptions, key, strlen(key), &len, &err) 22 | ----db->rep->Get(options->rep, Slice(key, keylen), &tmp); 23 | ------DB::Get 24 | --------DBImpl::Get 25 | ----------DBImpl::GetImpl 26 | ------------if (!skip_memtable) 27 | --------------MemTable::Get//sv->mem->Get 28 | --------------sv->imm->Get 29 | ------------else 30 | --------------sv->current->Get 31 | --rocksdb_close(db) 32 | ``` -------------------------------------------------------------------------------- /02.Read/notes_Read/assets/1599805914-06897fdc14ea93138b4a19e1b3cc5f0b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/02.Read/notes_Read/assets/1599805914-06897fdc14ea93138b4a19e1b3cc5f0b.png -------------------------------------------------------------------------------- /02.Read/notes_Read/assets/1599805914-0a90e60de512338b578afc2adfe4ac52.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/02.Read/notes_Read/assets/1599805914-0a90e60de512338b578afc2adfe4ac52.png -------------------------------------------------------------------------------- /02.Read/notes_Read/assets/1599805914-0e0cf6338619205c20df532489e2ce0b.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/02.Read/notes_Read/assets/1599805914-0e0cf6338619205c20df532489e2ce0b.jpg -------------------------------------------------------------------------------- /02.Read/notes_Read/assets/1599805914-2af0d6a8616c66698a486e46a8384235.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/02.Read/notes_Read/assets/1599805914-2af0d6a8616c66698a486e46a8384235.jpg -------------------------------------------------------------------------------- /02.Read/notes_Read/assets/1599805914-53418207f5137d4efce858bd1bea6168.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/02.Read/notes_Read/assets/1599805914-53418207f5137d4efce858bd1bea6168.jpg -------------------------------------------------------------------------------- /02.Read/notes_Read/assets/1599805914-59c30fbdcdd086529bfb5c52adaeca73.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/02.Read/notes_Read/assets/1599805914-59c30fbdcdd086529bfb5c52adaeca73.jpg -------------------------------------------------------------------------------- /02.Read/notes_Read/assets/1599805914-8b3dd79bf8afd7a11430ebf1130d3ca1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/02.Read/notes_Read/assets/1599805914-8b3dd79bf8afd7a11430ebf1130d3ca1.jpg -------------------------------------------------------------------------------- /02.Read/notes_Read/assets/1599805914-915e1d5ae3b19ca34d53586d92f43e84.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/02.Read/notes_Read/assets/1599805914-915e1d5ae3b19ca34d53586d92f43e84.jpg -------------------------------------------------------------------------------- /02.Read/notes_Read/assets/1599805914-91b156f25d95a9e328b0baacd7b85807.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/02.Read/notes_Read/assets/1599805914-91b156f25d95a9e328b0baacd7b85807.jpg -------------------------------------------------------------------------------- /02.Read/notes_Read/assets/1599805914-a1392f3898a550da5a612043db71cc88.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/02.Read/notes_Read/assets/1599805914-a1392f3898a550da5a612043db71cc88.jpg -------------------------------------------------------------------------------- /02.Read/notes_Read/assets/1599805914-a9fac8fee725dd09425da4a9ea30189b.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/02.Read/notes_Read/assets/1599805914-a9fac8fee725dd09425da4a9ea30189b.jpg -------------------------------------------------------------------------------- /02.Read/notes_Read/assets/1599805914-c217cb89e646b43f26f4ea490a78d642.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/02.Read/notes_Read/assets/1599805914-c217cb89e646b43f26f4ea490a78d642.jpg -------------------------------------------------------------------------------- /02.Read/notes_Read/assets/1599805914-c4e3a5ed8a667c9a841587e16e7bf518.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/02.Read/notes_Read/assets/1599805914-c4e3a5ed8a667c9a841587e16e7bf518.png -------------------------------------------------------------------------------- /02.Read/notes_Read/assets/1599805914-d0252f7da8480c3241f26682fbfb54ee.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/02.Read/notes_Read/assets/1599805914-d0252f7da8480c3241f26682fbfb54ee.png -------------------------------------------------------------------------------- /02.Read/notes_Read/assets/1599805914-efc3e77847382f4f689a8248bfa0424d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/02.Read/notes_Read/assets/1599805914-efc3e77847382f4f689a8248bfa0424d.png -------------------------------------------------------------------------------- /02.Read/notes_Read/assets/1599805914-f00a283b92d885aa4be05f5e9828756a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/02.Read/notes_Read/assets/1599805914-f00a283b92d885aa4be05f5e9828756a.png -------------------------------------------------------------------------------- /02.Read/notes_Read/index.md: -------------------------------------------------------------------------------- 1 | # [RocksDB 原理介绍:读过程源码分析] 2 | 3 | 4 | # RocksDB 读流程源码分享 5 | 6 | 7 | ## 整体逻辑 8 | 9 | RocksDB的读相关操作在逻辑上并不复杂,基本继承了LevelDB的流程步骤,但在细节上有了很多优化,并额外引入了column family的概念。 10 | ![](assets/1599805914-4120ce649b7b667cba47c0bf78833c95.jpg)![](assets/1599805914-91b156f25d95a9e328b0baacd7b85807.jpg) 11 | 当用户通过Get()接口进行key-value pair搜索时,会拿到一个超级版本supper version,并依次从memtable、immutable memtable队列、sstable缓存中搜索。 12 | ![](assets/1599805914-e654212e95c48e4de48c4e31928b8306.jpg)![](assets/1599805914-efc3e77847382f4f689a8248bfa0424d.png) 13 | 14 | ![](assets/1599805914-a1392f3898a550da5a612043db71cc88.jpg) 15 | 16 | 17 | ## 对外接口 18 | 19 | ![](assets/1599805914-3a3abd349fa289522ede171c5bb946b4.jpg)![](assets/1599805914-915e1d5ae3b19ca34d53586d92f43e84.jpg) 20 | RocksDB 对外提供了Get(key), Put(key), Delete(key) and NewIterator()等操作,我们可以直接从官方的文档里面找到测试程序,以此为入口开始代码分析(examples/c\_simple\_example.c)。 21 | 代码中的rocksdb\_put、rocksdb\_get作为全局函数调用DB::put/DB:get接口,本质是调用的数据库实现类DBImpl的操作。 22 | 写入过程中key和value被打包成slice对象传入put接口,写入WriteBatch,后续将批量写入Memtable。 23 | Slice由一个size变量和一个指向外部内存区域的指针构成。使用slice可以减少之后的key、value在传值过程中的拷贝操作。 24 | 而在读的过程中,引入了PinnabeSlice作为引用出参,同样是减少内存拷贝,并做到了生命周期控制,防止block\_cache中的数据在返回前被释放,最终get不到数据的问题。(BlockIter退出前通过DelegateCleanUpsTo将cleanup交给PinnabeSlice) 25 | 26 | ## 数据库实现层逻辑 27 | 28 | ![](assets/1599805914-a3ccba3bfcc0c97926f6cbaa4987bcf3.jpg)![](assets/1599805914-0e0cf6338619205c20df532489e2ce0b.jpg) 29 | Rocksdb::DB是个抽象基类,其中定义些纯虚函数,包括Get,Put操作等,具体实现时调用它的子类rocksdb::DBImpl实现这些接口功能。Get的功能就是油DBImpl::GetImpl完成。 30 | 其中通过SuperVersion可以找到当前的Memtable、Immutable Memtable队列、以及当前Version(由此可以找到当前版本的sstable结构中的所有文件),它是由columnfamily提供维护的。 31 | 而Version时由一个时间点所有有效SST文件组成。每次campact完成,将会创建新的version。 32 | 旧版本的super version和version在引用为零时会被清除。 33 | 34 | ## 在内存中搜索 35 | 36 | ![](assets/1599805914-76a27ff7bb644d3537f20e22c113fb4e.jpg)![](assets/1599805914-2af0d6a8616c66698a486e46a8384235.jpg) 37 | 38 | 在Memtable和Immutable memtable中搜索调用的函数是一致的。其默认实现是基于跳表完成的。跳表迭代器SkipListRep::Iterator iter通过FindGreaterOrEqual实现搜索定位,并根据获得目标对象的key决定是继续搜索还是返回结果。![](assets/1599805914-d02090239df754ef8d63d8c7eb6c85ae.jpg) 39 | 40 | ![](assets/1599805914-53418207f5137d4efce858bd1bea6168.jpg)![](assets/1599805914-19eaf10987d1e0700f1ed46f529fcec6.jpg) 41 | 42 | 值得注意的是,rocksdb引入了范围删除和Merge等运算操作,所以在值类型判断的时候要比leveldb复杂的多。 43 | 44 | ![](assets/1599805914-8b3dd79bf8afd7a11430ebf1130d3ca1.jpg) 45 | 46 | ## 在sstable中搜索 47 | 48 | 如果在内存中没有得到结果,那么下一步就是从sstable中找寻了。但是这一步并不是马上会产生I/O,因为rocksdb/leveldb都做了一定程度的cache(table cache/block cache),只有cache中没有的时候才回去读磁盘。 49 | ![](assets/1599805914-12d327bf97ead72bf1c3d907042799fa.jpg)![](assets/1599805914-59c30fbdcdd086529bfb5c52adaeca73.jpg) 50 | 51 | 这里面有两个函数值得注意,一个是GetNextFile()明确了我们每一次去读哪个文件,按什么顺序读。另一个是TableCache::Get明确了每个文件我们怎么读里面的内容。 52 | 53 | 在GetNextFile中,RocksDB实现了一个基于FileIndexer得fractional cascading优化寻找sst算法。常规的办法是通过两层循环先在每层内全局二分搜索,然后从0层循环到最高层。通过维护FileIndexer,对于第1层以上的层且层文件数多于3个时,可以根据之前上一层读过的最后一个文件的key-range和要搜索的key值,对本层要读的sstable文件进行范围收紧。这里有最多五种不同情况,每一种都可以至少确定一个right\_bound或者left\_bound。 54 | ![](assets/1599805914-1c4c1537e01dda1e66a9ea2d7cd4c2aa.jpg)![](assets/1599805914-a9fac8fee725dd09425da4a9ea30189b.jpg) 55 | 56 | 另外一个函数是TableCache::Get。 57 | ![](assets/1599805914-b4107df9da7c4754a652f101ccbd10df.jpg)![](assets/1599805914-d0252f7da8480c3241f26682fbfb54ee.png) 58 | 59 | 其中的FindTable实现从缓存中去拿选出的sstable文件的基本元信息,如果缓存里面没有就需要先创建(BlockBasedTable::Open)一个,同时还要插入缓存(cache\_->Insert)。Open涉及的内容很多,主要是通过读footer在TableReader对象里面缓存meta block和index block里面的信息。 60 | 然后就是BlockBasedTable::Get去根据table拿到key-value了。首先考虑拿过滤器判断想搜索的内容是否可能存在。接着用NewIndexIterator去seek值被包括的块,优先尝试从cache中读这个快,然后再考虑用过滤器判断值是否在块内,最后用 NewDataBlockIterator去seek快内的值。最后SaveValue按照key-value的类型决定返回还是继续搜索。 61 | ![](assets/1599805914-6131d0eb028a89385f7c1c269db4a13f.jpg)![](assets/1599805914-c217cb89e646b43f26f4ea490a78d642.jpg) 62 | 63 | ![](assets/1599805914-06897fdc14ea93138b4a19e1b3cc5f0b.png) 64 | ![](assets/1599805914-020392068f9b4e5defe7339729c5acb4.jpg) 65 | 66 | ## 其他 67 | 68 | 1. 69 | 70 | In-place updates for equal keys and similar sized values 71 | 72 | Summary: 73 | Currently for each put, a fresh memory is allocated, and a new entry is added to the memtable with a new sequence number irrespective of whether the key already exists in the memtable. This diff is an attempt to update the value inplace for existing keys. It currently handles a very simple case: 74 | 1\. Key already exists in the current memtable. Does not inplace update values in immutable memtable or snapshot 75 | 2\. Latest value type is a 'put' ie kTypeValue 76 | 3\. New value size is less than existing value, to avoid reallocating memory 77 | 78 | In-place updates with callback function 79 | existing\_value - pointer to previous value (from both memtable and sst). nullptr if key doesn't exist 80 | existing\_value\_size - pointer to size of existing\_value). nullptr if key doesn't 81 | exist delta\_value - Delta value to be 82 | merged with the existing\_value. Stored in transaction logs. merged\_value - Set when delta is applied on the previous value. 83 | 84 | UpdateStatus (\*inplace\_callback)(char\* existing\_value, 85 | 86 | \`\`\` 87 | UpdateStatus (\*inplace\_callback)(char\* existing\_value, uint32\_t\* existing\_value\_size, Slice delta\_value, std::string\* merged\_value) 88 | \`\`\` 89 | 90 | Applicable only when inplace\_update\_support is true, this callback function is called at the time of updating the memtable as part of a Put operation, lets say Put(key, delta\_value). It allows the 'delta\_value' specified as part of the Put operation to be merged with an 'existing\_value' of the key in the database. 91 | If the merged value is smaller in size that the 'existing\_value', then this function can update the 'existing\_value' buffer inplace and the corresponding 'existing\_value'\_size pointer, if it wishes to. The callback should return UpdateStatus::UPDATED\_INPLACE. In this case. (In this case, the snapshot-semantics of the rocksdb Iterator is not atomic anymore). 92 | If the merged value is larger in size than the 'existing\_value' or the application does not wish to modify the 'existing\_value' buffer inplace, then the merged value should be returned via \*merge\_value. It is set by merging the 'existing\_value' and the Put 'delta\_value'. The callback should return UpdateStatus::UPDATED in this case. This merged value will be added to the memtable. 93 | If merging fails or the application does not wish to take any action, then the callback should return UpdateStatus::UPDATE\_FAILED. 94 | Please remember that the original call from the application is Put(key, delta\_value). So the transaction log (if enabled) will still contain (key, delta\_value). The 'merged\_value' is not stored in the transaction log. Hence the inplace\_callback function should be consistent across db reopens. 95 | 96 | 2. 97 | 98 | ```plain 99 | class LookupKey { 100 | public: 101 | // Initialize *this for looking up user_key at a snapshot with 102 | // the specified sequence number. 103 | LookupKey(const Slice& _user_key, SequenceNumber sequence); 104 | 105 | ~LookupKey(); 106 | 107 | // Return a key suitable for lookup in a MemTable. 108 | Slice memtable_key() const { 109 | return Slice(start_, static_cast(end_ - start_)); 110 | } //在memtable中值比较时使用 111 | 112 | // Return an internal key (suitable for passing to an internal iterator) 113 | Slice internal_key() const { 114 | return Slice(kstart_, static_cast(end_ - kstart_)); 115 | }//在sst中值比较时使用 116 | 117 | // Return the user key 118 | Slice user_key() const { 119 | return Slice(kstart_, static_cast(end_ - kstart_ - 8)); 120 | } 121 | 122 | private: 123 | // We construct a char array of the form: 124 | // klength varint32 <-- start_ 125 | // userkey char[klength] <-- kstart_ 126 | // tag uint64 127 | // <-- end_ 128 | // The array is a suitable MemTable key. 129 | // The suffix starting with "userkey" can be used as an InternalKey. 130 | const char* start_; 131 | const char* kstart_; 132 | const char* end_; 133 | char space_[200]; // Avoid allocation for short keys 134 | 135 | // No copying allowed 136 | LookupKey(const LookupKey&); 137 | void operator=(const LookupKey&); 138 | }; 139 | ``` 140 | 141 | ![](assets/1599805914-c4e3a5ed8a667c9a841587e16e7bf518.png)![](assets/1599805914-0a90e60de512338b578afc2adfe4ac52.png) 142 |  143 | 144 | 145 | 146 | 排序时,squence大的排在前面,即seek时默认读到最新的值。 147 | 148 | -------------------------------------------------------------------------------- /02.Read/notes_Read_Flow/Read.md: -------------------------------------------------------------------------------- 1 | #1.SSTable Read 2 | 3 | 用户每次要访问一个 SSTable 时,只需要先读取 IndexBlock 和 MetaBlock,如果通过了 BloomFilter 的检验,再从 IndexBlock 的索引结构中判断应该读取哪个 DataBlock,这样只需再读取对应 DataBlock 就可以获得数据。 4 | 5 | ![](images/1.png) 6 | ![](images/2.png) 7 | 8 | 读取SST 9 | 前面我们介绍过Rocksdb的Get()接口,首先在Memtable中查找,之后是Immutable Memtable,最后再去SST文件中查找: 10 | 通过Version中记录的信息遍历Level中的所有SST文件,利用SST文件记录的边界最大,最小key- smallest_key和largest_key来判断查找的key是否有可能存在 11 | 如果在该Level中可能存在,调用TableReader的接口来查找SST文件 12 | 首先通过SST文件中的Filter来初判key是否存在 13 | 如果存在key存在,进入Data Block中查找 14 | 在Data Block利用Block的迭代器BlockIter利用二分查找BinarySeek或者前缀查找PrefixSeek 15 | 如果查找成功则返回,如果该Level不存在查找的Key,继续利用Version中的信息进行下一个Level的查找,直到最大的一个Level 16 | 17 | -------------------------------------------------------------------------------- /02.Read/notes_Read_Flow/images/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/02.Read/notes_Read_Flow/images/1.png -------------------------------------------------------------------------------- /02.Read/notes_Read_Flow/images/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/02.Read/notes_Read_Flow/images/2.png -------------------------------------------------------------------------------- /02.Read/read.txt: -------------------------------------------------------------------------------- 1 | 1.读流程概要描述: 2 | 3 | 【Read 流程】如下: 4 | 5 | 在 MemTable 中查找,无法命中转到下一流程; 6 | 在 immutable memtable 中查找,查找不中转到下一流程; 7 | 在第0层SSTable中查找,无法命中转到下一流程; 8 | 对于L0 的文件,RocksDB 采用遍历的方法查找,所以为了查找效率 RocksDB 会控制 L0 的文件个数。 9 | 在剩余SSTable中查找。 10 | 对于 L1 层以及 L1 层以上层级的文件,每个 SSTable 没有交叠,即 Key 没有重复,可以使用二分查找快速找到 key 所在的 Level 以及 SST。 11 | 12 | 13 | 14 | 15 | 16 | 3.整体逻辑 17 | RocksDB的读相关操作在逻辑上并不复杂,基本继承了LevelDB的流程步骤,但在细节上有了很多优化,并额外引入了column family的概念。 18 | 19 | --对外接口层 20 | DB:level最外层接口 21 | Snapshot:主要作用是记录当前sequenceNumber,干涉compaction的合并数据。 22 | 23 | --逻辑层 24 | DBImpl:组织各个组件的中控层。 25 | VersionSet: 管理所有Version的生命周期。 26 | Version:1.接受最外层接口的请求,并转发到sstable层,同时记录compaction的状态。2.管理iterators。3.管理mainfest文件。 27 | 28 | --存储层 29 | SSTable:管理levelDB文件存储层的核心组件。 30 | Memtable:核心操作由skiplist实现。 31 | Log::Writer:写入binlog 32 | 33 | --OS适配层 34 | Env:操作系统接口,负责对接操作系统功能,隐藏系统细节。包括:1.文件操作。2.定时任务。 35 | 36 | 当用户通过Get()接口进行key-value pair搜索时,会拿到一个超级版本supper version,并依次从memtable、immutable memtable队列、sstable缓存中搜索。 37 | 38 | 39 | 40 | 41 | 42 | 8.读流程 43 | select * from t where id=1 44 | -- 在 WriteBatch 中查找 45 | -- 在Memable 中查找 46 | -- 在 Immutable MemTable 中查找 47 | -- 在Block Cache和SST 文件中查找 48 | 49 | 50 | 9.Fraction Cascading 51 |  1层以上并且该层文件数多于3才会执行。 52 |  在某一层进行查询时,会根据比较的结果设置查询的上限和下限。 53 |  在下一层进行查询时,会使用这个查询的上限和下限减少文件搜索的范围。 54 | 55 | 还是没搞明白什么是 Fraction Cascading. 56 | 57 | 10.读流程 iterator操作相关 58 | 构建数据的 Iterator 59 | 通过Seek函数定位 记录的可能位置 60 | 执行Next并进行过 滤,合并 61 | 62 | 63 |  Iterator 64 |  Seek 65 |  Next 66 |  Prev 67 |  SeekToFirst 68 |  SeekToLast 69 |  key 70 |  value 71 | 72 | DBIter 73 | --MergingIterator 74 | ---- MemTable Iterator 75 | ---- Table Iterator 76 | 77 | 上层Iterator的Seek或Next函数会调用下层Iterator的Seek或Next函数, 取决于上层Iterator的工作内容,下层Iterator的相应函数可能会调用多次。 78 | 79 | 11.范围查询搜索过程 80 | 81 | select * from t where id>=1 层级搜索 82 | 1. 通过VersionStroageInfo类可以获得文件元数据,包括每个文件包含Key的最大 值和最小值等,这些信息随文件的变化而维护。 83 | 2. 通过二分搜索找到可能包含Key的文件。 84 | 3. 对于第0层,由于范围重叠,必须要扫描每个文件。 85 | 86 | 87 | select * from t where id>=1 文件内部搜索 88 | 1. 从Footer定位 IndexBlock 89 | 2. 使用Index Block得到offset, 计算filter编号, 然后使用Filter Block中的offset array获得filter, 使用filter与Key 判断是否Match 90 | 3. 根据offset,获 取Data Block 91 | 92 | 93 | select * from t where id>=1 Index搜索 94 | 每个Data Block有一个 索引项,索引项的Key 大于等于前Data Block 的最大Key,小于后 Data Block的最小Key. 95 | 96 | 97 | select * from t where id>=1 Block内部搜索 98 | 1. 从结尾读取 num_restarts获得 Restart数组的起始位 置,由于每个Restart 项长度固定,可以计算 出第i个项的位置。 99 | 2. 在Restart Point 区域二分查询,找 到恰好较小的重启 点。 100 | 3. 使用重启点定位 一条Record,向下 顺序搜索,找到恰 好较大或等于的记 录。 101 | 102 | select * from t where id>=1 合并与遍历 103 | 1.使用堆排序将 多层列表合并 为一个 104 | 2.在遍历这个有 序列表时,去 除新版本元素 105 | 3.去除重复、被 删除的元素, 并做Merge 106 | 4.将WriteBatch 的操作更新到 列表上 107 | 108 | 12.MVCC 109 | --MVCC实现 110 |  当前事务的写入在提交前存储在自己的WriteBatch中,其他事务读取不到。 111 |  RR隔离级别下,事务的读取在第一次执行读取操作时获取SequenceNumber。 112 |  读取过程中会过滤大于SequenceNumber的记录。 113 | 114 | --MVCC等值查询和范围查询 115 |  在等值查询和范围查询中略有不同。 116 |  等值查询中,当在Iterator中执行Seek操作时,由于Key的比较规则, SequenceNumber较大的项已被跳过,而在Next操作中,不会找到不等于目标 Key的项; 117 |  而在范围查询中Sequence Number较大的项可能在Next操作中被取出来,因此 需要再次进行过滤。 118 | 119 | --范围查询例子 120 | Select * from t where id >= 5; 121 | 给出一个Seek和Next过程中实现MVCC的例子,假设有列表(元素格式为Key: SequenceNumber):[5:10, 5:9, 5:8, 6:10, 6:9, 6:8],当前快照Sequence Number值为9. 122 | Seek(5) 123 | [5:10, 5:9, 5:8, 6:10, 6:9, 6:8] 124 | Next 125 | 1, 向前一步,得到[5:10, 5:9, 5:8, 6:10, 6:9, 6:8],发现重复,跳过。 126 | 2, 向前两步,得到[5:10, 5:9, 5:8, 6:10, 6:9, 6:8],Sequence Number不符合跳过。 3, 向前三步,得到[5:10, 5:9, 5:8, 6:10, 6:9, 6:8],符合,Next结束。 127 | 128 | 129 | 等值查询 130 | Memtable 131 | --SkipList iterator 132 | --TableReader(Filter, MVCC) 133 | ----Block Iterator 134 | --GetContext(Merge)//会一直传下去 135 | 136 | 范围查询 137 | MemTableIterator 138 | --SkipList Iterator 139 | DBIter(去重,MVCC,Merge) 140 | TableIterator (Filter) 141 | --BlockIterator 142 | 143 | 144 | 145 | 146 | -------------------------------------------------------------------------------- /03.Write/code_DBImpl_WriteImpl.md: -------------------------------------------------------------------------------- 1 | #1.DBImpl::WriteImpl 2 | 3 | ```cpp 4 | DBImpl::WriteImpl 5 | --switch enable_piped_wirtes 6 | ----PipelinedWriteImpl//这里简单画了,很多东西和另一个分支重复 7 | ----WriteThread::Writer w(write_options, my_batch, callback, log_ref, disable_memtable) 8 | ----write_thread_.JoinBatchGroup(&w) 9 | ----switch 10 | ----case STATE_GROUP_LEADER 11 | ------WriteThread::WriteGroup wal_write_group 12 | ------EnterAsBatchGroupLeader 13 | ------WriteToWAL 14 | ------ExitAsBatchGroupLeader 15 | --------if (LinkGroup(write_group, &newest_memtable_writer_)) 16 | ----------SetState(write_group_leader, STATE_MEMTABLE_WRITER_LEADER) 17 | --------//Line the remaining of the group to memtable writer list, 将wal_write_group连到newest_memtable_writer队列中,然后选择MEMTABLE_WRITER队列中的Leader,并唤醒它。 18 | --------if (!newest_writer.compare_exchange_strong(newest_writer, null)){ 19 | Writer* next_leader = newest_writer; 20 | while (next_leader->link_order != last_writer) { 21 | next_leader = next_leader->link_order; 22 | assert(next_leader != null); 23 | } 24 | next_leader->link_order = null; 25 | SetState(next_leader, STATE_GROUP_LEADER);//选择newest_writer中的leader,并唤醒它。 26 | } 27 | AwaitState(leader, STATE_MEMTABLE_WRITER_LEADER|STATE_PARALLEL_MEMTABLE_WRITER | STATE_COMPLETED, &eabgl_ctx) 28 | //沉睡于MEMTABLE_WRITER队列中 29 | ----case STATE_MEMTABLE_WRITER_LEADER 30 | ------EnterAsMemtableWriter 31 | ------switch if memtable_write_group.size > 1 && mmutable_db_options_allow_concurrent_memtable_write 32 | --------LauchParallelMemtableWrites 33 | ------switch else 34 | --------WriteBatchInternal::InsertInto 35 | --------ExitAsMemtableWriter 36 | ----case STATE_PARALLEL_MEMTABLE_WRITER: 37 | ------//类似非pipeline 38 | ----case STATE_COMPLETED: 39 | ------return w.FinalStatus() 40 | 41 | --switch !enable_piped_wirtes 42 | ----WriteThread writer//建立写操作,链表的节点。 43 | ----WriteThread::JoinBatchGroup//将要写的batch的write加入队列 44 | ------bool linked_as_leader = LinkOne(w, &newest_writer_)//将当前writer原子的加入到group 45 | ------if (! linked_as_leader) 46 | --------WriteThread::AwaitState 47 | ----switch 48 | ----case STATE_PARALLEL_MEMTABLE_WRITE 49 | ------WriteBatchInternal::InsertInto 50 | ------CompleteParallelMemTableWriter 51 | --------AwaitState(w, STATE_COMPLETED, &cpmtw_ctx)//如果不是最后一个 52 | ------ExitAsBatchGroupFollower 53 | ----case STATE_COMPLETED 54 | ------return w_FinalStatus() 55 | ----case STATE_GROUP_LEADER 56 | ------WriteThread::EnterAsBatchGroupLeader//把leader下所有的write都连接到一个WriteGroup里 57 | ------switch 58 | ------!two_write_queues_(2PC) 59 | --------ConcurrentWriteToWAL 60 | ------two_write_queues_ 61 | --------WriteToWAL 62 | ------switch 63 | ------!parallel 64 | --------WriteBatchInternal::InsertInto 65 | ------parallel 66 | --------WriteThread::LaunchParallelMemTableWrites 67 | ----------for (auto w:*write_group) 68 | SetState(w, STATE_PARALLEL_MEMTABLE_WRITER); 69 | --------WriteThread::WriteBatchInternal::InsertInto 70 | --------WriteThread::CompleteParallelMemTableWrites 71 | ----------AwaitState(w, STATE_COMPLETED, &cpmtw_ctx)//如果不是最后一个 72 | ------ExitAsBatchGroupLeader 73 | --------switch 74 | --------enable_pipelined_write_//对应的是上面PipelinedWriteImpl中的内容 75 | --------!enable_pipelined_write_ 76 | ----------SetState(next_leader, STATE_GROUP_LEADER)//选择newest_writer_队列中新的leader并唤醒它。 77 | ``` -------------------------------------------------------------------------------- /03.Write/code_DBImpl_WriteImpl_2.md: -------------------------------------------------------------------------------- 1 | #1.数据库实现层逻辑: DBImpl::WriteImpl 2 | 3 | 4 | 每次写请求并不是直接读写Memtable的,而是打包进Writer等待批量写入的,这样的一个重要意义是可以使得一个事务内的的操作一次性写入数据和维护版本等信息。 5 | 后续多个线程的Writer会组成一个Group,并选出第一个为leader,以组的形式完成日志提交和Memtable写入。 6 | 7 | 8 | RocksDB的维护和更新非常及时。几个有代表的优化和新功能是:pipelinewrite、concurrent_memtable_write、2pc状态下的concurrent_WriteToWAL。随着新功能的增加,在数据库层的代码逻辑也越来越复杂,分支非常多。 9 | 10 | 总的来说rocksdb会将多个写线程组成一个group,leader负责 group内所有writer的WAL及memtable的提交,提交完后唤醒所有的follwer,向上层返回。 11 | 后续更新支持 allow_concurrent_memtable_write 选项,在之前的基础上,leader提交完WAL后,group里所有线程并发写 memtable,流程如下图。 12 | 13 | 而 enable_pipelined_write 选项,引入了流水线特性,第一个 group 的 WAL 提交后,在执行 memtable 写入前,下一个 group 同时开启。 14 | 15 | ```cpp 16 | DBImpl::WriteImpl 17 | --//if enable_pipelined_write 18 | ----WriteThread::Writer w(write_options, my_batch, callback, log_ref,disable_memtable, pre_release_callback); 19 | ----WriteThread::JoinBatchGroup(&w); 20 | ------linked_as_leader = LinkOne(w, &newest_writer_); 21 | ------if (linked_as_leader) { 22 | SetState(w, STATE_GROUP_LEADER); 23 | } 24 | ------if (!linked_as_leader) { 25 | /** 26 | * Wait util: 27 | * 1) An existing leader pick us as the new leader when it finishes 28 | * 2) An existing leader pick us as its follewer and 29 | * 2.1) finishes the memtable writes on our behalf 30 | * 2.2) Or tell us to finish the memtable writes in pralallel 31 | * 3) (pipelined write) An existing leader pick us as its follower and 32 | * finish book-keeping and WAL write for us, enqueue us as pending 33 | * memtable writer, and 34 | * 3.1) we become memtable writer group leader, or 35 | * 3.2) an existing memtable writer group leader tell us to finish memtable 36 | * writes in parallel. 37 | */ 38 | AwaitState(w, STATE_GROUP_LEADER | STATE_MEMTABLE_WRITER_LEADER | 39 | STATE_PARALLEL_MEMTABLE_WRITER | STATE_COMPLETED, 40 | &jbg_ctx); 41 | } 42 | ----//if (w.state == WriteThread::STATE_PARALLEL_MEMTABLE_WRITER) 43 | ------WriteBatchInternal::InsertInto 44 | ------write_thread_.CompleteParallelMemTableWriter(&w) 45 | --------AwaitState(w, STATE_COMPLETED, &cpmtw_ctx); 46 | ------write_thread_.ExitAsBatchGroupFollower(&w); 47 | 48 | ----//if (w.state == WriteThread::STATE_COMPLETED) 49 | ------ return w.FinalStatus(); 50 | 51 | ----//if w.state == WriteThread::STATE_GROUP_LEADER 52 | ------write_thread_.EnterAsBatchGroupLeader// 把次leader下所有writer都链接到一个writegroup中 53 | ------//if (!two_write_queues_) 54 | --------WriteToWAL 55 | ------//if (two_write_queues_) 56 | --------ConcurrentWriteToWAL 57 | ------//if (!parallel)//非并发写 58 | --------WriteBatchInternal::InsertInto 59 | ------// if (parallel) 并发写 60 | --------write_thread_.LaunchParallelMemTableWriters 61 | --------WriteBatchInternal::InsertInto 62 | --------write_thread_.CompleteParallelMemTableWriter 63 | --------write_thread_.ExitAsBatchGroupLeader 64 | 65 | --//if not enable_pipelined_write 66 | ----DBImpl::PipelinedWriteImpl 67 | ------WriteThread::Writer w(write_options, my_batch, callback, log_ref,disable_memtable); 68 | ------write_thread_.JoinBatchGroup 69 | ------// if STATE_GROUP_LEADER 70 | --------WriteThread::WriteGroup wal_write_group; 71 | --------write_thread_.EnterAsBatchGroupLeader 72 | --------WriteToWAL 73 | --------ExitAsBatchGroupLeader 74 | ----------if (LinkGroup(write_group, &newest_memtable_writer_)) { 75 | // The leader can now be different from current writer. 76 | SetState(write_group.leader, STATE_MEMTABLE_WRITER_LEADER); 77 | } 78 | // Link the ramaining of the group to memtable writer list. 将wal_write_group连接到newest_memtable_writer_队列中,然后选择MEMTABLE_WRITER队列中的leader,并唤醒它。 79 | ----------// Reset newest_writer_ and wake up the next leader. 80 | Writer* newest_writer = last_writer; 81 | if (!newest_writer_.compare_exchange_strong(newest_writer, nullptr)) { 82 | Writer* next_leader = newest_writer; 83 | while (next_leader->link_older != last_writer) { 84 | next_leader = next_leader->link_older; 85 | assert(next_leader != nullptr); 86 | } 87 | next_leader->link_older = nullptr; 88 | SetState(next_leader, STATE_GROUP_LEADER);//选择newest_writer队列中的新leader,并唤醒它。 89 | } 90 | AwaitState(leader, STATE_MEMTABLE_WRITER_LEADER | 91 | STATE_PARALLEL_MEMTABLE_WRITER | STATE_COMPLETED, &eabgl_ctx); 92 | 93 | //自己沉睡于MEMTABLE_WRITER队列中。 94 | ------// if STATE_MEMTABLE_WRITER_LEADER 95 | --------write_thread_.EnterAsMemTableWriter 96 | --------if (memtable_write_group.size > 1 && immutable_db_options_.allow_concurrent_memtable_write) { 97 | write_thread_.LaunchParallelMemTableWriters 98 | --------else 99 | WriteBatchInternal::InsertInto 100 | write_thread_.ExitAsMemTableWriter 101 | ``` 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /03.Write/code_Memtable_Add.md: -------------------------------------------------------------------------------- 1 | #1.Memtable::Add 2 | 3 | ```cpp 4 | Memtable::Add 5 | --std::unique_ptr &table = (type==KTypeRangeDeletion)? range_del_table_ : table_; 6 | --KeyHandle handle = table->Allocate(encoded_len, &buf) 7 | --//通过判断key-value的类型来选择memtable,范围删除的kv,插入range_del_table_ 8 | --switch !allow_concurrent 9 | ----switch insert_with_hint_prefix_extractor->InDomain(key_slice) 10 | ------insert_with_hint_prefix_extractor->Transform(key_slice) 11 | ------table->InsertKeyWithHint//所谓hint,就是利用slice中的信息。 12 | --------SkipList::Insert 13 | ----switch else 14 | ------table->InserKey(handle) 15 | --------SkipList::Insert 16 | ----prefix_bloom_->Add(prefix_extractor->Transform(key)) 17 | --switch allow_concurrent 18 | ----table->InserKeyConcurrently(handle) 19 | ------SkipList::Insert 20 | ----prefix_bloom_->AddConcurrently(prefix_extractor->Transform(key))//修改Bloom filter 21 | 22 | ``` -------------------------------------------------------------------------------- /03.Write/code_WriteBatchInternal_InsertInto.md: -------------------------------------------------------------------------------- 1 | #1.WriteBatchInternal::InsertInto 2 | 3 | ```cpp 4 | WriteBatchInternal::InsertInto 5 | --MemtableInserter inserter 6 | --//Iterate这个方法将WriteBatch中的内容除了头的12字节外,一条条取记录,然后根据类型调用handler(MemtableInserter)中的方法处理。 7 | --for (auto w:write_group) { 8 | w->status = w->batch->Iterate(&inserter)} 9 | ----ReadRecordFromWriteBatch 10 | ------switch(tag) 11 | ------MemTableInserter::PutCF 12 | --------PutCFImpl 13 | ----------SeekToColumnFamily 14 | ----------switch !inplcate_update_support 15 | ------------Memtable::Add 16 | ----------switch inplcate_update_support 17 | ------------switch inpace_callback==null 18 | --------------Memtable::Update 19 | ----------------std::unique_ptr iter(table->GetDynamicPrefixIterator()) 20 | ----------------iter->Seek(lkey.internal_key(), mem_key.data()) 21 | ------------------typename SkipList::Node* SkipList::FindGreaterOrEqual(const Key &key) 22 | ----------------switch iter->Valid() && new_size <= pre_size//找到了,且新的value在原来的地方写的下 23 | ------------------EncodeVarint32(const_cast(key_ptr)+key_len, new_size) 24 | ----------------switch 找不到或者写不下 25 | ------------------Memtable::Add 26 | 27 | ------------switch inpace_callback!=null 28 | --------------if (mem->UpdateCallback(sequence_, key, value))//memtable中找,根据callback要求处理 29 | ----------------std::unique_ptr iter(table->GetDynamicPrefixIterator()) 30 | ----------------iter->Seek(lkey.internal_key(), mem_key.data()) 31 | ----------------switch iter->Valid()//找到了 32 | ------------------auto status = moptions_inplace_callback(pre_buffer, &new_pre_size, delta, &str_value) 33 | ------------------switch 34 | --------------------EncodeVarint32(const_cast(key_ptr)+key_len, new_size)//status返回为原地修改,且写得下 35 | --------------------Memtable::Add//写不下 36 | ----------------switch iter->Valid()//找不到 37 | ------------------DBImpl::Get 38 | ------------------auto status = moptions->inplace_callback//调用callback函数,可以比较新旧值并对获取的值进行处理 39 | ------------------Memtable::Add//按照status存不同的值 40 | ----------MaybeAdvanceSeq 41 | ----------CheckMemtableFull 42 | 43 | ``` -------------------------------------------------------------------------------- /03.Write/code_insert.md: -------------------------------------------------------------------------------- 1 | #1.insert 2 | 3 | ```cpp 4 | mysql_execute_command 5 | --mysql_insert 6 | ----write_record 7 | ------handler::ha_write_row 8 | --------myrocks::ha_rocksdb::write_row 9 | ----------myrocks::ha_rocksdb::skip_unique_check 10 | ----------myrocks::ha_rocksdb::update_write_row//!!!!!!!!!!!!!!!!!!! 11 | ------------myrocks::get_or_create_tx 12 | --------------get_tx_from_thd 13 | ----------------thd_ha_data 14 | ------------------thd->ha_data[hton->slot].ha_ptr 15 | --------------new Rdb_writebatch_impl(thd);//slave 16 | --------------new Rdb_transaction_impl(thd); 17 | --------------start_tx 18 | ----------------rdb->BeginTransaction(write_opts, tx_opts, m_rocksdb_reuse_tx); 19 | ------------myrocks::ha_rocksdb::get_pk_for_update 20 | --------------rocksdb::update_hidden_pk_val 21 | ------------myrocks::ha_rocksdb::check_uniqueness_and_lock 22 | --------------myrocks::ha_rocksdb::check_and_lock_unique_pk 23 | ----------------get_for_update 24 | ------------------TransactionBaseImpl::GetForUpdate 25 | --------------------PessimisticTransaction::TryLock 26 | ------------myrocks::ha_rocksdb::update_indexes 27 | --------------myrocks::ha_rocksdb::update_pk 28 | ----------------myrocks::ha_rocksdb::convert_record_to_storage_format 29 | ----------------myrocks::Rdb_transaction_impl::put 30 | ------------------rocksdb::TransactionBaseImpl::Put 31 | --------------------rocksdb::PessimisticTransaction::TryLock 32 | ----------------------rocksdb::TransactionBaseImpl::SetSnapshotIfNeeded 33 | ----------------------rocksdb::TransactionBaseImpl::TrackKey//把所有的锁,保存到map里 34 | ------------------------rocksdb::TransactionBaseImpl::TrackKey 35 | --------------------rocksdb::TransactionBaseImpl::GetBatchForWrite 36 | 37 | ------------myrocks::ha_rocksdb::do_bulk_commit//myrocks::ha_rocksdb::update_write_row end !!!!!!!!!!!!!!!! 38 | --------------myrocks::ha_rocksdb::commit_in_the_middle 39 | ----------binlog_log_row 40 | 41 | ``` -------------------------------------------------------------------------------- /03.Write/code_rocksdb_open.md: -------------------------------------------------------------------------------- 1 | #1. rocksdb_put 2 | 3 | ###对外接口 4 | RocksDB 对外提供了Get(key), Put(key), Delete(key) and NewIterator()等操作,我们可以直接从官方的文档里面找到测试程序,以此为入口开始代码分析(examples/c_simple_example.c)。 5 | 6 | 7 | 代码中的rocksdb_put、rocksdb_get作为全局函数调用DB::put/DB:get接口,本质是调用的数据库实现类DBImpl的操作。 8 | 其中key和value被打包成slice对象传入put接口,写入WriteBatch::rep_(要么都提交,要么都回滚),后续将批量写入Memtable。 9 | 10 | Slice由一个size变量和一个指向外部内存区域的指针构成。使用slice可以减少之后的key、value在传值过程中的拷贝操作。 11 | writeoptions是写的配置信息,明确是否需要写日志以及是否需要关闭异步写等参数选项。 12 | 13 | 14 | ```cpp 15 | c_simple_example 16 | --rocksdb_open 17 | ----DB::Open(options->rep, std::string(name), &db) 18 | ------DBImpl::Open 19 | --rocksdb_writeoptions_create 20 | --rocksdb_put 21 | ----db->rep->Put(options->rep, Slice(key, keylen), Slice(val, vallen) 22 | /* 23 | // Pre-allocate size of write batch conservatively. 24 | // 8 bytes are taken by header, 4 bytes for count, 1 byte for type, 25 | // and we allocate 11 extra bytes for key length, as well as value length. 26 | */ 27 | ------DB::Put 28 | --------WriteBatch::Put 29 | ----------WriteBatchInternal::Put 30 | ------------PutLengthPrefixedSlice(&b->rep_, key); 31 | ------------PutLengthPrefixedSlice(&b->rep_, value); 32 | --------DBImpl::Write 33 | ----------DBImpl::WriteImpl 34 | ``` -------------------------------------------------------------------------------- /03.Write/notes_Write/assets/1599790983-1952bf94d857fbfee01a8622abff4aaa.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/03.Write/notes_Write/assets/1599790983-1952bf94d857fbfee01a8622abff4aaa.jpg -------------------------------------------------------------------------------- /03.Write/notes_Write/assets/1599790983-2fff9db269164343308cb0b2bcd3a205.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/03.Write/notes_Write/assets/1599790983-2fff9db269164343308cb0b2bcd3a205.jpg -------------------------------------------------------------------------------- /03.Write/notes_Write/assets/1599790983-5883cea91e22f4cda4318d71a4749c12.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/03.Write/notes_Write/assets/1599790983-5883cea91e22f4cda4318d71a4749c12.jpg -------------------------------------------------------------------------------- /03.Write/notes_Write/assets/1599790983-5975086c9ff1b27b2385d22520849dfc.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/03.Write/notes_Write/assets/1599790983-5975086c9ff1b27b2385d22520849dfc.jpg -------------------------------------------------------------------------------- /03.Write/notes_Write/assets/1599790983-61f8a4250d7696c63f00974dc6493483.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/03.Write/notes_Write/assets/1599790983-61f8a4250d7696c63f00974dc6493483.jpg -------------------------------------------------------------------------------- /03.Write/notes_Write/assets/1599790983-787a17debceb521f00cdba94def64519.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/03.Write/notes_Write/assets/1599790983-787a17debceb521f00cdba94def64519.png -------------------------------------------------------------------------------- /03.Write/notes_Write/assets/1599790983-8f6c9493a0946f5c439974e5f72d3d96.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/03.Write/notes_Write/assets/1599790983-8f6c9493a0946f5c439974e5f72d3d96.jpg -------------------------------------------------------------------------------- /03.Write/notes_Write/assets/1599790983-b3c3f34dcb7ec56aab554d76965f5919.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/03.Write/notes_Write/assets/1599790983-b3c3f34dcb7ec56aab554d76965f5919.jpg -------------------------------------------------------------------------------- /03.Write/notes_Write/assets/1599790983-b640bb893d51adb07a58fd58e2b9c91b.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/03.Write/notes_Write/assets/1599790983-b640bb893d51adb07a58fd58e2b9c91b.jpg -------------------------------------------------------------------------------- /03.Write/notes_Write/assets/1599790983-b81bfceaf78fa30e7148a3eeddd8563e.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/03.Write/notes_Write/assets/1599790983-b81bfceaf78fa30e7148a3eeddd8563e.jpg -------------------------------------------------------------------------------- /03.Write/notes_Write/assets/1599790983-c07b72388716c152c487bf13a04d713b.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/03.Write/notes_Write/assets/1599790983-c07b72388716c152c487bf13a04d713b.jpg -------------------------------------------------------------------------------- /03.Write/notes_Write/assets/1599790983-ccbe426b51b41450f25eb5f3c365f7c0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/03.Write/notes_Write/assets/1599790983-ccbe426b51b41450f25eb5f3c365f7c0.jpg -------------------------------------------------------------------------------- /03.Write/notes_Write/assets/1599790983-d466f0bf2fc753abe549876527f64a6c.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/03.Write/notes_Write/assets/1599790983-d466f0bf2fc753abe549876527f64a6c.jpg -------------------------------------------------------------------------------- /03.Write/notes_Write/assets/1599790983-d8bb9c8341039f73ba8269ffafdd6543.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/03.Write/notes_Write/assets/1599790983-d8bb9c8341039f73ba8269ffafdd6543.jpg -------------------------------------------------------------------------------- /03.Write/notes_Write/assets/1599790983-e768b003699851b8035aee6a0cef7ca7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/03.Write/notes_Write/assets/1599790983-e768b003699851b8035aee6a0cef7ca7.jpg -------------------------------------------------------------------------------- /03.Write/notes_Write/assets/1599790983-e7add1e3b77288c83c7958f577f8e650.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/03.Write/notes_Write/assets/1599790983-e7add1e3b77288c83c7958f577f8e650.jpg -------------------------------------------------------------------------------- /03.Write/notes_Write/assets/1599790983-f00a283b92d885aa4be05f5e9828756a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/03.Write/notes_Write/assets/1599790983-f00a283b92d885aa4be05f5e9828756a.png -------------------------------------------------------------------------------- /03.Write/notes_Write/assets/1599790983-f92eed5c356cfe7fc4affeddfb8c0ca2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/03.Write/notes_Write/assets/1599790983-f92eed5c356cfe7fc4affeddfb8c0ca2.jpg -------------------------------------------------------------------------------- /03.Write/notes_Write/index.md: -------------------------------------------------------------------------------- 1 | 2 | # [RocksDB 原理介绍:写过程源码分析] 3 | 4 | 5 | 6 | # RocksDB 写流程源码分享 7 | 8 | ## 一、整体逻辑 9 | 10 | Rocksdb的写入性能十分优秀,下面梳理一下大概的逻辑流程。 11 | ![](assets/1599790983-ccbe426b51b41450f25eb5f3c365f7c0.jpg) 12 | RocksDB整体框架如上图所示。 13 | 14 | * Memtable:KV在内存中的存储结构,有序的,可读写,默认用SkipList实现。 15 | * Immutable Memtable:与Memtable结构一样,区别是只读。 16 | * SSTable:Immutable Memtable写入到文件后的结构,有多层,只读。 17 | * WAL:写操作日志记录,用于重启后恢复内存中尚未持久化的数据。 18 | * MANIFEST:记录当前有效的SSTable。 19 | * CURRENT:记录当前有效的MANIFEST。 20 | 21 | 插入操作比较好理解,直接写入writebatch就可 以了。 22 | 23 | 删除操作在writebatch里本质上也是写入,只不过是标记了待删除的key。 24 | 25 | 对应到Myrocks,如果更新主键或二级索引(RocksDB的key区域),会删除原有记录,插入新纪录,其他情况会直接插入新数据不会删除原有的数据。 26 | 27 | 28 | 29 | 在具体写入数据时,会先写WAL日志,然后将key-value插入Memtable,Memtable满了或者其他条件被触发后会构造新的Memtable,并将老的Memtable转化为只读的immutable Memtable。immutable Memtable达到一定数量后需要刷盘成为SStable,初始层号为L0,后期L0还将通过compact向高层级合 30 | 并。 31 | ![](assets/1599790983-61f8a4250d7696c63f00974dc6493483.jpg) 32 | 以上就是RocksDB在LSM数理论上的实现逻辑。下面我们透过代码来看具体实现。选用的版本为github/facebook/rocksdb,2018年7月11日版本(commit:35b38a232c1d357a7a885b9b4b8442e24a8433d7 ) 33 | 34 | ![](assets/1599790983-d8bb9c8341039f73ba8269ffafdd6543.jpg) 35 | 36 | 我们可以将实现过程大致抽象为:外部接口层、数据库逻辑层、WriteBatch层、Memtable层,再往下还有跳表层操作,以及后续的Memtable切换和Flush到SStable等操作。 37 | 38 | ## 二、对外接口 39 | 40 | RocksDB 对外提供了Get(key), Put(key), Delete(key) and NewIterator()等操作,我们可以直接从官方的文档里面找到测试程序,以此为入口开始代码分析(examples/c\_simple\_example.c)。 41 | 42 | ![](assets/1599790983-5883cea91e22f4cda4318d71a4749c12.jpg) 43 | 44 | 代码中的rocksdb\_put、rocksdb\_get作为全局函数调用DB::put/DB:get接口,本质是调用的数据库实现类DBImpl的操作。 45 | 其中key和value被打包成slice对象传入put接口,写入WriteBatch::rep\_(要么都提交,要么都回滚),后续将批量写入Memtable。 46 | 47 | Slice由一个size变量和一个指向外部内存区域的指针构成。使用slice可以减少之后的key、value在传值过程中的拷贝操作。 48 | writeoptions是写的配置信息,明确是否需要写日志以及是否需要关闭异步写等参数选项。 49 | 50 | ## 三、数据库实现层逻辑 51 | 52 | 每次写请求并不是直接读写Memtable的,而是打包进Writer等待批量写入的,这样的一个重要意义是可以使得一个事务内的的操作一次性写入数据和维护版本等信息。后续多个线程的Writer会组成一个Group,并选出第一个为leader,以组的形式完成日志提交和Memtable写入。 53 | 54 | ![](assets/1599790983-d466f0bf2fc753abe549876527f64a6c.jpg) 55 | 56 | RocksDB的维护和更新非常及时。几个有代表的优化和新功能是:pipelinewrite、concurrent\_memtable\_write、2pc状态下的concurrent\_WriteToWAL。随着新功能的增加,在数据库层的代码逻辑也越来越复杂,分支非常多。 57 | 58 | * 总的来说rocksdb会将多个写线程组成一个group,leader负责 group内所有writer的WAL及memtable的提交,提交完后唤醒所有的follwer,向上层返回。 59 | * 后续更新支持 allow\_concurrent\_memtable\_write 选项,在之前的基础上,leader提交完WAL后,group里所有线程并发写 memtable,流程如下图。 60 | ![](assets/1599790983-c07b72388716c152c487bf13a04d713b.jpg) 61 | 62 | * 而 enable\_pipelined\_write 选项,引入了流水线特性,第一个 group 的 WAL 提交后,在执行 memtable 写入前,下一个 group 同时开启。 63 | 64 | ![](assets/1599790983-e7add1e3b77288c83c7958f577f8e650.jpg) 65 |  66 | 67 | 68 | 更为完整的函数调用关系如下图。 69 | 70 | ![](assets/1599790983-5975086c9ff1b27b2385d22520849dfc.jpg) 71 | 72 | ## 四、WriteBatch层操作 73 | 74 | 在WriteBatch层是个衔接层,主要工作就是按照不同情况,调用下层接口实现Insert。这里面牵扯的不同情况是是否inplace update(在Memtable中原地更新键值一致的元组的值,而不是追加一条新的);以及是否需要inplace\_callback。另外随着有数据插入,本层会及时更新Memtable布隆过滤器的哈希值。 75 | 76 | ![](assets/1599790983-b3c3f34dcb7ec56aab554d76965f5919.jpg) 77 | 78 | ## 五、Memtable层操作 79 | 80 | Memtable是RocksDB它最重要的数据结构之一。除了默认的跳表(SkipList skiplist.h inlineskiplist.)之外,它还在leveldb基础上增加了其他的内存表,例如:HashSkipList、HashLinkList、Vector 等。但是目前跳表依然是默认选项,实现的功能及优化也最完整。下面的内容基于RocksDB在优化后的跳表上面实现的Memtable。 81 | 跳表类中的struct Splice在concurrent insert条件下可以用来缓存节点在各层的前驱和后继。 82 | 83 | ![](assets/1599790983-8f6c9493a0946f5c439974e5f72d3d96.jpg) 84 | 85 | ![](assets/1599790983-f92eed5c356cfe7fc4affeddfb8c0ca2.jpg) 86 | 87 | ![](assets/1599790983-b640bb893d51adb07a58fd58e2b9c91b.jpg) 88 | 89 | ![](assets/1599790983-2fff9db269164343308cb0b2bcd3a205.jpg) 90 | 91 | ![](assets/1599790983-e768b003699851b8035aee6a0cef7ca7.jpg) 92 | 93 | ![](assets/1599790983-3dcfe05b453c90dc017d40911b853456.jpg) 94 | 95 | ![](assets/1599790983-b81bfceaf78fa30e7148a3eeddd8563e.jpg) 96 | 97 | 在Node结构体里面只有一个Node\_\[0\]指向同一层的下一个节点(创建的时候复用做层高)。其他信息需要通过越界访问。 98 | ![](assets/1599790983-5601b1280c25adfc3ba40eb52c7ff307.jpg) 99 | 每个节点生成的过程如下。 100 | 101 | ![](assets/1599790983-1952bf94d857fbfee01a8622abff4aaa.jpg) 102 | 103 | ## 六、切换到immutable Memtable 104 | 105 | 在Rocksdb中,Memtable 和 Immutable memtable都位于内存, 唯一的区别是Memtable可读可写,而Immutable memtable是只读的,不允许写入。 106 | 在内存中,DBImpl-> VersionSet-> ColumnFamilySet-> ColumnFamilyData 内有两个成员: MemTable\* mem_和 MemTableList imm_,分别指向Memtable和Immutable memtable队列。 107 | 108 | 以下几个情况会出现切换: 109 | 110 | * memtable内存超过write\_buffer\_size 111 | * WAL日志满,WAL日志超过rocksdb\_max\_total\_wal\_size,会从所有的colomn family中找出含有最老日志(the earliest log containing a prepared section)的memtable进行切换+ Buffer满,全局的write buffer超过rocksdb\_db\_write\_buffer\_size时,会从所有的colomn family中找出最先创建的memtable进行切换 112 | * flush memtable前 113 | 114 | 下面简单梳理memtable切换的实现(DBImpl::SwitchMemtable) 115 | 116 | * NewWritableFile //创建日志文件 117 | * 修改参数使后台可以在immutable过多的时候启动 flush 118 | * ConstructNewMemtable //创建memtable 119 | * cfd->imm()->Add(cfd->mem(), &context->memtables_to\_free_); //设置immutable 120 | * new\_mem->Ref(); //增加引用 121 | * cfd->SetMemtable(new\_mem); //设置新的memtable 122 | 123 | ## 七、其他写入控制 124 | 125 | rocksdb在提交写入时,需考虑以下几种情况,详见PreprocessWrite(DBImpl::WriteImpl和DBImpl::PipelinedWriteImpl中) 126 | 127 | * WAL日志满,WAL日志超过rocksdb\_max\_total\_wal\_size,会从所有的colomn family中找出含有最老日志(the earliest log containing a prepared section)的column family进行flush, 以释放WAL日志空间 128 | * Buffer满,全局的write buffer超过rocksdb\_db\_write\_buffer\_size时,会从所有的colomn family中找出最先创建的memtable进行切换,详见HandleWriteBufferFull 129 | * 某些条件会触发延迟写: max\_write\_buffer\_number > 3且 未刷immutable memtable总数 >=max\_write\_buffer\_number-1 自动compact开启时,level0的文件总数 >= level0\_slowdown\_writes\_trigger 130 | * 某些条件会触发停写 : 未刷immutable memtable总数 >=max\_write\_buffer\_number 自动compact开启时,level0的文件总数 >= level0\_stop\_writes\_trigger 具体可参考官方 wiki:RecalculateWriteStallConditions 131 | 132 | ## 八、WriteToWAL内容补充 133 | 134 | ![](assets/1599790983-787a17debceb521f00cdba94def64519.png) 135 | 136 | 写日志可以简单分为三个阶段,第一阶段往WritableFileWriter的buf\_里面写,第二阶段write到系统缓存,第三阶段是将系统缓存刷盘。 137 | 138 | 刷盘有三种策略, 139 | 140 | 一种是每次提交都刷盘(WriteOptions::sync = true  -> need\_log\_sync=true),WriteToWAL会调用fsync,安全性比较高,但会占用较多IO。 141 | 142 | 一种是配置了参数wal\_bytes\_per\_sync,每写入wal\_bytes\_per\_sync大小的WAL,就将之前的WAL刷磁盘。 143 | 144 | 最后一种,也是默认的是将刷盘交由系统处理,仅在memtable切换、flush等操作触发时再去主动调用刷盘函数。 145 | 146 | 147 | 148 | -------------------------------------------------------------------------------- /03.Write/write.txt: -------------------------------------------------------------------------------- 1 | 1.写入流程概要流程: 2 | 3 | 写入流程可以分为以下三步: 4 | 5 | 将解析后的记录( key-value )写入到 WriteBatch 6 | 将WAL日志写入log文件 7 | 将WriteBatch中的内容写到memtable中,事务完成 8 | 其中,2、3 两步在 commit 阶段完成。 9 | 10 | 在 MyRocks 中,复用了 MySQL 的主从同步流程,因此,在开启 binlog 的情况下,还需要写入 binlog。 11 | 12 | 13 | 14 | 15 | 3.myrocks各种写操作。 16 | 插入操作比较好理解,直接写入writebatch就可 以了。 17 | 18 | 删除操作在writebatch里本质上也是写入,只不过是标记了待删除的key。 19 | 20 | 对应到Myrocks,如果更新主键或二级索引(RocksDB的key区域),会删除原有记录,插入新纪录,其他情况会直接插入新数据不会删除原有的数据。 21 | 22 | 在具体写入数据时,会先写WAL日志,然后将key-value插入Memtable,Memtable满了或者其他条件被触发后会构造新的Memtable,并将老的Memtable转化为只读的immutable Memtable。immutable Memtable达到一定数量后需要刷盘成为SStable,初始层号为L0,后期L0还将通过compact向高层级合 23 | 并。 24 | 25 | 以上就是RocksDB在LSM数理论上的实现逻辑。下面我们透过代码来看具体实现。选用的版本为github/facebook/rocksdb,2018年7月11日版本(commit:35b38a232c1d357a7a885b9b4b8442e24a8433d7 ) 26 | 27 | 4.总体模块划分 28 | 我们可以将实现过程大致抽象为:外部接口层、数据库逻辑层、WriteBatch层、Memtable层,再往下还有跳表层操作,以及后续的Memtable切换和Flush到SStable等操作。 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /04.Mainfest/Mainfest.md: -------------------------------------------------------------------------------- 1 | # 1.整体架构 2 | RocksDB 整个 LSM 树的信息需要常驻内存,以让 RocksDB 快速进行 kv 查找或者进行 compaction 任务,RocksDB 会用文件把这些信息固化下来,这个文件就是 Manifest 文件。 3 | 4 | Manifest 文件作为事务性日志文件,只要数据库有变化,Manifest都会记录。其内容 size 超过设定值后会被 VersionSet::WriteSnapShot 重写。 5 | RocksDB 进程 Crash 后 Reboot 的过程中,会首先读取 Manifest 文件在内存中重建 LSM 树,然后根据 WAL 日志文件恢复 memtable 内容。 6 | 7 | 8 | Memtable:KV在内存中的存储结构,有序的,可读写,默认用SkipList实现。 9 | Immutable Memtable:与Memtable结构一样,区别是只读。 10 | SSTable:Immutable Memtable写入到文件后的结构,有多层,只读。 11 | WAL:写操作日志记录,用于重启后恢复内存中尚未持久化的数据。 12 | MANIFEST:记录当前有效的SSTable。 13 | CURRENT:记录当前有效的MANIFEST 14 | 15 | # 2.MAINFEST功能 16 | 在RocksDB中MANIFEST保存了存储引擎(sstable)的内部的一些状态元数据,简单来说当系统重启,或者程序异常被退出之后,RocksDB需要有一种机制能够恢复到一个一致性的状态, 而这个一致性的状态就是靠MANIFEST来保证的. 17 | MANIFEST = { CURRENT, MANIFEST-* } 18 | CURRENT = File pointer to the latest manifest log 19 | 20 | MANIFEST- = Contains snapshot of RocksDB state and subsequent modifications 21 | 22 | ---- 23 | manifest-log-file = { version, version-edit* } = { version-edit* } 24 | version-edit = Any RocksDB state change 25 | version = { version-edit* } 26 | 当MANIFEST-超过指定大小之后,MANIFEST会刷新一个新文件,当新的文件刷新到磁盘(并且文件名更新)之后,老的文件会被删除掉.这里可以认为每一次MANIFEST的更新都代表一次snapshot. 27 | 28 | 29 | # 3.mainfest和compaction的关系 30 | RocksDB 整个 LSM 树的信息需要常驻内存,以让 RocksDB 快速进行 kv 查找或者进行 compaction 任务,RocksDB 会用文件把这些信息固化下来,这个文件就是 Manifest 文件。 31 | 32 | # 4.Version 33 | version里边保存了各个level下每个sstable的fileMetaData, fileMetaData里存放了filenumber, filesize, smallestkey, largestkey等信息 34 | 35 | # 5.VersionEdit 36 | versionEdit里边保存了此次compact新生成的sstable所处level和MetaData 37 | 同时保存了需要被删除的sstable,(即被compact的sstable),所处level和filenumber 38 | 39 | # 6.VersionSet 40 | versionSet里边维护了一个双向的环状的version链表 41 | 读操作或compact操作会增加version的引用计数,当其引用计数减少为0时,会在链表中删除。 42 | 43 | Version挂到VersionSet中,并初始化VersionSet的manifestfilenumber, nextfilenumber,lastsequence,lognumber,prevlognumber_ 信息 44 | -------------------------------------------------------------------------------- /05.VersionSet/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/05.VersionSet/1.png -------------------------------------------------------------------------------- /05.VersionSet/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/05.VersionSet/2.png -------------------------------------------------------------------------------- /05.VersionSet/VersionSet.md: -------------------------------------------------------------------------------- 1 | #1.VersionSet 2 | 3 | ![](1.png) 4 | 5 | #2.VersionSet和Version的关系 6 | 7 | 1. ![](2.png) -------------------------------------------------------------------------------- /06.Memtable_Flush_Read/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/06.Memtable_Flush_Read/1.png -------------------------------------------------------------------------------- /06.Memtable_Flush_Read/Flush_notes/assets/1599735083-34cda6ae8edfa5c5a7e02414b7cd3633.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/06.Memtable_Flush_Read/Flush_notes/assets/1599735083-34cda6ae8edfa5c5a7e02414b7cd3633.png -------------------------------------------------------------------------------- /06.Memtable_Flush_Read/Flush_notes/assets/1599735083-605ef45264358719c2a6fef49fc5d479.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/06.Memtable_Flush_Read/Flush_notes/assets/1599735083-605ef45264358719c2a6fef49fc5d479.png -------------------------------------------------------------------------------- /06.Memtable_Flush_Read/Flush_notes/assets/1599735083-a661d3a5ad30776e7b95a01a5335d799.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/06.Memtable_Flush_Read/Flush_notes/assets/1599735083-a661d3a5ad30776e7b95a01a5335d799.png -------------------------------------------------------------------------------- /06.Memtable_Flush_Read/Flush_notes/assets/1599735083-c49da31b58abf43f5f9356792b02b92e.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/06.Memtable_Flush_Read/Flush_notes/assets/1599735083-c49da31b58abf43f5f9356792b02b92e.png -------------------------------------------------------------------------------- /06.Memtable_Flush_Read/Flush_notes/index.md: -------------------------------------------------------------------------------- 1 | 2 | # 3 Flush 3 | 4 | Minor Compaction:将内存中Immutable Memtable转储到磁盘SSTable。 5 | 6 | Major Compaction:磁盘上的SSTable文件从低层向高层转储。 7 | 8 | ![](assets/1599735083-605ef45264358719c2a6fef49fc5d479.png) 9 | 10 | 执行 flush 操作的线程数可配置(rocksdb\_max\_background\_flushes)。 11 | 12 | 入口函数为:FlushMemTable()。 13 | 14 | level 0 存在冗余数据,level 1 ~ level n 不存在数据冗余。 15 | 16 | ## 3.1 flush 触发机制 17 | 18 | Immutable memtable 的数量是否超过了 min\_write\_buffer\_number\_to\_merge。 19 | 20 | 【构造 flush 任务】 21 | 22 | ![](assets/1599735083-a661d3a5ad30776e7b95a01a5335d799.png) 23 | 24 | 【任务执行流程】 25 | 26 | ![](assets/1599735083-c49da31b58abf43f5f9356792b02b92e.png) 27 | 28 | 流程如下: 29 | 30 | 1. 遍历 immutable-list,如果没有其它线程正在执行 flush,则将 flush 任务加入队列 31 | 2. 通过迭代器逐一扫描Immutable memtable 中的 key-value,将 key-value 写入到 data-block  32 | 3. 如果 data block 大小已经超过 block\_size (比如16k),或者是最后一对 key-value,则触发一次 block-flush 33 | 4. 根据压缩算法对 block 进行压缩,并生成对应的 index block 记录(begin\_key, last\_key, offset) 34 | 5. 至此若干个 block 已经写入文件,并为每个 block 生成了 index-block 记录 35 | 6. 写入 index-block,meta block,metaindex block 以及 footer 信息到文件尾 36 | 7. 将 SST 文件的元信息写入 manifest 文件 37 | 38 | flush 的过程是将 immutable table 写入 level 0,level 0 各个 SST 内部没有冗余数据,但 SST 之间会有 Key 的交叠。 39 | 40 | ## 3.2 flush 的调用点 41 | 42 | 调用 FlushMemtable 的原因如下 43 | 44 | ```java 45 | enum class FlushReason : int { 46 |   kOthers = 0x00, 47 |   kGetLiveFiles = 0x01, 48 |   kShutDown = 0x02, 49 |   kExternalFileIngestion = 0x03, 50 |   kManualCompaction = 0x04, 51 |   kWriteBufferManager = 0x05, 52 |   kWriteBufferFull = 0x06, 53 |   kTest = 0x07, 54 |   kDeleteFiles = 0x08, 55 |   kAutoCompaction = 0x09, 56 |   kManualFlush = 0x0a, 57 | }; 58 | ``` 59 | 60 | 【checkpoint】 61 | 62 | checkpoint 时会调用 flush,但是条件无法达成 63 | 64 | ![](assets/1599735083-34cda6ae8edfa5c5a7e02414b7cd3633.png) 65 | 66 | -------------------------------------------------------------------------------- /06.Memtable_Flush_Read/Memtable_Flush.md: -------------------------------------------------------------------------------- 1 | #1.MemTable 切换 2 | 3 | ![](1.png) 4 | 5 | memtable 发生切换的条件有 6 | * memtable内存超过write_buffer_size会切换 7 | * WAL日志满,WAL日志超过rocksdb_max_total_wal_size,会从所有的colomn family中找出含有最老日志(the earliest log containing a prepared section)的memtable进行切换Buffer满,全局的write buffer超过rocksdb_db_write_buffer_size时,会从所有的colomn family中找出最先创建的memtable进行切换 8 | * flush memtable前 9 | 10 | ```cpp 11 | memtable 切换实现 12 | NewWritableFile //创建日志文件 13 | ConstructNewMemtable //创建memtable 14 | cfd->imm()->Add(cfd->mem(), &context->memtables_to_free_); //设置immutable 15 | cfd->SetMemtable(new_mem); //设置新的memtable 16 | 17 | ``` 18 | 19 | #2.MemTable Flush 20 | 21 | 22 | 触发flush的条件有 23 | * WAL日志满,WAL日志超过rocksdb_max_total_wal_size,会从所有的colomn family中找出含有最老日志的column family进行flush 24 | * Buffer满,全局的write buffer超过rocksdb_db_write_buffer_size时,会从所有的colomn family中找出最先创建的memtable的column family进行flush 25 | * 手动设置参数force_flush_memtable_now/rocksdb_force_flush_memtable_and_lzero_now时 26 | * CompactRange时 27 | * 创建checkpoint时 28 | * shutdown时avoid_flush_during_shutdown=0 flush all 29 | 30 | #3. 切换到immutable Memtable 31 | 在Rocksdb中,Memtable 和 Immutable memtable都位于内存, 唯一的区别是Memtable可读可写,而Immutable memtable是只读的,不允许写入。 32 | 在内存中,DBImpl-> VersionSet-> ColumnFamilySet-> ColumnFamilyData 内有两个成员: MemTable* mem和 MemTableList imm,分别指向Memtable和Immutable memtable队列。 33 | 34 | 以下几个情况会出现切换: 35 | 36 | * memtable内存超过write_buffer_size 37 | * WAL日志满,WAL日志超过rocksdb_max_total_wal_size,会从所有的colomn family中找出含有最老日志(the earliest log containing a prepared section)的memtable进行切换+ Buffer满,全局的write buffer超过rocksdb_db_write_buffer_size时,会从所有的colomn family中找出最先创建的memtable进行切换 38 | * flush memtable前 39 | 下面简单梳理memtable切换的实现(DBImpl::SwitchMemtable) 40 | 41 | * NewWritableFile //创建日志文件 42 | * 修改参数使后台可以在immutable过多的时候启动 flush 43 | * ConstructNewMemtable //创建memtable 44 | * cfd->imm()->Add(cfd->mem(), &context->memtablesto_free); //设置immutable 45 | * new_mem->Ref(); //增加引用 46 | * cfd->SetMemtable(new_mem); //设置新的memtable 47 | -------------------------------------------------------------------------------- /06.Memtable_Flush_Read/memtable.txt: -------------------------------------------------------------------------------- 1 | 1.memtable原理 2 | 内存表是RocksDB它最重要的数据结构之一。除了默认的跳表(SkipList skiplist.h inlineskiplist.h)之外,它还增加了各种其他的内存表,例如:HashSkipList、HashLinkList、Vector 等。以空间换时间的有序链表 相比平衡二叉树而言,简单了不少的(对于大多数操作需要O(log n)平均时间)。 3 | 4 | 空间复杂度: O(n) (期望) 5 | 跳跃高度: O(log n)(期望) 6 | 7 | 相关操作的时间复杂度: 8 | 查找: O(log n)     (期望) 9 | 插入:   O(log n)     (期望) 10 | 删除: O(log n)     (期望) 11 | 12 | 13 | search 14 | 1 从顶层的头结点出发; 15 | 2 若下一结点为目标值,则返回结果; 16 | 3 若下一结点小于目标值,则前进; 17 | 4 若下一结点大于目标值或为NULL,则: 18 | 若当前处于最底层,则返回NULL; 19 | 下降一层,重复2-4步。 20 | 21 | 22 | Insert 23 | 1 计算出新结点的层数lv; 24 | 2 从lv层的头结点出发,开始查找过程; 25 | 3 如果找到目标值,说明key重复; 26 | 4 如果当前处于最底层,则创建新结点, 27 | 并依次将新结点插入到1-lv层 28 | 29 | 30 | 31 | 2.memtable 发生切换的条件有 32 | (1)memtable内存超过write_buffer_size会切换 33 | (2)WAL日志满,WAL日志超过rocksdb_max_total_wal_size,会从所有的colomn family中找出含有最老日志(the earliest log containing a prepared section)的memtable进行切换 34 | (3)Buffer满,全局的write buffer超过rocksdb_db_write_buffer_size时,会从所有的colomn family中找出最先创建的memtable进行切换 35 | (4)flush memtable前 36 | 37 | 3.memtable 切换实现 38 | NewWritableFile //创建日志文件 39 | ConstructNewMemtable //创建memtable 40 | cfd->imm()->Add(cfd->mem(), &context->memtables_to_free_); //设置immutable 41 | cfd->SetMemtable(new_mem); //设置新的memtable 42 | 43 | 44 | 4.触发flush的条件有 45 | WAL日志满,WAL日志超过rocksdb_max_total_wal_size,会从所有的colomn family中找出含有最老日志的column family进行flush 46 | Buffer满,全局的write buffer超过rocksdb_db_write_buffer_size时,会从所有的colomn family中找出最先创建的memtable的column family进行flush 47 | 手动设置参数force_flush_memtable_now/rocksdb_force_flush_memtable_and_lzero_now时 48 | CompactRange时 49 | 创建checkpoint时 50 | shutdown时avoid_flush_during_shutdown=0 flush all 51 | 52 | 53 | 5.flush 触发机制, 构造 flush 任务 54 | DBImpl::FlushMemTable//是否需要flush 55 | --SwitchMemtable//开启新的memtable 56 | --SchedulePendingFlush//memtable是否已经flush 57 | ----AddToFlushQueue//将immutable memtable 加入任务队列 58 | --MaybeScheduleFlushOrCompaction//异步执行任务 59 | --WaitForFlushMemTable//等待flush完成 60 | 61 | 6.flush 触发机制, 任务执行流程 62 | DBImpl::BackgroundFlush 63 | --PopFirstFromFlushQueue//获取一个任务 64 | --FlushMemTableToOutputFile 65 | ----FlushJob flush_job//构建job 66 | ----flush_job.Run//执行job 67 | ------WriteLevel0Table//将memtable写入level0 68 | --------BuildTable//创建文件 69 | ----------NewWritableFile 70 | ----------file_writer->Sync//写入记录 71 | ------edit_->AddFile//记录version edit. 72 | 73 | 7.flush流程描述: 74 | 流程如下: 75 | 76 | (1)遍历 immutable-list,如果没有其它线程正在执行 flush,则将 flush 任务加入队列 77 | (2)通过迭代器逐一扫描Immutable memtable 中的 key-value,将 key-value 写入到 data-block 78 | (3)如果 data block 大小已经超过 block_size (比如16k),或者是最后一对 key-value,则触发一次 block-flush 79 | (4)根据压缩算法对 block 进行压缩,并生成对应的 index block 记录(begin_key, last_key, offset) 80 | (5)至此若干个 block 已经写入文件,并为每个 block 生成了 index-block 记录 81 | (6)写入 index-block,meta block,metaindex block 以及 footer 信息到文件尾 82 | (7)将 SST 文件的元信息写入 manifest 文件 83 | (8)flush 的过程是将 immutable table 写入 level 0,level 0 各个 SST 内部没有冗余数据,但 SST 之间会有 Key 的交叠。 84 | 85 | 86 | 8.flush调用点 87 | enum class FlushReason : int { 88 | kOthers = 0x00, 89 | kGetLiveFiles = 0x01, 90 | kShutDown = 0x02, 91 | kExternalFileIngestion = 0x03, 92 | kManualCompaction = 0x04, 93 | kWriteBufferManager = 0x05, 94 | kWriteBufferFull = 0x06, 95 | kTest = 0x07, 96 | kDeleteFiles = 0x08, 97 | kAutoCompaction = 0x09, 98 | kManualFlush = 0x0a, 99 | }; 100 | 101 | 102 | BackupCommand::DoCommand 103 | --BackupEngine::Open 104 | --backup_engine->CreateNewBackup 105 | ----BackupEngineImpl::CreateNewBackupWithMetadata 106 | ------GetAbsolutePath 107 | ------backup_env_->FileExists 108 | ------GarbageCollect 109 | ------CreateDir 110 | ------CheckpointImpl::CreateCustomCheckpoint 111 | --------db_->GetSortedWalFiles 112 | --------db_->GetLiveFiles 113 | --------db_->FlushWAL 114 | --------db_->GetSortedWalFiles 115 | ------new_backup->SetSequenceNumber 116 | 117 | 9.Memtable是RocksDB它最重要的数据结构之一。 118 | 除了默认的跳表(SkipList skiplist.h inlineskiplist.)之外,它还在leveldb基础上增加了其他的内存表,例如:HashSkipList、HashLinkList、Vector 等。 119 | 但是目前跳表依然是默认选项,实现的功能及优化也最完整。下面的内容基于RocksDB在优化后的跳表上面实现的Memtable。 120 | 跳表类中的struct Splice在concurrent insert条件下可以用来缓存节点在各层的前驱和后继。 121 | 122 | MemTable::Add 123 | --//根据kv的类型选择memtable,范围删除的kv插入range_del_table 124 | --char* p = EncodeVarint32(buf, internal_key_size); 125 | --memcpy(p, key.data(), key_size); 126 | --Slice key_slice(p, key_size); 127 | --p = EncodeVarint32(p, val_size); 128 | --memcpy(p, value.data(), val_size); 129 | --if (!allow_concurrent) { 130 | // Extract prefix for insert with hint. 131 | ----if (insert_with_hint_prefix_extractor_ != nullptr && insert_with_hint_prefix_extractor_->InDomain(key_slice)) { 132 | ------Slice prefix = insert_with_hint_prefix_extractor_->Transform(key_slice); 133 | ------table->InsertWithHint(handle, &insert_hints_[prefix]);//所谓hint,就是利用splice中的缓存信息。 134 | ----else { 135 | ------table->Insert(handle); 136 | ----prefix_bloom_->Add(prefix_extractor_->Transform(key)); 137 | --else (allow_concurrent) 138 | ----table->InsertConcurrently(handle); 139 | ----prefix_bloom_->AddConcurrently(prefix_extractor_->Transform(key));//修改bloom过滤器的值。 140 | 141 | 142 | 10.切换到immutable Memtable 143 | 在Rocksdb中,Memtable 和 Immutable memtable都位于内存, 唯一的区别是Memtable可读可写,而Immutable memtable是只读的,不允许写入。 144 | 在内存中,DBImpl-> VersionSet-> ColumnFamilySet-> ColumnFamilyData 内有两个成员: MemTable* mem和 MemTableList imm,分别指向Memtable和Immutable memtable队列。 145 | 146 | 以下几个情况会出现切换: 147 | 148 | memtable内存超过write_buffer_size 149 | WAL日志满,WAL日志超过rocksdb_max_total_wal_size,会从所有的colomn family中找出含有最老日志(the earliest log containing a prepared section)的memtable进行切换+ Buffer满,全局的write buffer超过rocksdb_db_write_buffer_size时,会从所有的colomn family中找出最先创建的memtable进行切换 150 | flush memtable前 151 | 下面简单梳理memtable切换的实现(DBImpl::SwitchMemtable) 152 | 153 | NewWritableFile //创建日志文件 154 | 修改参数使后台可以在immutable过多的时候启动 flush 155 | ConstructNewMemtable //创建memtable 156 | cfd->imm()->Add(cfd->mem(), &context->memtablesto_free); //设置immutable 157 | new_mem->Ref(); //增加引用 158 | cfd->SetMemtable(new_mem); //设置新的memtable 159 | 160 | 11.其他写入控制(flush触发条件) 161 | rocksdb在提交写入时,需考虑以下几种情况,详见PreprocessWrite(DBImpl::WriteImpl和DBImpl::PipelinedWriteImpl中) 162 | 163 | WAL日志满,WAL日志超过rocksdb_max_total_wal_size,会从所有的colomn family中找出含有最老日志(the earliest log containing a prepared section)的column family进行flush, 以释放WAL日志空间 164 | Buffer满,全局的write buffer超过rocksdb_db_write_buffer_size时,会从所有的colomn family中找出最先创建的memtable进行切换,详见HandleWriteBufferFull 165 | 某些条件会触发延迟写: max_write_buffer_number > 3且 未刷immutable memtable总数 >=max_write_buffer_number-1 自动compact开启时,level0的文件总数 >= level0_slowdown_writes_trigger 166 | 某些条件会触发停写 : 未刷immutable memtable总数 >=max_write_buffer_number 自动compact开启时,level0的文件总数 >= level0_stop_writes_trigger 具体可参考官方 wiki:RecalculateWriteStallConditions 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | -------------------------------------------------------------------------------- /07.SSTable/SSTable.txt: -------------------------------------------------------------------------------- 1 | 1.SSTable结构 2 | SSTable 是 RocksDB 数据在外存的存储单位。每个 SSTable 是一个单独的文 件,默认使用BlockBasedTable。 3 | SSTable 由 Block 组成, Block 也是 RocksDB 最小的外存读取单位,包括 DataBlock, MetaBlock, IndexBlock 三种类型, 4 | DataBlock 记录用户的数据, 5 | MetaBlock 记录该 SSTable 的 BloomFilter, 6 | IndexBlock 记录每个 DataBlock 的范围。 7 | 8 | struct BlockBasedTable::Rep 9 | 10 | 2.sstable查找流程 11 | 12 | 用户每次要访问一个 SSTable 时,只需要先读取 IndexBlock 和 MetaBlock,如果通过了 BloomFilter 的检验,再从 IndexBlock 的索引结构中判断应该读取哪个 DataBlock,这样只需再读取对应 DataBlock 就可以获得数据。 13 | 14 | 读取指定key-->读取sstable文件尾部48个字节-->magic合法性检查-->读取DataIndexBlock的offset和size-->加载DataIndexBlock的内容进内存-->| 15 | | 16 | | 17 | | 18 | 遍历该重启点下的key,找到对应的value<--二分查找Datablock中指定key所在的重启点<---CRC校验<--加载Datablock近内存<--二分查找指定key的Datablock的offset和size <--| 19 | 20 | -------------------------------------------------------------------------------- /07.SSTable/notes_BlockBasedTable_Compress/assets/1599809496-129d10647bd625a17d08c5beed7cb935.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/07.SSTable/notes_BlockBasedTable_Compress/assets/1599809496-129d10647bd625a17d08c5beed7cb935.jpg -------------------------------------------------------------------------------- /07.SSTable/notes_BlockBasedTable_Compress/assets/1599809496-238e3b3f0214099cb6460b877c84eaaf.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/07.SSTable/notes_BlockBasedTable_Compress/assets/1599809496-238e3b3f0214099cb6460b877c84eaaf.jpg -------------------------------------------------------------------------------- /07.SSTable/notes_BlockBasedTable_Compress/assets/1599809496-2b7642dbf9770ff944caea94f6969de8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/07.SSTable/notes_BlockBasedTable_Compress/assets/1599809496-2b7642dbf9770ff944caea94f6969de8.jpg -------------------------------------------------------------------------------- /07.SSTable/notes_BlockBasedTable_Compress/assets/1599809496-2d47f8db799645507174403e2552ff4c.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/07.SSTable/notes_BlockBasedTable_Compress/assets/1599809496-2d47f8db799645507174403e2552ff4c.jpg -------------------------------------------------------------------------------- /07.SSTable/notes_BlockBasedTable_Compress/assets/1599809496-448f9751afeaff9818d21fa91d3f1e86.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/07.SSTable/notes_BlockBasedTable_Compress/assets/1599809496-448f9751afeaff9818d21fa91d3f1e86.jpg -------------------------------------------------------------------------------- /07.SSTable/notes_BlockBasedTable_Compress/assets/1599809496-4f8f578093fb39fdc229f2cd8e956b5a.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/07.SSTable/notes_BlockBasedTable_Compress/assets/1599809496-4f8f578093fb39fdc229f2cd8e956b5a.jpg -------------------------------------------------------------------------------- /07.SSTable/notes_BlockBasedTable_Compress/assets/1599809496-5b8ae05399d451de702b6e30f4c6d969.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/07.SSTable/notes_BlockBasedTable_Compress/assets/1599809496-5b8ae05399d451de702b6e30f4c6d969.jpg -------------------------------------------------------------------------------- /07.SSTable/notes_BlockBasedTable_Compress/assets/1599809496-c69936d411ca842fdee77e7bd308e28d.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/07.SSTable/notes_BlockBasedTable_Compress/assets/1599809496-c69936d411ca842fdee77e7bd308e28d.jpg -------------------------------------------------------------------------------- /07.SSTable/notes_BlockBasedTable_Compress/assets/1599809496-d2def64aba145010465142c7f1d7d4e1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/07.SSTable/notes_BlockBasedTable_Compress/assets/1599809496-d2def64aba145010465142c7f1d7d4e1.png -------------------------------------------------------------------------------- /07.SSTable/notes_BlockBasedTable_Compress/assets/1599809496-dfd540a17a3481088f4113e044a0ce4f.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/07.SSTable/notes_BlockBasedTable_Compress/assets/1599809496-dfd540a17a3481088f4113e044a0ce4f.jpg -------------------------------------------------------------------------------- /07.SSTable/notes_BlockBasedTable_Compress/assets/1599809496-e114a7374b67035ccb6780088849fea3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/07.SSTable/notes_BlockBasedTable_Compress/assets/1599809496-e114a7374b67035ccb6780088849fea3.jpg -------------------------------------------------------------------------------- /07.SSTable/notes_BlockBasedTable_Compress/assets/1599809496-e3269e0b73e22c2d9abc55f43c799b5f.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/07.SSTable/notes_BlockBasedTable_Compress/assets/1599809496-e3269e0b73e22c2d9abc55f43c799b5f.jpg -------------------------------------------------------------------------------- /07.SSTable/notes_BlockBasedTable_Compress/assets/1599809496-e55c2da22b87c957e06672ffdadcb1d9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/07.SSTable/notes_BlockBasedTable_Compress/assets/1599809496-e55c2da22b87c957e06672ffdadcb1d9.jpg -------------------------------------------------------------------------------- /07.SSTable/notes_BlockBasedTable_Compress/assets/1599809496-ea7ab4ef16e96a0b772ee45fa4a1a759.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/07.SSTable/notes_BlockBasedTable_Compress/assets/1599809496-ea7ab4ef16e96a0b772ee45fa4a1a759.jpg -------------------------------------------------------------------------------- /07.SSTable/notes_BlockBasedTable_Compress/assets/1599809496-f00a283b92d885aa4be05f5e9828756a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/07.SSTable/notes_BlockBasedTable_Compress/assets/1599809496-f00a283b92d885aa4be05f5e9828756a.png -------------------------------------------------------------------------------- /07.SSTable/notes_BlockBasedTable_Compress/assets/1599809496-fd31e70d53272a46125468ed7945ccfe.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/07.SSTable/notes_BlockBasedTable_Compress/assets/1599809496-fd31e70d53272a46125468ed7945ccfe.jpg -------------------------------------------------------------------------------- /07.SSTable/notes_BlockBasedTable_Compress/index.md: -------------------------------------------------------------------------------- 1 | # [RocksDB 原理介绍:RockDB/Terark SST读写与压缩技术] 2 | 3 | 4 | ### RockDB/Terark SST读写与压缩技术 5 | 6 | 参考资料: 7 | [https://github.com/facebook/rocksdb/wiki/Rocksdb-BlockBasedTable-Format](https://github.com/facebook/rocksdb/wiki/Rocksdb-BlockBasedTable-Format) 官方wiki 8 | [https://www.jianshu.com/p/9b5ade5212ab](https://www.jianshu.com/p/9b5ade5212ab) 周肃 9 | [https://zhuanlan.zhihu.com/p/26204236](https://zhuanlan.zhihu.com/p/26204236) terarkdb 10 | [https://blog.csdn.net/heyc861221/article/details/80129903](https://blog.csdn.net/heyc861221/article/details/80129903) 何永灿CSDN 11 | [https://www.jianshu.com/p/cf5aed36b22d](https://www.jianshu.com/p/cf5aed36b22d) Glitter试做一号机 12 | 13 | ## 一、RocksDB BlockBasedTable 基本结构与压缩技术 14 | 15 | ### 1.1 基本结构 16 | 17 | BlockBasedTable对应文件基本结构: 18 | 19 | ```plain 20 | 21 | [data block 1] 22 | [data block 2] 23 | ... 24 | [data block N] 25 | [meta block 1: filter block] (see section: "filter" Meta Block) 26 | [meta block 2: stats block] (see section: "properties" Meta Block) 27 | [meta block 3: compression dictionary block] (see section: "compression dictionary" Meta Block) 28 | [meta block 4: range deletion block] (see section: "range deletion" Meta Block) 29 | ... 30 | [meta block K: future extended block] (we may add more meta blocks in the future) 31 | [metaindex block] 32 | [index block] 33 | [Footer] (fixed size; starts at file_size - sizeof(Footer)) 34 | 35 | ``` 36 | 37 | 1. SST文件的开头是key/value对按序排列, 分配在连续的block中。默认的block大小为4k 38 | 2. 在data block后面是一些meta block 39 | 3. index block记录了每个data block的索引。它的key值大于或等于被索引的block的最后一个key, 小于下一个data block的第一个key;value是data block对应的BlockHandle 40 | 4. 文件的末尾是一个定长footer 41 | 42 | BlockHandles(内部句柄): 43 | 44 | ```plain 45 | offset: varint64 46 | size: varint64 47 | ``` 48 | 49 | footer: 50 | 51 | ```plain 52 | metaindex_handle: char[p]; // Block handle for metaindex 53 | index_handle: char[q]; // Block handle for index 54 | padding: char[40-p-q]; // zeroed bytes to make fixed length 55 | // (40==2*BlockHandle::kMaxEncodedLength) 56 | magic: fixed64; // 0x88e241b785f4cff7 (little-endian) 57 | ``` 58 | 59 | Meta Block包括: 60 | 61 | 1. Filter信息 (Full filter、Partitioned Filter、Block-based filter) 62 | 2. Properties (文件总大小、索引大小等) 63 | 3. 压缩字典 支持`kZlibCompression`, `kLZ4Compression`, `kLZ4HCCompression`, and `kZSTDNotFinalCompression`。字典的生成发生在 compact 阶段,默认只有copact到最后一层时才会用到。 64 | 4. 范围删除信息(他们只有到最终层level时才会被彻底合并吸收,否则将作为一个元素正常参与到flush和compact) 65 | 66 | 压缩: 67 | 68 | 1. BlockBasedTable 因为key是连续的,所以采用了前缀压缩技术。 69 | 2. 同时对每个block可采用多种压缩技术,同一个实例中的不同level可以采用不同的配置。 70 | ![](assets/1599809496-d2def64aba145010465142c7f1d7d4e1.png) 71 | ![](assets/1599809496-2d47f8db799645507174403e2552ff4c.jpg) 72 | 73 | > For reference, several fast compression algorithms were tested and compared on a server running Linux Debian (Linux version 4.14.0-3-amd64), with a Core i7-6700K CPU @ 4.0GHz, using lzbench, an open-source in-memory benchmark by @inikep compiled with gcc 7.3.0, on the Silesia compression corpus. 74 | 75 | ### 1.2 读写对象 76 | 77 | 读写操作时,sst的操作对象: 78 | 79 | ![](assets/1599809496-c69936d411ca842fdee77e7bd308e28d.jpg) 80 | 81 | 1. `TableFactory` 82 | 工厂类接口, 用来创建指定的TableReader和TableBuilder对象. 83 | 2. `BlockBasedTableFactory` 84 | rocksdb默认sst,可以在DB::Open(Options…)中修改其他存储结构。创建BlockBasedTableReader和BlockBasedTableBuilder. 85 | 3. `TableBuilder` 86 | 可以认为, 一个Table就是一个SST文件, 只不过Table并不会把整个SST文件的内容持有, 而是当写满一个block, 就会flush到SST文件中. `TableBuilder`就定义了构建一个Table(SST File)的结构, 主要是Add接口, 接收调用者传进来的kv. Finish接口在数据写完之后, 将后续的meta block, index block等写入在data block后面. 87 | 4. `BlockBasedTableBuilder` 88 | 实现了TableBuilder接口. 并定义了如何写下block的方法, 同时实现了将block插入到压缩cache中的私有方法. 89 | 5. `BlockBuilder` 90 | 用来构造Block的对象, 可复用. 当一个block构造完成, flush到sst文件中, 就调用Reset方法, 清空buffer和成员变量, 继续构造下一个Block. 91 | 92 | ## 二、RocksDB SST 读写实现 93 | 94 | ### 2.1 SST 写操作 95 | 96 | sst是基于文件级的操作,由TableBuilder等对象完成。单词的put写入并不一定会激发sst写文件。真正的调用来自backgroud flush 和backgroud compact(recover和repair的时候也会)。即将immutable memtable刷到L0层sst,以及sst文件的compact操作,会产生新的sst,触发sst写操作。 97 | ![](assets/1599809496-931096c2f43631570d2b35900f10b168.jpg)![](assets/1599809496-e55c2da22b87c957e06672ffdadcb1d9.jpg) 98 | 99 | ![](assets/1599809496-129d10647bd625a17d08c5beed7cb935.jpg) 100 | 101 | ### 2.2 SST 读操作 102 | 103 | sst读操作在get环节被触发(实际上在compact环节也会) 104 | ![](assets/1599809496-b0657908415ab90a7ec48b2a2ff748a1.jpg)![](assets/1599809496-448f9751afeaff9818d21fa91d3f1e86.jpg) 105 | 上图中先从内存读,然后通过cache读sst文件 106 | ![](assets/1599809496-dd46b4432c4d2713edb6fe3014a4d331.jpg)![](assets/1599809496-2b7642dbf9770ff944caea94f6969de8.jpg) 107 | 上图讲的是如何选择要读的sst文件,之后会进入单个具体的sst 108 | ![](assets/1599809496-1a0ae19fbe4a67b067a911ab98d9f003.jpg)![](assets/1599809496-5b8ae05399d451de702b6e30f4c6d969.jpg) 109 | 110 | ### 2.3 具体接口实现 111 | 112 | 通过对TableFactory、TableBuilder的具体工厂类实现,我们可以实现不同的sst文件读写操作。 113 | 下面以BlockBasedTable::Add为例进行介绍。 114 | 如类图所示,BlockBasedTableBuilder继承了TableBuilder接口,对外提供了Add、Finish、Abandon等接口,并持有一个Rep内部类,包装了一些选项,目标文件指针,和另外一个主要的结构BlockBuilder。 115 | 116 | BlockBasedTableBuilder的Add方法实现: 117 | 118 | 1. 判断key的类型,有值类型和范围删除两种 119 | 2. 判断当前的key是否比上一个key大,保证有序 120 | 3. 判断是否需要flush当前的block到文件中,还需标记索引信息并清空data block 121 | 4. 将kv插入到data block中 122 | 部分实现代码如下: 123 | 124 | ```plain 125 | void BlockBasedTableBuilder::Add(const Slice& key, const Slice& value) { 126 | Rep* r = rep_; 127 | ValueType value_type = ExtractValueType(key); 128 | if (IsValueType(value_type)) { 129 | ... 130 | if (r->props.num_entries > 0) { 131 | assert(r->internal_comparator.Compare(key, Slice(r->last_key)) > 0); 132 | } 133 | ... 134 | auto should_flush = r->flush_block_policy->Update(key, value); 135 | if (should_flush) { 136 | assert(!r->data_block.empty()); 137 | Flush(); 138 | if (ok()) { 139 | r->index_builder->AddIndexEntry(&r->last_key, &key, r->pending_handle); 140 | } 141 | ... 142 | r->last_key.assign(key.data(), key.size()); 143 | r->data_block.Add(key, value); 144 | r->props.num_entries++; 145 | r->props.raw_key_size += key.size(); 146 | ... 147 | ``` 148 | 149 | r->data\_block 即是BlockBuilder::Add。这里值得注意的是key的前缀压缩技术。 150 | 151 | 1. 前缀压缩:BlockBuilder使用前缀压缩来保存数据,以节省空间。 152 | 2. restart point:并不是所有的kv都使用前缀压缩,而是有一个分界点,每当使用前缀压缩保存了K个key,下一个kv就不适用前缀压缩,而是保存整个key,它的offset就是一个restart point。restart point保存在一个数组中,写在block的尾部,用来做二分查找。 153 | 3. value的保存紧跟在对应的key的后面。 154 | 155 | ```plain 156 | | shared_bytes (varint32) | unshared_bytes(varint32) | value_length(varint32) | key_delta(unshared_bytes) 差异部分key | value(char[value_length]) | 157 | ... // n个上面的结构 158 | | restarts(uint32[num_restarts]) | num_restarts(uint32) | // block尾部记录了restart点的数量和一个偏移数组,用来找到各个重启点 159 | // 当shared_bytes = 0 时,代表一个restart point。 160 | ``` 161 | 162 | BlockBuilder持有一个成员last_key_,保存上一个Add的key,用来与当前的key计算相同的前缀长度。 163 | BlockBuilder的Add逻辑如下: 164 | 165 | 1. 判断是否需要restart point 166 | 2. 如果不需要restart point,将当前插入的key与前一个key比较前缀(difference\_offset),得到可以压缩的前缀长度。 167 | 3. 得到所有需要的数据后,按照上面介绍的一个entry的格式,append到buffer中。 168 | 4. 当需要flush block时,调用Flush方法,Flush方法调用了WriteBlock方法。WriteBlock会调用BlockBuilder::Finish,将restart points append到buffer\_中。 169 | 5. 将block写文件前会根据设置调用压缩(CompressBlock)。 170 | 6. 更新索引项 171 | 172 | 在所有data block写完之后, 会在BlockBasedTableBuilder的Finish方法中,将后续的meta blocks, meta index block, index block和footer等写入到文件中. 173 | 174 | ## 三、TerarkDB 概述 175 | 176 | Terark公司提出了“可检索压缩(Searchable Compression)”的概念,其核心也是直接在压缩的数据上执行搜索和访问,但数据模型本身就是KeyValue模型。 177 | 178 | * 摒弃传统数据库的块压缩技术,采用全局压缩; 179 | * 对Key和Value使用不同的全局压缩技术; 180 | * 对Key使用有搜索功能的全局压缩技术COIndex; 181 | * 对Value使用可定点访问的全局压缩技术PA-Zip 182 | ![](assets/1599809496-4f8f578093fb39fdc229f2cd8e956b5a.jpg) 183 | * ![](assets/1599809496-e3269e0b73e22c2d9abc55f43c799b5f.jpg) 184 | 185 | ### 优势 186 | 187 | * 压缩率更高 188 | * 磁盘文件更小 189 | * 数据规模越大,压缩率越高 190 | * 内存用量更少 191 | * 只需 mmap 压缩的文件,无双缓存问题(读的时候) 192 | * 随机访问更快 193 | * 直接访问压缩的数据 194 | * 只访问需要的数据,没有无效解压 195 | * 顺应硬件发展趋势:随机少量读,高 IOPS 196 | 197 | ### 劣势 198 | 199 | * 速度慢 200 | 201 | * 算法复杂,计算量大 202 | * 算法的内存访问局部性较差,CPU Cache miss 较高。 203 | * 内存用量大 204 | * 全局压缩,需要更多工作内存 205 | * 顺序读慢 206 | 207 | * 顺序读慢在 CO-Index,PA-Zip 的顺序读是很快的。具体来说,Nested Succinct Trie,它的数据高度压缩,结构复杂,内存访问的局部性差 208 | * 传统存储引擎的块压缩,非常适合顺序读 209 | * 建议: 210 | 211 | * 限制内存 & 优化调度 212 | * 限制压缩算法的并发线程数、内存上限…… 213 | 214 | ## 四、TerarkDB 读写适配 215 | 216 | TerarkDB有基于RocksDB的实现版本,基于早期RocksDB版本,修改sst逻辑,并mergeRockDB官方修改。 217 | TerarkDB对RocksDB的修改可以分为三部分。 218 | 219 | 1. sst修改,这部分也可以看做读写操作对于底层压缩的适配部分(table工厂类的实现)。 220 | 2. 压缩核心算法,包括key(CO-Index)压缩和value(PA-Zip)压缩 221 | 3. 为了实现压缩算法对RocksDB本身也进行了一定改造和参数配置。创建支持PA-Zip的sst时,TerarkDB需要对输入扫描两遍,第一遍计算 Value 的各种统计特征,并且收集全局字典,第二遍执行压缩。详情阅读 [TerarkDB SST 的创建过程](https://github.com/Terark/terarkdb/wiki/TerarkDB-SST-%E7%9A%84%E5%88%9B%E5%BB%BA%E8%BF%87%E7%A8%8B)。 另外TerarkDB默认使用UniversalCompact。 222 | 223 | TerarkDB 仅仅开源了SST 读写的适配部分,即继承自 TableFactory TableBuilder 的子类实现代码。 224 | 225 | ![](assets/1599809496-ea7ab4ef16e96a0b772ee45fa4a1a759.jpg) 226 | 227 | 另外TerarkDB也有配合RockDB嵌入到MySQL的版本。上图可也比较清晰的看到其模块结构。 228 | 229 | ### 4.1 写适配 230 | 231 | 前面介绍过,写的过程主要调用的sst接口是BuildTable(内部调用NewTableBuilder创建TableBiulder)和TableBiulder::Add、TableBiulder::Finish操作。 232 | 233 | ![](assets/1599809496-dfd540a17a3481088f4113e044a0ce4f.jpg) 234 | 235 | ![](assets/1599809496-cf900c382b1a342352a695a2b80a8bcf.jpg) 236 | 237 | ### 4.2 读适配 238 | 239 | 读操作暴露的接口变化更少。可以参考前文RocksDB SST的代码调用。 240 | 主要是在TableCache::Get中GetTableReader调用TerarkTableFactory::NewTableReader创建reader,然后调用TerarkTable::Open打开sst文件(其中包括索引加载NestLoudsTrieBuildCache等操作)。 241 | 以及读出kv时,使用TerarkTableReader调用TerarkTableSubReader::Get获取数据,其中由TerarkIndex::Find去搜索之前加在的索引然后直接拿到解压后的数据。 242 | 243 | ## 五、TerarkDB 压缩技术 244 | 245 | ### 5.1 对Key的压缩:CO-Index 246 | 247 | 对比传统实现索引的数据结构,TerarkDB宣城,其Nested Succinct Trie的空间占用是原有的十几分之一甚至几十分之一。而在保持该压缩率的同时,该算法还支持丰富的搜索功能:精确搜索;范围搜索;顺序遍历;前缀搜索;正则表达式搜索。根据一有资料可知,其Nested Succinct Trie至少涉及三项技术。 248 | (1)Compressed Tries 249 | ![](assets/1599809496-238e3b3f0214099cb6460b877c84eaaf.jpg) 250 | (2)Succinct Sturcture 251 | 252 | * 一个树说他是succinct,则表示这个树的空间占用接近信息论中的里面极限,也就是要表示一个集合中所有的item的最小bit数。 253 | 254 | * 一个大小为n的集合至少需要log2(n)的bit数来编码(好像学过) 255 | 256 | * 一个度为k的树表示每个node最多可以有k个子节点 257 | 258 | * ordinal tree表示每个节点可以有任意数量的有序子节点的树 259 | 260 | * LOUDS是编码一个ordinal-tree的方法,LOUDS通过宽度优先遍历所有节点并通过一个一元码来编码每个节点的度 261 | 262 | * 通过rank和select原语来访问一个LOUDS编码的树,给定一个bit序列,rank1(i)统计到i为止的1的个数(rank0(i)统计0),select1(i)返回第i个1的位置(这个过程是常数级的时间复杂度) 263 | 264 | ![](assets/1599809496-e114a7374b67035ccb6780088849fea3.jpg) 265 | 266 | (3)Nested Sturcture 267 | ![](assets/1599809496-fd31e70d53272a46125468ed7945ccfe.jpg) 268 | 路径压缩后再做一次trie 269 | 270 | ### 5.2 对value的压缩: PA-Zip 271 | 272 | PA-Zip: Point Accessible Zip,存的是value。:每条数据关联一个 ID,数据压缩好之后,就可以用相应的 ID 访问那条数据。可以看做是一个抽象的 array,核心功能是把 ID 作为抽象数组的下标,去访问该抽象数组的元素。 273 | PA-Zip 对整个数据库中的所有 Value (KeyValue 数据库中所有 Value 的集合)进行全局压缩,而不是按 block/page 进行压缩。 274 | 这这种压缩是 Terark 专门针对数据库的需求(KeyValue 模型),专门精心设计的一个压缩算法,用来彻底解决传统数据库压缩的问题: 275 | 该算法综合使用了很多种技术,非常复杂,并且涉及到 Terark 的一些专利和保密算法。 276 | Key 以全局压缩的形式保存在 IndexCO-Index 中,Value 以全局压缩的形式保存在 PA-Zip中。搜索一个 kKey,会得到一个内部 ID,根据这个内部 ID,去全局压缩的 Value 集合 PA-Zip 中定点访问该 ID 对应的 Value,整个过程中只触碰访问需要的数据,不需要触碰其它数据。 277 | 278 | RocksDB中的BlockBasedTable就是一个块压缩的SSTable,使用块压缩,索引只定位到块。创建BlockBasedTable时,Key Value被逐条填入buffer,当buffer尺寸达到预定大小(块尺寸,当然,一般buffer尺寸不会精确地刚好等于预设的块尺寸),就将buffer压缩并写入BlockBasedTable文件,并记录文件偏移和buffer中的第一个Key(创建index要用),如果单条数据太大,比预设的块尺寸还大,这条数据就单独占一个块(单条数据不管多大也不会分割成多个块)。 279 | 280 | 总的来说,对比两种方案。rocksdb 压缩的单位是 Block,比 PA-Zip 的压缩单位(SST)要小,rocksdb 解压的单位也是 Block,比 PA-Zip 解压单位(Value)要大。PA-Zip 宣称采用全局压缩,rocksdb 是传统的分块+通用流式压缩(gzip,snappy,zstd…)。 PA-Zip 讲取得更好的压缩率,同时解压的时候也更省内存。 281 | 282 | -------------------------------------------------------------------------------- /07.SSTable/notes_SSTable/SSTable.md: -------------------------------------------------------------------------------- 1 | #1.SSTable 2 | 3 | ![](images/SSTable.png) 4 | SSTable 是 RocksDB 数据在外存的存储单位。每个 SSTable 是一个单独的文 件,默认使用BlockBasedTable。 5 | SSTable 由 Block 组成, Block 也是 RocksDB 最小的外存读取单位,包括 DataBlock, MetaBlock, IndexBlock 三种类型, 6 | 7 | * DataBlock 记录用户的数据, 8 | * MetaBlock 记录该 SSTable 的 BloomFilter, 9 | * IndexBlock 记录每个 DataBlock 的范围。 10 | 11 | 12 | 每个sst文件打开是一个TableReader(BlockBasedTable)对象,它会缓存在table_cache中,这个对象里包含了meta block和index block,所以table_cache基本上也是这两个东西占用的内存。 13 | 14 | ##1.1 Meta Block 15 | Bloom filter用于判断一个元素是不是在一个集合里,当一个元素被加入集合时,通过k个散列函数将这个元素映射成一个位数组中的k个点,把它们置为1。检索时如果这些点有任何一个为0,则被检元素一定不在;如果都是1,则被检元素很可能在。这就是布隆过滤器的基本思想。 优点:布隆过滤器存储空间和插入/查询时间都是常数O(k)。 缺点:有一定的误算率,同时标准的Bloom Filter不支持删除操作。 Bloom Filter通过极少的错误换取了存储空间的极大节省。 16 | 17 | ##1.2 Index Block 18 | 默认index block是一整块,如果使用了partitioned index,那么index block会切分成很多小块,并且建立一个二级索引,索引块在index block的最后一块 19 | 20 | 21 | ![](images/sstable2.png) 22 | 23 | -------------------------------------------------------------------------------- /07.SSTable/notes_SSTable/images/SSTable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/07.SSTable/notes_SSTable/images/SSTable.png -------------------------------------------------------------------------------- /07.SSTable/notes_SSTable/images/sstable2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/07.SSTable/notes_SSTable/images/sstable2.png -------------------------------------------------------------------------------- /08.Transaction/Lock_Transaction/assets/1599734854-07954a83df2ed5dc425295a5c1877caa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/08.Transaction/Lock_Transaction/assets/1599734854-07954a83df2ed5dc425295a5c1877caa.png -------------------------------------------------------------------------------- /08.Transaction/Lock_Transaction/assets/1599734854-0ed43024a0919cc9fa07884732981e9f.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/08.Transaction/Lock_Transaction/assets/1599734854-0ed43024a0919cc9fa07884732981e9f.png -------------------------------------------------------------------------------- /08.Transaction/Lock_Transaction/assets/1599734854-1b23341a0ea959e27f487265ba5d5708.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/08.Transaction/Lock_Transaction/assets/1599734854-1b23341a0ea959e27f487265ba5d5708.png -------------------------------------------------------------------------------- /08.Transaction/Lock_Transaction/assets/1599734854-2f9836f391212bcdbcc26adc40f65e36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/08.Transaction/Lock_Transaction/assets/1599734854-2f9836f391212bcdbcc26adc40f65e36.png -------------------------------------------------------------------------------- /08.Transaction/Lock_Transaction/assets/1599734854-32d93a478ad3e9dd7372f15fd88763ed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/08.Transaction/Lock_Transaction/assets/1599734854-32d93a478ad3e9dd7372f15fd88763ed.png -------------------------------------------------------------------------------- /08.Transaction/Lock_Transaction/assets/1599734854-678504024bb2f7a16179588202d904d0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/08.Transaction/Lock_Transaction/assets/1599734854-678504024bb2f7a16179588202d904d0.png -------------------------------------------------------------------------------- /08.Transaction/Lock_Transaction/assets/1599734854-6d0552a02407e8e35223501ba3d6345e.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/08.Transaction/Lock_Transaction/assets/1599734854-6d0552a02407e8e35223501ba3d6345e.png -------------------------------------------------------------------------------- /08.Transaction/Lock_Transaction/assets/1599734854-7d0b001256239b0414d1db2d339a39e6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/08.Transaction/Lock_Transaction/assets/1599734854-7d0b001256239b0414d1db2d339a39e6.png -------------------------------------------------------------------------------- /08.Transaction/Lock_Transaction/assets/1599734854-ab2d832e26f095bc9f3a2f2d166f6f26.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/08.Transaction/Lock_Transaction/assets/1599734854-ab2d832e26f095bc9f3a2f2d166f6f26.png -------------------------------------------------------------------------------- /08.Transaction/Lock_Transaction/assets/1599734854-bdc36cdd5f142f117c08227fd7fa7e7e.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/08.Transaction/Lock_Transaction/assets/1599734854-bdc36cdd5f142f117c08227fd7fa7e7e.png -------------------------------------------------------------------------------- /08.Transaction/Lock_Transaction/assets/1599734854-f00a283b92d885aa4be05f5e9828756a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/08.Transaction/Lock_Transaction/assets/1599734854-f00a283b92d885aa4be05f5e9828756a.png -------------------------------------------------------------------------------- /08.Transaction/Lock_Transaction/assets/1599734854-fdc1ca0cc049d7b1250ec0503aba58f2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/08.Transaction/Lock_Transaction/assets/1599734854-fdc1ca0cc049d7b1250ec0503aba58f2.png -------------------------------------------------------------------------------- /08.Transaction/Lock_Transaction/index.md: -------------------------------------------------------------------------------- 1 | # [RocksDB 原理介绍:读+锁+事务] 2 | 3 | # 2 锁 4 | 5 | ## 2.1 InnoBD 中的锁 6 | 7 | ### 2.1.1 锁的类型 8 | 9 | 按照锁的粒度,可以分为 【**表锁**】 和 【**行锁**】。按照锁的类型,可以分为 【**共享锁**】和【**排它锁**】。 10 | 11 | **表锁**:对整个表加锁。 12 | 13 | **行锁**:对一行数据加锁。 14 | 15 | **共享锁(S)**:可多个事务共享读取,阻止其他事务的写操作。显式加锁方式 SELECT ... LOCK IN SHARE MODE。表示对 select 语句涉及的范围添加共享锁,允许其它事务读取该范围内的数据,但不允许其它事务修改。 16 | 17 | **排他锁(X)**:单个事务独占,阻止其他事务的排他锁和共享锁。显式加锁方式 SELECT ... FOR UPDATE。FOR UPDATE 是排它锁。 18 | 19 | **意向锁**:提高事务加表锁时的效率,意向锁也分为【**意向共享锁**】和【意向排它锁】。 20 | 21 | 例如:在 T1 加行锁时,数据库会默认为全表添加一个意向锁。 22 | 23 | 此时,T2 若要加表锁: 24 | 25 | 1. 会先判断是否有表锁; 26 | 2. 再判断是否有意向锁。 27 | 28 | 有意向锁表示表中的某些行有锁,此时 T2 会等待行锁释放。若没有意向锁,则需要遍历整个表来判断是否有行锁,效率很低。 29 | 30 | * * * 31 | 32 | ### 2.1.2 锁的范围 33 | 34 | 1. **Record lock**:锁住的是索引,而非记录本身 35 | 2. **Gap lock**:在索引记录之间的间隙中加锁,并不包括索引记录本身。 36 | 1. 幻读针对另一个 session 的 insert 操作,不可重复读则针对于另一个 session 的 delete/update 操作。MySQL 中,默认为 RR,select 是读取不到另一个 session 的 insert 的,但是 update/delete 却能更新另一个 session 的 insert,出现了幻读,但是满足定义。 37 | 2. 根本原因是 MySQL 的 update/delete 会生成一个新的 lsn,通过 mvcc 判断,大于另一个 session 的事务的 lsn,满足可见性要求,因此出现幻读。 38 | 3. gap lock 的目的是实现 RR 的隔离级别,解决不可重复读的问题。Innodb 的实现方式,一定程度上避免了幻读,但无法彻底解决。update 能够更新其他 session 的写入。 39 | ![](assets/1599734854-6d0552a02407e8e35223501ba3d6345e.png) 40 | 3. Next-key lock:行锁加间隙锁,即 Record lock + Gap lock 41 | 42 | 43 | 具体情况见下表: 44 | 45 | ![](assets/1599734854-fdc1ca0cc049d7b1250ec0503aba58f2.png) 46 | 47 | ![](assets/1599734854-0ed43024a0919cc9fa07884732981e9f.png) 48 | 49 | ![](assets/1599734854-bdc36cdd5f142f117c08227fd7fa7e7e.png) 50 | 51 |
 

t4 无主键,id2 辅助索引

t5 id 为主键,id2 辅助索引

for update id=5

任何位置都无法插入

升级为表锁

优化为 record lock,任何位置可以插入

for update id2=5

id2=5 加 record lock

id2=4/6 无法插入

索引记录前后加 gap lock

id2=5 加 record lock

id2=4/6 无法插入

索引记录前后加 gap lock

52 | 53 | ## 2.2 RockDB 的锁 54 | 55 | ### 2.2.1 支持的锁 56 | 57 | 支持行锁中的 排它锁 select for update 和 共享锁 select lock in share mode。所有的锁信息都保存在内存中。每个 CF 都维护了一个锁的 map。 58 | 59 | 不支持 gap lock: 60 | 61 | * 会对唯一键(包括主键、唯一索引)加排它锁,避免重复插入、插入的数据被其他事务修改。 62 | * 但是其他事务可以随意在间隙中插入数据。 63 | 64 | InnoDB 中写操作会加 Next-key lock,阻塞一定范围内其他事务的写操作。而 RocksDB 不支持 gap lock,则会导致下图中的问题。 65 | 66 | ![](assets/1599734854-ab2d832e26f095bc9f3a2f2d166f6f26.png) 67 | 68 | * 上图的两条 SQL 可能的执行顺序如下: 69 | 70 | ![](assets/1599734854-07954a83df2ed5dc425295a5c1877caa.png) 71 | 72 | 因此,必须使用 RBR(Row Based Binary Logging),即行模式的复制,binlog 中记录的是一行数据的状态。 73 | 74 | 【**GAP Lock 缺失的问题**】 75 | 76 | 没有 gap lock,在事务并发执行时,不会阻塞写操作,因而会更容易触发死锁。 77 | 78 | 在之前的 RocksDB 中,T1更新1000W 行,当其更新到第900W 行时,T2 更新了最后100W 行,此时会产生死锁。 79 | 80 | InnoDB 中,会申请 1000W 行的写锁,因此会阻塞其他事务更新相关的行,不会出现死锁。 81 | 82 | ### 2.2.2 加锁行为分析 83 | 84 | * MyRocks只会对主键和唯一索引加锁,普通索引不会加锁。 85 | 86 | * 只有插入或更新了唯一索引时,才会对唯一索引加锁,对唯一索引加锁的目的是为了保证唯一性。 87 | 88 | * 按主键锁定查找不存在的行时,会对不存在的行主键加X锁。 89 | 90 | * 按二级索引查找时,只会对主键加锁,不会对二级锁引加锁。 91 | 92 | * S锁只应用于SELECT … IN SHARE MODE语句。 93 | 94 | 95 | * * * 96 | 97 | # 3 事务 98 | 99 | ## 3.1 通用功能 100 | 101 | RocksDB 支持 beign / commit / rollback,不支持 savepoint。 102 | 103 | savepoint 创建回滚点:语法 savepoint a1; 创建了名为 a1 的回滚点 104 | 105 | ## 3.2 特性限制 106 | 107 | ### 3.2.1 一致性快照 108 | 109 | 创建一致性快照的时间点,可以分为 start transaction 时创建 / 执行第一条 SQL 时创建 110 | 111 | * PostgreSQL:在 begin 时创建快照 112 | * RocksDB:在 begin 后,执行第一条语句时创建一致性快照,创建时间晚于 PG,但创建快照后的行为与 PG 相同,即 select for update、update 等操作仍然从快照中读取 113 | * InnoDB:在第一条语句执行时创建一致性快照,但 select for update、update 等操作会查询最新数据,而不是从快照中读取 114 | 115 | RocksDB 中通过 sequence number 来实现 mvcc 实现。。RocksDB 的主键由 User key (string) | sequence number (7 bytes) 两部分组成,通过 sequence number 来实现 MVCC。 116 | 117 | * User key 为用户指定的主键,同样 User key 的记录可能有多条。 118 | * sequence number 是每条记录的序列号,每条记录都不相同。 119 | 120 | ### 3.2.2 MVCC 121 | 122 | RocksDB 生成快照时,获取当前记录的 sequence number 记作 Sn,事务查询时会比对 sequence number,只能看到 S <= Sn 的记录。 123 | 124 | ### 3.2.3 隔离级别 125 | 126 | * InnoDB:事务中直接 select,会读取快照,select for update、update 等操作会直接读取当前的最新数据,这样会导致幻读,在ANSI 标准中,RR 可以出现幻读,因此,InnoDB 的实现没有问题 127 | * RocksDB:支持 RC + RR 两种隔离级别 128 | * RC:每执行一条语句都会创建一个快照,保证能读取到最新提交的数据 129 | * RR:begin 后执行第一条语句时创建快照,与 MySQL 创建 read view 的时机相同 130 | 131 | ### 3.2.4 XA 132 | 133 | XA是X/Open DTP组织(X/Open DTP group)基于两阶段提交协议定义的一组中间件与数据库之间的接口规范,用来完成一个分布式事务。 134 | 135 | X/Open DTP模型包括:应用程序(AP)、事务管理器(TM)、资源管理器(RM)、通信资源管理器(CRM)四部分。 136 | 137 | 事务管理器:由proxy 充当; 138 | 139 | 资源管理器:指的是数据库; 140 | 141 | 通信资源管理器:指的是消息中间件。 142 | 143 | ![](assets/1599734854-1b23341a0ea959e27f487265ba5d5708.png) 144 | 145 | MySQL 中分为【内部 XA(隐式 XA)】 与 【外部 XA(显式 XA)】 146 | 147 | 【外部 XA】:用于实现跨 MySQL 实例的分布式事务 148 | 149 | 【内部 XA】:用于 engine & binlog、engine & engine 的两阶段提交 150 | 151 | 旧版本 RocksDB 不支持 XA,因此,在事务提交过程中无法保证 写binlog 与 commit 构成一个分布式事务,master commit 但是未写入 binlog 时发生宕机,已提交事务无法同步到备库,会导致从库的数据与主库不一致。因此,需要开启 semi-sync(半同步),即多个从库中有任何一个返回同步成功(写入 relay log)后,master 才能 commit,避免因 XA 缺失造成主从数据不一致。 152 | 153 | ### 3.2.5 RocksDB XA 154 | 155 | 新版的 RocksDB 已经支持了 XA & 2PC。请参考:[https://github.com/facebook/rocksdb/wiki/Two-Phase-Commit-Implementation](https://github.com/facebook/rocksdb/wiki/Two-Phase-Commit-Implementation) 156 | 157 | #### 3.2.5.1 实现 158 | 159 | 之前,RocksDB 的 WriteBatch 操作的构成如下: 160 | 161 | ```java 162 | Sequence(0);NumRecords(3);Put(a,1);Merge(a,1);Delete(a); 163 | ``` 164 | 165 | 引入 XA 后,新增了四个操作: 166 | 167 | 1. Prepare(xid) 168 | 2. EndPrepare() 169 | 3. Commit(xid) 170 | 4. Rollback(xid) 171 | 172 | 支持 XA 后,分布式事务的写操作如下: 173 | 174 | ```java 175 | Sequence(0);NumRecords(6);Prepare(foo);Put(a,b);Put(x,y);EndPrepare();Put(j,k);Commit(foo); 176 | ``` 177 | 178 | Prepare 与 EndPrepare 相当于一对括号,将 ID 为 ‘foo’ 的交易括起来,foo 就是 xid。 179 | 180 | Commit 与 Rollback用来标识 事务 xid 的最终状态。 181 | 182 | #### 3.2.5.2 兼容性 183 | 184 | 需要考虑向下兼容的问题,旧版本的 rocksdb 无法识别2PC 的新增操作类型,将导致无法使用 WAL 恢复数据。RocksDB 可以简单跳过 prepare 等无法识别的标记。 185 | 186 | 187 | 188 | -------------------------------------------------------------------------------- /08.Transaction/transaction.txt: -------------------------------------------------------------------------------- 1 | 1.一些关键的类 2 | TransactionDB --rocksdb 3 | Transaction --rocksdb 4 | 5 | 一种是 Rdb_writebatch_impl 另一种 是 Rdb_transaction_impl 6 | 7 | TransactionLockMgr 8 | 9 | 10 | 2.【Transaction::GetForUpdate】 11 | 12 | |... 13 | |// 事务txn读取abc并独占该key,确保不被外部事务再修改 14 | |s = txn->GetForUpdate(read_options, “abc”, &value); 15 | |assert(s.ok()); 16 | | 17 | |// 通过TransactionDB::Put接口在事务外写abc 18 | |// 不会成功 19 | |s = txn_db->Put(write_options, “abc”, “value0”); 20 | | 21 | |s = txn->Commit(); 22 | |assert(s.ok()); 23 | |... 24 | |有时候在事务中需要对某一个key进行先读后写,此时则不能在写时才进行该key的独占及冲突检测操作,所以使用GetForUpdate接口读取该key并进行独占 25 | 26 | 27 | 28 | 3.【Transaction::SetSnapshot】 29 | 30 | |txn = txn_db->BeginTransaction(write_options); 31 | |// 设置事务txn使用的snapshot为当前全局Sequence Number 32 | |txn->SetSnapshot(); 33 | | 34 | |// 使用TransactionDB::Put接口在事务外部写abc 35 | |// 此时全局Sequence Number会加1 36 | |db->Put(write_options, “key1”, “value0”); 37 | |assert(s.ok()); 38 | | 39 | |// 事务txn写入abc 40 | |s = txn->Put(“abc”, “value1”); 41 | |s = txn->Commit(); 42 | |// 这里会失败,因为在事务设置了snapshot之后,事务后来写的key 43 | |// 在事务外部有过其他写操作,所以这里不会成功 44 | |// Pessimistic会在Put时失败,Optimistic会在Commit时失败 45 | |前面说过,TransactionDB在事务中需要写入某个key时才对其进行独占或冲突检测,有时希望在事务一开始就对其之后所有要写入的所有key进行独占, 46 | |此时可以通过SetSnapshot来实现,设置了Snapshot后,外部一旦对事务中将要进行写操作key做过修改, 47 | |则该事务最终会失败(失败点取决于是Pessimistic还是Optimistic,Pessimistic因为在Put时就进行冲突检测, 48 | |所以Put时就失败,而Optimistic则会在Commit是检测到冲突,失败) 49 | 50 | 4.【Transaction & TransactionDB 基本用法 】 51 | |Options options; 52 | |TransactionDBOptions txn_db_options; 53 | |options.create_if_missing = true; 54 | |TransactionDB* txn_db; 55 | | 56 | |// 打开DB(默认Pessimistic) 57 | |Status s = TransactionDB::Open(options, txn_db_options, kDBPath, &txn_db); 58 | |assert(s.ok()); 59 | | 60 | |// 创建一个事务 61 | |Transaction* txn = txn_db->BeginTransaction(write_options); 62 | |assert(txn); 63 | | 64 | |// 事务txn读取一个key 65 | |s = txn->Get(read_options, "abc", &value); 66 | |assert(s.IsNotFound()); 67 | | 68 | |// 事务txn写一个key 69 | |s = txn->Put("abc", "def"); 70 | |assert(s.ok()); 71 | | 72 | |// 通过TransactionDB::Get在事务外读取一个key 73 | |s = txn_db->Get(read_options, "abc", &value); 74 | | 75 | |// 通过TrasactionDB::Put在事务外写一个key 76 | |// 这里并不会有影响,因为写的不是"abc",不冲突 77 | |// 如果是"abc"的话 78 | |// 则Put会一直卡住直到超时或等待事务Commit(本例中会超时) 79 | |s = txn_db->Put(write_options, "xyz", "zzz"); 80 | | 81 | |s = txn->Commit(); 82 | |assert(s.ok()); 83 | |// 析构事务 84 | |delete txn; 85 | |delete txn_db; 86 | 87 | 88 | 5.RocksDB XA 89 | |新版的 RocksDB 已经支持了 XA & 2PC。请参考:https://github.com/facebook/rocksdb/wiki/Two-Phase-Commit-Implementation 90 | | 91 | |实现: 92 | |之前,RocksDB 的 WriteBatch 操作的构成如下: 93 | | 94 | |Sequence(0);NumRecords(3);Put(a,1);Merge(a,1);Delete(a); 95 | |引入 XA 后,新增了四个操作: 96 | | 97 | |Prepare(xid) 98 | |EndPrepare() 99 | |Commit(xid) 100 | |Rollback(xid) 101 | |支持 XA 后,分布式事务的写操作如下: 102 | | 103 | |Sequence(0);NumRecords(6);Prepare(foo);Put(a,b);Put(x,y);EndPrepare();Put(j,k);Commit(foo); 104 | |Prepare 与 EndPrepare 相当于一对括号,将 ID 为 ‘foo’ 的交易括起来,foo 就是 xid。 105 | | 106 | |Commit 与 Rollback用来标识 事务 xid 的最终状态。 -------------------------------------------------------------------------------- /09.Compaction/Compaction_Flush/assets/1599735083-2d2424d8966e15cee6e2c83a77d3fe67.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/09.Compaction/Compaction_Flush/assets/1599735083-2d2424d8966e15cee6e2c83a77d3fe67.png -------------------------------------------------------------------------------- /09.Compaction/Compaction_Flush/assets/1599735083-34cda6ae8edfa5c5a7e02414b7cd3633.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/09.Compaction/Compaction_Flush/assets/1599735083-34cda6ae8edfa5c5a7e02414b7cd3633.png -------------------------------------------------------------------------------- /09.Compaction/Compaction_Flush/assets/1599735083-3ff4fd416ddf4a888e7bcd46c91a862f.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/09.Compaction/Compaction_Flush/assets/1599735083-3ff4fd416ddf4a888e7bcd46c91a862f.png -------------------------------------------------------------------------------- /09.Compaction/Compaction_Flush/assets/1599735083-605ef45264358719c2a6fef49fc5d479.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/09.Compaction/Compaction_Flush/assets/1599735083-605ef45264358719c2a6fef49fc5d479.png -------------------------------------------------------------------------------- /09.Compaction/Compaction_Flush/assets/1599735083-62c3719f0938ee9fa0e36d6199307509.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/09.Compaction/Compaction_Flush/assets/1599735083-62c3719f0938ee9fa0e36d6199307509.png -------------------------------------------------------------------------------- /09.Compaction/Compaction_Flush/assets/1599735083-879c181377d82488a9aace78ae77b2a7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/09.Compaction/Compaction_Flush/assets/1599735083-879c181377d82488a9aace78ae77b2a7.png -------------------------------------------------------------------------------- /09.Compaction/Compaction_Flush/assets/1599735083-8d2370cb4efeb3ff4221bd07a5782658.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/09.Compaction/Compaction_Flush/assets/1599735083-8d2370cb4efeb3ff4221bd07a5782658.png -------------------------------------------------------------------------------- /09.Compaction/Compaction_Flush/assets/1599735083-a55dce20a8cea75b77e00a816fda3e14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/09.Compaction/Compaction_Flush/assets/1599735083-a55dce20a8cea75b77e00a816fda3e14.png -------------------------------------------------------------------------------- /09.Compaction/Compaction_Flush/assets/1599735083-a60a6309f97eb05ce2760324e44d106a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/09.Compaction/Compaction_Flush/assets/1599735083-a60a6309f97eb05ce2760324e44d106a.png -------------------------------------------------------------------------------- /09.Compaction/Compaction_Flush/assets/1599735083-a661d3a5ad30776e7b95a01a5335d799.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/09.Compaction/Compaction_Flush/assets/1599735083-a661d3a5ad30776e7b95a01a5335d799.png -------------------------------------------------------------------------------- /09.Compaction/Compaction_Flush/assets/1599735083-aa14f863ecd211ed666d0e4bc8e658dd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/09.Compaction/Compaction_Flush/assets/1599735083-aa14f863ecd211ed666d0e4bc8e658dd.png -------------------------------------------------------------------------------- /09.Compaction/Compaction_Flush/assets/1599735083-c49da31b58abf43f5f9356792b02b92e.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/09.Compaction/Compaction_Flush/assets/1599735083-c49da31b58abf43f5f9356792b02b92e.png -------------------------------------------------------------------------------- /09.Compaction/Compaction_Flush/assets/1599735083-dc3ed654edc5d384fd5a47868463e2b7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/09.Compaction/Compaction_Flush/assets/1599735083-dc3ed654edc5d384fd5a47868463e2b7.png -------------------------------------------------------------------------------- /09.Compaction/Compaction_Flush/assets/1599735083-e3d634e297618efecfac00c9e78393ae.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/09.Compaction/Compaction_Flush/assets/1599735083-e3d634e297618efecfac00c9e78393ae.png -------------------------------------------------------------------------------- /09.Compaction/Compaction_Flush/assets/1599735083-f00a283b92d885aa4be05f5e9828756a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/09.Compaction/Compaction_Flush/assets/1599735083-f00a283b92d885aa4be05f5e9828756a.png -------------------------------------------------------------------------------- /09.Compaction/Compaction_Flush/assets/1599735083-fb459c1c4c89891b329f090e98aeb942.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/09.Compaction/Compaction_Flush/assets/1599735083-fb459c1c4c89891b329f090e98aeb942.png -------------------------------------------------------------------------------- /09.Compaction/compaction.txt: -------------------------------------------------------------------------------- 1 | 1.Level Compaciton 【compaction 流程】 2 | (1)按score排序加入队列 3 | (2)根据minkey:maxkey选择本层SST 4 | (3)根据minkey:maxkey选择下层SST 5 | (4)将涉及的SST组成compaction 6 | (5)构造compaction job 7 | (6)job run 8 | 9 | 2. compaction 触发条件 10 | compaction触发机制是通过判断 各个 level 11 | 12 | Total File Size 13 | SST 个数 14 | 是否超过配置,来决定是否发生 compaction。 15 | 16 | 【level 0 触发条件】 17 | 18 | SST 文件个数达到参数 level0_file_num_compaction_trigger 19 | level 0 的 file size 超过 max_bytes_for_level_base 20 | 【level 1~n 触发条件】 21 | 22 | level n 当前的 total file size 是否 大于 max_bytes_for_level_base 23 | 注:max_bytes_for_level_base 又称作 Target Size。 24 | 25 | 3.compaction 优先级计算score 26 | 27 | 当多个 level 同时达到触发条件时,需要确定 compaction 的先后顺序。 28 | 29 | 因此,各个 level 需要计算出一个值 score,score 大的值先执行 compaction。 30 | 31 | RocksDB 中通过一个任务队列维护compaction任务,score 大的 level 优先加入任务队列等待处理。 32 | 33 | score 的计算方式如下: 34 | 35 | 【level 0】: Snum = total file num / level0_file_num_compaction_trigger 36 | 37 | Ssize = total file size / max_bytes_for_level_base 38 | 39 | level 0 score = max(Snum, Sszie) 40 | 41 | 同时,level 0 的 compaction 操作会影响写入。 42 | 43 | 为了不影响写入,直接将 level 0 的 score 调高,优先 compaction。 44 | 45 | 具体调整方式如下: 46 | level0_file_num_compaction_trigger, 4, num 为 4 时,达到 compact 条件, 4 <= num < 20 时 Score = num / 4 47 | level0_slow_down_writes_trigger, 20, num 为 20 时,RocksDB 会减慢写入速度, 20 <= num && num < 24 时 Score = 10000 48 | level0_stop_writes_trigger, 24, num 为 24 时,RocksDB 停止写入文件,尽快对 L0 进行 compact, 24 <= num 时 Score = 1000000 49 | 50 | 【level 1~】:score = Level 1 ~ n file size / max_bytes_for_level_base 51 | 52 | 53 | 4.compaction 动态调整触发条件 54 | 每层的 Target Size 都不同,通过 level_compaction_dynamic_level_bytes (true / false) 配置 Target Size 的动态调整。 55 | 56 | 【level_compaction_dynamic_level_bytes 为 false】关闭动态调整,target size 计算规则如下: 57 | 58 | 【level 0 的 target size】:max_bytes_for_level_base 的值 59 | 【level n 的 target size】:Target_Size(Ln+1) = Target_Size(Ln) * max_bytes_for_level_multiplier * max_bytes_for_level_multiplier_additional[n] 60 | 相关参数 61 | max_bytes_for_level_multiplier:乘法因子 62 | max_bytes_for_level_multiplier_additional[n]:各 level 的调整参数,默认为1 63 | 64 | 65 | 【例如】:level 1 的 max_bytes_for_level_base 是 10GB,max_bytes_for_level_multiplier=10,max_bytes_for_level_multiplier_additional[1]=1。 66 | 67 | 则 level 1,2,3,4 的 max_bytes_for_level_base 分别为:10GB,100GB,1000GB,10000GB 68 | 69 | 【level_compaction_dynamic_level_bytes 为 true】动态调整,target size 计算规则如下: 70 | 71 | 设:有 n 个 level,则 max_bytes_for_level_base 为最后一个 level n 的数据量阈值 72 | 73 | level n-1 = level n / max_bytes_for_level_multiplier。 74 | 【例如】:n=6,level n size = 276GB,max_bytes_for_level_multiplier = 10,则 level 1 ~ level 6 的 target size 为: 75 | 76 | 77 | level 1:0 78 | level 2:0 79 | level 3:0.276 GB 80 | level 4: 2.76 GB 81 | level 5: 27.6 GB 82 | level 6: 276 GB 83 | 84 | 85 | 5.compaction 并发执行 86 | 多个并发任务不会有重叠的key 87 | 为提高并发,rocksDB 通过 max_subcompactions 参数,能够将一个文件拆分为多个子任务,并发执行,类似于表级并行复制。 88 | 89 | 6.配置项 90 | options.max_bytes_for_level_base: 每层存放 SST 的数据量限制 91 | 92 | options.level_compaction_dynamic_level_bytes: 动态每层存放 SST 的数据量限制(level SST size limitation) 93 | 94 | options.max_bytes_for_level_multiplier and options.max_bytes_for_level_multiplier_additional: 动态调整 level size limitation 时的系数 95 | 96 | options.expanded_compaction_factor: 待确认 97 | 98 | options.source_compaction_factor: 待确认 99 | 100 | options.max_grandparent_overlap_factor: only for level-based compactions 待确认 101 | 102 | options.soft_rate_limit and options.hard_rate_limit: deprecated 待确认 103 | 104 | options.hard_pending_compaction_bytes_limit: only used for level-based compaction 待确认 105 | 106 | options.compaction_pri: only supported in level-based compaction 待确认 107 | 108 | 7.Manual Compaction 109 | 用户可以直接手动发起 compaction。 110 | 111 | 【适用场景】 112 | 113 | 将数据 compaction 到 最底层,减少写放大,适用于 读负载高的业务; 114 | 修改 level num,将指定范围内的数据压缩到最底层,然后将文件直接迁移到指定的 level; 115 | 修改配置(例如:压缩算法、block size、bloom filter settings、索引格式)。 116 | 【使用方式】 117 | 118 | mysql> set session rocksdb_manual_compaction_threads=16; 119 | Query OK, 0 rows affected (0.00 sec) 120 | 121 | mysql> set global rocksdb_compact_cf='cf_node_pk'; 122 | Query OK, 0 rows affected (25 min 13.80 sec) 123 | 【注意事项】 124 | 125 | 手动触发 compaction 会导致终端挂起,直至 compaction 结束 126 | 多个 session 发出 compaction 请求,请求会被添加到 FIFO 队列,顺序执行 127 | MyRocks 同一时刻只能执行一个手动 campaction 请求 128 | 发起 compaction 请求的终端断开连接,若请求未开始则会被取消 129 | 以开始处理的 compaction 请求无法终止 130 | 131 | 132 | DBImpl::RunManualCompaction 133 | --env_->Schedule(&DBImpl::BGWorkCompaction, ca, Env::Priority::LOW, this,&DBImpl::UnscheduleCallback); 134 | --DBImpl::BGWorkCompaction 135 | ----DBImpl::BackgroundCallCompaction 136 | ------DBImpl::BackgroundCompaction 137 | --------PopFirstFromCompactionQueue 138 | --------CompactionJob compaction_job 139 | --------compaction_job.Prepare 140 | --------compaction_job.Run 141 | --------c->ReleaseCompactionFiles 142 | --------NotifyOnCompactionCompleted -------------------------------------------------------------------------------- /10.CF/CF/assets/1599739636-5cfa77592484edd684e065afefecc134.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/10.CF/CF/assets/1599739636-5cfa77592484edd684e065afefecc134.png -------------------------------------------------------------------------------- /10.CF/CF/assets/1599739636-a14f3446821d50a8ce3f2b442c760fe6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/10.CF/CF/assets/1599739636-a14f3446821d50a8ce3f2b442c760fe6.png -------------------------------------------------------------------------------- /10.CF/CF/assets/1599739636-e79622b86376382f60a6d81702611c06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/10.CF/CF/assets/1599739636-e79622b86376382f60a6d81702611c06.png -------------------------------------------------------------------------------- /10.CF/CF/index.md: -------------------------------------------------------------------------------- 1 | # [RocksDB 原理介绍:CF] 2 | 3 | # 0 综述 4 | 5 | MyRocks 是关系型数据库,数据以 schema、table 进行管理。 6 | 7 | MyRocks :Row → key-value 8 | 9 | 本文主要论述以下几个方面: 10 | 11 | * Column Family 的定义; 12 | * MyRocks 数据字典; 13 | * MyRocks 数据记录格式; 14 | 15 | # 1 Column Family 16 | 17 | ## 1.1 CF 的由来 18 | 19 | * **没有 CF 时的数据组织形式** 20 | 21 | ![](assets/1599739636-a14f3446821d50a8ce3f2b442c760fe6.png) 22 | 23 | * **带来的问题:** 24 | * 将数据组织在一起——不便于管理 25 | * select、update 效率低——需要遍历所有数据 26 | * 逻辑删除一张表困难 27 | 28 | 29 | * **为了解决上述问题引入 CF** 30 | * 将逻辑上相关的 k-v 划分到独立的 CF 31 | * 逻辑划分与物理划分保持一致 32 | 33 | ![](assets/1599739636-e79622b86376382f60a6d81702611c06.png) 34 | 35 | ## 1.2 CF 的本质 36 | 37 | * 引入 CF 的目的是:**将逻辑上相关的元组(k-v 对)划分到一个独立的物理单元中,进而提高效率、便于管理。**维基百科中给出的定义总结如下: 38 | * k-v 数据库中的 CF <=> 关系数据库的 table 39 | * CF 的 SST <=> table 的 ibd 40 | * 上述的表述不够严谨,在 RocksDB 的 wiki 中将 CF 比作 DB 41 | * 多个表的数据可以保存到一个 CF 里面,从这个角度来看,CF 可以看作是表的集合,因此可以看做 DB 42 | 43 | ## 1.3 MyRocks 的 CF 44 | 45 | * **MyRocks CF 分为三类:** 46 | 47 | 1. default:保存所有未指定 CF 的索引 48 | 2. \_\_system\_\_:保存数据字典,即元信息、映射关系 49 | 3. 自定义 CF:用户创建的 CF 50 | 51 | ```java 52 | mysql> SELECT * FROM INFORMATION_SCHEMA.ROCKSDB_GLOBAL_INFO; 53 | +--------------+--------------+--------------------------------+ 54 | | TYPE         | NAME         | VALUE                          | 55 | +--------------+--------------+--------------------------------+ 56 | | BINLOG       | FILE         | cq01-sys-replace001-bin.000011 | 57 | | BINLOG       | POS          | 2517                           | 58 | | BINLOG       | GTID         |                                | 59 | | MAX_INDEX_ID | MAX_INDEX_ID | 265                            | 60 | | CF_FLAGS     | 0            | default [0]                    | 61 | | CF_FLAGS     | 1            | __system__ [0]                 | 62 | | CF_FLAGS     | 2            | pk_cf [0]                      | 63 | | CF_FLAGS     | 3            | idx_cf [0]                     | 64 | | CF_FLAGS     | 4            | cf_b [0]                       | 65 | +--------------+--------------+--------------------------------+ 66 | 9 rows in set (0.00 sec) 67 | ``` 68 | 69 | 70 | 71 | * **创建一张表并插入数据,相关信息保存如下:** 72 | 73 | ![](assets/1599739636-5cfa77592484edd684e065afefecc134.png) 74 | -------------------------------------------------------------------------------- /11.Dictionary/Dic_table.txt: -------------------------------------------------------------------------------- 1 | 1.CF 的本质 2 | 引入 CF 的目的是:将逻辑上相关的元组(k-v 对)划分到一个独立的物理单元中,进而提高效率、便于管理。维基百科中给出的定义总结如下: 3 | k-v 数据库中的 CF <=> 关系数据库的 table 4 | CF 的 SST <=> table 的 ibd 5 | 上述的表述不够严谨,在 RocksDB 的 wiki 中将 CF 比作 DB 6 | 多个表的数据可以保存到一个 CF 里面,从这个角度来看,CF 可以看作是表的集合,因此可以看做 DB 7 | 8 | 2.MyRocks 的 CF 9 | MyRocks CF 分为三类: 10 | default:保存所有未指定 CF 的索引 11 | __system__:保存数据字典,即元信息、映射关系 12 | 自定义 CF:用户创建的 CF 13 | 14 | mysql> SELECT * FROM INFORMATION_SCHEMA.ROCKSDB_GLOBAL_INFO; 15 | +--------------+--------------+--------------------------------+ 16 | | TYPE | NAME | VALUE | 17 | +--------------+--------------+--------------------------------+ 18 | | BINLOG | FILE | cq01-sys-replace001-bin.000011 | 19 | | BINLOG | POS | 2517 | 20 | | BINLOG | GTID | | 21 | | MAX_INDEX_ID | MAX_INDEX_ID | 265 | 22 | | CF_FLAGS | 0 | default [0] | 23 | | CF_FLAGS | 1 | __system__ [0] | 24 | | CF_FLAGS | 2 | pk_cf [0] | 25 | | CF_FLAGS | 3 | idx_cf [0] | 26 | | CF_FLAGS | 4 | cf_b [0] | 27 | +--------------+--------------+--------------------------------+ 28 | 9 rows in set (0.00 sec) 29 | 30 | 3.MyRocks 数据字典 —— 元信息 31 | MyRocks 元信息包含以下几类 32 | enum DATA_DICT_TYPE { 33 | DDL_ENTRY_INDEX_START_NUMBER= 1, // 表和索引之间的映射关系 34 | INDEX_INFO= 2, // 索引id和索引属性的关系 35 | CF_DEFINITION= 3, // CF 属性 36 | BINLOG_INFO_INDEX_NUMBER= 4, // binlog位点及gtid信息,binlog_commit更新此信息 37 | DDL_DROP_INDEX_ONGOING= 5, // 等待删除的索引信息 38 | INDEX_STATISTICS= 6, // 索引统计信息 39 | MAX_INDEX_ID= 7, // 当前的index id,每次创建索引index id都从这个获取和更新 40 | DDL_CREATE_INDEX_ONGOING= 8, // 等待创建的索引信息 41 | END_DICT_INDEX_ID= 255 42 | }; 43 | 44 | 4.数据字典 —— 创建一张表 45 | 建表语句 46 | mysql> show create table t1\G 47 | *************************** 1. row *************************** 48 | Table: t1 49 | Create Table: CREATE TABLE `t1` ( 50 | `id` int(11) NOT NULL AUTO_INCREMENT, 51 | `a` int(11) DEFAULT NULL, 52 | `b` char(8) COLLATE latin1_bin DEFAULT NULL, 53 | PRIMARY KEY (`id`), 54 | KEY `idx1` (`b`) COMMENT 'cf_b' 55 | ) ENGINE=ROCKSDB DEFAULT CHARSET=latin1 COLLATE=latin1_bin 56 | 1 row in set (0.00 sec) 57 | 58 | 59 | 建表流程: 60 | 61 | /** 62 | @brief 63 | create() is called to create a database. The variable name will have the name 64 | of the table. 65 | 66 | @details 67 | When create() is called you do not need to worry about 68 | opening the table. Also, the .frm file will have already been 69 | created so adjusting create_info is not necessary. You can overwrite 70 | the .frm file at this point if you wish to change the table 71 | definition, but there are no methods currently provided for doing 72 | so. 73 | 74 | Called from handle.cc by ha_create_table(). 75 | 76 | @return 77 | HA_EXIT_SUCCESS OK 78 | other HA_ERR error code (can be SE-specific) 79 | 80 | @see 81 | ha_create_table() in handle.cc 82 | */ 83 | 84 | rea_create_table 85 | 86 | 87 | ha_create_table 88 | --ha_rocksdb::create 89 | ----ha_rocksdb::get_table_if_exists 90 | ----ha_rocksdb::create_key_defs 91 | ------create_cfs//创建cf 92 | ------create_key_def//创建索引信息 93 | ----Rdb_ddl_manager::put_and_write //Put table definition of `tbl` into the mapping, and also write it to the on-disk data dictionary. 94 | /* 95 | Put table definition DDL entry. Actual write is done at 96 | Rdb_dict_manager::commit. 97 | 98 | We write 99 | dbname.tablename -> version + {key_entry, key_entry, key_entry, ... } 100 | 101 | Where key entries are a tuple of 102 | ( cf_id, index_nr ) 103 | */ 104 | ------put_dict 105 | --------add_cf_flags 106 | --------add_or_update_index_cf_mapping 107 | --------put_key 108 | 109 | 110 | 5.数据字典 —— 元信息存储 111 | 元信息以 key-value 形式保存在 __system__ CF 中 112 | 各个步骤与记录的对应关系如下 113 | CF_DEFINITION:定义新的 CF,并分配 cf_id 114 | 115 | INDEX_INFO:创建索引信息,索引所属 CF ID、索引 ID、索引类型(主键索引、辅助索引、隐藏索引) 116 | 117 | DDL_ENTRY_INDEX_START_NUMBER:dbname+tblname 与 索引的映射 118 | 119 | 120 | 6.数据字典 —— 元信息查询 121 | 创建 CF cf_b:用于保存辅助索引 idx1 的数据 122 | mysql> SELECT * FROM INFORMATION_SCHEMA.ROCKSDB_GLOBAL_INFO; 123 | +--------------+--------------+--------------------------------+ 124 | | TYPE | NAME | VALUE | 125 | +--------------+--------------+--------------------------------+ 126 | | BINLOG | FILE | cq01-sys-replace001-bin.000011 | 127 | | BINLOG | POS | 2517 | 128 | | BINLOG | GTID | | 129 | | MAX_INDEX_ID | MAX_INDEX_ID | 265 | 130 | | CF_FLAGS | 0 | default [0] | 131 | | CF_FLAGS | 1 | __system__ [0] | 132 | | CF_FLAGS | 2 | pk_cf [0] | 133 | | CF_FLAGS | 3 | idx_cf [0] | 134 | | CF_FLAGS | 4 | cf_b [0] | 135 | +--------------+--------------+--------------------------------+ 136 | 9 rows in set (0.00 sec) 137 | 138 | 139 | 创建两个索引 140 | 主键索引:PRIMARY,索引 ID : 264,由 CF default 管理,类型为1 141 | 辅助索引:idx1,索引 ID : 265,由 CF cf_b 管理,类型为2 142 | mysql> SELECT INDEX_NAME,INDEX_NUMBER,COLUMN_FAMILY,CF,INDEX_TYPE,KV_FORMAT_VERSION FROM INFORMATION_SCHEMA.rocksdb_ddl where TABLE_NAME='t1'; 143 | +------------+--------------+---------------+---------+------------+-------------------+ 144 | | INDEX_NAME | INDEX_NUMBER | COLUMN_FAMILY | CF | INDEX_TYPE | KV_FORMAT_VERSION | 145 | +------------+--------------+---------------+---------+------------+-------------------+ 146 | | PRIMARY | 264 | 0 | default | 1 | 13 | 147 | | idx1 | 265 | 4 | cf_b | 2 | 13 | 148 | +------------+--------------+---------------+---------+------------+-------------------+ 149 | 2 rows in set (0.00 sec) 150 | 151 | 152 | 建立 dbname 与 tablename 的映射关系 153 | mysql> SELECT TABLE_SCHEMA,TABLE_NAME,INDEX_NAME,INDEX_NUMBER,COLUMN_FAMILY,CF,INDEX_TYPE,KV_FORMAT_VERSION FROM INFORMATION_SCHEMA.rocksdb_ddl where TABLE_NAME='t1'; 154 | +--------------+------------+------------+--------------+---------------+---------+------------+-------------------+ 155 | | TABLE_SCHEMA | TABLE_NAME | INDEX_NAME | INDEX_NUMBER | COLUMN_FAMILY | CF | INDEX_TYPE | KV_FORMAT_VERSION | 156 | +--------------+------------+------------+--------------+---------------+---------+------------+-------------------+ 157 | | test | t1 | PRIMARY | 264 | 0 | default | 1 | 13 | 158 | | test | t1 | idx1 | 265 | 4 | cf_b | 2 | 13 | 159 | +--------------+------------+------------+--------------+---------------+---------+------------+-------------------+ 160 | 2 rows in set (0.00 sec) 161 | 162 | -------------------------------------------------------------------------------- /11.Dictionary/enum_DATA_DICT_TYPE.md: -------------------------------------------------------------------------------- 1 | #1.enum DATA_DICT_TYPE 2 | 3 | ```cpp 4 | 5 | enum DATA_DICT_TYPE { 6 | DDL_ENTRY_INDEX_START_NUMBER= 1, // 表和索引之间的映射关系 7 | INDEX_INFO= 2, // 索引id和索引属性的关系 8 | CF_DEFINITION= 3, // CF 属性 9 | BINLOG_INFO_INDEX_NUMBER= 4, // binlog位点及gtid信息,binlog_commit更新此信息 10 | DDL_DROP_INDEX_ONGOING= 5, // 等待删除的索引信息 11 | INDEX_STATISTICS= 6, // 索引统计信息 12 | MAX_INDEX_ID= 7, // 当前的index id,每次创建索引index id都从这个获取和更新 13 | DDL_CREATE_INDEX_ONGOING= 8, // 等待创建的索引信息 14 | END_DICT_INDEX_ID= 255 15 | }; 16 | ``` -------------------------------------------------------------------------------- /11.Dictionary/notes_Dictionary/assets/1599739636-8ef3252da2a37e93f3359306d5368b12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/11.Dictionary/notes_Dictionary/assets/1599739636-8ef3252da2a37e93f3359306d5368b12.png -------------------------------------------------------------------------------- /11.Dictionary/notes_Dictionary/assets/1599739636-fc1c3061b56bd2f8326d0a98a39ad0c4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/11.Dictionary/notes_Dictionary/assets/1599739636-fc1c3061b56bd2f8326d0a98a39ad0c4.png -------------------------------------------------------------------------------- /11.Dictionary/notes_Dictionary/index.md: -------------------------------------------------------------------------------- 1 | # 2 MyRocks 数据字典 2 | 3 | ## 2.1 元信息 4 | 5 | * **MyRocks 元信息包含以下几类** 6 | 7 | ```java 8 | enum DATA_DICT_TYPE { 9 |     DDL_ENTRY_INDEX_START_NUMBER= 1, // 表和索引之间的映射关系 10 |     INDEX_INFO=                   2, // 索引id和索引属性的关系 11 |     CF_DEFINITION=                3, // CF 属性 12 |     BINLOG_INFO_INDEX_NUMBER=     4, // binlog位点及gtid信息,binlog_commit更新此信息 13 |     DDL_DROP_INDEX_ONGOING=       5, // 等待删除的索引信息 14 |     INDEX_STATISTICS=             6, // 索引统计信息 15 |     MAX_INDEX_ID=                 7, // 当前的index id,每次创建索引index id都从这个获取和更新 16 |     DDL_CREATE_INDEX_ONGOING=     8, // 等待创建的索引信息 17 |     END_DICT_INDEX_ID=          255 18 | }; 19 | ``` 20 | 21 | ## 2.2 创建一张表 22 | 23 | ### 2.2.1 建表语句 24 | 25 | ```java 26 | mysql> show create table t1\G 27 | *************************** 1. row *************************** 28 |        Table: t1 29 | Create Table: CREATE TABLE `t1` ( 30 |   `id` int(11) NOT NULL AUTO_INCREMENT, 31 |   `a` int(11) DEFAULT NULL, 32 |   `b` char(8) COLLATE latin1_bin DEFAULT NULL, 33 |   PRIMARY KEY (`id`), 34 |   KEY `idx1` (`b`) COMMENT 'cf_b' 35 | ) ENGINE=ROCKSDB DEFAULT CHARSET=latin1 COLLATE=latin1_bin 36 | 1 row in set (0.00 sec) 37 | ``` 38 | 39 | ### 2.2.2 建表流程 40 | 41 | ![](assets/1599739636-fc1c3061b56bd2f8326d0a98a39ad0c4.png) 42 | 43 | ## 2.3 元信息存储 44 | 45 | * 元信息以 key-value 形式保存在 \_\_system\_\_ CF 中 46 | * 各个步骤与记录的对应关系如下 47 | * CF\_DEFINITION:定义新的 CF,并分配 cf\_id 48 | 49 | * INDEX\_INFO:创建索引信息,索引所属 CF ID、索引 ID、索引类型(主键索引、辅助索引、隐藏索引) 50 | 51 | * DDL\_ENTRY\_INDEX\_START\_NUMBER:dbname+tblname 与 索引的映射 52 | 53 | 54 | ![](assets/1599739636-8ef3252da2a37e93f3359306d5368b12.png) 55 | 56 | ## 2.4 元信息查询 57 | 58 | * 创建 CF cf\_b:用于保存辅助索引 idx1 的数据 59 | 60 | ```java 61 | mysql> SELECT * FROM INFORMATION_SCHEMA.ROCKSDB_GLOBAL_INFO; 62 | +--------------+--------------+--------------------------------+ 63 | | TYPE         | NAME         | VALUE                          | 64 | +--------------+--------------+--------------------------------+ 65 | | BINLOG       | FILE         | cq01-sys-replace001-bin.000011 | 66 | | BINLOG       | POS          | 2517                           | 67 | | BINLOG       | GTID         |                                | 68 | | MAX_INDEX_ID | MAX_INDEX_ID | 265                            | 69 | | CF_FLAGS     | 0            | default [0]                    | 70 | | CF_FLAGS     | 1            | __system__ [0]                 | 71 | | CF_FLAGS     | 2            | pk_cf [0]                      | 72 | | CF_FLAGS     | 3            | idx_cf [0]                     | 73 | | CF_FLAGS     | 4            | cf_b [0]                       | 74 | +--------------+--------------+--------------------------------+ 75 | 9 rows in set (0.00 sec) 76 | ``` 77 | 78 | 79 | 80 | * 创建两个索引 81 | * 主键索引:PRIMARY,索引 ID : 264,由 CF default 管理,类型为1 82 | * 辅助索引:idx1,索引 ID : 265,由 CF cf\_b 管理,类型为2 83 | 84 | ```java 85 | mysql> SELECT INDEX_NAME,INDEX_NUMBER,COLUMN_FAMILY,CF,INDEX_TYPE,KV_FORMAT_VERSION FROM INFORMATION_SCHEMA.rocksdb_ddl where TABLE_NAME='t1'; 86 | +------------+--------------+---------------+---------+------------+-------------------+ 87 | | INDEX_NAME | INDEX_NUMBER | COLUMN_FAMILY | CF      | INDEX_TYPE | KV_FORMAT_VERSION | 88 | +------------+--------------+---------------+---------+------------+-------------------+ 89 | | PRIMARY    |          264 |             0 | default |          1 |                13 | 90 | | idx1       |          265 |             4 | cf_b    |          2 |                13 | 91 | +------------+--------------+---------------+---------+------------+-------------------+ 92 | 2 rows in set (0.00 sec) 93 | ``` 94 | 95 | 96 | 97 | * 建立 dbname 与 tablename 的映射关系 98 | 99 | ```java 100 | mysql> SELECT TABLE_SCHEMA,TABLE_NAME,INDEX_NAME,INDEX_NUMBER,COLUMN_FAMILY,CF,INDEX_TYPE,KV_FORMAT_VERSION FROM INFORMATION_SCHEMA.rocksdb_ddl where TABLE_NAME='t1'; 101 | +--------------+------------+------------+--------------+---------------+---------+------------+-------------------+ 102 | | TABLE_SCHEMA | TABLE_NAME | INDEX_NAME | INDEX_NUMBER | COLUMN_FAMILY | CF      | INDEX_TYPE | KV_FORMAT_VERSION | 103 | +--------------+------------+------------+--------------+---------------+---------+------------+-------------------+ 104 | | test         | t1         | PRIMARY    |          264 |             0 | default |          1 |                13 | 105 | | test         | t1         | idx1       |          265 |             4 | cf_b    |          2 |                13 | 106 | +--------------+------------+------------+--------------+---------------+---------+------------+-------------------+ 107 | 2 rows in set (0.00 sec) 108 | ``` 109 | 110 | -------------------------------------------------------------------------------- /12.Record_Format/Record_Format.txt: -------------------------------------------------------------------------------- 1 | 1.记录格式 2 | 向表 t1 中插入一行数据 3 | mysql> SELECT * FROM t1; 4 | +----+------+------+ 5 | | id | a | b | 6 | +----+------+------+ 7 | | 1 | 1 | a | 8 | +----+------+------+ 9 | 1 row in set (0.00 sec) 10 | 11 | 12 | 主键索引记录 13 | key: index_id, M(pk) 14 | value: unpack_info, NULL-bitmap,a,b 15 | 16 | 17 | index_id:索引 ID,全局唯一,4B 18 | M(pk):转化后的主键,转化后的 pk 可以直接 memcmp 19 | unpack_info:pk 逆转化的信息 20 | NULL-bitmap: 表示为 NULL 的字段 21 | a/b:数据 22 | 综上:数据与主键索引保存在一起,MyRocks 的主键索引为聚簇索引 23 | 24 | 25 | 26 | 二级索引记录 27 | key: index_id,NULL-byte, M(b),M(pk) 28 | value: unpack_info 29 | 30 | 31 | index_id:二级索引 ID 32 | NULL-byte:索引 b 是否为空 33 | M(b):转化后的二级索引 34 | M(pk):转化后的主键 35 | unpack_info:逆转化信息 36 | 37 | 38 | 2.索引转化 39 | rocksdb为了比较方便,将key字段转化为可以直接memcmp比较的形式。 40 | 41 | (1) 整型 42 | 需要考虑符号位 43 | 44 | 1 表示为:00000000 00000000 00000000 00000001 45 | 0x00 00000000 46 | 0x01 00000000 47 | 0x02 00000000 48 | 0x03 00000001 49 | -1 表示为:11111111 11111111 11111111 11111111 50 | 0x00 11111111 51 | 0x01 11111111 52 | 0x02 11111111 53 | 0x03 11111111 54 | 直接比较,则 -1 > 1,因此需要转化,规则为:0x00 ^ 128 (1000 0000) 55 | 56 | 1 表示为: 57 | 0x00 10000000 -> 0x80 58 | 0x01 00000000 -> 0x00 59 | 0x02 00000000 -> 0x00 60 | 0x03 00000001 -> 0x01 61 | -1 表示为: 62 | 0x00 01111111 -> 0x7F 63 | 0x01 11111111 -> 0xFF 64 | 0x02 11111111 -> 0xFF 65 | 0x03 11111111 -> 0xFF 66 | 转化后可以直接比较 1 > -1 67 | 68 | (2) char 69 | 不足的位直接补空格 0x20 70 | 71 | (3) varchar 72 | const int VARCHAR_CMP_LESS_THAN_SPACES = 1; 73 | const int VARCHAR_CMP_EQUAL_TO_SPACES = 2; 74 | const int VARCHAR_CMP_GREATER_THAN_SPACES = 3; 75 | 76 | Example: m_segment_size=5, collation=latin1_bin: 77 | 78 | 'abcd\0' => [ 'abcd' ][ '\0 ' ] 79 | 'abcd' => [ 'abcd' ] 80 | 'abcd ' => [ 'abcd' ] 81 | 'abcdZZZZ' => [ 'abcd' ][ 'ZZZZ' ] 82 | 83 | 3.key 的封装 84 | 将 key 封装为 internal key 85 | user_key:key 86 | seq_num:全局递增 sequence,用于 MVCC 87 | value_type:put、merge、delete 88 | 89 | 90 | 将 internal key 封装为 memtable key,memtable 中的最终数据格式 91 | 92 | 三个概念: 93 | 逻辑key(纯粹的payload), 94 | internal key(增加seqence num, value type,用于rocksdb磁盘存储), 95 | memtable key(增加key size, value size 用于memtable). 96 | 97 | 4.插入一行数据 98 | mysql> SELECT * FROM t1; 99 | +----+------+------+ 100 | | id | a | b | 101 | +----+------+------+ 102 | | 1 | 1 | a | 103 | +----+------+------+ 104 | 1 row in set (0.00 sec) 105 | 106 | 107 | mysql> SELECT COLUMN_FAMILY,INDEX_NUMBER,SST_NAME FROM INFORMATION_SCHEMA.ROCKSDB_INDEX_FILE_MAP WHERE INDEX_NUMBER IN (SELECT INDEX_NUMBER FROM INFORMATION_SCHEMA.ROCKSDB_DDL WHERE TABLE_NAME = 't1'); 108 | +---------------+--------------+------------+ 109 | | COLUMN_FAMILY | INDEX_NUMBER | SST_NAME | 110 | +---------------+--------------+------------+ 111 | | 0 | 264 | 000134.sst | 112 | | 4 | 265 | 000136.sst | 113 | +---------------+--------------+------------+ 114 | 115 | 116 | 117 | (1)主键索引:保存在 000134.sst 118 | 119 | '0000010880000001' seq:108, type:1 => 0001000000 6120202020202020 120 | key:0000010880000001 121 | index_id:264 -> 0x108 -> 0x00 0x00 0x01 0x08 122 | M(pk):1 -> 0x80 0x00 0x00 0x01,符号位翻转 123 | seq:108 124 | type:1 -> PUT 125 | value:00010000006120202020202020 126 | NULL-bitmap:0x00 ,每一位对应一列,a\b 两列均非空 -> 0x00 127 | column a:0x01 0x00 0x00 0x00 128 | 数字1 大端表示形式 129 | column b:0x61 0x20 0x20 0x20 0x20 0x20 0x20 0x20 130 | 'a' -> 0x61 131 | 补7个空格 -> 0x20 132 | 133 | 134 | (2)辅助索引:保存在 000136.sst 135 | 136 | 137 | '0000010901612020202020202080000001' seq:109, type:1 => 138 | key:0000010901612020202020202080000001 139 | index_id:265 -> 0x109 -> 0x00 0x00 0x01 0x09 140 | NULL-byte:0x01,标识字段 b 是否可以为NULL 141 | M(b):‘a[ ]*7’ -> 0x61 0x20 0x20 0x20 0x20 0x20 0x20 0x20 142 | M(id):0x80 0x00 0x00 0x01 143 | seq_num:109 144 | type:1 -> PUT 145 | value:NULL 146 | pack_info:无需额外的逆转化信息,该字段为 NULL 147 | 148 | 5.源码分析 149 | (1)操作类型,例如 delete 操作的 type 为 kTypeDeletion = 0x0 150 | // dbformat.h 定义的操作类型 151 | // Value types encoded as the last component of internal keys. 152 | // DO NOT CHANGE THESE ENUM VALUES: they are embedded in the on-disk 153 | // data structures. 154 | // The highest bit of the value type needs to be reserved to SST tables 155 | // for them to do more flexible encoding. 156 | enum ValueType : unsigned char { 157 | kTypeDeletion = 0x0, 158 | kTypeValue = 0x1, 159 | kTypeMerge = 0x2, 160 | kTypeLogData = 0x3, // WAL only. 161 | kTypeColumnFamilyDeletion = 0x4, // WAL only. 162 | kTypeColumnFamilyValue = 0x5, // WAL only. 163 | kTypeColumnFamilyMerge = 0x6, // WAL only. 164 | kTypeSingleDeletion = 0x7, 165 | kTypeColumnFamilySingleDeletion = 0x8, // WAL only. 166 | kTypeBeginPrepareXID = 0x9, // WAL only. 167 | kTypeEndPrepareXID = 0xA, // WAL only. 168 | kTypeCommitXID = 0xB, // WAL only. 169 | kTypeRollbackXID = 0xC, // WAL only. 170 | kTypeNoop = 0xD, // WAL only. 171 | kTypeColumnFamilyRangeDeletion = 0xE, // WAL only. 172 | kTypeRangeDeletion = 0xF, // meta block 173 | kTypeColumnFamilyBlobIndex = 0x10, // Blob DB only 174 | kTypeBlobIndex = 0x11, // Blob DB only 175 | // When the prepared record is also persisted in db, we use a different 176 | // record. This is to ensure that the WAL that is generated by a WritePolicy 177 | // is not mistakenly read by another, which would result into data 178 | // inconsistency. 179 | kTypeBeginPersistedPrepareXID = 0x12, // WAL only. 180 | kMaxValue = 0x7F // Not used for storing records. 181 | }; 182 | 183 | 184 | (2)Internal Key 的比较: 185 | user key 升序比较 186 | sequence number 降序比较 187 | 在 memtable 中排序时,全局递增、局部递减,目的是保证 sequence number 大的数据(最新的数据)靠前存放,提高查找效率 188 | 因此,sequence number 是直接存放的,没有进行亦或等转化 189 | dbformat.cc:111,就是想 memtable 中插入时,确定插入位置的 compare 方法 190 | int InternalKeyComparator::Compare(const ParsedInternalKey& a, 191 | const ParsedInternalKey& b) const { 192 | // Order by: 193 | // increasing user key (according to user-supplied comparator) 194 | // decreasing sequence number 195 | // decreasing type (though sequence# should be enough to disambiguate) 196 | int r = user_comparator_->Compare(a.user_key, b.user_key); 197 | PERF_COUNTER_ADD(user_key_comparison_count, 1); 198 | if (r == 0) { 199 | if (a.sequence > b.sequence) { 200 | r = -1; 201 | } else if (a.sequence < b.sequence) { 202 | r = +1; 203 | } else if (a.type > b.type) { 204 | r = -1; 205 | } else if (a.type < b.type) { 206 | r = +1; 207 | } 208 | } 209 | return r; 210 | } 211 | 212 | (3)write batch 中的 rep_ 成员就是需要写入到 memtable 中数据,其中保存了 sequence number、count、data,其具体结构如下 213 | write_batch.cc:10 注释 214 | // WriteBatch::rep_ := 215 | // sequence: fixed64 216 | // count: fixed32 217 | // data: record[count] 218 | // record := 219 | // kTypeValue varstring varstring 220 | // kTypeDeletion varstring 221 | // kTypeSingleDeletion varstring 222 | // kTypeRangeDeletion varstring varstring 223 | // kTypeMerge varstring varstring 224 | // kTypeColumnFamilyValue varint32 varstring varstring 225 | // kTypeColumnFamilyDeletion varint32 varstring 226 | // kTypeColumnFamilySingleDeletion varint32 varstring 227 | // kTypeColumnFamilyRangeDeletion varint32 varstring varstring 228 | // kTypeColumnFamilyMerge varint32 varstring varstring 229 | // kTypeBeginPrepareXID varstring 230 | // kTypeEndPrepareXID 231 | // kTypeCommitXID varstring 232 | // kTypeRollbackXID varstring 233 | // kTypeBeginPersistedPrepareXID varstring 234 | // kTypeNoop 235 | // varstring := 236 | // len: varint32 237 | // data: uint8[len] 238 | 239 | 6.Key格式 240 |  Internal Key 241 | | User key (string) | sequence number (7 bytes) | value type (1 byte) | 242 |  Lookup Key 243 | | Size (int32变长)| User key (string) | sequence number (7 bytes) | value type (1 byte) | 244 | 245 | 246 | 247 | 248 | -------------------------------------------------------------------------------- /12.Record_Format/Record_Format/assets/1599739636-013e3948422f51f8b8bbb9ad105b080b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/12.Record_Format/Record_Format/assets/1599739636-013e3948422f51f8b8bbb9ad105b080b.png -------------------------------------------------------------------------------- /12.Record_Format/Record_Format/assets/1599739636-415fcbda855d2da975a98602829a4863.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/12.Record_Format/Record_Format/assets/1599739636-415fcbda855d2da975a98602829a4863.png -------------------------------------------------------------------------------- /12.Record_Format/Record_Format/assets/1599739636-4b8aadf246b7854c260e07a4b63e46b0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/12.Record_Format/Record_Format/assets/1599739636-4b8aadf246b7854c260e07a4b63e46b0.png -------------------------------------------------------------------------------- /12.Record_Format/Record_Format/assets/1599739636-684894f81be8e04ac63db190f8053e79.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/12.Record_Format/Record_Format/assets/1599739636-684894f81be8e04ac63db190f8053e79.png -------------------------------------------------------------------------------- /12.Record_Format/Record_Format/assets/1599739636-7588a070837408a85cc6c06a269cd5d7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/12.Record_Format/Record_Format/assets/1599739636-7588a070837408a85cc6c06a269cd5d7.png -------------------------------------------------------------------------------- /12.Record_Format/Record_Format/assets/1599739636-f00a283b92d885aa4be05f5e9828756a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/12.Record_Format/Record_Format/assets/1599739636-f00a283b92d885aa4be05f5e9828756a.png -------------------------------------------------------------------------------- /12.Record_Format/Record_Format/index.md: -------------------------------------------------------------------------------- 1 | # 3 MyRocks 数据记录格式 2 | 3 | ## 3.1 记录格式 4 | 5 | * 向表 t1 中插入一行数据 6 | 7 | ```java 8 | mysql> SELECT * FROM t1; 9 | +----+------+------+ 10 | | id | a    | b    | 11 | +----+------+------+ 12 | |  1 |    1 | a    | 13 | +----+------+------+ 14 | 1 row in set (0.00 sec) 15 | ``` 16 | 17 | 18 | 19 | * **主键索引记录** 20 | 21 | ```plain 22 | key: index_id, M(pk) 23 | value: unpack_info, NULL-bitmap,a,b 24 | ``` 25 | 26 | ![](assets/1599739636-7588a070837408a85cc6c06a269cd5d7.png) 27 | 28 | 1. index\_id:索引 ID,全局唯一,4B 29 | 2. M(pk):转化后的主键,转化后的 pk 可以直接 memcmp 30 | 3. unpack\_info:pk 逆转化的信息 31 | 4. NULL-bitmap: 表示为 NULL 的字段 32 | 5. a/b:数据 33 | 34 | **综上:数据与主键索引保存在一起,MyRocks 的主键索引为聚簇索引** 35 | 36 | * **二级索引记录** 37 | 38 | ```java 39 | key: index_id,NULL-byte, M(b),M(pk) 40 | value: unpack_info 41 | ``` 42 | 43 | ![](assets/1599739636-684894f81be8e04ac63db190f8053e79.png) 44 | 45 | 1. index\_id:二级索引 ID 46 | 2. NULL-byte:索引 b 是否为空 47 | 3. M(b):转化后的二级索引 48 | 4. M(pk):转化后的主键 49 | 5. unpack\_info:逆转化信息 50 | 51 | ## 3.2 索引转化 52 | 53 | rocksdb为了比较方便,将key字段转化为可以直接memcmp比较的形式。 54 | 55 | ### 3.2.1 整型 56 | 57 | 需要考虑符号位 58 | 59 | * 1 表示为:00000000 00000000 00000000 00000001 60 | * 0x00 00000000 61 | * 0x01 00000000 62 | * 0x02 00000000 63 | * 0x03 00000001 64 | * \-1 表示为:11111111 11111111 11111111 11111111 65 | * 0x00 11111111 66 | * 0x01 11111111 67 | * 0x02 11111111 68 | * 0x03 11111111 69 | 70 | 直接比较,则 -1 > 1,因此需要转化,规则为:0x00 ^ 128 (1000 0000) 71 | 72 | * 1 表示为: 73 | * 0x00 10000000  -> 0x80 74 | * 0x01 00000000  -> 0x00 75 | * 0x02 00000000  -> 0x00 76 | * 0x03 00000001  -> 0x01 77 | * \-1 表示为: 78 | * 0x00 01111111  -> 0x7F 79 | * 0x01 11111111  -> 0xFF 80 | * 0x02 11111111  -> 0xFF 81 | * 0x03 11111111  -> 0xFF 82 | 83 | 转化后可以直接比较 1 > -1 84 | 85 | ### 3.2.2 char 86 | 87 | 不足的位直接补空格 0x20 88 | 89 | ### 3.2.3 varchar 90 | 91 | ```java 92 | const int VARCHAR_CMP_LESS_THAN_SPACES = 1; 93 | const int VARCHAR_CMP_EQUAL_TO_SPACES = 2; 94 | const int VARCHAR_CMP_GREATER_THAN_SPACES = 3; 95 |   96 |  Example: m_segment_size=5, collation=latin1_bin: 97 |   98 |   'abcd\0'   => [ 'abcd' ][ '\0    ' ] 99 |   'abcd'     => [ 'abcd' ] 100 |   'abcd   '  => [ 'abcd' ] 101 |   'abcdZZZZ' => [ 'abcd' ][ 'ZZZZ' ] 102 | ``` 103 | 104 | ## 3.3 key 的封装 105 | 106 | * **将 key 封装为 internal key** 107 | 108 | 1. user\_key:key 109 | 2. seq\_num:全局递增 sequence,用于 MVCC 110 | 3. value\_type:put、merge、delete 111 | 112 | ![](assets/1599739636-415fcbda855d2da975a98602829a4863.png) 113 | 114 | * **将 internal key 封装为 memtable key,memtable 中的最终数据格式** 115 | 116 | 117 | 118 | ![](assets/1599739636-4b8aadf246b7854c260e07a4b63e46b0.png) 119 | 120 | ## 3.4 插入一行数据 121 | 122 | ```java 123 | mysql> SELECT * FROM t1; 124 | +----+------+------+ 125 | | id | a    | b    | 126 | +----+------+------+ 127 | |  1 |    1 | a    | 128 | +----+------+------+ 129 | 1 row in set (0.00 sec) 130 |   131 |   132 | mysql> SELECT COLUMN_FAMILY,INDEX_NUMBER,SST_NAME FROM INFORMATION_SCHEMA.ROCKSDB_INDEX_FILE_MAP WHERE INDEX_NUMBER IN (SELECT INDEX_NUMBER FROM INFORMATION_SCHEMA.ROCKSDB_DDL WHERE TABLE_NAME = 't1'); 133 | +---------------+--------------+------------+ 134 | | COLUMN_FAMILY | INDEX_NUMBER | SST_NAME   | 135 | +---------------+--------------+------------+ 136 | |             0 |          264 | 000134.sst | 137 | |             4 |          265 | 000136.sst | 138 | +---------------+--------------+------------+ 139 | ``` 140 | 141 | * 主键索引:保存在 000134.sst 142 | 143 | 144 | ![](assets/1599739636-7588a070837408a85cc6c06a269cd5d7.png) 145 | 146 | ```java 147 | '0000010880000001' seq:108, type:1 => 0001000000 6120202020202020 148 | ``` 149 | 150 | 1. key:0000010880000001 151 | 1. index\_id:264  -> 0x108 -> 0x00 0x00 0x01 0x08 152 | 2. M(pk):1 -> 0x80 0x00 0x00 0x01,符号位翻转 153 | 3. seq:108 154 | 4. type:1 -> PUT 155 | 2. value:00010000006120202020202020 156 | 1. NULL-bitmap:0x00 ,每一位对应一列,a\\b 两列均非空 -> 0x00 157 | 2. column a:0x01 0x00 0x00 0x00 158 | 1. 数字1 大端表示形式 159 | 3. column b:0x61 0x20 0x20 0x20 0x20 0x20 0x20 0x20 160 | 1. 'a' -> 0x61 161 | 2. 补7个空格 -> 0x20 162 | 163 | 164 | 165 | * 辅助索引:保存在 000136.sst 166 | 167 | ![](assets/1599739636-684894f81be8e04ac63db190f8053e79.png) 168 | 169 | ```java 170 | '0000010901612020202020202080000001' seq:109, type:1 => 171 | ``` 172 | 173 | 1. key:0000010901612020202020202080000001 174 | 1. index\_id:265 -> 0x109 -> 0x00 0x00 0x01 0x09 175 | 2. NULL-byte:0x01,标识字段 b 是否可以为NULL 176 | 3. M(b):‘a\[ \]\*7’ -> 0x61  0x20 0x20 0x20 0x20 0x20 0x20 0x20 177 | 4. M(id):0x80 0x00 0x00 0x01 178 | 5. seq\_num:109 179 | 6. type:1 -> PUT 180 | 2. value:NULL 181 | 1. pack\_info:无需额外的逆转化信息,该字段为 NULL 182 | 183 | ## 3.5 数据分布 184 | 185 | ![](assets/1599739636-013e3948422f51f8b8bbb9ad105b080b.png) 186 | 187 | # 4 源码分析 188 | 189 | * 操作类型,例如 delete 操作的 type 为 kTypeDeletion = 0x0 190 | 191 | ```java 192 | // dbformat.h 定义的操作类型 193 | // Value types encoded as the last component of internal keys. 194 | // DO NOT CHANGE THESE ENUM VALUES: they are embedded in the on-disk 195 | // data structures. 196 | // The highest bit of the value type needs to be reserved to SST tables 197 | // for them to do more flexible encoding. 198 | enum ValueType : unsigned char { 199 |   kTypeDeletion = 0x0, 200 |   kTypeValue = 0x1, 201 |   kTypeMerge = 0x2, 202 |   kTypeLogData = 0x3,               // WAL only. 203 |   kTypeColumnFamilyDeletion = 0x4,  // WAL only. 204 |   kTypeColumnFamilyValue = 0x5,     // WAL only. 205 |   kTypeColumnFamilyMerge = 0x6,     // WAL only. 206 |   kTypeSingleDeletion = 0x7, 207 |   kTypeColumnFamilySingleDeletion = 0x8,  // WAL only. 208 |   kTypeBeginPrepareXID = 0x9,             // WAL only. 209 |   kTypeEndPrepareXID = 0xA,               // WAL only. 210 |   kTypeCommitXID = 0xB,                   // WAL only. 211 |   kTypeRollbackXID = 0xC,                 // WAL only. 212 |   kTypeNoop = 0xD,                        // WAL only. 213 |   kTypeColumnFamilyRangeDeletion = 0xE,   // WAL only. 214 |   kTypeRangeDeletion = 0xF,               // meta block 215 |   kTypeColumnFamilyBlobIndex = 0x10,      // Blob DB only 216 |   kTypeBlobIndex = 0x11,                  // Blob DB only 217 |   // When the prepared record is also persisted in db, we use a different 218 |   // record. This is to ensure that the WAL that is generated by a WritePolicy 219 |   // is not mistakenly read by another, which would result into data 220 |   // inconsistency. 221 |   kTypeBeginPersistedPrepareXID = 0x12,  // WAL only. 222 |   kMaxValue = 0x7F                       // Not used for storing records. 223 | }; 224 | ``` 225 | 226 | * Internal Key 的比较: 227 | * user key 升序比较 228 | * sequence number 降序比较 229 | * 在 memtable 中排序时,全局递增、局部递减,目的是保证 sequence number 大的数据(最新的数据)靠前存放,提高查找效率 230 | * 因此,sequence number 是直接存放的,没有进行亦或等转化 231 | * ```plain 232 | dbformat.cc:111,就是想 memtable 中插入时,确定插入位置的 compare 方法 233 | ``` 234 | 235 | 236 | ```java 237 | int InternalKeyComparator::Compare(const ParsedInternalKey& a, 238 |                                    const ParsedInternalKey& b) const { 239 |   // Order by: 240 |   //    increasing user key (according to user-supplied comparator) 241 |   //    decreasing sequence number 242 |   //    decreasing type (though sequence# should be enough to disambiguate) 243 |   int r = user_comparator_->Compare(a.user_key, b.user_key); 244 |   PERF_COUNTER_ADD(user_key_comparison_count, 1); 245 |   if (r == 0) { 246 |     if (a.sequence > b.sequence) { 247 |       r = -1; 248 |     } else if (a.sequence < b.sequence) { 249 |       r = +1; 250 |     } else if (a.type > b.type) { 251 |       r = -1; 252 |     } else if (a.type < b.type) { 253 |       r = +1; 254 |     } 255 |   } 256 |   return r; 257 | } 258 | ``` 259 | 260 | * write batch 中的 rep\_ 成员就是需要写入到 memtable 中数据,其中保存了 sequence number、count、data,其具体结构如下 261 | * write\_batch.cc:10 注释 262 | 263 | ```java 264 | // WriteBatch::rep_ := 265 | //    sequence: fixed64 266 | //    count: fixed32 267 | //    data: record[count] 268 | // record := 269 | //    kTypeValue varstring varstring 270 | //    kTypeDeletion varstring 271 | //    kTypeSingleDeletion varstring 272 | //    kTypeRangeDeletion varstring varstring 273 | //    kTypeMerge varstring varstring 274 | //    kTypeColumnFamilyValue varint32 varstring varstring 275 | //    kTypeColumnFamilyDeletion varint32 varstring 276 | //    kTypeColumnFamilySingleDeletion varint32 varstring 277 | //    kTypeColumnFamilyRangeDeletion varint32 varstring varstring 278 | //    kTypeColumnFamilyMerge varint32 varstring varstring 279 | //    kTypeBeginPrepareXID varstring 280 | //    kTypeEndPrepareXID 281 | //    kTypeCommitXID varstring 282 | //    kTypeRollbackXID varstring 283 | //    kTypeBeginPersistedPrepareXID varstring 284 | //    kTypeNoop 285 | // varstring := 286 | //    len: varint32 287 | //    data: uint8[len] 288 | ``` 289 | 290 | # 5 会议总结 291 | 292 | **1 CF 是否有个数限制?** 293 | 294 | **答:**RocksDB 使用 map 来管理 CF 的信息,没有对 CF 的个数进行限制,但是,每个 CF 有独立的memtabl、SST 等数据文件,因此,物理上限制了 CF 的个数。 295 | 296 | **2 latin1 是否支持中文?** 297 | 298 | **答:**支持 299 | 300 | ```java 301 | mysql> show create table t2\G                         302 | *************************** 1. row *************************** 303 |        Table: t2 304 | Create Table: CREATE TABLE `t2` ( 305 |   `name` varchar(255) COLLATE latin1_bin NOT NULL DEFAULT '', 306 |   `age` int(11) DEFAULT NULL, 307 |   `address` varchar(255) COLLATE latin1_bin DEFAULT NULL, 308 |   PRIMARY KEY (`name`), 309 |   KEY `idx_addr` (`address`) COMMENT 'cf_b' 310 | ) ENGINE=ROCKSDB DEFAULT CHARSET=latin1 COLLATE=latin1_bin 311 | 1 row in set (0.00 sec) 312 | mysql> select * from t2;     313 | +-----------+------+---------+ 314 | | name      | age  | address | 315 | +-----------+------+---------+ 316 | | 刘昭毅 |   20 | 北京  | 317 | +-----------+------+---------+ 318 | 1 row in set (0.00 sec) 319 | ``` 320 | 321 | **3 MyRocks 列的元信息如何管理?** 322 | 323 | 与 innodb 保持一致,使用了 table 类,table 对象的 fileds 保存了列的元信息,本质上是使用 frm 保存元信息。 324 | 325 | **4 为什么二级索引记录的 key 中要包含主键,而不是将主键放到 value 中?** 326 | 327 | **5 主键中包含 varchar 类型的列时,segment\_size=5 时,‘abcd’ 与 ‘abcd    ’经过转化是否相同,若相同,那么读取时怎样知道字符串中空格的个数?** 328 | 329 | **答:**两个字符串相同,末尾的空格会被去掉,查询出来的数据末尾没有空格。 330 | 331 | 若 ‘abcd’ 与 ‘abcd    ’ 同时作为唯一主键,会报主键冲突。 332 | -------------------------------------------------------------------------------- /12.Record_Format/enum_ValueType.md: -------------------------------------------------------------------------------- 1 | #1.enum ValueType 2 | 3 | ```cpp 4 | // dbformat.h 定义的操作类型 5 | // Value types encoded as the last component of internal keys. 6 | // DO NOT CHANGE THESE ENUM VALUES: they are embedded in the on-disk 7 | // data structures. 8 | // The highest bit of the value type needs to be reserved to SST tables 9 | // for them to do more flexible encoding. 10 | enum ValueType : unsigned char { 11 | kTypeDeletion = 0x0, 12 | kTypeValue = 0x1, 13 | kTypeMerge = 0x2, 14 | kTypeLogData = 0x3, // WAL only. 15 | kTypeColumnFamilyDeletion = 0x4, // WAL only. 16 | kTypeColumnFamilyValue = 0x5, // WAL only. 17 | kTypeColumnFamilyMerge = 0x6, // WAL only. 18 | kTypeSingleDeletion = 0x7, 19 | kTypeColumnFamilySingleDeletion = 0x8, // WAL only. 20 | kTypeBeginPrepareXID = 0x9, // WAL only. 21 | kTypeEndPrepareXID = 0xA, // WAL only. 22 | kTypeCommitXID = 0xB, // WAL only. 23 | kTypeRollbackXID = 0xC, // WAL only. 24 | kTypeNoop = 0xD, // WAL only. 25 | kTypeColumnFamilyRangeDeletion = 0xE, // WAL only. 26 | kTypeRangeDeletion = 0xF, // meta block 27 | kTypeColumnFamilyBlobIndex = 0x10, // Blob DB only 28 | kTypeBlobIndex = 0x11, // Blob DB only 29 | // When the prepared record is also persisted in db, we use a different 30 | // record. This is to ensure that the WAL that is generated by a WritePolicy 31 | // is not mistakenly read by another, which would result into data 32 | // inconsistency. 33 | kTypeBeginPersistedPrepareXID = 0x12, // WAL only. 34 | kMaxValue = 0x7F // Not used for storing records. 35 | }; 36 | ``` -------------------------------------------------------------------------------- /13.Backup_Restore/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/13.Backup_Restore/1.png -------------------------------------------------------------------------------- /13.Backup_Restore/Backup_Restore.md: -------------------------------------------------------------------------------- 1 | #1.Backup and Restore 2 | 3 |  There are two distinct representations of backup engines: (1) BackupEngine for creating new backups, and (2) BackupEngineReadOnly for restoring from backup. Either one can be used to get info about backups. 4 | 5 | ![](1.png) 6 | 7 | 8 | When you call BackupEngine::CreateNewBackup(): 9 | 1. Disable file deletions 10 | 2. Get live files (includes table files, current, options and manifest file). 11 | 3. Copy live files to the backup directory. Since table files are immutable and filenames unique, we don‘t copy a table file that is already present in the backup directory. For example, if there is a file 00050.sst already backed up and GetLiveFiles() returns 00050.sst, we will not copy that file to the backup directory. If flush_before_backup was set to false, we also need to copy log files to the backup directory. We call GetSortedWalFiles() and copy all live files to the backup directory. 12 | 4. Re-enable file deletions 13 | 14 | 15 | #2.Create Snapshot 16 | 17 | 创建快照过程: 18 | 1.禁止SST文件的删除操作,创建空目录 19 | 2. Memtable刷盘 20 | 3. 在备份目录下为每个SST文件的创建硬链接 21 | 4. copy MANIFEST和OPTIONS文件到备份目录下 22 | 5. 备份WAL文件,最后一个WAL文件通过copy方式,其他WAL文件通过硬链接备份到备份目录 23 | 6. 允许SST文件的删除操作 24 | 25 | #3.Backup 26 | 27 | 备份过程: 28 | 1. 用SET GLOBAL rocksdb_create_checkpoint 建立快照 29 | 2. 依次备份快照中的文件,先备份SST文件, 再备份WAL,MANIFEST和OPTIONS文件。备份SST过程会比较长,如果超过checkpoint_interval,SST文件还没有备份完,就会清理当前快照,返回步骤1重新开始。 30 | 3. 步骤1、2完成后,rocksdb相关的文件备份完成,清理最后一次checkpoint快照文件。 31 | 4. 开始备份mysql其他文件。 32 | -------------------------------------------------------------------------------- /13.Backup_Restore/backupRestore.txt: -------------------------------------------------------------------------------- 1 | 1.CreateNewBackup 2 | CreateNewBackup 3 | --BackupEngineImpl::CreateNewBackupWithMetadata 4 | 5 | When you call BackupEngine::CreateNewBackup(): 6 | Disable file deletions 7 | Get live files (includes table files, current, options and manifest file). 8 | Copy live files to the backup directory. Since table files are immutable and filenames unique, we don‘t copy a table file that is already present in the backup directory. For example, if there is a file 00050.sst already backed up and GetLiveFiles() returns 00050.sst, we will not copy that file to the backup directory. If flush_before_backup was set to false, we also need to copy log files to the backup directory. We call GetSortedWalFiles() and copy all live files to the backup directory. 9 | Re-enable file deletions 10 | 11 | 12 | 2.There are two distinct representations of backup engines: 13 | (1) BackupEngine for creating new backups, and 14 | (2) BackupEngineReadOnly for restoring from backup. Either one can be used to get info about backups. 15 | 16 | 3.创建快照过程: 17 | |1. 禁止SST文件的删除操作,创建空目录 18 | |2. Memtable刷盘 19 | |3. 在备份目录下为每个SST文件的创建硬链接 20 | |4. copy MANIFEST和OPTIONS文件到备份目录下 21 | |5. 备份WAL文件,最后一个WAL文件通过copy方式,其他WAL文件通过硬链接备份到备份目录 22 | |6. 允许SST文件的删除操作 23 | 24 | 25 | 4.备份过程: 26 | |1.用SET GLOBAL rocksdb_create_checkpoint 建立快照 27 | |2.依次备份快照中的文件,先备份SST文件, 再备份WAL,MANIFEST和OPTIONS文件。备份SST过程会比较长,如果超过checkpoint_interval,SST文件还没有备份完,就会清理当前快照,返回步骤1重新开始。 28 | |3.步骤1、2完成后,rocksdb相关的文件备份完成,清理最后一次checkpoint快照文件。 29 | |4.开始备份mysql其他文件。 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /14.Compress/Compress/assets/1599812462-1fb3efa259235bef7af7e38e61f78027.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/14.Compress/Compress/assets/1599812462-1fb3efa259235bef7af7e38e61f78027.jpg -------------------------------------------------------------------------------- /14.Compress/Compress/assets/1599812462-6db59fc68e8468498b87fb5ab0acc9b8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/14.Compress/Compress/assets/1599812462-6db59fc68e8468498b87fb5ab0acc9b8.jpg -------------------------------------------------------------------------------- /14.Compress/Compress/assets/1599812462-ab21f743e6d0ad1af1fcee59ec5daabf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/14.Compress/Compress/assets/1599812462-ab21f743e6d0ad1af1fcee59ec5daabf.png -------------------------------------------------------------------------------- /14.Compress/Compress/assets/1599812462-f00a283b92d885aa4be05f5e9828756a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/14.Compress/Compress/assets/1599812462-f00a283b92d885aa4be05f5e9828756a.png -------------------------------------------------------------------------------- /14.Compress/Compress/assets/1599812462-f329489c1e9b323316cbe0edc589b04e.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/14.Compress/Compress/assets/1599812462-f329489c1e9b323316cbe0edc589b04e.png -------------------------------------------------------------------------------- /14.Compress/Compress/index.md: -------------------------------------------------------------------------------- 1 | 2 | # [RocksDB 原理介绍:压缩功能优化] 3 | 4 | 5 | # RocksDB 压缩功能优化 6 | 7 | ## 不同压缩策略对数据库大小的影响 8 | 9 | MyRocks和RocksDB兼容多重压缩策略,且可以根据sst文件所处的不同level层指定不同压缩算法。由于数据最终回compact到最后一层,即bottom层(默认L6),我们只分析讨论对bottom层采用不同压缩算发,以及不同压缩级别对数据库大小的影响。 10 | 11 | 使用业务模拟数据进行测试,多个表dump出后的文本文件约10G。datablock为16K,且将所有数据compact到最后一层。 12 | ![](assets/1599812462-f329489c1e9b323316cbe0edc589b04e.png) 13 | 14 | ![](assets/1599812462-ab21f743e6d0ad1af1fcee59ec5daabf.png) 15 | 16 | 17 | 18 | 从压缩率上来看,zstd仅仅是小胜zlib。但在更完善的测试中,zstd在读写性能和资源站用上都优于zlib。后续测试将在zstd-6下进行。 19 | 20 | ## Zstandard 原理简介 21 | 22 | Zstandard(简称zstd) 使用的 LZ77,同时结合熵编码法的有限状态熵(tANS) 23 | 其中,LZ77系列的压缩算法编码器和解码器都必须保存一定数量的最近的数据,如最近2 KB、4 KB或者32 KB的数据。保存这些数据的结构叫作滑动窗口,因为这样所以LZ77有时也称作滑动窗口压缩。 24 | 25 | MyRocks和RocksDB都可以使用Zstandard的作为数据最小单位(block)落盘前的压缩算法,可以整体上取得优于30%的压缩效果,同时压缩和解压速率远超Zlib。 26 | 27 | ## Zstandard 创建额外字典优化压缩 28 | 29 | 前面提到zstd压缩数据时会使用滑动窗口中保存的数据,即当前压缩对象之前的一定数量的字符(前序窗口)。因此对短字符或批量的小文件(几KB或更小)压缩时,压缩效果可能达不到逾期。为了解决这个功能zstd提供了创建字典接口。即在对大量的短字符加密前,先进行数据采样组成字典,后续加密时,将使用字典进行编码比较。 30 | 31 | 有了字典后,没有足够组成前序窗口字符串的小文件也能取得较好的压缩效果。这里字典的质量就非常重要了,字典中的抽样字符数据应该具有足够的代表性,这样在后续文件的压缩中,带压缩字符串在和字典中数据比较时才能获得较大相似性。因此zstd的高版本中加入了对字典的训练功能(在zstd的v0.8版本以后有稳定集成,v1.3以后字典和字典训练得到进一步优化)。在生成最终字典前可以更大量的采样,然后通过训练接口进行采样数据的典型性过滤和优化最终形成字典。一般字典的大小不超过100k,但是训练前的采样量可以是其100倍大小。 32 | 根据官方的测试,对批量小文件(1k左右)使用字典压缩,不仅压缩率有了较大提升,压缩和解压时间也显著缩短。 33 | 34 | ## MyRocks/RocksDB ZSTD与字典的集成 35 | 36 | MyRocks/RocksDB底层存储默认使用BlockBasedTable格式,是支持zstd压缩以及字典等接口的。MariaDB也可以通过对适配部分的修改实现接口调用。根据Facebook的参数调配建议,一般只将zstd用在最后一层(L6)的压缩。另外MyRocks/RocksDB底层sst在压缩时是以一个datablock为单位压缩存取的(默认4K),使用字典有可能取得更好的压缩效果。 37 | 38 | ZSTD的压缩发生在sst写过程。sst是基于文件级的操作,由TableBuilder等对象完成。单次的put写入并不一定会激发sst写文件。真正的调用来自backgroud flush 和backgroud compact(recover和repair的时候也会)。即将immutable memtable刷到L0层sst,以及sst文件的compact操作,会产生新的sst,触发sst写操作。 39 | 由于L0层一般不采用压缩,所以ZSTD的压缩只发生在compact过程中。 40 | 41 | ![](assets/1599812462-6db59fc68e8468498b87fb5ab0acc9b8.jpg) 42 | 43 | 其中的 ProcessKeyValueCompaction的 biulder->Add(BlockBasedTable::Add)的后续调用中,会在数据落盘前会调用CompressBlock,将每块rocksdb\_block\_size大小的datablock数据压缩。 44 | 字典的创建同时在ProcessKeyValueCompaction中进行。在将迭代器返回的归并结果写入第一个新sst文件时,会通过采样缓存部分key-value键值对拼接到dict\_sample\_data(string类型),如果开启了字典训练功能,在第一个sst文件写完时,会将sample传入zstd的trainner接口训练更精炼且具有典型性的字典,否则直接拿sample作为字典。 45 | 46 | 在一次compact中,对于第一个sst文件,在其写完前,字典并没有完成,所以它在压缩落盘的时候并没有使用字典。第一个文件完成后,生成了字典,后续sst文件使用该字典优化压缩。 47 | ![](assets/1599812462-1fb3efa259235bef7af7e38e61f78027.jpg) 48 | 49 | 使用业务模拟数据进行测试,多个表dump出后的文本文件约10G。datablock为16K,且将所有数据compact到最后一层,且使用zstd-6进行含字典压缩,查看不同字典和训练集大小对最终压缩后数据库大小的影响。 50 | 51 | | 52 | datablock size 53 | 54 | | 55 | 56 | database size 57 | 58 | | 59 | | --- | --- | 60 | | NO dictionary | 4.48G | 61 | | 1600K tainning data 16K dict | 4.47G | 62 | | 6400K tainning data 64K dict | 4.48G | 63 | 64 | 使用sysbench生成数据,三张表,每张表1G,共3G数据。datablock设为4K。 65 | 66 | | 67 | datablock size 68 | 69 | | 70 | 71 | database size 72 | 73 | | 74 | | --- | --- | 75 | | NO dictionary | 1.8G | 76 | | skip data tainning,16K dict | 1.8G | 77 | | 16K tainning data 16K dict | 1.8G | 78 | | 32K tainning data 32K dict | 1.85G | 79 | | 64K tainning data 64K dict | 1.92G | 80 | | NO COMPRESSION | 8.69G | 81 | 82 | 我们将NO COMPRESSION情况下的sst文件导出,使用zstd压缩的api接口全局压缩,无论是否使用字典,压缩后文件的大小都在1.8G左右。 83 | 84 | 从数据来看,在当前配置下,使用字典对进一步提高压缩率效果不大。 85 | 后期线上环境使用的datablock不会低于16K,对字典的需求下降。且因为设置 86 | target\_file\_size\_multiplier=2,sst层数约大,单块的容量越大(L6层单块sst将接近1G)。字典的典型代表性将因为这些参数条件受到挑战。 87 | 88 | ## 影响压缩的其他因素 89 | 90 | Myrocks/Rocksdb 可以通过rocksdb\_xxx\_cf\_options配置中的compression\_per\_level、bottommost\_compression、compression\_opts等项对columnfamily名为xxx的数据进行压缩参数调配,如`compression_per_level=kNoCompression;bottommost_compression=kZSTD;ccompression_opts=-14:6:0:16K:1600K`。ccompression\_opts这个参数可同时作用于zlib、lz4、zstd。ccompression\_opts最多可以有4个值,其中第二个和后两个值对zstd有效,分别代表压缩机别(6,1-22,越大压缩效果越好,压缩时间和资源消耗也越多),字典最大size(16KB),字典训练前最大采样量(1600KB)。 91 | 另外zstd是以sst中的datablock为单位进行压缩的,datablock的大小(rocksdb\_block\_size)直接影响压缩性能。适当调大rocksdb\_block\_size,可以取得更好压缩率。但因为BlockBasedTable读取单条key-value也需要读取和解压整块datablock,所以调大rocksdb\_block\_size会影响读性能(IO/CPU/内存)。 92 | 93 | 使用业务模拟数据进行测试,多个表dump出后的文本文件约10G。设置不同的datablock查看最终将所有数据compact到最后一层,且使用zstd-6进行无字典压缩的效果。 94 | 95 | | 96 | datablock size 97 | 98 | | 99 | 100 | database size 101 | 102 | | 103 | | --- | --- | 104 | | 4K | 5.22G | 105 | | 16K | 4.48G | 106 | | 64K | 4.15G | 107 | | 256K | 3.9G | 108 | | 4M | 3.54G | 109 | 110 | 后期将关注更大规模数据下,增大datablock对压缩率以及系统资源占用的综合影响。 111 | 112 | ## 其他压缩思路 113 | 114 | terakDB提出了全新的压缩思路,这部分内容在另一篇wiki《RockDB/Terark SST读写与压缩技术》中有更多分析。其核心除了将key和value分开压缩保存外,需要对数据进行两次扫描,第一次进行属性信息统计,生成压缩策略;第二次才进行真正压缩,是一种Grammar-Based Compressor。这种压缩算法原则上能更好根据数据特征进行压缩,但由于最佳语法(Optimal Grammar)问题被证明是NP-Hard,所以实现方法将对压缩效果有较大影响。 115 | 116 | -------------------------------------------------------------------------------- /15.Lock/notes_Lock_Transaction/assets/1601900301-07954a83df2ed5dc425295a5c1877caa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/15.Lock/notes_Lock_Transaction/assets/1601900301-07954a83df2ed5dc425295a5c1877caa.png -------------------------------------------------------------------------------- /15.Lock/notes_Lock_Transaction/assets/1601900301-0ed43024a0919cc9fa07884732981e9f.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/15.Lock/notes_Lock_Transaction/assets/1601900301-0ed43024a0919cc9fa07884732981e9f.png -------------------------------------------------------------------------------- /15.Lock/notes_Lock_Transaction/assets/1601900301-1b23341a0ea959e27f487265ba5d5708.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/15.Lock/notes_Lock_Transaction/assets/1601900301-1b23341a0ea959e27f487265ba5d5708.png -------------------------------------------------------------------------------- /15.Lock/notes_Lock_Transaction/assets/1601900301-2f9836f391212bcdbcc26adc40f65e36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/15.Lock/notes_Lock_Transaction/assets/1601900301-2f9836f391212bcdbcc26adc40f65e36.png -------------------------------------------------------------------------------- /15.Lock/notes_Lock_Transaction/assets/1601900301-32d93a478ad3e9dd7372f15fd88763ed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/15.Lock/notes_Lock_Transaction/assets/1601900301-32d93a478ad3e9dd7372f15fd88763ed.png -------------------------------------------------------------------------------- /15.Lock/notes_Lock_Transaction/assets/1601900301-678504024bb2f7a16179588202d904d0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/15.Lock/notes_Lock_Transaction/assets/1601900301-678504024bb2f7a16179588202d904d0.png -------------------------------------------------------------------------------- /15.Lock/notes_Lock_Transaction/assets/1601900301-6d0552a02407e8e35223501ba3d6345e.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/15.Lock/notes_Lock_Transaction/assets/1601900301-6d0552a02407e8e35223501ba3d6345e.png -------------------------------------------------------------------------------- /15.Lock/notes_Lock_Transaction/assets/1601900301-7d0b001256239b0414d1db2d339a39e6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/15.Lock/notes_Lock_Transaction/assets/1601900301-7d0b001256239b0414d1db2d339a39e6.png -------------------------------------------------------------------------------- /15.Lock/notes_Lock_Transaction/assets/1601900301-ab2d832e26f095bc9f3a2f2d166f6f26.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/15.Lock/notes_Lock_Transaction/assets/1601900301-ab2d832e26f095bc9f3a2f2d166f6f26.png -------------------------------------------------------------------------------- /15.Lock/notes_Lock_Transaction/assets/1601900301-bdc36cdd5f142f117c08227fd7fa7e7e.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/15.Lock/notes_Lock_Transaction/assets/1601900301-bdc36cdd5f142f117c08227fd7fa7e7e.png -------------------------------------------------------------------------------- /15.Lock/notes_Lock_Transaction/assets/1601900301-f00a283b92d885aa4be05f5e9828756a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/15.Lock/notes_Lock_Transaction/assets/1601900301-f00a283b92d885aa4be05f5e9828756a.png -------------------------------------------------------------------------------- /15.Lock/notes_Lock_Transaction/assets/1601900301-fdc1ca0cc049d7b1250ec0503aba58f2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/15.Lock/notes_Lock_Transaction/assets/1601900301-fdc1ca0cc049d7b1250ec0503aba58f2.png -------------------------------------------------------------------------------- /15.Lock/notes_Lock_Transaction/index.md: -------------------------------------------------------------------------------- 1 | # 2 锁 2 | 3 | ## 2.1 InnoBD 中的锁 4 | 5 | ### 2.1.1 锁的类型 6 | 7 | 按照锁的粒度,可以分为 【**表锁**】 和 【**行锁**】。按照锁的类型,可以分为 【**共享锁**】和【**排它锁**】。 8 | 9 | **表锁**:对整个表加锁。 10 | 11 | **行锁**:对一行数据加锁。 12 | 13 | **共享锁(S)**:可多个事务共享读取,阻止其他事务的写操作。显式加锁方式 SELECT ... LOCK IN SHARE MODE。表示对 select 语句涉及的范围添加共享锁,允许其它事务读取该范围内的数据,但不允许其它事务修改。 14 | 15 | **排他锁(X)**:单个事务独占,阻止其他事务的排他锁和共享锁。显式加锁方式 SELECT ... FOR UPDATE。FOR UPDATE 是排它锁。 16 | 17 | **意向锁**:提高事务加表锁时的效率,意向锁也分为【**意向共享锁**】和【意向排它锁】。 18 | 19 | 例如:在 T1 加行锁时,数据库会默认为全表添加一个意向锁。 20 | 21 | 此时,T2 若要加表锁: 22 | 23 | 1. 会先判断是否有表锁; 24 | 2. 再判断是否有意向锁。 25 | 26 | 有意向锁表示表中的某些行有锁,此时 T2 会等待行锁释放。若没有意向锁,则需要遍历整个表来判断是否有行锁,效率很低。 27 | 28 | * * * 29 | 30 | ### 2.1.2 锁的范围 31 | 32 | 1. **Record lock**:锁住的是索引,而非记录本身 33 | 2. **Gap lock**:在索引记录之间的间隙中加锁,并不包括索引记录本身。 34 | 1. 幻读针对另一个 session 的 insert 操作,不可重复读则针对于另一个 session 的 delete/update 操作。MySQL 中,默认为 RR,select 是读取不到另一个 session 的 insert 的,但是 update/delete 却能更新另一个 session 的 insert,出现了幻读,但是满足定义。 35 | 2. 根本原因是 MySQL 的 update/delete 会生成一个新的 lsn,通过 mvcc 判断,大于另一个 session 的事务的 lsn,满足可见性要求,因此出现幻读。 36 | 3. gap lock 的目的是实现 RR 的隔离级别,解决不可重复读的问题。Innodb 的实现方式,一定程度上避免了幻读,但无法彻底解决。update 能够更新其他 session 的写入。 37 | ![](assets/1601900301-6d0552a02407e8e35223501ba3d6345e.png) 38 | 3. Next-key lock:行锁加间隙锁,即 Record lock + Gap lock 39 | 40 | 41 | 具体情况见下表: 42 | 43 | ![](assets/1601900301-fdc1ca0cc049d7b1250ec0503aba58f2.png) 44 | 45 | ![](assets/1601900301-0ed43024a0919cc9fa07884732981e9f.png) 46 | 47 | ![](assets/1601900301-bdc36cdd5f142f117c08227fd7fa7e7e.png) 48 | 49 | 50 | 51 |

t4 无主键,id2 辅助索引

t5 id 为主键,id2 辅助索引

select for update id=5

任何位置都无法插入

升级为表锁

优化为 record lock,任何位置可以插入

select for update id2=5

id2=5 加 record lock

id2=4/6 无法插入

索引记录前后加 gap lock

id2=5 加 record lock

id2=4/6 无法插入

索引记录前后加 gap lock

52 | 53 | 54 | 55 | ## 2.2 RockDB 的锁 56 | 57 | ### 2.2.1 支持的锁 58 | 59 | 支持行锁中的 排它锁 select for update 和 共享锁 select lock in share mode。所有的锁信息都保存在内存中。每个 CF 都维护了一个锁的 map。 60 | 61 | 不支持 gap lock: 62 | 63 | * 会对唯一键(包括主键、唯一索引)加排它锁,避免重复插入、插入的数据被其他事务修改。 64 | * 但是其他事务可以随意在间隙中插入数据。 65 | 66 | InnoDB 中写操作会加 Next-key lock,阻塞一定范围内其他事务的写操作。而 RocksDB 不支持 gap lock,则会导致下图中的问题。 67 | 68 | ![](assets/1601900301-ab2d832e26f095bc9f3a2f2d166f6f26.png) 69 | 70 | * 上图的两条 SQL 可能的执行顺序如下: 71 | 72 | ![](assets/1601900301-07954a83df2ed5dc425295a5c1877caa.png) 73 | 74 | 75 | 76 | 因此,必须使用 RBR(Row Based Binary Logging),即行模式的复制,binlog 中记录的是一行数据的状态。 77 | 78 | 【**GAP Lock 缺失的问题**】 79 | 80 | 没有 gap lock,在事务并发执行时,不会阻塞写操作,因而会更容易触发死锁。 81 | 82 | 在之前的 RocksDB 中,T1更新1000W 行,当其更新到第900W 行时,T2 更新了最后100W 行,此时会产生死锁。 83 | 84 | InnoDB 中,会申请 1000W 行的写锁,因此会阻塞其他事务更新相关的行,不会出现死锁。 85 | 86 | ### 2.2.2 加锁行为分析 87 | 88 | * MyRocks只会对主键和唯一索引加锁,普通索引不会加锁。 89 | 90 | * 只有插入或更新了唯一索引时,才会对唯一索引加锁,对唯一索引加锁的目的是为了保证唯一性。 91 | 92 | * 按主键锁定查找不存在的行时,会对不存在的行主键加X锁。 93 | 94 | * 按二级索引查找时,只会对主键加锁,不会对二级锁引加锁。 95 | 96 | * S锁只应用于SELECT … IN SHARE MODE语句。 97 | 98 | 99 | * * * 100 | 101 | # 3 事务 102 | 103 | ## 3.1 通用功能 104 | 105 | RocksDB 支持 beign / commit / rollback,不支持 savepoint。 106 | 107 | savepoint 创建回滚点:语法 savepoint a1; 创建了名为 a1 的回滚点 108 | 109 | ## 3.2 特性限制 110 | 111 | ### 3.2.1 一致性快照 112 | 113 | 创建一致性快照的时间点,可以分为 start transaction 时创建 / 执行第一条 SQL 时创建 114 | 115 | * PostgreSQL:在 begin 时创建快照 116 | * RocksDB:在 begin 后,执行第一条语句时创建一致性快照,创建时间晚于 PG,但创建快照后的行为与 PG 相同,即 select for update、update 等操作仍然从快照中读取 117 | * InnoDB:在第一条语句执行时创建一致性快照,但 select for update、update 等操作会查询最新数据,而不是从快照中读取 118 | 119 | RocksDB 中通过 sequence number 来实现 mvcc 实现。。RocksDB 的主键由 User key (string) | sequence number (7 bytes) 两部分组成,通过 sequence number 来实现 MVCC。 120 | 121 | * User key 为用户指定的主键,同样 User key 的记录可能有多条。 122 | * sequence number 是每条记录的序列号,每条记录都不相同。 123 | 124 | ### 3.2.2 MVCC 125 | 126 | RocksDB 生成快照时,获取当前记录的 sequence number 记作 Sn,事务查询时会比对 sequence number,只能看到 S <= Sn 的记录。 127 | 128 | ### 3.2.3 隔离级别 129 | 130 | * InnoDB:事务中直接 select,会读取快照,select for update、update 等操作会直接读取当前的最新数据,这样会导致幻读,在ANSI 标准中,RR 可以出现幻读,因此,InnoDB 的实现没有问题 131 | * RocksDB:支持 RC + RR 两种隔离级别 132 | * RC:每执行一条语句都会创建一个快照,保证能读取到最新提交的数据 133 | * RR:begin 后执行第一条语句时创建快照,与 MySQL 创建 read view 的时机相同 134 | 135 | ### 3.2.4 XA 136 | 137 | XA是X/Open DTP组织(X/Open DTP group)基于两阶段提交协议定义的一组中间件与数据库之间的接口规范,用来完成一个分布式事务。 138 | 139 | X/Open DTP模型包括:应用程序(AP)、事务管理器(TM)、资源管理器(RM)、通信资源管理器(CRM)四部分。 140 | 141 | 事务管理器:由proxy 充当; 142 | 143 | 资源管理器:指的是数据库; 144 | 145 | 通信资源管理器:指的是消息中间件。 146 | 147 | ![](assets/1601900301-1b23341a0ea959e27f487265ba5d5708.png) 148 | 149 | MySQL 中分为【内部 XA(隐式 XA)】 与 【外部 XA(显式 XA)】 150 | 151 | 【外部 XA】:用于实现跨 MySQL 实例的分布式事务 152 | 153 | 【内部 XA】:用于 engine & binlog、engine & engine 的两阶段提交 154 | 155 | 旧版本 RocksDB 不支持 XA,因此,在事务提交过程中无法保证 写binlog 与 commit 构成一个分布式事务,master commit 但是未写入 binlog 时发生宕机,已提交事务无法同步到备库,会导致从库的数据与主库不一致。因此,需要开启 semi-sync(半同步),即多个从库中有任何一个返回同步成功(写入 relay log)后,master 才能 commit,避免因 XA 缺失造成主从数据不一致。 156 | 157 | ### 3.2.5 RocksDB XA 158 | 159 | 新版的 RocksDB 已经支持了 XA & 2PC。请参考:[https://github.com/facebook/rocksdb/wiki/Two-Phase-Commit-Implementation](https://github.com/facebook/rocksdb/wiki/Two-Phase-Commit-Implementation) 160 | 161 | #### 3.2.5.1 实现 162 | 163 | 之前,RocksDB 的 WriteBatch 操作的构成如下: 164 | 165 | ```java 166 | Sequence(0);NumRecords(3);Put(a,1);Merge(a,1);Delete(a); 167 | ``` 168 | 169 | 引入 XA 后,新增了四个操作: 170 | 171 | 1. Prepare(xid) 172 | 2. EndPrepare() 173 | 3. Commit(xid) 174 | 4. Rollback(xid) 175 | 176 | 支持 XA 后,分布式事务的写操作如下: 177 | 178 | ```java 179 | Sequence(0);NumRecords(6);Prepare(foo);Put(a,b);Put(x,y);EndPrepare();Put(j,k);Commit(foo); 180 | ``` 181 | 182 | Prepare 与 EndPrepare 相当于一对括号,将 ID 为 ‘foo’ 的交易括起来,foo 就是 xid。 183 | 184 | Commit 与 Rollback用来标识 事务 xid 的最终状态。 185 | 186 | #### 3.2.5.2 兼容性 187 | 188 | 需要考虑向下兼容的问题,旧版本的 rocksdb 无法识别2PC 的新增操作类型,将导致无法使用 WAL 恢复数据。RocksDB 可以简单跳过 prepare 等无法识别的标记。 189 | -------------------------------------------------------------------------------- /16.WAL/WAL.txt: -------------------------------------------------------------------------------- 1 | 1 WAL 2 | WAL文件由一堆变长的record组成,而每个record是由kBlockSize(32k)来分组,比如某一个record大于kBlockSize的话,他就会被切分为多个record(通过type来判断)。 3 | 4 | +-----+-----------+--+----+----------+------+-- ... ----+ 5 | File | r0 | r1 |P | r2 | r3 | r4 | | 6 | +-----+-----------+--+----+----------+------+-- ... ----+ 7 | |<--- kBlockSize --->|<-- kBlockSize ------>| 8 | rn = variable size records 9 | P = Padding 10 | 11 | 12 | 【record的格式】如下 13 | 14 | +---------+-----------+-----------+--- ... ---+ 15 | Record |CRC (4B) | Size (2B) | Type (1B) | Payload | 16 | +---------+-----------+-----------+--- ... ---+ 17 | CRC = 32bit hash computed over the payload using CRC 18 | Size = Length of the payload data 19 | Type = Type of record (kZeroType, kFullType, kFirstType, kLastType, kMiddleType ) 20 | The type is used to group a bunch of records together to represent blocks that are larger than kBlockSize 21 | Payload = Byte stream as long as specified by the payload size 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /16.WAL/code_WriteToWAL.md: -------------------------------------------------------------------------------- 1 | #1.WriteToWAL 2 | 3 | ```cpp 4 | DBImpl::WriteToWAL(const WriteThread::WriteGroup& write_group, 5 | --DBImpl::MergeBatch 6 | --DBImpl::WriteToWAL(const WriteBatch& merged_batch, 7 | ----WriteBatchInternal::Contents 8 | ----Writer::AddRecord 9 | ------WritableFileWriter::Append//dest_->Append 10 | --------Writer::EmitPhysicalRecord 11 | ----------WritableFileWriter::Flush// write out the cached data to the OS cache or storage if direct I/O 12 | ------------WriteBuffered(buf_.BufferStart(), buf_.CurrentSize()); 13 | ------------writable_file_->Flush 14 | ----alive_log_files_.back().AddSize(log_entry.size()); 15 | 16 | --log.writer->file()->Sync(immutable_db_options_.use_fsync); 17 | ----WritableFileWriter::Sync 18 | ------WritableFileWriter::Flush 19 | ------WritableFileWriter::SyncInternal 20 | --------if (use_fsync) 21 | ----------PosixWritableFile::Fsync//writable_file_->Fsync(); 22 | ------------fsync 23 | --------else 24 | ----------PosixWritableFile::Sync//ritable_file_->Sync(); 25 | ------------fdatasync 26 | 27 | ``` 28 | 29 | 写日志可以简单分为三个阶段,第一阶段往WritableFileWriter的buf_里面写,第二阶段write到系统缓存,第三阶段是将系统缓存刷盘。 30 | 31 | 刷盘有三种策略, 32 | 33 | 一种是每次提交都刷盘(WriteOptions::sync = true -> need_log_sync=true),WriteToWAL会调用fsync,安全性比较高,但会占用较多IO。 34 | 35 | 一种是配置了参数wal_bytes_per_sync,每写入wal_bytes_per_sync大小的WAL,就将之前的WAL刷磁盘。 36 | 37 | 最后一种,也是默认的是将刷盘交由系统处理,仅在memtable切换、flush等操作触发时再去主动调用刷盘函数。 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /16.WAL/notes_WAL/index.md: -------------------------------------------------------------------------------- 1 | 2 | # 1 WAL 3 | 4 | WAL文件由一堆变长的record组成,而每个record是由kBlockSize(32k)来分组,比如某一个record大于kBlockSize的话,他就会被切分为多个record(通过type来判断)。 5 | 6 | ```java 7 |      +-----+-----------+--+----+----------+------+-- ... ----+ 8 | File | r0  |      r1   |P | r2 |    r3    |  r4  |           | 9 |      +-----+-----------+--+----+----------+------+-- ... ----+ 10 |      |<--- kBlockSize --->|<-- kBlockSize ------>| 11 |  rn = variable size records 12 |  P = Padding 13 | ``` 14 | 15 | 【**record的格式**】如下 16 | 17 | ```java 18 |        +---------+-----------+-----------+--- ... ---+ 19 | Record |CRC (4B) | Size (2B) | Type (1B) | Payload   | 20 |        +---------+-----------+-----------+--- ... ---+ 21 | CRC = 32bit hash computed over the payload using CRC 22 | Size = Length of the payload data 23 | Type = Type of record (kZeroType, kFullType, kFirstType, kLastType, kMiddleType ) 24 |        The type is used to group a bunch of records together to represent blocks that are larger than kBlockSize 25 | Payload = Byte stream as long as specified by the payload size 26 | ``` 27 | -------------------------------------------------------------------------------- /17.WriteBatch/writebatch.txt: -------------------------------------------------------------------------------- 1 | 1.writeBatch使用例子 2 | db::WriteBatch batch; 3 | batch.Put(key1, value); 4 | batch.SingleDelete(key1); 5 | s = db->Write(rocksdb::WriteOptions(), &batch); 6 | 7 | 2.在WriteBatch层是个衔接层,主要工作就是按照不同情况,调用下层接口实现Insert。 8 | 这里面牵扯的不同情况是是否inplace update(在Memtable中原地更新键值一致的元组的值,而不是追加一条新的); 9 | 以及是否需要inplace_callback。另外随着有数据插入,本层会及时更新Memtable布隆过滤器的哈希值。 10 | 11 | WriteBatchInternal::InsertInto 12 | --MemTableInserter inserter(sequence, memtables, flush_scheduler, 13 | ignore_missing_column_families, recovery_log_number, 14 | db, concurrent_memtable_writes, 15 | nullptr /*has_valid_writes*/, seq_per_batch); 16 | --for (auto w : write_group) { 17 | w->status = w->batch->Iterate(&inserter); 18 | --WriteBatch::Iterate 19 | ----ReadRecordFromWriteBatch 20 | ----PutCF 21 | ------PutCFImpl 22 | --------SeekToColumnFamily 23 | --------if (!moptions->inplace_update_support) { 24 | MemTable::Add 25 | --------else if (moptions->inplace_callback == nullptr) 26 | ----------MemTable::Update 27 | ------------iter->Seek(lkey.internal_key(), mem_key.data()) 28 | ------------if (new_size <= prev_size) {//如果找得到,而且写的下,则原地update 29 | --------------EncodeVarint32(const_cast(key_ptr) + key_length, new_size); 30 | ------------else 31 | --------------Add(seq, kTypeValue, key, value); 32 | --------else (moptions->inplace_callback != nullptr) 33 | ----------MemTable::UpdateCallback 34 | ------------iter->Seek(lkey.internal_key(), memkey.data()); 35 | ------------if (iter->Valid()) 36 | --------------if status == UpdateStatus::UPDATED_INPLACE 37 | ----------------moptions_.inplace_callback 38 | ----------------EncodeVarint32 39 | ----------------memcpy(p, prev_buffer, new_prev_size); 40 | --------------if status == UpdateStatus::UPDATED 41 | ----------------Add(seq, kTypeValue, key, Slice(str_value)); 42 | --------MaybeAdvanceSeq(); 43 | --------CheckMemtableFull(); 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /31.Env/MockEnv.txt: -------------------------------------------------------------------------------- 1 | 1.br 2 | br main 3 | br /home/chenhui/rocksdb/env/mock_env.cc:28 4 | 5 | 6 | 2. 7 | Breakpoint 2, rocksdb::MemFile::MemFile (this=0x7ffff4891150, env=0x7ffff4827880, fn=..., _is_lock_file=false) at /home/chenhui/rocksdb/env/mock_env.cc:32 8 | 32 MurmurHash(fn.data(), static_cast(fn.size()), 0))), 9 | (gdb) bt 10 | #0 rocksdb::MemFile::MemFile (this=0x7ffff4891150, env=0x7ffff4827880, fn=..., _is_lock_file=false) at /home/chenhui/rocksdb/env/mock_env.cc:32 11 | #1 0x00007ffff75e4985 in rocksdb::MockEnv::NewWritableFile (this=0x7ffff4827880, fname=..., result=0x7fffffffdbe0, env_options=...) at /home/chenhui/rocksdb/env/mock_env.cc:497 12 | #2 0x000000000044a661 in rocksdb::MockEnvTest_Corrupt_Test::TestBody (this=0x7ffff4815950) at /home/chenhui/rocksdb/env/mock_env_test.cc:33 13 | #3 0x0000000000473b35 in testing::internal::HandleSehExceptionsInMethodIfSupported (object=0x7ffff4815950, method=&virtual testing::Test::TestBody(), location=0x482863 "the test body") 14 | at /home/chenhui/rocksdb/third-party/gtest-1.7.0/fused-src/gtest/gtest-all.cc:3824 15 | #4 0x000000000046e292 in testing::internal::HandleExceptionsInMethodIfSupported (object=0x7ffff4815950, method=&virtual testing::Test::TestBody(), location=0x482863 "the test body") 16 | at /home/chenhui/rocksdb/third-party/gtest-1.7.0/fused-src/gtest/gtest-all.cc:3860 17 | #5 0x00000000004558c5 in testing::Test::Run (this=0x7ffff4815950) at /home/chenhui/rocksdb/third-party/gtest-1.7.0/fused-src/gtest/gtest-all.cc:3897 18 | #6 0x000000000045602a in testing::TestInfo::Run (this=0x7ffff484a180) at /home/chenhui/rocksdb/third-party/gtest-1.7.0/fused-src/gtest/gtest-all.cc:4072 19 | #7 0x000000000045663c in testing::TestCase::Run (this=0x7ffff4830380) at /home/chenhui/rocksdb/third-party/gtest-1.7.0/fused-src/gtest/gtest-all.cc:4190 20 | #8 0x000000000045cc72 in testing::internal::UnitTestImpl::RunAllTests (this=0x7ffff4848800) at /home/chenhui/rocksdb/third-party/gtest-1.7.0/fused-src/gtest/gtest-all.cc:6062 21 | #9 0x00000000004748f1 in testing::internal::HandleSehExceptionsInMethodIfSupported (object=0x7ffff4848800, 22 | method=(bool (testing::internal::UnitTestImpl::*)(testing::internal::UnitTestImpl * const)) 0x45c9f4 , location=0x483138 "auxiliary test code (environments or event listeners)") 23 | at /home/chenhui/rocksdb/third-party/gtest-1.7.0/fused-src/gtest/gtest-all.cc:3824 24 | #10 0x000000000046eff8 in testing::internal::HandleExceptionsInMethodIfSupported (object=0x7ffff4848800, 25 | method=(bool (testing::internal::UnitTestImpl::*)(testing::internal::UnitTestImpl * const)) 0x45c9f4 , location=0x483138 "auxiliary test code (environments or event listeners)") 26 | at /home/chenhui/rocksdb/third-party/gtest-1.7.0/fused-src/gtest/gtest-all.cc:3860 27 | #11 0x000000000045ba1a in testing::UnitTest::Run (this=0x6a0d00 ) at /home/chenhui/rocksdb/third-party/gtest-1.7.0/fused-src/gtest/gtest-all.cc:5683 28 | #12 0x000000000044cbd0 in RUN_ALL_TESTS () at /home/chenhui/rocksdb/third-party/gtest-1.7.0/fused-src/gtest/gtest.h:20718 29 | #13 0x000000000044c677 in main (argc=1, argv=0x7fffffffe3f8) at /home/chenhui/rocksdb/env/mock_env_test.cc:84 30 | 31 | -------------------------------------------------------------------------------- /32.handler/handler.txt: -------------------------------------------------------------------------------- 1 | 1.信息: 2 | - 记录行数 3 | - 索引数据大小 4 | - 数据大小 5 | - 每个Key的记录数 6 | - 每个Key Prefix的不同Key数 7 | • 在Flush,Compaction和手动执行ANALYZETABLE 时更新 8 | 9 | 10 | 2.索引选择: 11 | 利用之前的数据并通过较为简单的查询,records_in_range实现了对于两个 Key之间的记录数的估算。主要的估算的公式为: 12 | ret = rows*sz/disk_size 13 | 其中rows和disk_size已经在上面一阶段求出,sz表示两个Key之间的数据所占 用的disk_size,sz的估算逻辑最终落到 BlockBasedTable::ApproximateOffsetOf通过IndexIterator的一次Seek获得 key所在Block的Offset. 14 | 当这个代价计算相等的情况下,默认先选择Primary Key. 但代价相等且满足索引读(covering index)的情况下选择Secondary Key. 15 | 16 | 3.索引选择: 17 | |假设有4个SST文件,我们要找的范围是99-211。 18 | |1. 定位99所在的文件根据文件的minKey和maxKey元数据,定位到时SST1,之后通 19 | |过SST1的Index搜索99所在的块(Seek),找到的Offset为28. 20 | |2. 设置size=0. 21 | |3. 遍历SST1(由99定位得到)以及之后的文件的元数据,如果211不在范围,size 加上文件大小,如果211在范围内,则定位211所在块,假设是在SST3的Offset为 2的位置。 22 | |4. 最终的结果:size + 2 – 28 = 35+30+2-28=39. 23 | 24 | 25 | 4. 26 | |最大User Key为要查找Key所属的IndexNumber加一,当执行select * from torder by id desc时,需要定位最大的id,这时Seek使用的Key就是这个。 27 | |最小User Key为要查找的IndexNumber,当执行select * from t时,需要定位最小 28 | |的id,这时Seek使用的Key就是这个。 29 | |下一个InternalKey,计算方式为IndexNumber + Key + 0(SequenceNumber 7bytes) + 0(Type Delete 1 byte).根据之前的排序方式,这个InternalKey在 Key相等的情况下最大而且一定不存在,因此可以定位下一个Key。 30 | 31 | 32 | 5.从handler层来看共有两条路径可以使用 33 | - 使用Key可以直接获得数据,则调用index_read_map或get_row_by_rowid,进入 rocksdb后,调用DBImpl::Get获取一条记录,这条路径也会通过TableIterator的 Seek函数找到目标行。 34 | - 需要遍历获得数据,则调用m_scan_it的Seek和Next,进入rocksdb后使用刚才描 述的BaseDeltaIterator进行遍历,如果使用的Iterator是在Secondary Key上进行遍 历,则会从Secondary Key上解析出Primary Key,再使用Primary Key进入等值的 读取流程。 35 | 36 | 5.具体代码流程 37 | (1)范围查询 38 | --m_scan_it Seek 39 | ----m_scan_it Next 40 | ----get_row_by_rowid 41 | 42 | (2)Primary Key等值查询 43 | --get_row_by_rowid 44 | 45 | (3)Unique Key等值查询 46 | --m_scan_it Seek 47 | ----get_row_by_rowid 48 | 49 | 调用get_row_by_rowid的条件。 50 | 如果不满足covering index需要获取完整记录, 则都需要调用 get_row_by_rowid 51 | 52 | 53 | 6.Arena 54 | -- 在Iterator层次创建的过程中为Iterator分配空间。 55 | -- 大于四分之一块大小时分配一个单独的块,单独使用。 56 | 57 | 7.Block Cache和Table Cache 58 | - 缓存默认使用ShardedLRUCache实现 59 | - Block Cache->使用文件编号和Offset作为Key查找Block 60 | - Table Cache->使用文件名查找TableReader 61 | 62 | 8.WriteBatchWithIndex 63 | - WriteBatch,主体是一个字符串,可以解析出其中的操作 64 | - Index,是一个SkipList,索引WriteBatch中的内容 65 | 66 | 10.SuperVersion 67 | - SuperVersion引用了当前Iterator所需搜索的内容,包括一个应用SST文件的 Version,以及MemTable,Immutable MemTable 68 | - 在Iterator遍历数据库时,SuperVersion保证了遍历所需的SST文件, MemTable不会被删除。 -------------------------------------------------------------------------------- /80.TTL/TTL.md: -------------------------------------------------------------------------------- 1 | #1.TTL 2 | 3 | 生存时间TTL 4 | 对于某些工作负载,在一段时间后需要删除行。通常的方法是创建一个时间列,并定期删除以删除旧行 这并不理想,因为删除会导致MyRocks中出现许多问题。我们必须消耗CPU成本去处理delete语句。 以及将删除标记写入数据库的IO成本。删除标记也可以使扫描速度变慢。 5 | 相反,我们可以利用压缩过滤器来执行删除操作。 6 | DDL语法 7 | TTL 是通过表注释定义的,有两个变体,隐式和显式时间戳 8 | 9 | ```sql 10 | CREATE TABLE t1 (a INT, b INT, c INT, PRIMARY KEY (a), KEY(b)) ENGINE=ROCKSDB COMMENT "ttl_duration=3600;"; 11 | 12 | CREATE TABLE t2 (a INT, b INT, c INT, ts BIGINT UNSIGNED NOT NULL, PRIMARY KEY (a), KEY(b)) ENGINE=ROCKSDB COMMENT "ttl_duration=3600;ttl_col=ts;"; 13 | ``` 14 | 15 | Read读过滤 16 | 对于可重复读隔离级别,或者如果在具有辅助键的表上定义TTL,则必须打开读取过滤。 这可以通过设置rocksdb_enable_ttl_read_filtering变量。(默认情况下已启用)来完成。 17 | 18 | 19 | 在上面的示例中,我们将ttl_duration设置为3600意味着我们希望从数据库中删除超过3600秒的行。 20 | t1表模式使用隐式时间戳,这意味着创建的时间呗隐式记录为插入或更新记录时的当前时间。记录的时间戳存储在记录中,但对用户不可见。 t2表模式使用显式时间戳,这由表注释中的ttl_col限定符表示。这意味着用于TTL的创建时间直接来自ts列。 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /90.Config/Config.md: -------------------------------------------------------------------------------- 1 | #1.Config 2 | 3 | 如果你想在相同的实例中同时使用InnoDB存储引擎和MyRocks存储引擎,需要在my.cnf中设置allow-multiple-engines参数 以及 删除skip-innodb参数。 使用混合存储引擎不建议在生产环境下使用。因为它不是真正的事务型,但是它可以用户实验的目的。 复制模式下,Slave节点的binlog记录是Statement模式是被允许的,但不能是Master。这是因为MyRocks 不支持next-key锁。 4 | 5 | MyRocks 的数据存储在RocksDB中,按指数计算,RocksDB 在内部分配一个 列族 来存储索引数据。 默认情况下,所有的数据存储在 default 列族中。你可以通过为给定索引设置 COMMENT 来更改列族。 在上面的例子中,主键存储在cf_link_pk列族中,id1_type索引数据存储在rev:cf_linke_id1_type列族中。 6 | 7 | MyRocks还有一个叫作 反向列族 的功能。如果索引主要用于降序扫描(ORDER BY … DESC),你可以通过在列族名称之前设置 rev: 来配置反向列族,在此示例中,id1_type属于反向列族。 8 | 对于给定的table表,MyRocks支持基于每个分区存储的列族,有关如何指定列族的更多信息,请参考 (四、性能调优/7.分区表列族.md) 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /91.Performance/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/91.Performance/1.png -------------------------------------------------------------------------------- /91.Performance/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/91.Performance/2.png -------------------------------------------------------------------------------- /91.Performance/perf_data.md: -------------------------------------------------------------------------------- 1 | #1.read 2 | 3 | ![](1.png) 4 | 5 | #2.write 6 | ![](2.png) -------------------------------------------------------------------------------- /92.Limitation/Limitation.md: -------------------------------------------------------------------------------- 1 | #1.MyRocks限制和不足 2 | 功能缺陷 3 | 不支持 Gap Lock,造成为了保证主从一致,binlog必须使用 ROW格式 4 | 原生不支持Online DDL (可以通过第三方工具实现) 5 | 不支持EXCHANGE PARTITION 6 | 不支持 Transportable Tablespace, Foreign Key, Spatial Index, Fulltext Index 7 | 大小写敏感,不支持*_bin collation 8 | ORDER BY 扫描需要考虑建表时定义的顺序,不支持同时顺序和逆序快速扫描 9 | 备份工具略有差异,支持mysqldump(物理)和myrocks_hotbackup python脚本(逻辑)不支持XtraBackup 10 | -------------------------------------------------------------------------------- /93.GTID/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/93.GTID/1.png -------------------------------------------------------------------------------- /93.GTID/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/93.GTID/2.png -------------------------------------------------------------------------------- /93.GTID/Enable_GTID.md: -------------------------------------------------------------------------------- 1 | #1.Enable GTID 2 | 3 | ![](1.png) 4 | ![](2.png) -------------------------------------------------------------------------------- /98.sample/code_c_simple_sample.md: -------------------------------------------------------------------------------- 1 | #1.c_simple_sample 2 | 3 | ```cpp 4 | c_simple_sample 5 | --rocksdb_open 6 | ----DB::Open 7 | ------DBImp::Open 8 | --rocksdb_writeoptions_create 9 | --rocksdb_put 10 | ----DB::Put 11 | ------WriteBatch::Put 12 | --------WriteBatchInternal::Put 13 | ----------PutLengthPrefixedSlice(key) 14 | ----------PutLengthPrefixedSlice(vale) 15 | ------DBImpl::Write 16 | --rocksdb_readoptions_create 17 | --rocksdb_get 18 | --rocksdb_close 19 | ``` -------------------------------------------------------------------------------- /98.sample/examples.txt: -------------------------------------------------------------------------------- 1 | 1.main 2 | main 3 | --rocksdb_options_create 4 | --rocksdb_open 5 | --rocksdb_backup_engine_open 6 | --rocksdb_writeoptions_create 7 | --rocksdb_put(db, writeoptions, key, strlen(key), value, strlen(value) + 1, &err); 8 | --rocksdb_readoptions_create 9 | --rocksdb_get(db, readoptions, key, strlen(key), &len, &err) 10 | --rocksdb_backup_engine_create_new_backup(be, db, &err); // create new backup in a directory specified by DBBackupPath 11 | --rocksdb_close(db); 12 | --rocksdb_restore_options_create 13 | --rocksdb_backup_engine_restore_db_from_latest_backup 14 | --rocksdb_open 15 | --rocksdb_backup_engine_close(be); 16 | --rocksdb_close(db) -------------------------------------------------------------------------------- /99.Research/Group_GC/Aerospike基础知识.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/99.Research/Group_GC/Aerospike基础知识.pdf -------------------------------------------------------------------------------- /99.Research/Group_GC/Aerospike源码分析.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/99.Research/Group_GC/Aerospike源码分析.pdf -------------------------------------------------------------------------------- /99.Research/Group_GC/Aerospike磁盘数据存储格式V0.2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/99.Research/Group_GC/Aerospike磁盘数据存储格式V0.2.pdf -------------------------------------------------------------------------------- /99.Research/Group_GC/Group_GC.txt: -------------------------------------------------------------------------------- 1 | 1.ssd_write 2 | 3 | 2.ssd_group_gc_top_group_init 4 | ssd_group_gc_top_group_init 5 | --ssd_fd_get 6 | ----cf_queue_pop(ssd->fd_q, (void*)&fd, CF_QUEUE_NOWAIT) 7 | --ioctl(fd, SFX_BLK_FTL_IOCTL_GET_EUID_SIZE, &group_size) 8 | 9 | 10 | 3.ssd_group_gc_register_group 11 | 12 | ssd_group_gc_trim_wblock/ssd_flush_swb/ssd_shadow_flush_swb/ssd_cold_start_sweep 13 | 14 | ssd_group_gc_refresh_info 15 | --ssd_group_gc_register_group 16 | ----cf_shash_get 17 | ----cf_malloc 18 | ----ssd_group_gc_top_group_init 19 | ----cf_shash_put_unique 20 | 21 | 4.drv_ssd 22 | typedef struct drv_ssd_s 23 | { 24 | struct as_namespace_s *ns; 25 | 26 | char *name; // this device's name 27 | char *wdname; // this device's whole disk name 28 | // if it's a partition 29 | char *shadow_name; // this device's shadow's name, if any 30 | 31 | uint32_t running; 32 | 33 | pthread_mutex_t write_lock; // lock protects writes to current swb 34 | ssd_write_buf *current_swb; // swb currently being filled by writes 35 | 36 | int commit_fd; // relevant for enterprise edition only 37 | int shadow_commit_fd; // relevant for enterprise edition only 38 | 39 | pthread_mutex_t defrag_lock; // lock protects writes to defrag swb 40 | ssd_write_buf *defrag_swb; // swb currently being filled by defrag 41 | 42 | cf_queue *fd_q; // queue of open fds 43 | cf_queue *shadow_fd_q; // queue of open fds on shadow, if any 44 | 45 | cf_queue *free_wblock_q; // IDs of free wblocks 46 | cf_queue *defrag_wblock_q; // IDs of wblocks to defrag 47 | cf_queue *defrag_group_q; // IDs of groups to defrag 48 | 49 | cf_queue *swb_write_q; // pointers to swbs ready to write 50 | cf_queue *swb_shadow_q; // pointers to swbs ready to write to shadow, if any 51 | cf_queue *swb_free_q; // pointers to swbs free and waiting 52 | cf_queue *post_write_q; // pointers to swbs that have been written but are cached 53 | 54 | cf_atomic64 n_defrag_wblock_reads; // total number of wblocks added to the defrag_wblock_q 55 | cf_atomic64 n_defrag_wblock_writes; // total number of swbs added to the swb_write_q by defrag 56 | cf_atomic64 n_wblock_writes; // total number of swbs added to the swb_write_q by writes 57 | 58 | volatile uint64_t n_tomb_raider_reads; // relevant for enterprise edition only 59 | 60 | cf_atomic32 defrag_sweep; // defrag sweep flag 61 | 62 | uint64_t file_size; 63 | int file_id; 64 | 65 | uint32_t open_flag; 66 | bool data_in_memory; 67 | bool started_fresh; // relevant only for warm or cool restart 68 | 69 | uint64_t io_min_size; // device IO operations are aligned and sized in multiples of this 70 | uint64_t commit_min_size; // commit (write) operations are aligned and sized in multiples of this 71 | 72 | cf_atomic64 inuse_size; // number of bytes in actual use on this device 73 | 74 | uint32_t write_block_size; // number of bytes to write at a time 75 | 76 | uint32_t sweep_wblock_id; // wblocks read at startup 77 | uint64_t record_add_older_counter; // records not inserted due to better existing one 78 | uint64_t record_add_expired_counter; // records not inserted due to expiration 79 | uint64_t record_add_max_ttl_counter; // records not inserted due to max-ttl 80 | uint64_t record_add_replace_counter; // records reinserted 81 | uint64_t record_add_unique_counter; // records inserted 82 | 83 | ssd_alloc_table *alloc_table; 84 | ssd_alloc_group_table *alloc_group_table; 85 | 86 | pthread_t maintenance_thread; 87 | pthread_t write_worker_thread[MAX_SSD_THREADS]; 88 | pthread_t shadow_worker_thread; 89 | pthread_t defrag_thread; 90 | 91 | histogram *hist_read; 92 | histogram *hist_large_block_read; 93 | histogram *hist_write; 94 | histogram *hist_shadow_write; 95 | histogram *hist_fsync; 96 | } drv_ssd; 97 | 98 | 99 | 5.struct drv_whole_ssds 100 | // each whole-disk 101 | typedef struct drv_whole_ssds_s { 102 | // next whole-disk 103 | drv_whole_ssds *next_drv; 104 | // whole-disk name 105 | char *wdname; 106 | // drv_group_info 107 | cf_shash *drv_group_hash; 108 | // 1. if ssd is whole-disk, this is itself, or 109 | // 2. if ssd(s) is(are) partition(s), this is list of (multiple) 110 | // partition(s) belong to this whole-disk 111 | drv_sub_ssd *sub_ssd_list; 112 | } drv_whole_ssds; 113 | 114 | 6.ssd_alloc_group_table 115 | // Top level structure for Group GC (per ssd). 116 | typedef struct ssd_alloc_group_table_s { 117 | // in bytes: partition offset (0 when whole disk) 118 | int64_t part_offset; 119 | cf_atomic64 current_group_id; 120 | // == number of wblocks per ssd 121 | uint32_t n_groups; 122 | // use wblock_id as index 123 | ssd_wblock_info *wblock_table; 124 | // low level group hash (ssd_group_info) 125 | cf_shash *ssd_group_hash; 126 | drv_whole_ssds *whole_ssd; 127 | struct { 128 | // counter: failed to get current_group_id 129 | uint64_t stat_fail_current_group_id; 130 | // counter: failed to get group_id 131 | uint64_t stat_fail_get_group_id; 132 | // counter: total trim 133 | uint64_t stat_trim; 134 | // counter: total completed defragged groups 135 | uint64_t stat_complete_df_group; 136 | // counter: total completed groups, 137 | uint64_t stat_complete_non_df_group; 138 | // counter: duplicate group_id push for defrag 139 | uint64_t stat_duplicate_df_push_group_id; 140 | // active summary: total groups in defrag queue 141 | int stat_defrag_q_group; 142 | } stat; 143 | } ssd_alloc_group_table; 144 | 145 | 7.drv_group_info 146 | // top-level (drive): group info 147 | //-- 148 | typedef struct drv_group_info_s { 149 | // (set once, never change) 150 | uint64_t group_id; 151 | uint64_t g_defrag_lwm_size; // threshold size for defrag (in bytes) 152 | uint64_t g_group_size; // hardware group size (in bytes) 153 | 154 | // (ever changing) 155 | cf_atomic64 g_inuse_size; // database inuse size (in bytes) 156 | cf_atomic64 n_g_swb_pending; // number of wblocks with pending swb 157 | cf_atomic64 defrag_ref; 158 | } drv_group_info; 159 | 160 | 161 | 8.ssd_group_info//注意区分两种group info 162 | // low-level (ssd): group info 163 | //-- 164 | typedef struct ssd_group_info_s { 165 | uint64_t group_id; // (set once, never change) 166 | drv_group_info *top_group_info; // (set once, never change) 167 | 168 | cf_queue *g_wblock_queue; // all wblocks (per group, within ssd) 169 | } ssd_group_info; 170 | 171 | 172 | 173 | 9.ssd_group_gc_push_group_id_by_wblock 174 | 175 | 176 | swb_dereference_and_release/ssd_block_free/run_load_queues 177 | 178 | 179 | ssd_group_gc_trigger_gc 180 | --ssd_group_gc_push_group_id_by_wblock 181 | ----drv_group_info *top_g = t_ssd->alloc_group_table->wblock_table[wblock_id].g.w_group->top_group_info; 182 | ----uint64_t group_id = t_ssd->alloc_group_table->wblock_table[wblock_id].g.w_group_id; 183 | ----drv_sub_ssd *next_ssd = t_ssd->alloc_group_table->whole_ssd->sub_ssd_list; 184 | ----cf_queue_push_unique(next_ssd->ssd->defrag_group_q, &group_id); 185 | 186 | //将group id加到所有的defrag_group_q,然后怎么搞?????? 187 | 188 | 189 | 10.ssd_wblock_info 190 | // Shortcut to access each wblock's group info. (per wblock) 191 | // The members keep changing. 192 | // Access with ssd->alloc_group_table->wg_info[wblock_id] 193 | // where: wblock_id is [0, 1, ..., max wblock_id) 194 | //-- 195 | typedef struct ssd_wblock_info_s { 196 | // group shortcut access 197 | struct { 198 | uint64_t w_group_id; 199 | ssd_group_info *w_group; 200 | } g; 201 | // cached wblock info, per wblock 202 | struct { 203 | // the actual size written to ssd per wblock 204 | cf_atomic32 w_inuse_sz; 205 | // if or not associated with a swb 206 | bool w_with_swb; 207 | } w; 208 | } ssd_wblock_info; 209 | 210 | 11.drv_sub_ssd 211 | // a single whole-disk or a single partition 212 | //-- 213 | typedef struct drv_sub_ssd_s { 214 | drv_sub_ssd *next_ssd; 215 | drv_ssd *ssd; 216 | } drv_sub_ssd; 217 | 218 | 12.run_defrag 219 | 220 | ssd_group_gc_push_group_id_by_wblock负责把group_id放入defrag_group_q, 221 | run_defrag负责把各个group_id取出来。。。进行碎片整理。 222 | 223 | 224 | run_defrag 225 | --free_wblock_count = cf_queue_sz(ssd->free_wblock_q); 226 | --pending_group_count = cf_queue_sz(ssd->defrag_group_q); 227 | --while (true) { 228 | ----//state machine code 229 | ----cf_queue_pop(ssd->defrag_group_q, &group_id, CF_QUEUE_NOWAIT)cf_shash_get(agt->ssd_group_hash, &group_id, &g) 230 | ----// We have to defrag wblocks one by one because: 231 | ----// - The disk header, aka wblock at index 0, is not tracked by 232 | ----// Group GC algorithm, trimming the entire group can definitely 233 | ----// cause the header data loss, consequently cause data loss on 234 | ----// the entire disk. 235 | ----// - With multiple disk partitions, a group can span over more 236 | ----// than one partition, i.e. a group tracked in one partition 237 | ----// (one drv_ssd structure) only contains part of the data in 238 | ----// the physical group, thus trimming the entire group erases 239 | ----// data in other partition. 240 | ----// - Checking if all wblocks are physically continuous would 241 | ----// involve many querying via ioctl() and address/offset 242 | ----// sort/concatenation operations, which is not seemingly 243 | ----// efficient. And the result of such attempt may finally 244 | ----// proven to be waste of resource now and then when processing 245 | ----// group by group. 246 | ----while (CF_QUEUE_OK == cf_queue_pop(g->g_wblock_queue, &wblock_id, CF_QUEUE_NOWAIT)) { 247 | ------ssd_defrag_wblock 248 | ----}//while cf_queue_pop(g->g_wblock_queue, &wblock_id,...... 249 | ----cf_atomic64_decr(&g->top_group_info->defrag_ref) 250 | ----agt->stat.stat_defrag_q_group--; 251 | ----agt->stat.stat_complete_df_group++; 252 | 253 | ----next_group_gc: 254 | ----while (CF_QUEUE_OK == cf_queue_pop(ssd->defrag_wblock_q, &wblock_id, CF_QUEUE_NOWAIT)) {//注意和第一个group gc不是一个q。第一个是g_wblock_queue 255 | ------cf_atomic64_incr(&ssd->n_defrag_wblock_reads); 256 | ------ssd_defrag_wblock(ssd, wblock_id, read_buf); 257 | ----}//while cf_queue_pop(ssd->defrag_wblock_q,...... 258 | ----continue;//goto while(true) outer loop 259 | 260 | ----if (cf_queue_sz(ssd->defrag_wblock_q) > q_min) 261 | ----cf_queue_pop(ssd->defrag_wblock_q, &wblock_id, 262 | ----ssd_defrag_wblock 263 | 264 | --}//while true 265 | 266 | 267 | 13.ssd_defrag_wblock 268 | 269 | ssd_defrag_wblock 270 | --ssd_fd_get 271 | ----cf_queue_pop(ssd->fd_q, (void*)&fd, CF_QUEUE_NOWAIT); 272 | ----file_offset = WBLOCK_ID_TO_BYTES(ssd, wblock_id); 273 | ----lseek(fd, (off_t)file_offset, SEEK_SET) 274 | ----read(fd, read_buf, ssd->write_block_size) 275 | ----ssd_fd_put 276 | ----while (wblock_offset < ssd->write_block_size && cf_atomic32_get(p_wblock_state->inuse_sz) != 0) { 277 | ------drv_ssd_block *block = (drv_ssd_block*)&read_buf[wblock_offset]; 278 | ------ssd_decrypt(ssd, file_offset + wblock_offset, block); 279 | ------// Found a good record, move it if it's current. 280 | ------int rv = ssd_record_defrag 281 | ----//end while (wblock_offset < ssd->write_block_size && .... 282 | ----ssd_release_vacated_wblock 283 | ------ssd_group_gc_trim_wblock 284 | 285 | 286 | 14.ssd_group_gc_trim_wblock 287 | 288 | ssd_group_gc_trim_wblock 289 | --ssd_fd_get 290 | --range[0] = WBLOCK_ID_TO_BYTES(ssd, wblock_id); 291 | --range[1] = ssd->write_block_size; 292 | --ioctl(fd, BLKDISCARD, &range)//硬件回收命令。 293 | --ssd_fd_put 294 | --ssd->alloc_group_table->stat.stat_trim++ 295 | --ssd_group_gc_refresh_info(+) 296 | --ssd_group_gc_set_current_id 297 | ----ioctl(fd, SFX_BLK_FTL_IOCTL_GET_OPEN_EUID, &group_id)//看下group id是否改变。。。。 298 | 299 | 300 | 15.ssd_group_gc_refresh_info 301 | 302 | ssd_group_gc_refresh_info 303 | --ssd_wblock_info *p_wblock_table = &agt->wblock_table[wblock_id]; 304 | --更新元信息 p_wblock_table 305 | --group_id = (WBLOCK_ID_TO_BYTES(ssd, wblock_id) + agt->part_offset) >> 9; // sector 306 | --ssd_fd_get 307 | --ioctl(fd, SFX_BLK_FTL_IOCTL_GET_EUID, &group_id); 308 | --ssd_fd_put 309 | --cf_shash_get(agt->ssd_group_hash, &group_id, &g) 310 | --p_wblock_table->g.w_group_id = group_id; 311 | --p_wblock_table->g.w_group = g; 312 | --cf_queue_push_unique(p_wblock_table->g.w_group->g_wblock_queue, &wblock_id); 313 | 314 | 315 | 16.ssd_record_defrag 316 | 317 | ssd_record_defrag 318 | --as_partition_getid//partition id,看起来像是as内部的概念,将磁盘划分成多个partition,现在需要搞清楚,ns->partition 和 ssd->partition什么关系?????? 319 | --as_partition_reserve 320 | --as_record_get(rsv.tree, &block->keyd, &r_ref); 321 | --defrag_move_record(ssd, wblock_id, block, r); 322 | --as_record_done(&r_ref, ns); 323 | --as_partition_release(&rsv); 324 | 325 | 326 | 17.drv_ssd_block 代表一个block。 327 | 328 | //-- 329 | // Per-record metadata on device. 330 | typedef struct drv_ssd_block_s { 331 | uint64_t sig; // deprecated 332 | uint32_t magic; 333 | uint32_t length; // total after this field - this struct's pointer + 16 334 | cf_digest keyd; 335 | uint32_t generation; 336 | cf_clock void_time; 337 | uint32_t bins_offset; // offset to bins from data 338 | uint32_t n_bins; 339 | uint64_t last_update_time; 340 | uint8_t data[]; 341 | } __attribute__ ((__packed__)) drv_ssd_block; 342 | 343 | 344 | 18.defrag_move_record 345 | 346 | -------------------------------------------------------------------------------- /99.Research/Group_GC/aerospike.txt: -------------------------------------------------------------------------------- 1 | 1.ssd_write 2 | as_storage_record_write 3 | --as_storage_record_write_ssd 4 | ----ssd_write 5 | 6 | 2. -------------------------------------------------------------------------------- /99.Research/PebblesDB/Compaction.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/99.Research/PebblesDB/Compaction.txt -------------------------------------------------------------------------------- /99.Research/PebblesDB/Version.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/99.Research/PebblesDB/Version.txt -------------------------------------------------------------------------------- /99.Research/PebblesDB/VersionSet.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisehead/myrocks_notes/af711a1ab2ffc35a069930f90b2be0a951f69659/99.Research/PebblesDB/VersionSet.txt --------------------------------------------------------------------------------