From 985a8b8173cbd3d85ef9ae4b5b8061e9f9021918 Mon Sep 17 00:00:00 2001 From: jiaqizho Date: Thu, 28 Apr 2022 15:51:01 +0800 Subject: [PATCH 01/25] [DNM]doc: add a design doc of pagestorage v2 --- ...hitecture-of-storage-engine-pagestorage.md | 163 ++++++++++++++++++ .../design/images/tiflash-dt-architecture.png | Bin 0 -> 16580 bytes .../images/tiflash-ps-v2-architecture.png | Bin 0 -> 42542 bytes 3 files changed, 163 insertions(+) create mode 100644 docs/design/0000-00-01-architecture-of-storage-engine-pagestorage.md create mode 100644 docs/design/images/tiflash-dt-architecture.png create mode 100644 docs/design/images/tiflash-ps-v2-architecture.png diff --git a/docs/design/0000-00-01-architecture-of-storage-engine-pagestorage.md b/docs/design/0000-00-01-architecture-of-storage-engine-pagestorage.md new file mode 100644 index 00000000000..129a6d52468 --- /dev/null +++ b/docs/design/0000-00-01-architecture-of-storage-engine-pagestorage.md @@ -0,0 +1,163 @@ +# Architecture Of Storage Engine - PageStorage + +- Authors(order by last name): [JaySon-Huang](https://github.com/JaySon-Huang),[flowbehappy](https://github.com/flowbehappy) [Jiaqi Zhou](https://github.com/jiaqizho) + +## Introduction + +PageStorage is one of the earliest components used as Delta merge storage.In the early days of the Tiflash, PageStorage was used as the basic storage moudle, All of IO from DT will store into PageStorage. Over time, we only place elements of `delta` and `metadata of DT` in PageStorage. + +PageStorage needs to handle a large amount of delta data read and write, this part of the data is hot data. And a small amount of DT meta information needs to be persisted, this part is cold data. The data of delta will eventually be merged into the stable part, eq. stored in the DT file. + +You can see the picture below. Described in the picture is the design of the entire DT. PageStorage carries the traffic of the upper half of DT. + +![tiflash-overall](./images/tiflash-dt-architecture.png) + + +As one of the important components of DMS, PageStorage mainly provides a KV storage service which also support MVCC. Unlike other KV services, the KV interface provided by PageStorage is limited. Key is limited to uint64_t, Value is limited to a buffer or a array of buffer(we called it fields) or null. + + +## Capability + +PageStorage supported: + +- Disk-based store +- Write/Read operation atomicity +- Full MVCC function +- KV store function + - basic write functions + - basic read functions + - read with MVCC +- GC + + +## Design + +Currently there are three versions of PageStorage (V1, V2, V3). The V1 version is no longer used by DT. Due to compatibility considerations, it's still used in some components, we considered to remove it the future. It won't be covered too much in this article. + +At present, most of our customers use the V2 version, which is also the version with the longest service time. Due to design issues, the V2 version has some problems that cannot be improved(will be introduced below), so we propose the V3 version. + +The V3 version has been proposed for a period of time, and it was officially used by customers in TiFlash v6.1.0. This version has improved the shortcomings of V2 very well. The redesign has made the module very good in terms of performance and maturity. + +### 1. V2 version + +![tiflash-overall](./images/tiflash-ps-v2-architecture.png) + +The picture below describes the design in PageStorage V2. +We provider a big PageMap which stored data and meta of pages. Also provider Writable/Readable pagefile to support write/read of page. + +Here is the basic elements in PageStorage V2: + +- WriteBatch: a batch of write + - upper layer used this struct to write a batch of buffer into PageStorage. +- Page: basic data elements + - it contains id, buffer, fields... + - A set of pages will as return value when upper layer read by a set of page id. +- PageFile: contains meta and data + - data: buffer will store into the this part + - meta: used to manager the data part record every data of page. +- PageFileReader: used to read a single pagefile. +- PageFileWriter: used to write a single pagefile. +- PageEntriesVersionSetWithDelta: The MVCC object + + +#### meta and data + +Pagefile is a directory on the filesystem which is named with type_id_level. For example page_55_1, “page” means that pagefile type is “formal”, “55” is the pagefile id, “1” is the level. + +Pagefile is composed of multiple meta buffers and multiple data buffers. Meta buffer keep the pagefile meta info, it contains multiple write batch buffer. + +Here is the meta sturct: + +Bits | Name | Description. | +--------------------|------------------|-----------------------| +0:32 | Meta Byte Size | The buffer length | +32:64 | version | The page format version, should be V1 or V2 | +64:128 | SequenceID | The ID of sequence | +128:N | Buffer list | Collection of meta buffer, length is flexible | +N:+64 | Checksum | Checksum | + +A write batch can contain a sequence of operations(put/upsert/del/ref), so a buffer list is a flexible list to record that. + +buffer list(operate:put/upsert) + +Bits | Name | Description. | +--------------------|------------------|-----------------------| +0:8 | Write Type | Write batch operation type | +8:72 | Page ID | The page Id | +72:136 | File ID | The pagefile Id | +136:168 | Level | The page level | +168:200 | Flag | The page Flag decide page detach or not | +200:264 | Page offset | The page offset which record data offset in current pagefile | +264:328 | Page size | The page size which record data size | +328:392 | Page checksum | The page checksum | +392:456 | Field offsets length | The length of field offset | + +buffer list(operate:del) + + +Bits | Name | Description. | +--------------------|------------------|-----------------------| +0:8 | Write Type | Write batch operation type | +8:72 | Page ID | The page Id | + +buffer list(op:ref) + +Bits | Name | Description. | +--------------------|------------------|-----------------------| +0:8 | Write Type | Write batch operation type | +8:72 | Page ID | The page Id | +72:136 | Origin Page ID | The Origin page id | + +Field offsets + +Bits | Name | Description. | +--------------------|------------------|-----------------------| +0:64 | Field Offset | The field offset | +64:128 | Field Checksum | The field checksum | + +The data part is more simple, it have not contain any of meta info, it only consists of buffer. + +Both of data and meta, we used append write + sync IO to write the file, data clean will be done by GC part. Also the file size of both is limited. If the data is larger than 128M(default), a new file will be automatically generated. + + +#### MVCC + +`PageEntriesVersionSetWithDelta` is the main class of the MVCC, It's a big list, collect all version of page entries(it's a extension of Page object), The entries contain with every single page. Once snapshot created, the list will add a new version of page entries. + +The API from PageStorage have a method named `getSnapshot()`. After call this method, MVCC will generate a snapshot which record all of page entries(in memory) in current time. + +Then every read API from PageStorage request the snapshot as one of the parameters.But by default, the snapshot can be nullptr, then we will create a new snapshot for it. + +In read method, PageStorage will use snapshot + page ids to find the page entries. After we got the entries, we will read the data buffer from pagefile and combine it to a set of page, Then return to the caller. + +Also anytime there is a del operation come, we will not immediately mark the data on the disk to be cleaned up. Instead GC part will do the clean job. + +#### GC + +GC relative the snapshots release. If none of the snapshots in memory are released, then the GC will not clean up any data. In general, snapshots are released every once in a while, This will generate a lot of "expired" data on disk. + +At each round of GC, the GC thread scans each pagefiles and calculates the valid rate of the pagefile through MVCC. valid rate equal to total valid size divide by total file size, If valid rate lower than 0.5(by default), we will do the compact GC. + +The compact GC will compact the valid page into a new pagefile. This process is in order not to generate spatial magnification.But the same, it will produce write amplification. + +During the GC process, we do not lock the entire PageStorage, but use more fine-grained locks to ensure that the current PageStorage can respond to read and write. + + +#### write/read example + +After write request come: + +- Get a idle PageFileWriter(which is not locked), and locked it to avoid anther thread write. +- Generate the meta record. +- Write buffer into the disk. +- Write the meta record into meta. + +After read request come: + +- Get a snapshot from caller or create a snapshot. +- Find the entries from the MVCC by snapshot + page ids. +- Using entries, read from disk. +- Combine the buffer and entries into pages and then return to the caller. + + + diff --git a/docs/design/images/tiflash-dt-architecture.png b/docs/design/images/tiflash-dt-architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..40b28a1296d0a714fa6874e00661218b76c1b200 GIT binary patch literal 16580 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 U)>kp$uM=P>$*IYfN}C7%A6r!@WdHyG literal 0 HcmV?d00001 diff --git a/docs/design/images/tiflash-ps-v2-architecture.png b/docs/design/images/tiflash-ps-v2-architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..4d91e8e270b74b4caa33dbeb6ad9729ec20e975b GIT binary patch literal 42542 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 literal 0 HcmV?d00001 From 5774ddfd976e0f2b374cbc55538d1db9deefa942 Mon Sep 17 00:00:00 2001 From: jiaqizho Date: Thu, 28 Apr 2022 15:56:12 +0800 Subject: [PATCH 02/25] update --- ...0-01-architecture-of-storage-engine-pagestorage.md | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/docs/design/0000-00-01-architecture-of-storage-engine-pagestorage.md b/docs/design/0000-00-01-architecture-of-storage-engine-pagestorage.md index 129a6d52468..c85f4272605 100644 --- a/docs/design/0000-00-01-architecture-of-storage-engine-pagestorage.md +++ b/docs/design/0000-00-01-architecture-of-storage-engine-pagestorage.md @@ -1,16 +1,16 @@ # Architecture Of Storage Engine - PageStorage -- Authors(order by last name): [JaySon-Huang](https://github.com/JaySon-Huang),[flowbehappy](https://github.com/flowbehappy) [Jiaqi Zhou](https://github.com/jiaqizho) +- Authors(order by last name): [JaySon-Huang](https://github.com/JaySon-Huang), [flowbehappy](https://github.com/flowbehappy), [Jiaqi Zhou](https://github.com/jiaqizho) ## Introduction -PageStorage is one of the earliest components used as Delta merge storage.In the early days of the Tiflash, PageStorage was used as the basic storage moudle, All of IO from DT will store into PageStorage. Over time, we only place elements of `delta` and `metadata of DT` in PageStorage. +PageStorage is one of the earliest components used in delta merge storage. In the early days of the TIFlash, PageStorage was used as the basic storage moudle, All of IO from DT will store into PageStorage. Over time, we only place elements of `delta` and `metadata of DT` in PageStorage. PageStorage needs to handle a large amount of delta data read and write, this part of the data is hot data. And a small amount of DT meta information needs to be persisted, this part is cold data. The data of delta will eventually be merged into the stable part, eq. stored in the DT file. You can see the picture below. Described in the picture is the design of the entire DT. PageStorage carries the traffic of the upper half of DT. -![tiflash-overall](./images/tiflash-dt-architecture.png) +![tiflash-dt-architecture](./images/tiflash-dt-architecture.png) As one of the important components of DMS, PageStorage mainly provides a KV storage service which also support MVCC. Unlike other KV services, the KV interface provided by PageStorage is limited. Key is limited to uint64_t, Value is limited to a buffer or a array of buffer(we called it fields) or null. @@ -24,9 +24,6 @@ PageStorage supported: - Write/Read operation atomicity - Full MVCC function - KV store function - - basic write functions - - basic read functions - - read with MVCC - GC @@ -40,7 +37,7 @@ The V3 version has been proposed for a period of time, and it was officially use ### 1. V2 version -![tiflash-overall](./images/tiflash-ps-v2-architecture.png) +![tiflash-ps-v2-architecture](./images/tiflash-ps-v2-architecture.png) The picture below describes the design in PageStorage V2. We provider a big PageMap which stored data and meta of pages. Also provider Writable/Readable pagefile to support write/read of page. From 3e84e4d8d0ace691bd462188c3f65a79d8153c09 Mon Sep 17 00:00:00 2001 From: jiaqizho Date: Thu, 28 Apr 2022 16:53:31 +0800 Subject: [PATCH 03/25] [DNM]doc: add v3 design into design doc of pagestorage --- ...hitecture-of-storage-engine-pagestorage.md | 215 +++++++++++++++++- .../images/tiflash-ps-v3-architecture.png | Bin 0 -> 80904 bytes docs/design/images/tiflash-ps-v3-freemap.png | Bin 0 -> 34714 bytes .../design/images/tiflash-ps-v3-wal-store.png | Bin 0 -> 30384 bytes 4 files changed, 212 insertions(+), 3 deletions(-) create mode 100644 docs/design/images/tiflash-ps-v3-architecture.png create mode 100644 docs/design/images/tiflash-ps-v3-freemap.png create mode 100644 docs/design/images/tiflash-ps-v3-wal-store.png diff --git a/docs/design/0000-00-01-architecture-of-storage-engine-pagestorage.md b/docs/design/0000-00-01-architecture-of-storage-engine-pagestorage.md index c85f4272605..62de676f34d 100644 --- a/docs/design/0000-00-01-architecture-of-storage-engine-pagestorage.md +++ b/docs/design/0000-00-01-architecture-of-storage-engine-pagestorage.md @@ -139,22 +139,231 @@ The compact GC will compact the valid page into a new pagefile. This process is During the GC process, we do not lock the entire PageStorage, but use more fine-grained locks to ensure that the current PageStorage can respond to read and write. - #### write/read example -After write request come: +After write request comes: - Get a idle PageFileWriter(which is not locked), and locked it to avoid anther thread write. - Generate the meta record. - Write buffer into the disk. - Write the meta record into meta. -After read request come: +After read request comes: - Get a snapshot from caller or create a snapshot. - Find the entries from the MVCC by snapshot + page ids. - Using entries, read from disk. - Combine the buffer and entries into pages and then return to the caller. +### 2. V3 version + +After our customers used TiFlash, we found some problems with the V2 version in the actual production environment. + +1. **There is a risk of data loss in the meta part**. The probability of this kind of risk happening is very small, and we have never encountered it. But in theory, as long as the checksum and buffer size fields in a single meta buffer are damaged at the same time, the subsequent buffers will be unavailable. +2. **The snapshot of MVCC needs to be optimized**. First, the memory occupied by the snapshot can be reduced, and secondly, we do not need such a complex structure to implement MVCC. +3. **The GC write amplification in the data part is too severe, The GC task is too heavy**. Since the data part is composed of append write, compact gc is frequently triggered, and the read and write volume of each gc will be large. + +Besides these three problem, we also changed lock mode and CRC implements, make the V3 better than V2. + +![tiflash-ps-v3-architecture](./images/tiflash-ps-v3-architecture.png) + +The V3 version of PageStorage consists of two main components, one is WALStore and the other is BlobStore. + +- WALStore(Write Ahead Log Store): Using the write ahead log file format to manager the meta part. +- PageDirectory: Provides the function of MVCC. Smaller memory usage and faster speed. +- BlobStore: Provides an address space management. Using the address multiplexing to manage the data part. + +#### WALStore + +WALStore using the fixed block space to manager the meta. + +![tiflash-ps-v3-walstore](./images/tiflash-ps-v3-wal-store.png) + +As you can see, we fixed the meta into one block in V3. +- A block contains multiple meta record. +- If a block have some spacemap remain, But it can't insert a meta. Then using a padding to full it. + +If crc and meta size are broken at the same time. It is only necessary to discard the data in a single block, and the block before or after will be preserved. This is because we will read meta in by single block, single block error won't effect others. This is also more convenient to troubleshoot the cause of meta errors. + +WALStore provider two main interfaces + +- **apply**: after apply, the meta info will be will be serialized to disk.This happens after writing data. +- **read**: Read serialized meta info from disk. This will happen after pagestorage restore. + +In addition, meta also needs to perform gc. Although the overall read/write sizes is not large, But it will also sort out some invalid meta information. + +The sturct of meta similar with V2, but still have some differents. + +buffer(operate:put/upsert) + +Bits | Name | Description. | +--------------------|------------------|-----------------------| +0:8 | Write Type | Write batch operation type | +8:16 | Flag | The page Flag decide page detach or not | +16:144 | Page Id | The combine of namespace id and page id | +144:272 | Version | The combine of sequence and epoch | +272:336 | Ref count | The page be ref count | +272:N | page entry | The page entry | + +page entry + +Bits | Name | Description. | +--------------------|------------------|-----------------------| +0:32 | Blob File id | The Blob File Id | +32:96 | Offset | The Page Entry offset | +96:160 | Size | The Page Entry size | +160:224 | Checksum | The Page Entry checksum | +224:288 | Tag | The Page tag | +288:352 | Field offsets length | The length of field offset | +352:N | Field offsets | The length field offsets | + + +Field offsets + +Bits | Name | Description. | +--------------------|------------------|-----------------------| +0:64 | Field Offset | The field offset | +64:128 | Field Checksum | The field checksum | + +buffer(operate:put/upsert) + +Bits | Name | Description. | +--------------------|------------------|-----------------------| +0:8 | Write Type | Write batch operation type | +8:16 | Flag | The page Flag decide page detach or not | +16:144 | Page Id | The combine of namespace id and page id | +144:272 | Version | The combine of sequence and epoch | +272:336 | Ref count | The page be ref count | +272:N | page entry | The page entry | + + +buffer(operate:ref) + +Bits | Name | Description. | +--------------------|------------------|-----------------------| +0:8 | Write Type | Write batch operation type | +8:132 | Page Id | The combine of namespace id and page id | +132:260 | Origin Page Id | The combine of namespace id and page id | +260:388 | Version | The combine of sequence and epoch | + +buffer(operate:put_ext) + +Bits | Name | Description. | +--------------------|------------------|-----------------------| +0:8 | Write Type | Write batch operation type | +8:132 | Page Id | The combine of namespace id and page id | +132:260 | Version | The combine of sequence and epoch | +260:324 | Ref count | The page be ref count | + +buffer(operate:del) + +Bits | Name | Description. | +--------------------|------------------|-----------------------| +0:8 | Write Type | Write batch operation type | +8:132 | Page Id | The combine of namespace id and page id | +132:260 | Version | The combine of sequence and epoch | + +The page id in V3 is is a 128bit unsigned int replace the 64bit. That is because instead of generating an instance of PageStorage for each table, there are only four instances of PageStorage globally(Type log/Type data/Type meta/KVStore). So we need additional 64bit space (eq. namespace id) to distinguish different tables. + +Multi of meta info will conbime into a block. So it is different with V2, we added a WAL format to pack the meta struct. + +Legacy record format: + +``` ++--------------+-----------+-----------+--- ... ---+ +|CheckSum (8B) | Size (2B) | Type (1B) | Payload | ++--------------+-----------+-----------+--- ... ---+ +``` + +- CheckSum: 64bit hash computed over the record type and payload using checksum algo (CRC64) +- Size: Length of the payload data +- Type: Type of record(ZeroType, FullType, FirstType, LastType, MiddleType) + - The type is used to group a bunch of records together to represent + - blocks that are larger than kBlockSize +- Payload: Byte stream as long as specified by the payload size + +Recyclable record format: + +``` ++--------------+-----------+-----------+----------------+--- ... ---+ +|CheckSum (8B) | Size (2B) | Type (1B) | Log number (4B)| Payload | ++--------------+-----------+-----------+----------------+--- ... ---+ +``` + +Same as above, with the addition of + +- Log number: 32bit log file number, so that we can distinguish between records written by the most recent log writer vs a previous one. + + +#### PageDirectory + +PageDirectory supports the function of PageDirectory MVCC. Its main component is a map. The key in map is page id and the value in map is the version chian. + +The version chain consists of [seq|epoch] and page entry, sequence + epoch determines the position of the page entry in the chain, page entry have similar implementation with V2, but simplifies unnecessary structures. + +Here is a PageDirectory example: + +``` +page id 1 : {[seq 1 + epoch 0, entry 1], [seq 2 + epoch 0, entry 2], [seq 3 + epoch 1, entry 3]} +page id 2 : {[seq 1 + epoch 0, entry 4], [seq 2 + epoch 0, entry 5], [seq 5 + epoch 0, entry 6]} +page id 100 : {[seq 1 + epoch 0, entry 7], [seq 10 + epoch 2, entry 8]} +``` + +In this example, we have 3 page with differe id. + +- page 1 has 3 different versions corresponding to 3 entries. page 100 has 2 different versions corresponding to 2 entries. +- The seq will increase If we have a corresponding new page written. +- The epoch will be increase If current entry have been compact GC. + +`getSnapshot()` in V3 is very different from V2, In V2, we actually generate snapshots, there are some copies of memory. But in V3, Snapshot only contains a sequence id which can filter the right pages from the PageDirectory. + +In upper example. If sequence in snapshot is 2 and query page id is 2. Then we will got the entry 5. + + +#### BlobStore + +BlobStore's name is earthy, but apt. It mainly stores Blob, which consists of three parts + +1. **BlobFile**: Blob stored file, used to write and read. +2. **BlobStat**: A space manager, one-to-one correspondence with blobfile. used to find/alloc/free address space in BlobFile. +3. **BlobStats**: Manage all BlobStat. Used to schedule all write requests. + +Different with V2 design, we decided to cancel append write mode, Instead, a data structure called a space map is used to manage the file address space. + +![tiflash-ps-v3-freemap](./images/tiflash-ps-v3-freemap.png) + + +This idea come form a rb-tree bitmap implements. Every node in rb-tree have a space which conbime with offset + size.But the difference is that bitmap uses rb-tree to record used locations, and we use rb-tree to record free locations.This is because record free locations will better to find space where you can insert. + +The spacemap(freemap) inside BlobStat, BlobStat can use it to reuse the reclaimed address space, thereby reducing write amplification. + +In addition, BlobStat also needs to provide external statistical status, such as the valid rate of the current BlobFile, the maximum capacity of the current Space map, and so on. These data must be calculated when inserting and releasing, otherwise we have to traverse the space map to get the data we need. + +The statistical status from BlobStat is very necessary and useful. It has two main functions + +1. Provide it to BlobStats so that BlobStats can determine whether to create a new Blobfile or reuse the old one. +2. When BlobStats selects BlobFile to write, it will write according to the BlobFile with the lowest valid rate. +3. When GC occurs, it can quickly determine whether the current BlobFile needs to be compact GC. +4. Because the release of the file address space may cause invalid data to be stored at the end of the BlobFile, the truncate operation can also be performed in time during GC to reduce space enlargement + +Finally, from the description of BlobStat, it is not difficult to find that BlobStats is used to schedule all write IO. Then write request happend, we will request a free space from BlobStats, Then we can put the buffer into that disk space. These operate all happend in memory, So we can maximize IO parallelization. + +#### GC + +The GC of V3 will be more complicated, but compared with V2, the GC of V3 is faster. In the test, the IO from V3 GC will be much less. + +1. Begin to GC, we need do WALStore GC at first, this part is lightweight. When the meta file recorded by wal reaches a certain level, we will dump it and update it. +2. After that, We need do MVCC GC, we will cleanup the expired snapshot in PageDirectory, also these expired page entry from expired snapshot will be mark clean in BlobStore. This means that we will mark the address space originally occupied by these expired page entries as unused. +3. Some of space in BlobStore be free after MVCC GC, Then BlobStore will check all BlobStat, find out if there is a blob with a low valid rate, and perform compact gc on it.The compact GC simliar with V2, it will copy the valid data ,and store it to other place. +4. If we do have compact GC happend in BlobStore, Then we need to tell PageDirectory that some data has been migrated and where is the new location. + +Although this looks complicated, in practice, the probability of BlobStore triggering full gc is very low, which means that we will not generate too many read and write IO during the GC process. + + + + + + + diff --git a/docs/design/images/tiflash-ps-v3-architecture.png b/docs/design/images/tiflash-ps-v3-architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..adef9fd268262464452e67c451d0a3232dfc773f GIT binary patch literal 80904 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%0AZlAQgMLElv= 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 ZMX`Kx{yHv%M*9x>^k0rA+c5wD literal 0 HcmV?d00001 diff --git a/docs/design/images/tiflash-ps-v3-freemap.png b/docs/design/images/tiflash-ps-v3-freemap.png new file mode 100644 index 0000000000000000000000000000000000000000..5fde00f2ae59d6c72f17a4e66a06b4cc5cbf1b28 GIT binary patch literal 34714 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 literal 0 HcmV?d00001 diff --git a/docs/design/images/tiflash-ps-v3-wal-store.png b/docs/design/images/tiflash-ps-v3-wal-store.png new file mode 100644 index 0000000000000000000000000000000000000000..d398ffc13ede956053b8eea5c7be237675df5556 GIT binary patch literal 30384 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

Vu! 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 literal 0 HcmV?d00001 From febc87a347e6443f224c2127334a20edb9472c18 Mon Sep 17 00:00:00 2001 From: jiaqizho Date: Tue, 17 May 2022 15:27:14 +0800 Subject: [PATCH 04/25] update --- ...hitecture-of-storage-engine-pagestorage.md | 227 ++++-------------- 1 file changed, 49 insertions(+), 178 deletions(-) diff --git a/docs/design/0000-00-01-architecture-of-storage-engine-pagestorage.md b/docs/design/0000-00-01-architecture-of-storage-engine-pagestorage.md index 62de676f34d..55b01206109 100644 --- a/docs/design/0000-00-01-architecture-of-storage-engine-pagestorage.md +++ b/docs/design/0000-00-01-architecture-of-storage-engine-pagestorage.md @@ -4,16 +4,16 @@ ## Introduction -PageStorage is one of the earliest components used in delta merge storage. In the early days of the TIFlash, PageStorage was used as the basic storage moudle, All of IO from DT will store into PageStorage. Over time, we only place elements of `delta` and `metadata of DT` in PageStorage. +`PageStorage` is the place where DT(DeltaTree Engine) actually stores data into. The latest data (i.e. delta data), the metadata in the engine are serialized directly into Pages. While the main data, i.e. stable data, is written in DTFiles format and managed as `ExternalPage`s in PS. -PageStorage needs to handle a large amount of delta data read and write, this part of the data is hot data. And a small amount of DT meta information needs to be persisted, this part is cold data. The data of delta will eventually be merged into the stable part, eq. stored in the DT file. +PageStorage needs to handle a large amount of delta data read and write, this part of the data is hot data. And a small amount of DT meta information needs to be persisted, this part is cold data. The data of delta will eventually be merged into the `stable part`(In DT). The `stable part` of DT can be referred to dmf file. -You can see the picture below. Described in the picture is the design of the entire DT. PageStorage carries the traffic of the upper half of DT. +You can see the picture below. Described in the picture is the design of the DT. PageStorage stored `delta`, the delta part of data always be updated. After `delta merge` happend, the delta in PageStorage will be merge to the stable part(in picture, it named stable value space). ![tiflash-dt-architecture](./images/tiflash-dt-architecture.png) -As one of the important components of DMS, PageStorage mainly provides a KV storage service which also support MVCC. Unlike other KV services, the KV interface provided by PageStorage is limited. Key is limited to uint64_t, Value is limited to a buffer or a array of buffer(we called it fields) or null. +As one of the important components of DT, PageStorage mainly provides a KV storage service which also support MVCC. Unlike other KV services, the KV interface provided by PageStorage is limited. Key is limited to uint64_t, Value is limited to a buffer or a array of buffer(we called it fields) or null. ## Capability @@ -31,147 +31,25 @@ PageStorage supported: Currently there are three versions of PageStorage (V1, V2, V3). The V1 version is no longer used by DT. Due to compatibility considerations, it's still used in some components, we considered to remove it the future. It won't be covered too much in this article. -At present, most of our customers use the V2 version, which is also the version with the longest service time. Due to design issues, the V2 version has some problems that cannot be improved(will be introduced below), so we propose the V3 version. +At present, most of our customers use the V2 version. Due to design issues, the V2 version has some problems that cannot be improved(will be introduced below), so we propose the V3 version. -The V3 version has been proposed for a period of time, and it was officially used by customers in TiFlash v6.1.0. This version has improved the shortcomings of V2 very well. The redesign has made the module very good in terms of performance and maturity. - -### 1. V2 version - -![tiflash-ps-v2-architecture](./images/tiflash-ps-v2-architecture.png) - -The picture below describes the design in PageStorage V2. -We provider a big PageMap which stored data and meta of pages. Also provider Writable/Readable pagefile to support write/read of page. - -Here is the basic elements in PageStorage V2: - -- WriteBatch: a batch of write - - upper layer used this struct to write a batch of buffer into PageStorage. -- Page: basic data elements - - it contains id, buffer, fields... - - A set of pages will as return value when upper layer read by a set of page id. -- PageFile: contains meta and data - - data: buffer will store into the this part - - meta: used to manager the data part record every data of page. -- PageFileReader: used to read a single pagefile. -- PageFileWriter: used to write a single pagefile. -- PageEntriesVersionSetWithDelta: The MVCC object - - -#### meta and data - -Pagefile is a directory on the filesystem which is named with type_id_level. For example page_55_1, “page” means that pagefile type is “formal”, “55” is the pagefile id, “1” is the level. - -Pagefile is composed of multiple meta buffers and multiple data buffers. Meta buffer keep the pagefile meta info, it contains multiple write batch buffer. - -Here is the meta sturct: - -Bits | Name | Description. | ---------------------|------------------|-----------------------| -0:32 | Meta Byte Size | The buffer length | -32:64 | version | The page format version, should be V1 or V2 | -64:128 | SequenceID | The ID of sequence | -128:N | Buffer list | Collection of meta buffer, length is flexible | -N:+64 | Checksum | Checksum | - -A write batch can contain a sequence of operations(put/upsert/del/ref), so a buffer list is a flexible list to record that. - -buffer list(operate:put/upsert) - -Bits | Name | Description. | ---------------------|------------------|-----------------------| -0:8 | Write Type | Write batch operation type | -8:72 | Page ID | The page Id | -72:136 | File ID | The pagefile Id | -136:168 | Level | The page level | -168:200 | Flag | The page Flag decide page detach or not | -200:264 | Page offset | The page offset which record data offset in current pagefile | -264:328 | Page size | The page size which record data size | -328:392 | Page checksum | The page checksum | -392:456 | Field offsets length | The length of field offset | - -buffer list(operate:del) - - -Bits | Name | Description. | ---------------------|------------------|-----------------------| -0:8 | Write Type | Write batch operation type | -8:72 | Page ID | The page Id | - -buffer list(op:ref) - -Bits | Name | Description. | ---------------------|------------------|-----------------------| -0:8 | Write Type | Write batch operation type | -8:72 | Page ID | The page Id | -72:136 | Origin Page ID | The Origin page id | - -Field offsets - -Bits | Name | Description. | ---------------------|------------------|-----------------------| -0:64 | Field Offset | The field offset | -64:128 | Field Checksum | The field checksum | - -The data part is more simple, it have not contain any of meta info, it only consists of buffer. - -Both of data and meta, we used append write + sync IO to write the file, data clean will be done by GC part. Also the file size of both is limited. If the data is larger than 128M(default), a new file will be automatically generated. - - -#### MVCC - -`PageEntriesVersionSetWithDelta` is the main class of the MVCC, It's a big list, collect all version of page entries(it's a extension of Page object), The entries contain with every single page. Once snapshot created, the list will add a new version of page entries. - -The API from PageStorage have a method named `getSnapshot()`. After call this method, MVCC will generate a snapshot which record all of page entries(in memory) in current time. - -Then every read API from PageStorage request the snapshot as one of the parameters.But by default, the snapshot can be nullptr, then we will create a new snapshot for it. - -In read method, PageStorage will use snapshot + page ids to find the page entries. After we got the entries, we will read the data buffer from pagefile and combine it to a set of page, Then return to the caller. - -Also anytime there is a del operation come, we will not immediately mark the data on the disk to be cleaned up. Instead GC part will do the clean job. - -#### GC - -GC relative the snapshots release. If none of the snapshots in memory are released, then the GC will not clean up any data. In general, snapshots are released every once in a while, This will generate a lot of "expired" data on disk. - -At each round of GC, the GC thread scans each pagefiles and calculates the valid rate of the pagefile through MVCC. valid rate equal to total valid size divide by total file size, If valid rate lower than 0.5(by default), we will do the compact GC. - -The compact GC will compact the valid page into a new pagefile. This process is in order not to generate spatial magnification.But the same, it will produce write amplification. - -During the GC process, we do not lock the entire PageStorage, but use more fine-grained locks to ensure that the current PageStorage can respond to read and write. - -#### write/read example - -After write request comes: - -- Get a idle PageFileWriter(which is not locked), and locked it to avoid anther thread write. -- Generate the meta record. -- Write buffer into the disk. -- Write the meta record into meta. - -After read request comes: - -- Get a snapshot from caller or create a snapshot. -- Find the entries from the MVCC by snapshot + page ids. -- Using entries, read from disk. -- Combine the buffer and entries into pages and then return to the caller. - -### 2. V3 version +### V3 version After our customers used TiFlash, we found some problems with the V2 version in the actual production environment. -1. **There is a risk of data loss in the meta part**. The probability of this kind of risk happening is very small, and we have never encountered it. But in theory, as long as the checksum and buffer size fields in a single meta buffer are damaged at the same time, the subsequent buffers will be unavailable. -2. **The snapshot of MVCC needs to be optimized**. First, the memory occupied by the snapshot can be reduced, and secondly, we do not need such a complex structure to implement MVCC. -3. **The GC write amplification in the data part is too severe, The GC task is too heavy**. Since the data part is composed of append write, compact gc is frequently triggered, and the read and write volume of each gc will be large. +1. **There is a risk of data loss in the meta part**. As long as the `checksum` and `buffer size` fields in a single meta buffer are damaged at the same time, the subsequent buffers will be unavailable.This situation may happen when When the disk failure or the meta been changed due to wrong operation. +2. **The snapshot of MVCC needs to be optimized**. First, the memory occupied by the snapshot can be reduced. Secondly, There are no need such a complex structure to implement MVCC. +3. **The GC write amplification in the data part is too severe, The GC task is too heavy**. Since the data part is composed of append write, `compact gc` is frequently triggered, The `compact gc` means PageStorage need read all of the valid data from the disk, then rewrite it into a new file. Each round of GC brings additional read and write overhead. -Besides these three problem, we also changed lock mode and CRC implements, make the V3 better than V2. +Besides these three problems, we also changed the `lock` implements and the `CRC` implements. ![tiflash-ps-v3-architecture](./images/tiflash-ps-v3-architecture.png) -The V3 version of PageStorage consists of two main components, one is WALStore and the other is BlobStore. +The V3 version of PageStorage consists of two main components, one is `WALStore` and the other is `BlobStore`. - WALStore(Write Ahead Log Store): Using the write ahead log file format to manager the meta part. -- PageDirectory: Provides the function of MVCC. Smaller memory usage and faster speed. -- BlobStore: Provides an address space management. Using the address multiplexing to manage the data part. +- PageDirectory: Provides the function of MVCC. Smaller memory usage and faster speed than V2. +- BlobStore: Provides an spaces management. Using the address multiplexing to manage the data part. #### WALStore @@ -179,20 +57,21 @@ WALStore using the fixed block space to manager the meta. ![tiflash-ps-v3-walstore](./images/tiflash-ps-v3-wal-store.png) -As you can see, we fixed the meta into one block in V3. -- A block contains multiple meta record. -- If a block have some spacemap remain, But it can't insert a meta. Then using a padding to full it. +WALStore used fixed size of a block to manage meta part. -If crc and meta size are broken at the same time. It is only necessary to discard the data in a single block, and the block before or after will be preserved. This is because we will read meta in by single block, single block error won't effect others. This is also more convenient to troubleshoot the cause of meta errors. +- A block contains multiple meta records. +- If a block has some space unused, Also unused space size less than a meta record size. Then using a `padding` to full the unused space. -WALStore provider two main interfaces +If crc and meta size are broken at the same time. It is only need to discard the data in a single block, and the neighboring block(the prev block or next block) will be preserved. Because WALStore will read meta from single block, If single block got fault won't effect others. Also more convenient to troubleshoot the cause of meta errors. -- **apply**: after apply, the meta info will be will be serialized to disk.This happens after writing data. -- **read**: Read serialized meta info from disk. This will happen after pagestorage restore. +WALStore provider two main interfaces: -In addition, meta also needs to perform gc. Although the overall read/write sizes is not large, But it will also sort out some invalid meta information. +- **apply**: After `apply`, the meta info will be serialized on disks. This happens after writing datas. +- **read**: Read serialized meta info from disks. This will happen after `PageStorage` restored. -The sturct of meta similar with V2, but still have some differents. +In addition, WALStore also needs GC. Although the overall read/write sizes is not large(about 4-6 mb read and write), But WALStore still need sort out some invalid meta information on disks. + +The sturction of meta similar with V2: buffer(operate:put/upsert) @@ -263,9 +142,9 @@ Bits | Name | Description. | 8:132 | Page Id | The combine of namespace id and page id | 132:260 | Version | The combine of sequence and epoch | -The page id in V3 is is a 128bit unsigned int replace the 64bit. That is because instead of generating an instance of PageStorage for each table, there are only four instances of PageStorage globally(Type log/Type data/Type meta/KVStore). So we need additional 64bit space (eq. namespace id) to distinguish different tables. +The page id in V3 is is a 128bit unsigned integer replace the 64bit(in v2). Because instead of generating three instances(meta/data/log) of PageStorage for each table, there are only four instances of PageStorage globally(log/data/meta/KVStore). So PageStorage need additional 64bit space (ie. namespace id) to distinguish different tables. -Multi of meta info will conbime into a block. So it is different with V2, we added a WAL format to pack the meta struct. +Multi of meta info will be conbimed into a block. So it is different with V2, WALStore added a WAL format to pack the meta struction. Legacy record format: @@ -292,14 +171,14 @@ Recyclable record format: Same as above, with the addition of -- Log number: 32bit log file number, so that we can distinguish between records written by the most recent log writer vs a previous one. +- Log number: 32bit log file number, so that WALStore can distinguish between records written by the most recent log writer vs a previous one. #### PageDirectory -PageDirectory supports the function of PageDirectory MVCC. Its main component is a map. The key in map is page id and the value in map is the version chian. +PageDirectory supports the function of PageDirectory MVCC. Its main component is a hashmap. The key in map is `page id` and the value in map is the `version chian`. -The version chain consists of [seq|epoch] and page entry, sequence + epoch determines the position of the page entry in the chain, page entry have similar implementation with V2, but simplifies unnecessary structures. +The `version chain` consists of `[seq|epoch]` and `page entry`, sequence + epoch determines the position of the page entry in the chain, `page entry` have similar implementation with V2, It is the embodiment of the page in memory, recording the location of the data. Here is a PageDirectory example: @@ -309,61 +188,53 @@ page id 2 : {[seq 1 + epoch 0, entry 4], [seq 2 + epoch 0, entry 5], [seq 5 + ep page id 100 : {[seq 1 + epoch 0, entry 7], [seq 10 + epoch 2, entry 8]} ``` -In this example, we have 3 page with differe id. +In this example, MVCC have 3 page with differe id. - page 1 has 3 different versions corresponding to 3 entries. page 100 has 2 different versions corresponding to 2 entries. -- The seq will increase If we have a corresponding new page written. -- The epoch will be increase If current entry have been compact GC. +- The seq will increase If MVCC have a corresponding new page written. +- The epoch will be increase If current entry have been full GC. -`getSnapshot()` in V3 is very different from V2, In V2, we actually generate snapshots, there are some copies of memory. But in V3, Snapshot only contains a sequence id which can filter the right pages from the PageDirectory. +`getSnapshot()` in V3 is very different from V2, In V2, MVCC actually generate snapshots, there are some copies of memory. But in V3, Snapshot only contains a `sequence id` which can filter the right pages from the PageDirectory. -In upper example. If sequence in snapshot is 2 and query page id is 2. Then we will got the entry 5. +In upper example. If sequence in snapshot is 2 and query page id is 2. Then MVCC will return +the entry 5. #### BlobStore -BlobStore's name is earthy, but apt. It mainly stores Blob, which consists of three parts +BlobStore's name is earthy, but apt. It mainly stores `Blob`, which consists of three parts 1. **BlobFile**: Blob stored file, used to write and read. -2. **BlobStat**: A space manager, one-to-one correspondence with blobfile. used to find/alloc/free address space in BlobFile. +2. **BlobStat**: A space manager, one-to-one correspondence with blobfile. used to find/alloc/free spaces in BlobFile. 3. **BlobStats**: Manage all BlobStat. Used to schedule all write requests. -Different with V2 design, we decided to cancel append write mode, Instead, a data structure called a space map is used to manage the file address space. +Different to V2 design, we decided to abandon the append write mode. Instead, a data structure called SpaceMap used to manage the file spaces. ![tiflash-ps-v3-freemap](./images/tiflash-ps-v3-freemap.png) -This idea come form a rb-tree bitmap implements. Every node in rb-tree have a space which conbime with offset + size.But the difference is that bitmap uses rb-tree to record used locations, and we use rb-tree to record free locations.This is because record free locations will better to find space where you can insert. +This idea come form a rb-tree bitmap implements. Every node in `rb-tree`(red black tree) have a space which conbime with offset + size. But the difference is that bitmap uses `rb-node`(the node from red black tree) to record used locations, BlobStore used `rb-node` to record free locations. Because record free locations better to find a free space which used to store a buffer of data. -The spacemap(freemap) inside BlobStat, BlobStat can use it to reuse the reclaimed address space, thereby reducing write amplification. +The SpaceMap inside `BlobStat`, BlobStat can use it to reuse the reclaimed space, thereby reducing write amplification. -In addition, BlobStat also needs to provide external statistical status, such as the valid rate of the current BlobFile, the maximum capacity of the current Space map, and so on. These data must be calculated when inserting and releasing, otherwise we have to traverse the space map to get the data we need. +In addition, BlobStat also needs to provide external statistical status, such as the valid rate of the current BlobFile, the maximum capacity of the current SpaceMap, and so on. These data must be calculated when inserting and removing, otherwise BlobStat have to traverse the SpaceMap to get the data we need. -The statistical status from BlobStat is very necessary and useful. It has two main functions +The statistical status from BlobStat is very necessary and useful. 1. Provide it to BlobStats so that BlobStats can determine whether to create a new Blobfile or reuse the old one. 2. When BlobStats selects BlobFile to write, it will write according to the BlobFile with the lowest valid rate. -3. When GC occurs, it can quickly determine whether the current BlobFile needs to be compact GC. -4. Because the release of the file address space may cause invalid data to be stored at the end of the BlobFile, the truncate operation can also be performed in time during GC to reduce space enlargement +3. When GC occurs, it can quickly determine whether the current BlobFile needs to be `full GC`(similar with `compact GC`). +4. Because the release of the file space may cause invalid data to be stored at the end of the BlobFile, the truncate operation can also be performed in time during GC to reduce space enlargement. -Finally, from the description of BlobStat, it is not difficult to find that BlobStats is used to schedule all write IO. Then write request happend, we will request a free space from BlobStats, Then we can put the buffer into that disk space. These operate all happend in memory, So we can maximize IO parallelization. +Finally, from the description of BlobStat, it is not difficult to find that BlobStats is used to schedule all write IO. Then write request happend, BlobStore will ask a unused space from BlobStats, Then BlobStore can put the buffer into that disk space. These operate all happend in memory, So BlobStore can maximize IO parallelization. #### GC -The GC of V3 will be more complicated, but compared with V2, the GC of V3 is faster. In the test, the IO from V3 GC will be much less. +The GC of V3 will be more complicated, but compared with V2, the GC of V3 is faster. The IO from V3 GC will be much less. -1. Begin to GC, we need do WALStore GC at first, this part is lightweight. When the meta file recorded by wal reaches a certain level, we will dump it and update it. -2. After that, We need do MVCC GC, we will cleanup the expired snapshot in PageDirectory, also these expired page entry from expired snapshot will be mark clean in BlobStore. This means that we will mark the address space originally occupied by these expired page entries as unused. -3. Some of space in BlobStore be free after MVCC GC, Then BlobStore will check all BlobStat, find out if there is a blob with a low valid rate, and perform compact gc on it.The compact GC simliar with V2, it will copy the valid data ,and store it to other place. -4. If we do have compact GC happend in BlobStore, Then we need to tell PageDirectory that some data has been migrated and where is the new location. +1. Begin to GC, PageStorage need do WALStore GC, this part is lightweight. When the meta file recorded by wal reach a certain level, WALStore will dump it and update it. +2. After that, PageStorage need do MVCC GC, MVCC will cleanup the expired snapshot in PageDirectory, also these expired page entry from expired snapshot will be mark clean in BlobStore. This means that all of SpaceMap in BlobStore will be updated. +3. Some of space in BlobStore be free after MVCC GC, Then BlobStore will check all BlobStat, find out if there is a blob with a low valid rate, and perform full gc on it.The full GC simliar with V2, it will copy the valid data ,and store it to other blob. +4. If There do have full GC happend in BlobStore(from step 3), Then BlobStore need to tell PageDirectory that some of data has been migrated and where is the new location. PageDirectory will apply the change from BlobStore. Although this looks complicated, in practice, the probability of BlobStore triggering full gc is very low, which means that we will not generate too many read and write IO during the GC process. - - - - - - - - - From 98b37a7b88e01de5cffd5cefe2dc748d4fc966a8 Mon Sep 17 00:00:00 2001 From: jiaqizho Date: Tue, 17 May 2022 15:31:20 +0800 Subject: [PATCH 05/25] add tech --- .../0000-00-01-architecture-of-storage-engine-pagestorage.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/design/0000-00-01-architecture-of-storage-engine-pagestorage.md b/docs/design/0000-00-01-architecture-of-storage-engine-pagestorage.md index 55b01206109..3ec254caed1 100644 --- a/docs/design/0000-00-01-architecture-of-storage-engine-pagestorage.md +++ b/docs/design/0000-00-01-architecture-of-storage-engine-pagestorage.md @@ -1,6 +1,7 @@ # Architecture Of Storage Engine - PageStorage - Authors(order by last name): [JaySon-Huang](https://github.com/JaySon-Huang), [flowbehappy](https://github.com/flowbehappy), [Jiaqi Zhou](https://github.com/jiaqizho) +- Technical Writer: [shichun-0415](https://github.com/shichun-0415) , [Jiaqi Zhou](https://github.com/jiaqizho) ## Introduction From a24651c0a595fc25a389e6e39a815b09906edf61 Mon Sep 17 00:00:00 2001 From: jiaqizho Date: Tue, 17 May 2022 15:32:36 +0800 Subject: [PATCH 06/25] update --- .../0000-00-01-architecture-of-storage-engine-pagestorage.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/design/0000-00-01-architecture-of-storage-engine-pagestorage.md b/docs/design/0000-00-01-architecture-of-storage-engine-pagestorage.md index 3ec254caed1..aa9bbab024a 100644 --- a/docs/design/0000-00-01-architecture-of-storage-engine-pagestorage.md +++ b/docs/design/0000-00-01-architecture-of-storage-engine-pagestorage.md @@ -5,9 +5,7 @@ ## Introduction -`PageStorage` is the place where DT(DeltaTree Engine) actually stores data into. The latest data (i.e. delta data), the metadata in the engine are serialized directly into Pages. While the main data, i.e. stable data, is written in DTFiles format and managed as `ExternalPage`s in PS. - -PageStorage needs to handle a large amount of delta data read and write, this part of the data is hot data. And a small amount of DT meta information needs to be persisted, this part is cold data. The data of delta will eventually be merged into the `stable part`(In DT). The `stable part` of DT can be referred to dmf file. +`PageStorage` is the place where DT(DeltaTree Engine) actually stores data into. The latest data (i.e. delta data), the metadata in the engine are serialized directly into Pages. While the main data, i.e. stable data, is written in DTFiles format and managed as `ExternalPage`s in PageStorage. You can see the picture below. Described in the picture is the design of the DT. PageStorage stored `delta`, the delta part of data always be updated. After `delta merge` happend, the delta in PageStorage will be merge to the stable part(in picture, it named stable value space). From dc4b01dbeb16e991587e5b5fa5f98c88951f2a63 Mon Sep 17 00:00:00 2001 From: jiaqizho Date: Wed, 18 May 2022 09:51:05 +0800 Subject: [PATCH 07/25] updtae --- ...chitecture-of-storage-engine-pagestorage.md} | 0 .../images/tiflash-ps-v2-architecture.png | Bin 42542 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) rename docs/design/{0000-00-01-architecture-of-storage-engine-pagestorage.md => 2022-04-28-architecture-of-storage-engine-pagestorage.md} (100%) delete mode 100644 docs/design/images/tiflash-ps-v2-architecture.png diff --git a/docs/design/0000-00-01-architecture-of-storage-engine-pagestorage.md b/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md similarity index 100% rename from docs/design/0000-00-01-architecture-of-storage-engine-pagestorage.md rename to docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md diff --git a/docs/design/images/tiflash-ps-v2-architecture.png b/docs/design/images/tiflash-ps-v2-architecture.png deleted file mode 100644 index 4d91e8e270b74b4caa33dbeb6ad9729ec20e975b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 42542 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 From 3a3995eaa9929d4273228e3a45f747fc622fb94b Mon Sep 17 00:00:00 2001 From: JaySon Date: Tue, 17 May 2022 18:28:42 +0800 Subject: [PATCH 08/25] Apply suggestions from code review --- ...22-04-28-architecture-of-storage-engine-pagestorage.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md b/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md index aa9bbab024a..824d84543ea 100644 --- a/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md +++ b/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md @@ -5,9 +5,9 @@ ## Introduction -`PageStorage` is the place where DT(DeltaTree Engine) actually stores data into. The latest data (i.e. delta data), the metadata in the engine are serialized directly into Pages. While the main data, i.e. stable data, is written in DTFiles format and managed as `ExternalPage`s in PageStorage. +`PageStorage` is the place where DT(DeltaTree Engine) actually stores data into. The latest data (i.e. delta data), and the metadata in the engine are serialized directly into Pages. While the main data (i.e. stable data), is written in DTFiles format and managed as `ExternalPage`s in PageStorage. -You can see the picture below. Described in the picture is the design of the DT. PageStorage stored `delta`, the delta part of data always be updated. After `delta merge` happend, the delta in PageStorage will be merge to the stable part(in picture, it named stable value space). +The below picture describes the "Delta ValueSpace" and "Stable ValueSpace" of DT. The data in "Delta ValueSpace" is continuously updated. After `delta merge` happened, the delta data in PageStorage will be read and compacted into the "Stable ValueSpace". ![tiflash-dt-architecture](./images/tiflash-dt-architecture.png) @@ -28,9 +28,7 @@ PageStorage supported: ## Design -Currently there are three versions of PageStorage (V1, V2, V3). The V1 version is no longer used by DT. Due to compatibility considerations, it's still used in some components, we considered to remove it the future. It won't be covered too much in this article. - -At present, most of our customers use the V2 version. Due to design issues, the V2 version has some problems that cannot be improved(will be introduced below), so we propose the V3 version. +Currently, there are three versions of PageStorage (V1, V2, V3). We won't describe the details of the V1/V2 in this article. The V2 design and implementation lead to high write amplification and CPU usage under some scenarios, so we propose the V3 version. ### V3 version From e959bd61858250f4fc2eecb837b000953bf37371 Mon Sep 17 00:00:00 2001 From: JaySon Date: Tue, 17 May 2022 18:42:19 +0800 Subject: [PATCH 09/25] Apply suggestions from code review --- ...-28-architecture-of-storage-engine-pagestorage.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md b/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md index 824d84543ea..33eb4a39802 100644 --- a/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md +++ b/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md @@ -30,16 +30,18 @@ PageStorage supported: Currently, there are three versions of PageStorage (V1, V2, V3). We won't describe the details of the V1/V2 in this article. The V2 design and implementation lead to high write amplification and CPU usage under some scenarios, so we propose the V3 version. -### V3 version -After our customers used TiFlash, we found some problems with the V2 version in the actual production environment. +### Background +We found some problems with the V2 version in the actual production environment. -1. **There is a risk of data loss in the meta part**. As long as the `checksum` and `buffer size` fields in a single meta buffer are damaged at the same time, the subsequent buffers will be unavailable.This situation may happen when When the disk failure or the meta been changed due to wrong operation. -2. **The snapshot of MVCC needs to be optimized**. First, the memory occupied by the snapshot can be reduced. Secondly, There are no need such a complex structure to implement MVCC. -3. **The GC write amplification in the data part is too severe, The GC task is too heavy**. Since the data part is composed of append write, `compact gc` is frequently triggered, The `compact gc` means PageStorage need read all of the valid data from the disk, then rewrite it into a new file. Each round of GC brings additional read and write overhead. +1. **There is a risk of data loss in the meta part**. As long as the `checksum` and `buffer size` fields in a single meta buffer are damaged at the same time, the subsequent buffers will be unavailable. This situation may happen when the disk failure happens or the meta part is changed by an unexpected operation. +2. **The snapshot of MVCC needs to be optimized**. First, the CPU usage occupied by the snapshot should be reduced. Secondly, the implementation of MVCC structure is too complex to be understood and maintained. +3. **The GC write amplification in the data part is high and the GC task is too heavy**. Since the data part is composed of append write, `compact gc` is frequently triggered. Besides, the meta part is bound to a data part by "PageFile", meaning that we have to apply `compact gc` to compact the small data in the meta part. The `compact gc` means PageStorage need read all of the valid data from the disk, then rewrite it into a new file. Each round of GC brings additional read and write overhead. Besides these three problems, we also changed the `lock` implements and the `CRC` implements. +### V3 version + ![tiflash-ps-v3-architecture](./images/tiflash-ps-v3-architecture.png) The V3 version of PageStorage consists of two main components, one is `WALStore` and the other is `BlobStore`. From f0e2cb906a58c4ca4e519db5fd3a93ae5aad5285 Mon Sep 17 00:00:00 2001 From: JaySon Date: Tue, 17 May 2022 19:12:00 +0800 Subject: [PATCH 10/25] Apply suggestions from code review --- ...hitecture-of-storage-engine-pagestorage.md | 43 ++++--------------- 1 file changed, 8 insertions(+), 35 deletions(-) diff --git a/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md b/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md index 33eb4a39802..3798dfb1cd6 100644 --- a/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md +++ b/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md @@ -44,7 +44,7 @@ Besides these three problems, we also changed the `lock` implements and the `CRC ![tiflash-ps-v3-architecture](./images/tiflash-ps-v3-architecture.png) -The V3 version of PageStorage consists of two main components, one is `WALStore` and the other is `BlobStore`. +The V3 version of PageStorage is composed of three main components, `PageDirectory`, `WALStore`, and `BlobStore`. - WALStore(Write Ahead Log Store): Using the write ahead log file format to manager the meta part. - PageDirectory: Provides the function of MVCC. Smaller memory usage and faster speed than V2. @@ -52,21 +52,20 @@ The V3 version of PageStorage consists of two main components, one is `WALStore` #### WALStore -WALStore using the fixed block space to manager the meta. - ![tiflash-ps-v3-walstore](./images/tiflash-ps-v3-wal-store.png) -WALStore used fixed size of a block to manage meta part. +WALStore used a fixed size of a block to manage the meta part. - A block contains multiple meta records. -- If a block has some space unused, Also unused space size less than a meta record size. Then using a `padding` to full the unused space. +- If a block has some space unused, and the unused space size is less than a meta record size. Then the unused space is filled by "padding". + +This file format is convenient to detect disk failure. If a disk failure happens, we can stop TiFlash from startup and returning wrong results. In the worst case when CRC and the length of a meta record are broken at the same time, we can try to discard the broken data by the fixed-length block, other blocks can be preserved and try to recover some data. -If crc and meta size are broken at the same time. It is only need to discard the data in a single block, and the neighboring block(the prev block or next block) will be preserved. Because WALStore will read meta from single block, If single block got fault won't effect others. Also more convenient to troubleshoot the cause of meta errors. -WALStore provider two main interfaces: +WALStore provides two main interfaces: -- **apply**: After `apply`, the meta info will be serialized on disks. This happens after writing datas. -- **read**: Read serialized meta info from disks. This will happen after `PageStorage` restored. +- **apply**: After `apply`, the meta info will be atomicity serialized on disks. This happens after writing data to BlobStore. +- **read**: Read serialized meta info from disks. This will happen when `PageStorage` is being restored. In addition, WALStore also needs GC. Although the overall read/write sizes is not large(about 4-6 mb read and write), But WALStore still need sort out some invalid meta information on disks. @@ -145,32 +144,6 @@ The page id in V3 is is a 128bit unsigned integer replace the 64bit(in v2). Beca Multi of meta info will be conbimed into a block. So it is different with V2, WALStore added a WAL format to pack the meta struction. -Legacy record format: - -``` -+--------------+-----------+-----------+--- ... ---+ -|CheckSum (8B) | Size (2B) | Type (1B) | Payload | -+--------------+-----------+-----------+--- ... ---+ -``` - -- CheckSum: 64bit hash computed over the record type and payload using checksum algo (CRC64) -- Size: Length of the payload data -- Type: Type of record(ZeroType, FullType, FirstType, LastType, MiddleType) - - The type is used to group a bunch of records together to represent - - blocks that are larger than kBlockSize -- Payload: Byte stream as long as specified by the payload size - -Recyclable record format: - -``` -+--------------+-----------+-----------+----------------+--- ... ---+ -|CheckSum (8B) | Size (2B) | Type (1B) | Log number (4B)| Payload | -+--------------+-----------+-----------+----------------+--- ... ---+ -``` - -Same as above, with the addition of - -- Log number: 32bit log file number, so that WALStore can distinguish between records written by the most recent log writer vs a previous one. #### PageDirectory From 5f172e059501584f39a601eb4053ac9f5bcdc642 Mon Sep 17 00:00:00 2001 From: JaySon-Huang Date: Wed, 18 May 2022 12:35:56 +0800 Subject: [PATCH 11/25] Update PageDirectory Signed-off-by: JaySon-Huang --- ...hitecture-of-storage-engine-pagestorage.md | 94 +++++++++---------- 1 file changed, 42 insertions(+), 52 deletions(-) diff --git a/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md b/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md index 3798dfb1cd6..ec7e44bc1b2 100644 --- a/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md +++ b/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md @@ -26,12 +26,11 @@ PageStorage supported: - GC -## Design +## Background Currently, there are three versions of PageStorage (V1, V2, V3). We won't describe the details of the V1/V2 in this article. The V2 design and implementation lead to high write amplification and CPU usage under some scenarios, so we propose the V3 version. -### Background We found some problems with the V2 version in the actual production environment. 1. **There is a risk of data loss in the meta part**. As long as the `checksum` and `buffer size` fields in a single meta buffer are damaged at the same time, the subsequent buffers will be unavailable. This situation may happen when the disk failure happens or the meta part is changed by an unexpected operation. @@ -40,38 +39,61 @@ We found some problems with the V2 version in the actual production environment. Besides these three problems, we also changed the `lock` implements and the `CRC` implements. -### V3 version +## Design ![tiflash-ps-v3-architecture](./images/tiflash-ps-v3-architecture.png) The V3 version of PageStorage is composed of three main components, `PageDirectory`, `WALStore`, and `BlobStore`. -- WALStore(Write Ahead Log Store): Using the write ahead log file format to manager the meta part. -- PageDirectory: Provides the function of MVCC. Smaller memory usage and faster speed than V2. - BlobStore: Provides an spaces management. Using the address multiplexing to manage the data part. +- PageDirectory: Provides the function of MVCC. Smaller memory usage and faster speed than V2. +- WALStore(Write Ahead Log Store): Using the write ahead log file format to manager the meta part. -#### WALStore -![tiflash-ps-v3-walstore](./images/tiflash-ps-v3-wal-store.png) +#### PageDirectory + +PageDirectory supports the function of MVCC, providing a read-only snapshot that does not block writes. It is mainly composed of an RB-tree map with the key is `page id` and the value is the `version chain`. -WALStore used a fixed size of a block to manage the meta part. +The `version chain` is another RB-tree map with key `PageVersion` and value `PageEntry`. `PageEntry` represents the location of the data in `BlobStore` and is sorted by `PageVersion`(``) in the `version chain`. `PageVersion` is used for filtering by a read-only snapshot. PageDirectory will increase the `sequence` in serial when applying WriteBatches. The page entries created in the same WriteBatch use the same `sequence`, and the epoch is initialized to 0. After we applied "full GC" that moves the data into another location, we will create page entries with the same sequence but the epoch is last epoch + 1. -- A block contains multiple meta records. -- If a block has some space unused, and the unused space size is less than a meta record size. Then the unused space is filled by "padding". +Creating a snapshot for PageStorage is simply atomically getting the `sequence id` from PageDirectory. And the `sequence id` in the snapshot will be used to filter the result and provide an immutable result from PageDirectory. PageDirectory always return the latest entry version that is less than `sequence id + 1`. -This file format is convenient to detect disk failure. If a disk failure happens, we can stop TiFlash from startup and returning wrong results. In the worst case when CRC and the length of a meta record are broken at the same time, we can try to discard the broken data by the fixed-length block, other blocks can be preserved and try to recover some data. +Here is a example: +``` +page id 1 : {[(seq 1, epoch 0), entry 1], [(seq 2, epoch 0), entry 2], [(seq 3, epoch 1), entry 3]} +page id 2 : {[(seq 1, epoch 0), entry 4], [(seq 2, epoch 0), entry 5], [(seq 5, epoch 0), entry 6]} +page id 100 : {[(seq 1, epoch 0), entry 7], [(seq 10, epoch 2), entry 8]} +``` + +In this example, PageDirectory have 3 page with different id. Page 1 has 3 different versions corresponding to 3 entries. page 100 has 2 different versions corresponding to 2 entries. If caller get a entry for page id 2 with a snapshot that sequence is 2, PageDirectory will return the entry 5. + + +### WALStore WALStore provides two main interfaces: -- **apply**: After `apply`, the meta info will be atomicity serialized on disks. This happens after writing data to BlobStore. +- **apply**: After `apply`, the WriteBatch info will be atomicity serialized on disks. This happens after writing data to BlobStore. - **read**: Read serialized meta info from disks. This will happen when `PageStorage` is being restored. -In addition, WALStore also needs GC. Although the overall read/write sizes is not large(about 4-6 mb read and write), But WALStore still need sort out some invalid meta information on disks. +In order to control the time of restoring WriteBatches and reconstruct the PageDirectory while startups, PageDirectory will dump its snapshot into WALStore and clear the old log files by the snapshot. + +#### File format +![tiflash-ps-v3-walstore](./images/tiflash-ps-v3-wal-store.png) + +WALStore builds upon its serialized write batches (the meta part) base on log file format. The log file format is similar to the log file format as rocksdb [Write Ahead Log File Format](https://github.com/facebook/rocksdb/wiki/Write-Ahead-Log-File-Format#log-file-format). + +This log file format is convenient to detect disk failure. If a disk failure happens, we can stop TiFlash from startup and returning wrong results. In the worst case when CRC and the length of a meta record are broken at the same time, we can try to discard the broken data by the fixed-length block, other blocks can be preserved and try to recover some data. -The sturction of meta similar with V2: +WALStore serializes the WriteBatch into an atomic record and writes it to the log file upon on "log file format". As the log file reader ensure we can get a complete record from the log file format, we don't need to serialize the byte length of the WriteBatch into record. The serialize structure of WriteBatch: -buffer(operate:put/upsert) +buffer(WriteBatch) +Bits | Name | Description. | +--------------------|-------------------|-----------------------| +0:32 | WriteBatch version| Write batch version | +32:N | Operations | A series of operations| + +buffer(operate:put) Bits | Name | Description. | --------------------|------------------|-----------------------| @@ -80,9 +102,9 @@ Bits | Name | Description. | 16:144 | Page Id | The combine of namespace id and page id | 144:272 | Version | The combine of sequence and epoch | 272:336 | Ref count | The page be ref count | -272:N | page entry | The page entry | +272:N | Page entry | The page entry | -page entry +Page entry Bits | Name | Description. | --------------------|------------------|-----------------------| @@ -92,8 +114,7 @@ Bits | Name | Description. | 160:224 | Checksum | The Page Entry checksum | 224:288 | Tag | The Page tag | 288:352 | Field offsets length | The length of field offset | -352:N | Field offsets | The length field offsets | - +352:N | Field offsets | The length field offsets | Field offsets @@ -102,7 +123,7 @@ Bits | Name | Description. | 0:64 | Field Offset | The field offset | 64:128 | Field Checksum | The field checksum | -buffer(operate:put/upsert) +buffer(operate:put) Bits | Name | Description. | --------------------|------------------|-----------------------| @@ -111,8 +132,7 @@ Bits | Name | Description. | 16:144 | Page Id | The combine of namespace id and page id | 144:272 | Version | The combine of sequence and epoch | 272:336 | Ref count | The page be ref count | -272:N | page entry | The page entry | - +272:N | Page entry | The page entry | buffer(operate:ref) @@ -140,36 +160,6 @@ Bits | Name | Description. | 8:132 | Page Id | The combine of namespace id and page id | 132:260 | Version | The combine of sequence and epoch | -The page id in V3 is is a 128bit unsigned integer replace the 64bit(in v2). Because instead of generating three instances(meta/data/log) of PageStorage for each table, there are only four instances of PageStorage globally(log/data/meta/KVStore). So PageStorage need additional 64bit space (ie. namespace id) to distinguish different tables. - -Multi of meta info will be conbimed into a block. So it is different with V2, WALStore added a WAL format to pack the meta struction. - - - -#### PageDirectory - -PageDirectory supports the function of PageDirectory MVCC. Its main component is a hashmap. The key in map is `page id` and the value in map is the `version chian`. - -The `version chain` consists of `[seq|epoch]` and `page entry`, sequence + epoch determines the position of the page entry in the chain, `page entry` have similar implementation with V2, It is the embodiment of the page in memory, recording the location of the data. - -Here is a PageDirectory example: - -``` -page id 1 : {[seq 1 + epoch 0, entry 1], [seq 2 + epoch 0, entry 2], [seq 3 + epoch 1, entry 3]} -page id 2 : {[seq 1 + epoch 0, entry 4], [seq 2 + epoch 0, entry 5], [seq 5 + epoch 0, entry 6]} -page id 100 : {[seq 1 + epoch 0, entry 7], [seq 10 + epoch 2, entry 8]} -``` - -In this example, MVCC have 3 page with differe id. - -- page 1 has 3 different versions corresponding to 3 entries. page 100 has 2 different versions corresponding to 2 entries. -- The seq will increase If MVCC have a corresponding new page written. -- The epoch will be increase If current entry have been full GC. - -`getSnapshot()` in V3 is very different from V2, In V2, MVCC actually generate snapshots, there are some copies of memory. But in V3, Snapshot only contains a `sequence id` which can filter the right pages from the PageDirectory. - -In upper example. If sequence in snapshot is 2 and query page id is 2. Then MVCC will return -the entry 5. #### BlobStore From 459e1985e873115d69383787be96445b72816de0 Mon Sep 17 00:00:00 2001 From: JaySon-Huang Date: Wed, 18 May 2022 13:18:35 +0800 Subject: [PATCH 12/25] Update BlobStore Signed-off-by: JaySon-Huang --- ...hitecture-of-storage-engine-pagestorage.md | 54 +++++++++---------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md b/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md index ec7e44bc1b2..e15f6b95720 100644 --- a/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md +++ b/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md @@ -50,6 +50,32 @@ The V3 version of PageStorage is composed of three main components, `PageDirecto - WALStore(Write Ahead Log Store): Using the write ahead log file format to manager the meta part. +#### BlobStore + +BlobStore's name is earthy, but apt indicating that it mainly stores blob data. BlobStore consists of three parts + +1. **BlobFile**: The file stores blob data. +2. **BlobStat**: A space manager that is used to find/allocate/free spaces in BlobFile. It corresponds one-to-one with the BlobFile. +3. **BlobStats**: Manage all BlobStat. Used to schedule all write requests. + +Instead of storing data with append write, BlobStore uses a data structure called SpaceMap to manage the free spaces and perform random write in BlobFile. + +![tiflash-ps-v3-freemap](./images/tiflash-ps-v3-freemap.png) + +This idea comes from [Space Maps in Ext4 filesystem](https://www.kernel.org/doc/ols/2010/ols2010-pages-121-132.pdf). Each node in RB-tree has a space that is represented by (offset, size). The difference is that bitmap uses the node to record used locations, but BlobStore used the node to record free locations. Because recording free locations is better to find a free space by the data size. + +We represent the `BlobStat` by a SpaceMap to reuse the space after being reclaimed, thereby reducing write amplification. + +In order to avoid getting some simple statistics by traversing the full SpaceMap, BlobStat provides external statistical status, such as the valid rate of the current BlobFile, the maximum capacity, and so on. These statistics are useful for: + +1. BlobStats can determine whether to create a new Blobfile or reuse the old one. +2. BlobStats choose the lowest valid rate among all BlobFile to reuse the reclaimed space whenever possible, avoid creating new BlobFiles +3. GC routine can use these statistics to quickly determine whether the current BlobFile needs to perform `compact GC` to avoid severe space amplification +4. GC routine can perform truncate to free the spaces at the end of BlobFiles on disk. Reducing space amplification + +What's more, when a write request happens, BlobStore will ask BlobStats for unused space from BlobStats. Since all disk spaces are scheduled by BlobStats and happen in memory, once the location is allocated from BlobStats, all actual write operations to the disk can be parallelized. So BlobStore allows maximum IO parallelism to utilise disk bandwidth. + + #### PageDirectory PageDirectory supports the function of MVCC, providing a read-only snapshot that does not block writes. It is mainly composed of an RB-tree map with the key is `page id` and the value is the `version chain`. @@ -162,34 +188,6 @@ Bits | Name | Description. | -#### BlobStore - -BlobStore's name is earthy, but apt. It mainly stores `Blob`, which consists of three parts - -1. **BlobFile**: Blob stored file, used to write and read. -2. **BlobStat**: A space manager, one-to-one correspondence with blobfile. used to find/alloc/free spaces in BlobFile. -3. **BlobStats**: Manage all BlobStat. Used to schedule all write requests. - -Different to V2 design, we decided to abandon the append write mode. Instead, a data structure called SpaceMap used to manage the file spaces. - -![tiflash-ps-v3-freemap](./images/tiflash-ps-v3-freemap.png) - - -This idea come form a rb-tree bitmap implements. Every node in `rb-tree`(red black tree) have a space which conbime with offset + size. But the difference is that bitmap uses `rb-node`(the node from red black tree) to record used locations, BlobStore used `rb-node` to record free locations. Because record free locations better to find a free space which used to store a buffer of data. - -The SpaceMap inside `BlobStat`, BlobStat can use it to reuse the reclaimed space, thereby reducing write amplification. - -In addition, BlobStat also needs to provide external statistical status, such as the valid rate of the current BlobFile, the maximum capacity of the current SpaceMap, and so on. These data must be calculated when inserting and removing, otherwise BlobStat have to traverse the SpaceMap to get the data we need. - -The statistical status from BlobStat is very necessary and useful. - -1. Provide it to BlobStats so that BlobStats can determine whether to create a new Blobfile or reuse the old one. -2. When BlobStats selects BlobFile to write, it will write according to the BlobFile with the lowest valid rate. -3. When GC occurs, it can quickly determine whether the current BlobFile needs to be `full GC`(similar with `compact GC`). -4. Because the release of the file space may cause invalid data to be stored at the end of the BlobFile, the truncate operation can also be performed in time during GC to reduce space enlargement. - -Finally, from the description of BlobStat, it is not difficult to find that BlobStats is used to schedule all write IO. Then write request happend, BlobStore will ask a unused space from BlobStats, Then BlobStore can put the buffer into that disk space. These operate all happend in memory, So BlobStore can maximize IO parallelization. - #### GC The GC of V3 will be more complicated, but compared with V2, the GC of V3 is faster. The IO from V3 GC will be much less. From 97141bde4c6c869260ad08b44c6837248f41ae29 Mon Sep 17 00:00:00 2001 From: JaySon-Huang Date: Wed, 18 May 2022 14:47:28 +0800 Subject: [PATCH 13/25] Update GC routine Signed-off-by: JaySon-Huang --- ...chitecture-of-storage-engine-pagestorage.md | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md b/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md index e15f6b95720..516e001b2e3 100644 --- a/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md +++ b/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md @@ -50,7 +50,7 @@ The V3 version of PageStorage is composed of three main components, `PageDirecto - WALStore(Write Ahead Log Store): Using the write ahead log file format to manager the meta part. -#### BlobStore +### BlobStore BlobStore's name is earthy, but apt indicating that it mainly stores blob data. BlobStore consists of three parts @@ -76,7 +76,7 @@ In order to avoid getting some simple statistics by traversing the full SpaceMap What's more, when a write request happens, BlobStore will ask BlobStats for unused space from BlobStats. Since all disk spaces are scheduled by BlobStats and happen in memory, once the location is allocated from BlobStats, all actual write operations to the disk can be parallelized. So BlobStore allows maximum IO parallelism to utilise disk bandwidth. -#### PageDirectory +### PageDirectory PageDirectory supports the function of MVCC, providing a read-only snapshot that does not block writes. It is mainly composed of an RB-tree map with the key is `page id` and the value is the `version chain`. @@ -188,13 +188,11 @@ Bits | Name | Description. | -#### GC +### The GC routine of PageStorage -The GC of V3 will be more complicated, but compared with V2, the GC of V3 is faster. The IO from V3 GC will be much less. +In the beginning, in order to control the time of restoring WriteBatches and reconstructing the PageDirectory while startups, PageStorage needs to apply GC on WALStore. When the log files of WALStore reach a certain number, PageDirectory will serialize the latest version of entries and dump the entries as a record into a new log file. After that, the log files generated before the PageDirectory snapshot (except the log file is being written) will be removed. Usually, this part is lightweight since we traverse the PageDirectory will small lock granularity. And the PageDirectory's snapshot size dumped to disk is relatively small. +After that, PageStorage applies GC on PageDirectory. It will clean up the expired snapshot and expired page entries. Those expired page entries will be marked as removed from BlobStore. All related space maps in BlobStore will be updated. +After the space maps are updated, BlobStore will check all BlobStat to find out whether there are BlobFiles with a low valid rate. In order to reduce the space amplification brought by those low valid rate BlobFiles, PageStorage will perform "full GC" on those BlobFiles. The full GC will copy the valid data to the new BlobFile(s). +If "full GC" does happen in BlobStore, then BlobStore informs PageDirectory that some pages have been migrated and where are the new locations of those pages. PageDirectory applies the changes from BlobStore. -1. Begin to GC, PageStorage need do WALStore GC, this part is lightweight. When the meta file recorded by wal reach a certain level, WALStore will dump it and update it. -2. After that, PageStorage need do MVCC GC, MVCC will cleanup the expired snapshot in PageDirectory, also these expired page entry from expired snapshot will be mark clean in BlobStore. This means that all of SpaceMap in BlobStore will be updated. -3. Some of space in BlobStore be free after MVCC GC, Then BlobStore will check all BlobStat, find out if there is a blob with a low valid rate, and perform full gc on it.The full GC simliar with V2, it will copy the valid data ,and store it to other blob. -4. If There do have full GC happend in BlobStore(from step 3), Then BlobStore need to tell PageDirectory that some of data has been migrated and where is the new location. PageDirectory will apply the change from BlobStore. - -Although this looks complicated, in practice, the probability of BlobStore triggering full gc is very low, which means that we will not generate too many read and write IO during the GC process. +In practice, the probability of BlobStore triggering "full GC" is very low, which means that we will not generate too many read and write IO during the GC process. From 06e13cbaa08795b2a2c25b0e41096f465baa681e Mon Sep 17 00:00:00 2001 From: JaySon-Huang Date: Wed, 18 May 2022 14:59:12 +0800 Subject: [PATCH 14/25] Update Signed-off-by: JaySon-Huang --- ...hitecture-of-storage-engine-pagestorage.md | 21 +++++-------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md b/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md index 516e001b2e3..972ec28ea57 100644 --- a/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md +++ b/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md @@ -124,10 +124,10 @@ buffer(operate:put) Bits | Name | Description. | --------------------|------------------|-----------------------| 0:8 | Write Type | Write batch operation type | -8:16 | Flag | The page Flag decide page detach or not | +8:16 | Flag | A preserved flag | 16:144 | Page Id | The combine of namespace id and page id | 144:272 | Version | The combine of sequence and epoch | -272:336 | Ref count | The page be ref count | +272:336 | Ref count | The page being ref count | 272:N | Page entry | The page entry | Page entry @@ -146,19 +146,8 @@ Field offsets Bits | Name | Description. | --------------------|------------------|-----------------------| -0:64 | Field Offset | The field offset | -64:128 | Field Checksum | The field checksum | - -buffer(operate:put) - -Bits | Name | Description. | ---------------------|------------------|-----------------------| -0:8 | Write Type | Write batch operation type | -8:16 | Flag | The page Flag decide page detach or not | -16:144 | Page Id | The combine of namespace id and page id | -144:272 | Version | The combine of sequence and epoch | -272:336 | Ref count | The page be ref count | -272:N | Page entry | The page entry | +0:64 | Field Offset | The field offset | +64:128 | Field Checksum | The field checksum | buffer(operate:ref) @@ -176,7 +165,7 @@ Bits | Name | Description. | 0:8 | Write Type | Write batch operation type | 8:132 | Page Id | The combine of namespace id and page id | 132:260 | Version | The combine of sequence and epoch | -260:324 | Ref count | The page be ref count | +260:324 | Ref count | The page being ref count | buffer(operate:del) From ad6262429fc9ce4ff2c517d8bbd156e375cfc52c Mon Sep 17 00:00:00 2001 From: JaySon-Huang Date: Wed, 18 May 2022 15:07:48 +0800 Subject: [PATCH 15/25] Update Signed-off-by: JaySon-Huang --- .../2022-04-28-architecture-of-storage-engine-pagestorage.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md b/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md index 972ec28ea57..5f20965d2d6 100644 --- a/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md +++ b/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md @@ -128,7 +128,7 @@ Bits | Name | Description. | 16:144 | Page Id | The combine of namespace id and page id | 144:272 | Version | The combine of sequence and epoch | 272:336 | Ref count | The page being ref count | -272:N | Page entry | The page entry | +272:N | Page entry | The page entry | Page entry @@ -140,7 +140,7 @@ Bits | Name | Description. | 160:224 | Checksum | The Page Entry checksum | 224:288 | Tag | The Page tag | 288:352 | Field offsets length | The length of field offset | -352:N | Field offsets | The length field offsets | +352:N | Field offsets | The length field offsets | Field offsets From bbf18a731cc592f85bdbb128171321f1e227da56 Mon Sep 17 00:00:00 2001 From: JaySon Date: Wed, 18 May 2022 15:13:00 +0800 Subject: [PATCH 16/25] Apply suggestions from code review Co-authored-by: shichun-0415 <89768198+shichun-0415@users.noreply.github.com> --- ...chitecture-of-storage-engine-pagestorage.md | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md b/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md index 5f20965d2d6..480ea0f5360 100644 --- a/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md +++ b/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md @@ -1,31 +1,25 @@ # Architecture Of Storage Engine - PageStorage -- Authors(order by last name): [JaySon-Huang](https://github.com/JaySon-Huang), [flowbehappy](https://github.com/flowbehappy), [Jiaqi Zhou](https://github.com/jiaqizho) -- Technical Writer: [shichun-0415](https://github.com/shichun-0415) , [Jiaqi Zhou](https://github.com/jiaqizho) +- Authors (order by last name): [JaySon-Huang](https://github.com/JaySon-Huang), [flowbehappy](https://github.com/flowbehappy), [Jiaqi Zhou](https://github.com/jiaqizho) +- Editorial reviewer: [shichun-0415](https://github.com/shichun-0415) ## Introduction -`PageStorage` is the place where DT(DeltaTree Engine) actually stores data into. The latest data (i.e. delta data), and the metadata in the engine are serialized directly into Pages. While the main data (i.e. stable data), is written in DTFiles format and managed as `ExternalPage`s in PageStorage. +`PageStorage` is where Delta Tree (DT) engine actually stores data. The latest data (i.e. delta data), and the metadata in the engine are serialized into Pages. The main data (i.e. stable data), is written in DTFiles format and managed as `ExternalPage`s in PageStorage. -The below picture describes the "Delta ValueSpace" and "Stable ValueSpace" of DT. The data in "Delta ValueSpace" is continuously updated. After `delta merge` happened, the delta data in PageStorage will be read and compacted into the "Stable ValueSpace". +The following figure describes the "Delta ValueSpace" and "Stable ValueSpace" of DT. The data in "Delta ValueSpace" is continuously updated. After `delta merge` occurs, the delta data in PageStorage will be read and compacted into the "Stable ValueSpace". ![tiflash-dt-architecture](./images/tiflash-dt-architecture.png) - - -As one of the important components of DT, PageStorage mainly provides a KV storage service which also support MVCC. Unlike other KV services, the KV interface provided by PageStorage is limited. Key is limited to uint64_t, Value is limited to a buffer or a array of buffer(we called it fields) or null. - - +As one of the important components of DT, PageStorage mainly provides a KV storage service that also supports MVCC. Unlike other KV services, the KV interface provided by PageStorage is limited. Specifically, Key is limited to uint64_t, and Value is limited to a buffer or an array of buffers (also called fields) or null. ## Capability -PageStorage supported: +PageStorage supports: - Disk-based store - Write/Read operation atomicity - Full MVCC function - KV store function - GC - - ## Background Currently, there are three versions of PageStorage (V1, V2, V3). We won't describe the details of the V1/V2 in this article. The V2 design and implementation lead to high write amplification and CPU usage under some scenarios, so we propose the V3 version. From 403a7a6d1c6842c2b513f76501e9bc07ee2e3732 Mon Sep 17 00:00:00 2001 From: JaySon Date: Wed, 18 May 2022 15:16:27 +0800 Subject: [PATCH 17/25] Apply suggestions from code review Co-authored-by: shichun-0415 <89768198+shichun-0415@users.noreply.github.com> --- ...04-28-architecture-of-storage-engine-pagestorage.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md b/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md index 480ea0f5360..d5b39db877f 100644 --- a/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md +++ b/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md @@ -22,14 +22,14 @@ PageStorage supports: - GC ## Background -Currently, there are three versions of PageStorage (V1, V2, V3). We won't describe the details of the V1/V2 in this article. The V2 design and implementation lead to high write amplification and CPU usage under some scenarios, so we propose the V3 version. +Currently, there are three versions of PageStorage (V1, V2, V3). This document does not cover the details of the V1/V2 versions. The V3 version is recommended because the V2 design and implementation might lead to high write amplification and CPU usage in some scenarios. -We found some problems with the V2 version in the actual production environment. +The following are problems of V2 in a production environment: -1. **There is a risk of data loss in the meta part**. As long as the `checksum` and `buffer size` fields in a single meta buffer are damaged at the same time, the subsequent buffers will be unavailable. This situation may happen when the disk failure happens or the meta part is changed by an unexpected operation. -2. **The snapshot of MVCC needs to be optimized**. First, the CPU usage occupied by the snapshot should be reduced. Secondly, the implementation of MVCC structure is too complex to be understood and maintained. -3. **The GC write amplification in the data part is high and the GC task is too heavy**. Since the data part is composed of append write, `compact gc` is frequently triggered. Besides, the meta part is bound to a data part by "PageFile", meaning that we have to apply `compact gc` to compact the small data in the meta part. The `compact gc` means PageStorage need read all of the valid data from the disk, then rewrite it into a new file. Each round of GC brings additional read and write overhead. +- **There is a risk of data loss in the meta part**. As long as the `checksum` and `buffer size` fields in a single meta buffer are damaged at the same time, the subsequent buffers will be unavailable. This situation may occur upon a disk failure or the meta part is changed by an unexpected operation. +- **The snapshot of MVCC needs to be optimized**. First, the CPU usage occupied by the snapshot should be reduced. Second, the implementation of the MVCC structure is too complex to be understood and maintained. +- **The GC write amplification in the data part is high and the GC task is too heavy**. Because the data part is composed of append write, `compact gc` is frequently triggered. Besides, the meta part is bound to a data part by "PageFile", meaning that we have to apply `compact gc` to compact the small data in the meta part. `compact gc` means that PageStorage needs to read all the valid data from the disk, then rewrite the data into a new file. Each round of GC brings additional read and write overhead. Besides these three problems, we also changed the `lock` implements and the `CRC` implements. From b3a175e44703e173ad517790537c1f29b3896c89 Mon Sep 17 00:00:00 2001 From: jiaqizho Date: Wed, 18 May 2022 17:18:13 +0800 Subject: [PATCH 18/25] Update docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md Co-authored-by: shichun-0415 <89768198+shichun-0415@users.noreply.github.com> --- ...022-04-28-architecture-of-storage-engine-pagestorage.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md b/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md index d5b39db877f..57282823844 100644 --- a/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md +++ b/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md @@ -39,10 +39,9 @@ Besides these three problems, we also changed the `lock` implements and the `CRC The V3 version of PageStorage is composed of three main components, `PageDirectory`, `WALStore`, and `BlobStore`. -- BlobStore: Provides an spaces management. Using the address multiplexing to manage the data part. -- PageDirectory: Provides the function of MVCC. Smaller memory usage and faster speed than V2. -- WALStore(Write Ahead Log Store): Using the write ahead log file format to manager the meta part. - +- BlobStore: provides space management by using address multiplexing to manage the data part. +- PageDirectory: provides the function of MVCC. It features smaller memory usage and faster speed than V2. +- WALStore (Write Ahead Log Store): uses the write-ahead log file format to manage the meta part. ### BlobStore From 3613ac48da17efde87cef016147385409ab9091b Mon Sep 17 00:00:00 2001 From: jiaqizho Date: Wed, 18 May 2022 17:18:31 +0800 Subject: [PATCH 19/25] Update docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md Co-authored-by: shichun-0415 <89768198+shichun-0415@users.noreply.github.com> --- .../2022-04-28-architecture-of-storage-engine-pagestorage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md b/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md index 57282823844..2a899358269 100644 --- a/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md +++ b/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md @@ -45,7 +45,7 @@ The V3 version of PageStorage is composed of three main components, `PageDirecto ### BlobStore -BlobStore's name is earthy, but apt indicating that it mainly stores blob data. BlobStore consists of three parts +BlobStore's name is earthy, but apt indicating that it mainly stores blob data. BlobStore consists of three parts: 1. **BlobFile**: The file stores blob data. 2. **BlobStat**: A space manager that is used to find/allocate/free spaces in BlobFile. It corresponds one-to-one with the BlobFile. From 4f43f30f7de2dc1689a7f0df112f4a95a0e4d905 Mon Sep 17 00:00:00 2001 From: jiaqizho Date: Wed, 18 May 2022 17:18:38 +0800 Subject: [PATCH 20/25] Update docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md Co-authored-by: shichun-0415 <89768198+shichun-0415@users.noreply.github.com> --- ...2022-04-28-architecture-of-storage-engine-pagestorage.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md b/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md index 2a899358269..20eb5281b0e 100644 --- a/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md +++ b/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md @@ -47,9 +47,9 @@ The V3 version of PageStorage is composed of three main components, `PageDirecto BlobStore's name is earthy, but apt indicating that it mainly stores blob data. BlobStore consists of three parts: -1. **BlobFile**: The file stores blob data. -2. **BlobStat**: A space manager that is used to find/allocate/free spaces in BlobFile. It corresponds one-to-one with the BlobFile. -3. **BlobStats**: Manage all BlobStat. Used to schedule all write requests. +- **BlobFile**: The file that stores blob data. +- **BlobStat**: A space manager used to find/allocate/free space in BlobFile. It has one-to-one mapping with the BlobFile. +- **BlobStats**: Manage all BlobStat. It is used to schedule all write requests. Instead of storing data with append write, BlobStore uses a data structure called SpaceMap to manage the free spaces and perform random write in BlobFile. From 316bef1906f2328aa2f781eb0c1240bc212467ac Mon Sep 17 00:00:00 2001 From: jiaqizho Date: Wed, 18 May 2022 17:18:53 +0800 Subject: [PATCH 21/25] Update docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md Co-authored-by: shichun-0415 <89768198+shichun-0415@users.noreply.github.com> --- .../2022-04-28-architecture-of-storage-engine-pagestorage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md b/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md index 20eb5281b0e..e09faf8039b 100644 --- a/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md +++ b/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md @@ -51,7 +51,7 @@ BlobStore's name is earthy, but apt indicating that it mainly stores blob data. - **BlobStat**: A space manager used to find/allocate/free space in BlobFile. It has one-to-one mapping with the BlobFile. - **BlobStats**: Manage all BlobStat. It is used to schedule all write requests. -Instead of storing data with append write, BlobStore uses a data structure called SpaceMap to manage the free spaces and perform random write in BlobFile. +Instead of storing data with append write, BlobStore uses a data structure called SpaceMap to manage the free space and perform random write in BlobFile. ![tiflash-ps-v3-freemap](./images/tiflash-ps-v3-freemap.png) From 2389c40b862b2c9fa74db2a3006675e8118d5404 Mon Sep 17 00:00:00 2001 From: jiaqizho Date: Wed, 18 May 2022 17:18:59 +0800 Subject: [PATCH 22/25] Update docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md Co-authored-by: shichun-0415 <89768198+shichun-0415@users.noreply.github.com> --- .../2022-04-28-architecture-of-storage-engine-pagestorage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md b/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md index e09faf8039b..1f9411ac14a 100644 --- a/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md +++ b/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md @@ -55,7 +55,7 @@ Instead of storing data with append write, BlobStore uses a data structure calle ![tiflash-ps-v3-freemap](./images/tiflash-ps-v3-freemap.png) -This idea comes from [Space Maps in Ext4 filesystem](https://www.kernel.org/doc/ols/2010/ols2010-pages-121-132.pdf). Each node in RB-tree has a space that is represented by (offset, size). The difference is that bitmap uses the node to record used locations, but BlobStore used the node to record free locations. Because recording free locations is better to find a free space by the data size. +The design is inspired by [Space Maps in Ext4 filesystem](https://www.kernel.org/doc/ols/2010/ols2010-pages-121-132.pdf). Each node in RB-tree has a space that is represented by (offset, size). The difference is that bitmap uses a node to record used locations, but BlobStore uses a node to record free locations. Recording free locations is a better way to find a free space by the data size. We represent the `BlobStat` by a SpaceMap to reuse the space after being reclaimed, thereby reducing write amplification. From dc16692b9098dc68fc4bf418098cdc0b4f6783af Mon Sep 17 00:00:00 2001 From: jiaqizho Date: Wed, 18 May 2022 17:19:17 +0800 Subject: [PATCH 23/25] Update docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md Co-authored-by: shichun-0415 <89768198+shichun-0415@users.noreply.github.com> --- .../2022-04-28-architecture-of-storage-engine-pagestorage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md b/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md index 1f9411ac14a..50204d5672b 100644 --- a/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md +++ b/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md @@ -57,7 +57,7 @@ Instead of storing data with append write, BlobStore uses a data structure calle The design is inspired by [Space Maps in Ext4 filesystem](https://www.kernel.org/doc/ols/2010/ols2010-pages-121-132.pdf). Each node in RB-tree has a space that is represented by (offset, size). The difference is that bitmap uses a node to record used locations, but BlobStore uses a node to record free locations. Recording free locations is a better way to find a free space by the data size. -We represent the `BlobStat` by a SpaceMap to reuse the space after being reclaimed, thereby reducing write amplification. +`BlobStat` is represented by a SpaceMap to reuse the space after being reclaimed, thereby reducing write amplification. In order to avoid getting some simple statistics by traversing the full SpaceMap, BlobStat provides external statistical status, such as the valid rate of the current BlobFile, the maximum capacity, and so on. These statistics are useful for: From ca9097f09bf01884bdf035c4ad12f2241b3bd911 Mon Sep 17 00:00:00 2001 From: jiaqizho Date: Wed, 18 May 2022 17:35:56 +0800 Subject: [PATCH 24/25] updatee --- ...022-04-28-architecture-of-storage-engine-pagestorage.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md b/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md index 50204d5672b..22d1c465c84 100644 --- a/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md +++ b/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md @@ -31,7 +31,7 @@ The following are problems of V2 in a production environment: - **The snapshot of MVCC needs to be optimized**. First, the CPU usage occupied by the snapshot should be reduced. Second, the implementation of the MVCC structure is too complex to be understood and maintained. - **The GC write amplification in the data part is high and the GC task is too heavy**. Because the data part is composed of append write, `compact gc` is frequently triggered. Besides, the meta part is bound to a data part by "PageFile", meaning that we have to apply `compact gc` to compact the small data in the meta part. `compact gc` means that PageStorage needs to read all the valid data from the disk, then rewrite the data into a new file. Each round of GC brings additional read and write overhead. -Besides these three problems, we also changed the `lock` implements and the `CRC` implements. +Besides resolving the preceding problems of V2, the V3 version also optimized the `lock` and `CRC` implementation. ## Design @@ -45,7 +45,7 @@ The V3 version of PageStorage is composed of three main components, `PageDirecto ### BlobStore -BlobStore's name is earthy, but apt indicating that it mainly stores blob data. BlobStore consists of three parts: +BlobStore mainly stores blob data. BlobStore consists of three parts: - **BlobFile**: The file that stores blob data. - **BlobStat**: A space manager used to find/allocate/free space in BlobFile. It has one-to-one mapping with the BlobFile. @@ -173,8 +173,11 @@ Bits | Name | Description. | ### The GC routine of PageStorage In the beginning, in order to control the time of restoring WriteBatches and reconstructing the PageDirectory while startups, PageStorage needs to apply GC on WALStore. When the log files of WALStore reach a certain number, PageDirectory will serialize the latest version of entries and dump the entries as a record into a new log file. After that, the log files generated before the PageDirectory snapshot (except the log file is being written) will be removed. Usually, this part is lightweight since we traverse the PageDirectory will small lock granularity. And the PageDirectory's snapshot size dumped to disk is relatively small. + After that, PageStorage applies GC on PageDirectory. It will clean up the expired snapshot and expired page entries. Those expired page entries will be marked as removed from BlobStore. All related space maps in BlobStore will be updated. + After the space maps are updated, BlobStore will check all BlobStat to find out whether there are BlobFiles with a low valid rate. In order to reduce the space amplification brought by those low valid rate BlobFiles, PageStorage will perform "full GC" on those BlobFiles. The full GC will copy the valid data to the new BlobFile(s). + If "full GC" does happen in BlobStore, then BlobStore informs PageDirectory that some pages have been migrated and where are the new locations of those pages. PageDirectory applies the changes from BlobStore. In practice, the probability of BlobStore triggering "full GC" is very low, which means that we will not generate too many read and write IO during the GC process. From 617d2eb652c7ce5f07a9dd12d1234a28cefaac79 Mon Sep 17 00:00:00 2001 From: jiaqizho Date: Wed, 18 May 2022 17:38:25 +0800 Subject: [PATCH 25/25] format --- .../2022-04-28-architecture-of-storage-engine-pagestorage.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md b/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md index 22d1c465c84..ed297ed9b29 100644 --- a/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md +++ b/docs/design/2022-04-28-architecture-of-storage-engine-pagestorage.md @@ -10,6 +10,7 @@ The following figure describes the "Delta ValueSpace" and "Stable ValueSpace" of DT. The data in "Delta ValueSpace" is continuously updated. After `delta merge` occurs, the delta data in PageStorage will be read and compacted into the "Stable ValueSpace". ![tiflash-dt-architecture](./images/tiflash-dt-architecture.png) + As one of the important components of DT, PageStorage mainly provides a KV storage service that also supports MVCC. Unlike other KV services, the KV interface provided by PageStorage is limited. Specifically, Key is limited to uint64_t, and Value is limited to a buffer or an array of buffers (also called fields) or null. ## Capability