Managing a lsm tree of key value pairs that is stored in a non-volatile memory

TWI937137BActive Publication Date: 2026-09-01PLIOPS LTD
View PDF 4 Cites 0 Cited by

Patent Information

Application Number
TW110133037
Authority / Receiving Office
TW · TW
Patent Type
Patents
Current Assignee / Owner
Priority Date
2020-09-07
Filing Date
2021-09-06
Publication Date
2026-09-01
Estimated Expiration
2041-09-05

AI Technical Summary

Technical Problem

LSM trees in modern key-value stores face memory I/O bottlenecks due to the increasing number of Bloom Filters (BF) required for data structure architectures, which cannot be optimized without degrading other performance metrics, and Fingerprint Filters (FF) face challenges in scaling false positive rates and maintaining up-to-date run IDs efficiently.

Method used

The implementation of Fingerprint Filters with compressed run IDs using Huffman encoding and alignment techniques to manage LSM trees in non-volatile memory, allowing for efficient maintenance of run IDs without additional memory I/O and reducing both memory and storage I/O.

Benefits of technology

Chucky, a method integrating Huffman-encoded FFs, reduces memory I/O to a constant amount and maintains a scalable false positive rate, improving read and write latencies and throughput by minimizing memory bandwidth bottlenecks.

✦ Generated by Eureka AI based on patent content.

Smart Images

  • Figure TWG2TB001908114_001
    Figure TWG2TB001908114_001
  • Figure TWG2TB001908114_002
    Figure TWG2TB001908114_002
  • Figure TWG2TB001908114_003
    Figure TWG2TB001908114_003
Patent Text Reader

Abstract

A method for managing a log structure merge (LSM) tree of key-value (KV) pairs, the LSM tree being stored in non-volatile memory, the method comprising: writing a current run from a buffer to a current run position within the LSM tree, the current run including the current KV pair; generating or receiving a current fingerprint indicating the current KV pair; performing a run write update of the MDS by adding a mapping between the current KV pair, the current fingerprint, and a current run identifier to a management data structure (MDS); updating the LSM tree by merging at least some runs of the LSM tree; and performing a merge update of the MDS to indicate a merge.
Need to check novelty before this filing date? Find Prior Art

Description

[Technical Field]

[0001] This invention relates to an LSM tree for managing key-value pairs stored in non-volatile memory. [Previous Technology]

[0002] Background of the Invention

[0003] Modern key-value stores (KV-stores) rely on LSM trees to store data in storage. LSM trees optimize these by buffering application writes in memory, flushing the buffer to storage as sorted runs when it's full, and merging runs in a logarithmic order. To optimize application point reads, each run has an in-memory Bloom filter, allowing runs that don't contain the target entry to be excluded. This design is used in OLTP, HTAP, social graphs, blockchains, and more.

[0004] LSM trees were originally designed for hard disk drives, which are 5-6 orders of magnitude slower than DRAM memory chips. However, the advent of SSDs has narrowed the performance gap between storage and memory access to 2-3 orders of magnitude. Today, memory I / O takes 100ns, while SSD read I / O (e.g., on Intel Optane) takes 10 microseconds.

[0005] KV-SSDs exacerbate this trend by embedding KV storage logic within the SSD and thus eliminating bus latency from storage I / O (on SATA or PCE).

[0006] Therefore, memory access is no longer negligible relative to storage access. This drives the need to modify the data structure architecture to eliminate sprawling memory bandwidth bottlenecks.

[0007] KV-SSD represents KV pairs using a variety of data structures called Bloom filters (BF).

[0008] It has been found that BF (Browser Flow) in modern KV storage is becoming a bottleneck for memory I / O. First consider the LSM tree design, which optimizes writes through lazy merging, thus allowing tens to hundreds of runs in the system. For such designs, probing each BF at a cost of 100ns can approach or even exceed the latency of SSD I / O, which fetches the target entry from the memory.

[0009] As the number of runs and thus the number of BFs in the system increases, this overhead will increase with the amount of data.

[0010] As data grows, this overhead will increase due to the presence of more runs and therefore more BFs in the system.

[0011] Furthermore, many application workloads exhibit skew, causing the most popular entries to reside in the block cache in memory. Locating an entry in this cache may still require traversing all BFs to first identify the run containing the target entry (and then the data block within it). Since there is no memory I / O in this case, the memory I / O used to probe the BF becomes a bottleneck.

[0012] A naive solution is to adjust the LSM tree to merge more greedily, resulting in fewer runs and thus fewer BFs to probe. However, increasing merge greediness also increases memory write I / O, which may outweigh the benefits of saving BF probes. Furthermore, adjusting merge greediness increases the cost of building BFs, which can account for over 70% of the merge overhead. BFs are immutable and must be rebuilt from scratch during each merge operation. Therefore, a more greedy merge run also necessitates a more greedy reconstruction of the BF.

[0013] It is impossible for BF to reduce both detection and construction costs at the same time. In fact, both costs increase with the amount of data, thus forcing applications to make increasingly worse trade-off choices.

[0014] Over the past decade, a new family of data structures has emerged as an alternative to Bloom filters. They operate by storing a fingerprint for each data item in a compact hash table. We call these fingerprint filters (FFs), and they include quotient filters, cuckoo filters, and so on. Their promise lies in their support for storing auxiliary and updatable information for each entry alongside its fingerprint. Thus, multiple Bloom filters in an LSM tree can be replaced with an updatable FF that maps each data entry to a fingerprint and an auxiliary run identifier (also known as a run ID or RUNID). During application reads, the fingerprint matches or cancels the target key, while the run ID indicates which run searches for each fingerprint match.

[0015] Therefore, FF requires far less memory I / O than BF to look up the run for a given entry.

[0016] Unlike BF, which requires a large amount of memory I / O, FF allows the target run of an entry to be found using a virtually constant amount of memory I / O.

[0017] Since FFs achieve probing in constant access time, they are expected to replace memory I / O across multiple BFs with constant access time.

[0018] Scaling False Positives and Updating

[0019] There are two challenges in using FF for LSM tree filtering.

[0020] First, the false alarm rate (FPR) of FF cannot be scaled well.

[0021] This is due to the run ID, which must grow with the amount of data to uniquely identify more runs.

[0022] Assuming a fixed memory budget, the run ID must "steal" bits from the fingerprint as the data grows. This increases the FPR and leads to more memory I / O.

[0023] The second challenge is to efficiently keep the run ID within FF up-to-date. A possible solution is to issue a read command to the memory to check if the entry exists before writing, and if it does, update its run ID.

[0024] However, additional read I / O to memory is expensive. In summary, existing FF-based designs cannot reduce memory I / O, while BF-based designs cannot reduce memory I / O. [Summary of the Invention]

[0025] A method for managing a log structure merge (LSM) tree of key-value (KV) pairs, the LSM tree being stored in non-volatile memory, the method comprising: writing a current run from a buffer to a current run position within the LSM tree, the current run including a current KV pair; generating or receiving a current fingerprint indicating the current KV pair; performing a run write update of the MDS by adding a mapping between the current KV pair, the current fingerprint, and a current run identifier to a management data structure (MDS); updating the LSM tree by merging at least some runs of the LSM tree; and performing a merge update of the MDS to indicate the merge.

Implementation Method

[0049] Description of the example embodiments

[0050] Numerous specific details are set forth in the following detailed description in order to provide a thorough understanding of the invention. However, those skilled in the art will understand that the invention may be practiced without these specific details. In other instances, well-known methods, processes, and components have not been described in detail so as not to obscure the invention.

[0051] The subject matter of the invention is particularly pointed out and clearly claimed at the end of the specification. However, the invention, its methods of operation and organization, as well as its objects, features and advantages, can be best understood by referring to the following detailed description when read in conjunction with the accompanying drawings.

[0052] It will be understood that, for the sake of simplicity and clarity, the elements shown in the figures are not necessarily drawn to scale. For example, for clarity, the dimensions of some elements may be enlarged relative to other elements. Furthermore, reference numerals may be repeated in multiple figures where deemed appropriate to indicate corresponding or similar elements.

[0053] Because the embodiments shown in this invention can be largely implemented using electronic components and circuits known to those skilled in the art, the degree of detail to be explained will not be greater than the degree that must be considered as described above in order to understand and comprehend the basic concepts of the invention, and so as not to confuse or distract from the teachings of the invention.

[0054] Any reference to the method in the specification shall be modified as necessary to apply to the device or system capable of performing the method and / or the non-transitory computer-readable medium storing instructions for performing the method.

[0055] Any references to the system or device in the specification shall be modified as necessary to apply to methods that can be executed by the system, and / or as necessary to apply to non-transitory computer-readable media that store instructions that can be executed by the system.

[0056] Any reference in the specification to a non-transitory computer-readable medium shall be modified as necessary to apply to a device or system capable of executing instructions stored in a non-transitory computer-readable medium, and / or to a method of executing the instructions.

[0057] Any combination of any modules or units listed in any of the accompanying drawings, any part of the specification and / or any request item may be provided.

[0058] The specification and / or drawings may relate to a processor. The processor may be a processing circuitry system. The processing circuitry system may be implemented as a central processing unit (CPU) and / or one or more other integrated circuits, such as application-specific integrated circuits (ASICs), field-programmable gate arrays (FPGAs), fully custom integrated circuits, or combinations thereof.

[0059] Any combination of any steps of any method shown in the specification and / or drawings may be provided.

[0060] Any combination of any subject that can provide any requested item.

[0061] Any combination of systems, units, components, processors, and sensors shown in the specification and / or drawings may be provided.

[0062] Term compression and encoding are used interchangeably.

[0063] The fingerprint filter (FF) is an example of a managed data structure (MDS) that can store fingerprints and run IDs.

[0064] A method, system, and computer-readable medium may be provided for efficiently managing LSM trees stored in non-volatile memory such as SSD memory.

[0065] A method for reducing the false alarm rate of fingerprints in an appropriate manner can be provided.

[0066] provides a method for efficiently keeping the run ID within the FF up-to-date. For example, this is true even if there is no additional read of the memory to check if an entry exists before writing, and if it does, to update its run ID.

[0067] A combination of the two methods can be provided.

[0068] For simplicity of explanation, most of the following text will refer to a solution. An example of the solution is referred to as a Chucky-Huffman encoded key-value store. It should be noted that Huffman encoding can be replaced by another variable-length code.

[0069] To reduce false positives, run IDs have been found to be highly compressible. This is because their distribution is approximately geometric, meaning that entries with larger run IDs are far more common than those with smaller run IDs. This allows larger runs to be encoded with fewer bits, and smaller runs to be encoded with more bits. The space saved can be dedicated to fingerprints so that fingerprints remain large as data grows.

[0070] To reduce updates, it has been found that the run ID can be updated in a timely manner during the merge operation, while the target entry is stored in memory. Therefore, we can keep the run ID up-to-date without introducing any additional storage I / O.

[0071] It has been discovered that Chucky can simultaneously reduce memory and storage I / O. It achieves this by replacing BFs with a single FF with a compressed run ID, which is updated during the merge operation.

[0072] The following text will illustrate an example of run ID compression using Huffman coding, while identifying and addressing the challenges that arise from it: (1) how to align fingerprints and compressed run IDs within the bucket of FF, and (2) how to efficiently encode and decode run IDs.

[0073] Chucky can use compressed bits to keep the fingerprint large, thereby ensuring a reducible false alarm rate as data grows.

[0074] Chucky can fit into any FF. Here is just one example illustrating how to customize Chucky for FF, such as the Cuckoo Filter.

[0075] The instruction manual shows how to replace the BF with an FF that has an auxiliary run ID, which is updated as needed during the merging process. The run ID is auxiliary because it includes both the run ID and the fingerprint.

[0076] The specification shows that run IDs are extremely compressible, and we have investigated how to minimize their size using Huffman coding.

[0077] The instruction manual shows how to align the compressed run ID and fingerprint within the FF bucket to achieve good space utilization.

[0078] The instruction manual shows how to efficiently encode and decode run IDs.

[0079] The instruction manual shows how to integrate Chucky with the Cuckoo filter.

[0080] The manual states that experiments show Chucky reduces both memory I / O and storage I / O.

[0081] Figure 1 includes graphs 11 and 12, which show a comparison between Chucky's performance and existing technology solutions.

[0082] By analyzing the information theory entropy of run IDs, this specification demonstrates that run IDs are extremely compressible, and that we can further reduce the entropy by sorting a series of run IDs and assigning them a single code, thereby achieving even greater compressibility.

[0083] This specification describes a compressed variant called multinomial Huffman, which assigns Huffman codes to buckets based on the probability that a given combination of run IDs will overlap in the bucket.

[0084] The specification shows that compressed run IDs introduce the problem of bucket overflow, and we introduce two techniques to solve this problem: Variable Minimally Bounded Fingerprint and Leftover Huffman, a method of allocating code based on the remaining space in the bucket after the fingerprint.

[0085] The specification shows how to support the updating of duplicate entries in the LSM tree without creating an infinite recursion chain in the Cuckoo filter.

[0086] Chucky can be extended to many widely used LSM tree designs, which are suitable for different application workloads.

[0087] The instruction manual shows how to restore the Cuckoo filter after a power failure.

[0088] An LSM tree consists of multiple levels with exponentially growing capacity. Level 0 is the first level of a buffer (and / or storage) in memory, while all other levels are in storage. Applications insert key-value pairs into the buffer. When the buffer reaches its capacity, its contents are flushed to a sorted array, which is called the run in level 1 of the storage.

[0089] Various merging strategies can be implemented. The first merging strategy is sequential merging, where, as soon as a given level i reaches its capacity, its run will be merged into level i+1. Then, level i+1 can replace level i and can be regarded as the modified level i.

[0090] To merge runs, their entries are taken from storage to memory for sorting and merging, and then written back to storage as a new run. The number of levels L is logT(N), where T is the capacity ratio between any two adjacent levels, and N is the ratio between the total amount of data and the size of the buffer in memory.

[0091] Another merging strategy can be called multi-level merging, which involves merging multiple levels at once. This can occur for various reasons, such as when it is predicted that a merging will result in a level being almost full. This merging strategy can be called predictive merging.

[0092] Figure 0 provides examples of the different list terms used throughout the article to describe the LSM tree. These terms are: the term definition L Levels of an LSM tree T LSM tree size ratio N The ratio of data volume to buffer size K For the merging trigger threshold of level 1 Z For the merging threshold of levels H Number of Bloom filter hash functions R Expected point read I / O cost M Filter memory budget Table 0

[0093] Non-in-place (i.e., out-of-place) updates and deletions are performed by inserting key-value entries with updated values ​​into a buffer (for deletion, the value is a tombstone). Whenever two runs are merged simultaneously, containing two entries with the same key, the older entry is discarded as the newer entry replaces it. To always find the latest version of an entry, the application read traverses the runs from newest to oldest at each level and terminates when it finds the first entry with a matching key. If its value is a tombstone, the read returns a negative result to the application. For each run in memory, there is a fence pointer array containing the minimum / maximum key at each data block, allowing the relevant block within a run to be found with a single memory I / O.

[0094] The LSM tree design space spans many variations that benefit different application workloads. The two most common are Leveling and Tiering (used by default in RocksDB and Cassandra, respectively). This is illustrated in Figure 2.

[0095] When using hierarchical merging, the merge is performed greedily within each level (i.e., it is performed as soon as a new run begins). Therefore, there is at most one run per level, and each entry is merged approximately T / 2 times on average within each level.

[0096] When using hierarchical merging, the merge is performed lazily within each level (i.e., it is only performed when the level is full). Therefore, there are at most approximately T runs per level, and each entry is merged once per level.

[0097] Hierarchical merging is more beneficial for read and space optimization, while layered merging is more beneficial for write optimization. This trade-off can be fine-tuned by changing the size ratio T.

[0098] Figure 2 also shows Lazy Leveling, a hybrid mode that uses leveling at the highest level and hierarchical merging at all lower levels to provide a favorable trade-off between the two (i.e., for space-sensitive write-heavy applications that are primarily point reads).

[0099] The recent Dostoevsky framework uses the following two parameters to summarize these three variants: (1) a threshold Z for the number of runs at the highest level before triggering the merge, and (2) a threshold K for the number of runs at each lower level before triggering the merge.

[0100] Figure 2, along with Tables 1 and 2, shows how to set these parameters to assume each of the three designs. Table 1 Hierarchical Merging Inert hierarchical merging Layered merging Detection cost O(L) O(LT) O(LT) Construction cost O(LT) O(L+T) O(L) Blocked Bloom Filter Memory I / O Table 2 Hierarchical Merging Inert hierarchical merging Layered merging Uniform O() O(*T) O(*T) Optimal O() O() O(T) Bloom filter false alarm rate complexity

[0101] For example, see equation (1), which represents Ai as the maximum number of runs at level i and A as the maximum number of runs in the system, as follows regarding these parameters. (1) For i between 1 and L, Ai = K; for any other value of i, Ai = Z. A = Ai = (L-1) * k + z (for i between 1 and L).

[0102] For example, Chucky can be built on top of Dostoevsky and can span multiple LSM tree variants to accommodate different workloads.

[0103] While some designs (such as HBase and Cassandra) merge all runs at once, others (such as RocksDB) partition each run into multiple files called Ordered String Tables (SSTs) and merge them at the granularity of SSTs. This allows for finer control over how the merge overhead is scheduled in terms of space and time, but it increases write amplification. For ease of explanation, the documentation describes the merge as if it were happening at the granularity of a run, but this also applies to designs that rely on SSTs used for merging.

[0104] We use RocksDB’s dynamic hierarchical size adaptation technology, which sets the capacity of hierarchical 1 to L-1 based on the number of entries at the maximum hierarchical level to limit storage space expansion.

[0105] We assume preemptive merging, whereby we detect when levels 1 to i are close to capacity and merge all their runs at once, instead of letting the merging proceed slowly (trickling) between levels and causing more write amplification.

[0106] Bloom Filter—Each run in an LSM tree has a corresponding Bloom filter (BF) in memory. A Bloom filter is a space-saving probabilistic data structure used to test whether a key is a member of a set. All Bloom filters are persistently stored so that they can be recovered in case of system failure. A BF is a bit array with h hash functions. For each inserted key, we map it to h random bits using each hash function, setting them from 0 to 1 or leaving them set to 1. Checking the presence of a key requires checking its h bits. If any bit is set to 0, we get a negative. If all bits are set to 1, we get a true or false alarm. The false alarm rate (FPR) is , where M is the number of bits per entry.

[0107] As we increase M, the probability of bit collisions decreases, and therefore the FPR decreases. In industry-leading KV stores (such as RocksDB), the number of bits per entry is typically set to ten.

[0108] BF does not support deletion (i.e., resetting the bit back to 0) because this may result in false negatives. For this reason, a new BF is created from scratch for new runs resulting from merging.

[0109] BF requires h memory I / O operations for insertion and positive lookup. For negative lookup, an average of two memory I / O operations are required because approximately 50% of the bits are set to zero, and therefore the expected number of bits to be checked before resulting in zero is two.

[0110] To optimize memory I / O, arrays of contiguous block elements (BFs), each with the size of a cache line, have been proposed. Keys are inserted by first hashing the key to one of the constituent BFs and then inserting the key into it. This requires only one memory I / O for any insertion or query. The cost is a slight increase in FPR (Functional Rate of Return).

[0111] RocksDB recently switched from standard BF to blocking BF. In this article, we use both approaches as baselines, and we focus more on blocking BF because they are more competitive.

[0112] For an LSM tree with a blocking BF, the application query consumes at most one memory I / O (i.e., one memory I / O per run filter).

[0113] On the other hand, the application update consumes amortized memory I / O (the average number of times an entry is merged and thus inserted into a new BF).

[0114] Table 1 summarizes these costs for each variant of the LSM tree variant.

[0115] It was observed that both cost indicators increased with the increase of the number of levels L, and therefore also with the increase of the amount of data.

[0116] Second, we observed an inverse relationship between these metrics: the more greedy we set the merging of the LSM tree (i.e., by changing the merging strategy or by fine-tuning the size ratio), the probe cost decreases as the BF decreases, while the construction cost increases as the BF is rebuilt more greedily.

[0117] Therefore, it is impossible to improve one of these indicators without lowering the others. Figure 1 illustrates this relationship conceptually.

[0118] In the industry, KV storage sets a uniform number of bits per entry for all levels of BF.

[0119] However, this method has recently been considered suboptimal.

[0120] The optimal approach is to reallocate approximately 1 bit per entry from the highest level and use it to linearly allocate more bits per entry for filters at lower levels.

[0121] While this slightly increases the FPR at the highest level, it exponentially reduces the FPR at lower levels, resulting in a smaller sum of FPR.

[0122] Equations 2 and 3 represent the FSR for these two methods: (2)(3)

[0123] The intuition of equation (2) is that as the data increases, the FPR increases because there are more runs and therefore more BFs, and false alarms may occur in more BFs.

[0124] On the other hand, equation (3) shows that, using the optimal method, the relationship between memory and FPR is independent of the number of levels and therefore independent of the amount of data. The reason is that as the LSM tree grows, smaller levels are assigned exponentially smaller FPRs, which leads to the convergence of the sum of FPRs.

[0125] We summarize the corresponding FPR complexity in Table 2 and visualize it conceptually in Figure 1.

[0126] While our primary goal is to increase the memory bandwidth of BF, we must also at least match the scalability of FPR with the optimal BF approach to be competitive in all performance aspects.

[0127] Fingerprint filters (FFs) are a family of data structures that have recently emerged as an alternative to Bloom filters. At the core of an FF is a compact hash table storing the fingerprint of a key, where the fingerprint is an F-bit string obtained by hashing the key. To test for membership in a set, the FF hashes the key in question into a bucket and compares its fingerprint to all fingerprints in the bucket. If a match is found, we get a positive result. FFs cannot return false negatives, and the probability of it returning a false negative is at least 2-F.

[0128] The fingerprint size F determines the trade-off between accuracy and space. Various proposed FFs differ in their collision resolution methods, which involve swapping entries between buckets to resolve collisions. For example, the Cuckoo Filter uses a variant of the Cuckoo Hash, while the Quotient Filter uses a variant of the Linear Probe.

[0129] Although different conflict resolution methods give different FFs with slightly different performance and space properties, all FFs to date share a common set of expected properties for our problem. First, they support querying and updating in nearly constant time, with memory usage similar to Bloom filters. Second, unlike Bloom filters, FFs support storing updatable auxiliary information for each entry alongside the fingerprint of each entry. These capabilities allow multiple Bloom filters in an LSM tree to be replaced with a single FF that maps data entries to runs residing in the LSM tree. This design promises to allow the target run of an entry to be found with a small and constant number of memory I / Os, unlike Bloom filters which require at least one memory I / O among numerous filters.

[0130] Despite this commitment, this approach presents two challenges. The first challenge is how to keep the run ID up-to-date when entries are merged in the LSM tree. The second challenge is how to keep the run ID size appropriate as the amount of data grows.

[0131] Case Study—The recent SlimDB system is the first system to integrate LSM trees with FF. Therefore, it provides an interesting case study and baseline for addressing the two challenges mentioned above.

[0132] To keep the run ID in FF up-to-date, SlimDB performs read I / O on the memory for each application to check if the entry exists, and if it does, updates its run ID in FF. This involves significant overhead in terms of memory I / O, especially for applications that perform blind writes.

[0133] Secondly, SlimDB uses binary encoding to represent run IDs. Therefore, each run ID contains bits to uniquely identify all runs. Consequently, as the number of levels L increases, more bits are needed. This is not a problem for SlimDB, as it is designed for systems with limited memory budgets. In fact, SlimDB uses additional memory to completely prevent false alarms by storing the full key of the collision fingerprint in memory. SlimDB also proposes a novel fence index format.

[0134] In contrast, we are interested in applications with a more compact M-bit budget per entry, where M is a small constant that does not increase.

[0135] Under this constraint, equation (4) represents the FPR of a single entry in relation to the number of bits M and the run ID size D of each entry. (4)

[0136] By substituting the run ID size D, the lower bound simplifies to, which means that the FPR increases with the number of levels because the run ID steals bits from the fingerprint.

[0137] Chucky is an LSM-based KV storage that simultaneously reduces memory and storage I / O. It achieves this by replacing the Bloom filter with a fingerprint filter and innovating in two aspects.

[0138] During the merge operation, Chucky keeps the run ID in the FF up-to-date without incurring additional memory I / O costs. Furthermore, it allows run ID inheritance across merge operations to avoid FF updates and thus reduce memory I / O. In this way, Chucky reduces and separates the costs of updating and querying the FF, as shown in Figure 1.

[0139] Chucky compresses run IDs to prevent their size from increasing as data grows and to extract bits from the fingerprint. Therefore, Chucky reduces the FPR and thus reduces memory I / O, as shown in Figure 1.

[0140] For the sake of generality and ease of explanation, we will now abstract the details of FF's conflict resolution method.

[0141] Figure 3 illustrates Chucky's architecture, which uses a Management Data Structure (MDS) to map each physical entry in the LSM tree to an MDS entry, which may include a fingerprint and a run ID. The figure also shows the query and update workflows with solid and dashed lines, respectively.

[0142] In Figure 3, keys k1, k2, and k3 reside in different runs but happen to be mapped to the same FF bucket by the FF hash function. Keys k2 and k3 have conflicting fingerprints Y, while key k1 has a different fingerprint X. Applying a query to key k3, we arrive at the bucket shown in the figure and first iterate through the fingerprints belonging to the newer run (i.e., look for the version of the latest entry). For Run 1, we get a negative because the fingerprints are different. For Run 2, we get a false positive, resulting in wasted memory I / O. For Run 3, we get a true positive, so the target entry is returned to the application.

[0143] Whenever the LSM tree buffer flushes a batch of new application updates to memory, Chucky adds an FF entry for each key (including tombstones) in that batch. For example, consider entry k1 in Figure 3, which initially had a version in Run 3. Then, as part of Run 1, a new version of this entry is flushed to memory. Therefore, Chucky adds a new FF entry to indicate this updated version. This results in temporary space amplification (SA), which is later resolved by merging, while the entries are brought to memory for sorted merging.

[0144] This SA is moderate because the exponential structure of the LSM tree limits the average number of versions per entry (e.g., for hierarchical merge or lazy hierarchical merge). In fact, BF exhibits exactly the same memory SA because each version of an entry across different run BF occupies M bits per entry.

[0145] For each obsolete entry identified and discarded during the merge runtime, Chucky removes the corresponding entry from FF.

[0146] For each other entry, Chucky updates its run ID to the newly created run ID.

[0147] Therefore, Chucky maintains the run ID of FF without requiring any additional storage I / O.

[0148] Furthermore, Chucky allows the inheritance of run IDs during merge operations to avoid FF updates and save memory I / O. It achieves this by setting the run ID of the oldest run at level i of the LSM tree to j. Thus, the range of run IDs is from 1 to A, where A is the number of runs (from equation (1)). In practice, this means that the run ID of an entry only changes when that entry is merged into a new level, not when a given entry remains at the same level after the merge.

[0149] For example, in Figure 3, when Runs 1, 2, and 3 are merged into a new run at level 3, the new run is also assigned run ID 3. During the merge operation, we identify and remove the old version of entry k1 from the FF, and update the run IDs of the new versions of entries k2 and k1 to 3. However, we keep the run ID of entry k3 the same because the new run inherits the ID of the old run 3.

[0150] The application query probes FF once, while the update accesses it amortized L times (once each time an update entry enters a new level).

[0151] Table 3 summarizes these properties. Compared to the memory I / O complexity of BF in Table 1, Chucky reduces the query cost to a constant. Furthermore, it reduces the update cost of a more greedy merge strategy, thus separating the memory I / O costs of queries and updates. In this way, Chucky dominates the Bloom filter in terms of memory bandwidth. Table 3 Hierarchical Merging Inert hierarchical merging Layered merging Application query O(L) O(1) O(1) Application Update O(L) O(L) O(L) Chucky's call complexity table 4 Hierarchical Merging Inert hierarchical merging Layered merging unified O() O(*T) O(*T) Optimal O() O() O(T) FPR bounds without run ID compression

[0152] As we saw earlier, the binary-coded run ID in the FF bucket grows with the amount of data, thus extracting bits from the fingerprint and increasing the false alarm rate. To prevent this problem, we will now discuss in detail how to use compression to make the run ID as small as possible.

[0153] run IDs are highly compressible because they follow an approximate geometric probability distribution.

[0154] We use equation (5) to formulate it, which represents pi as the score of user data at level i in the LSM tree. (5)

[0155] The run with ID j is located at level [j / K] of the LSM tree. Therefore, its frequency is the level probability p[j / K] (from Equation 5) divided by the number of runs at that level A[j / K] (from Equation 1).

[0156] Therefore, in equation (6), we denote fj as the frequency of the j-th run ID. (6)

[0157] For smaller run levels, these probabilities decrease exponentially. Therefore, fewer bits can be used to represent larger run IDs, and more bits can be used to represent smaller run IDs. Since the frequency of smaller run IDs decreases exponentially, the average number of bits used to represent run IDs will remain small.

[0158] In order to establish a limit on how much run IDs can be compressed, we derive their Shannon entropy, which represents the lower bound on the average number of bits required to represent an item within a given probability distribution.

[0159] In equation (7), we simplify by stating the definition of entropy on different run ID probabilities, substituting Ai and pi into equations (1) and (5) respectively.

[0160] Interestingly, entropy converges to a constant that is independent of the number of levels and therefore does not increase with the amount of data.

[0161] Intuitively, the exponential decrease in the probability of a lower-level run ID outweighs the fact that a lower-level run ID requires more bits to uniquely represent it. (7)

[0162] By substituting equation (7) into the run ID length D of the equation, we obtain the FPR bounds in Table 4. These bounds apply to any FF where the number of fingerprints checked per lookup is a small constant (i.e., all FFs in practice so far).

[0163] The fact that these limits are lower than the limits for optimal BF in Table 2 further confirms our method; in terms of FPR, FF with compressed run ID can match or even improve BF. % In the next section, we show how to do this in practice.

[0164] To compress run IDs in practice, we use Huffman coding. The Huffman encoder takes the run ID and its probability (from equation (6)) as input. As output, it returns a binary code to represent each run ID, and thus the more frequent run IDs are assigned shorter codes. This is done by creating a binary tree from the run IDs by first concatenating the least likely run IDs as a subtree. The final code length of the run ID corresponds to its depth in the resulting tree.

[0165] Figure 4 shows a lazy hierarchical merged LSM tree with labeled run IDs (the tree's parameters are T=5, K=4, Z=1), each ID having a corresponding frequency in Equation (6). We input these run IDs and their frequencies into a Huffman encoder to obtain the Huffman tree shown alongside. The code of a run is given by the edge labels of the tree connecting the path from the root node to the leaf node of a given run ID. For example, the codes for run IDs 4, 8, and 9 are 011011, 010, and 1, respectively.

[0166] Using Huffman coding, no code is the first code of another code. This property allows for unique decoding of the input bitstream by traversing the Huffman tree from the root until a leaf is reached, outputting the run ID at the given leaf, and then starting again from the root. For example, the input bitstream 11001 is uniquely decoded into run IDs 9, 9, and 7 based on the Huffman tree in Figure 4. This property allows for unique decoding of all run IDs within a bucket without the need for delimiters.

[0167] We use the average code length (ACL) defined in equation (8) to measure the size of the encoded run ID, where lj is the code length assigned to the j-th run.

[0168] For example, this equation calculates 1.52 bits for the Huffman tree in Figure 4. This is a 62% saving compared to binary encoding, which requires four bits to uniquely represent each of the nine run IDs. (8)

[0169] It is well known in information theory that the upper bound of the ACL of Huffman coding is the entropy plus 1. The intuition of adding 1 is that each code length is rounded up to an integer. We denote it as , where H is the entropy in equation (7). Therefore, we expect the ACL in this case to converge and become independent of the amount of data, the same as in equation (7).

[0170] We verify this in Figure 5 by increasing the number of levels in the example of Figure 4 and showing the Huffman ACL, which does indeed converge. Intuitively, while runs at smaller levels are assigned longer codes, the frequency of these codes decreases exponentially. In contrast, binary encoding requires more bits to uniquely represent all run IDs. Therefore, Huffman encoding allows for better reduction in memory footprint.

[0171] Among compression methods that encode one symbol at a time, Huffman coding is known to be optimal because it minimizes the ACL. However, the exact ACL is difficult to analyze because the Huffman tree structure is difficult to predict from the outset. Instead, we can derive a stricter upper bound on equation (8) than before by assuming a less general coding method and observing that the Huffman ACL will be at least as short. For example, we can use a unary encoded first code of length L-i+1 bits (1) to represent level i, and then use a truncated binary encoded last code of length approximately log2(Ai) (2) to uniquely represent each Airun at level i, to represent each run ID. This is actually Golomb coding, which also applies to our problem and is easier to analyze.

[0172] However, we focus on Huffman coding because it allows encoding multiple symbols at once. We temporarily utilize this capability. We derive the average length of this coding as ACLUB in equation (9) and show it in Figure 5 as a reasonable strict upper bound for Huffman ACL. (9)

[0173] Figure 5 further plots the entropy of the run ID frequency distribution in equation (7).

[0174] As shown in the figure, there is a gap between the Huffman ACL and the entropy. In fact, Figure 6 shows that the gap between the ACL and the entropy increases as the size ratio T of the LSM tree increases. (This figure is drawn for a hierarchically merged LSM tree (i.e., K=1, Z=1)). The reason is that, so far, one run ID has been encoded at a time, which means that each run ID requires at least one bit to be represented by a code. Therefore, the ACL cannot be less than one bit per run ID. On the other hand, as the probability distribution becomes more skewed, the entropy continues to decrease toward zero because the information content in the distribution (i.e., the amount of surprise) decreases. The general approach to overcoming this limitation in information theory is to encode multiple symbols at a time, as we will explore now.

[0175] A common technique for achieving high load factor with moderate FPR sacrifice in FF is to store multiple fingerprints per bucket.

[0176] Now we show how to leverage this FF design decision to collectively encode all run IDs within the bucket to further drive compression.

[0177] Figure 7 provides an example illustrating how to encode permutations of two run IDs at a time in a hierarchical merged LSM tree (with two levels and a size ratio T of 10). The probability of a permutation is the product of the probabilities of its constituent run IDs in equation (6). For example, the probabilities of permutations 21 and 22 are 10 / 11 * 1 / 11 and (10 / 11)^2, respectively. By inputting all possible run ID permutations of size 2 and their probabilities into a Huffman encoder, a Huffman tree labeled Perms is obtained, with an ACL of 0.63 in Figure 7.

[0178] This is an improvement over encoding one run ID at a time. The intuition behind this improvement is that the most common permutations can be represented with fewer bits than the number of symbols in the permutation.

[0179] Figure 6 shows that as the permutation size increases, the ACL of the generated Huffman tree approaches the entropy. % When the permutation size is 4 or larger, this entropy is very close to the ACL.

[0180] In the example of Figure 7, the same run ID has two permutations: 21 and 12. For a query encountering either permutation, the same lookup process follows: check the key of Run 1 (i.e., first the fingerprint, and if positive, also in memory), and if not found, continue checking Run 2. The fact that both permutations trigger the same process means that the permutations encode redundant information about the order. Conversely, combinations of Run IDs can be encoded, as shown in Figure 7, where combination 12 replaces the previous two permutations.

[0181] Unlike A^S, there are fewer combinations than permutations, requiring fewer bits to represent them, and therefore ACL may drop even lower than before.

[0182] To reduce the limits of ACLs by combining the codes, a new entropy formula H is derived in Equation 10 by subtracting all information about the order from the original entropy formula H (from Equation (7)). This order information is equivalent to arranging the S run IDs by log2(S!) bits, while binomially discounting log2(j!) bits for any run ID repeated j times. Since the combination is multinomial, another way to derive the same formula is through a multinomial entropy function. Dividing by S normalizes the formula to each entry, not each bucket. (10)

[0183] Figure 8 compares the H combination with H when the number of run IDs in the collective encoding is increased. (This example uses a hierarchical merged LSM tree where T=10, K=1, Z=1, and L=6). It is observed that the more run IDs in the collective encoding, the more the H combination decreases, because it eliminates more redundant information about the order relative to H.

[0184] In order to use the encoded combinations in practice, the fingerprints in each bucket must be sorted according to their run IDs so that it is possible to identify which fingerprint corresponds to which run ID. For actual encoding, all possible combinations and their probabilities are fed into the Huffman encoder. We use a multinomial distribution in equation (11) to represent the probability c of combination c, where c(j) represents the number of times run ID j appears in combination.

[0185] For example, for combination 12 in Figure 7, we have S=2, c(1) = 1 and c(2) = 1. Therefore, the probability is (11).

[0186] For a combination, the ACL is , where C is the set of all combinations, and Ic is the code length of combination c (we divide by S to represent the ACL per run ID rather than per bucket). We observe that the combination ACL dominates the permutation ACL in Figure 8, and it converges with the combination entropy as we increase the number of collectively encoded run IDs.

[0187] In the remainder of this paper, we continue to use code combinations because they achieve optimal compression.

[0188] Align the code with the fingerprint

[0189] Due to compression, the run ID code length is variable, making it challenging to align them with fingerprints in the FF bucket. We illustrate this in Figure 9(A) by aligning a combined run ID code of two entries with two five-bit fingerprints (FP) in a hexadecimal FF bucket. This example is based on the LSM tree instance in Figure 4, except that instead of encoding each run ID individually, the run ID combination is now encoded. The terms lx,y in the figure are the code lengths assigned to buckets with consistent run IDs x and y. We observe that while some codes and fingerprints are perfectly aligned within the bucket (line I), others exhibit underflow (line II) and overflow (lines III and IV).

[0190] Because the code is shorter, buckets with frequent run IDs will overflow. These are undesirable because they waste bits that could have been used to increase the fingerprint size. On the other hand, because the code is longer, buckets with less frequent run IDs will overflow. These are undesirable because they require storing the remaining bucket contents elsewhere, thus increasing memory overhead.

[0191] In Figure 10, we illustrate the competition between overflow and non-overflow using a curve labeled as a uniform fingerprint. This figure is plotted for a lazy hierarchical merged LSM tree with configurations T=5, K=4, Z=1, L=6 and an FF with 32-bit buckets containing 4 entries. The figure varies the maximum allowed fraction of overflowing FF buckets and measures the maximum possible corresponding fingerprint size.

[0192] As shown in the figure, when the fingerprint size is uniform, the fingerprint size must be reduced rapidly to ensure less overflow.

[0193] To address this issue, we learned that the run ID combination distribution (in Equation (11)) is heavy-tailed because the underlying run ID distribution is approximately geometric. Therefore, our approach is to ensure that the code and fingerprint are perfectly aligned in the most probable combinations by adjusting the size of the code and fingerprint while allowing all other combinations to overflow along the heavy tail of the distribution. This is achieved in two steps using two complementary techniques: Scalable Fingerprint (MF) and Fluid Aligned Coding (FAC).

[0194] Extendable fingerprint (MF) — To facilitate alignment, MF allows entries from different LSM tree levels to have different fingerprint sizes.

[0195] However, even if it is swapped across buckets via FF's conflict resolution mechanism, the fingerprint length of a single entry remains unchanged. This means that no fingerprint bits need to be dynamically cut or added. Once an entry is moved to a new level, MF will assign it a new fingerprint size if necessary, while simultaneously placing it into memory for sorting and merging.

[0196] The problem with MF is how to choose the fingerprint length for each level to achieve an optimal balance between fingerprint size and overflow. We construct this as an integer programming problem. Where FPi represents the (positive integer) length of the fingerprint of the entry at level i. The goal is to maximize the average fingerprint size, as shown in equation (12): (12) Maximize

[0197] Use an additional parameter NOV to constrain this problem to obtain a score of non-overflow buckets that is expected to be guaranteed (ideally at least 0.9999). Use this parameter to define Cfreq as a subset of C that contains only the most probable combination of run IDs in C, whose cumulative probability is just higher than NOV.

[0198] We add it to the problem as a constraint in Equation 13, which requires that for all c in Cfreq, the code length (denoted as lc) plus the cumulative fingerprint length (denoted as cFP) does not exceed the number of bits B in the bucket: (13)

[0199] Although integer programming is NP-complete and therefore difficult to optimize globally, we utilize the specific structure of the problem and the efficient hill-climbing method shown in Algorithm 1. This algorithm initializes all fingerprint sizes to zero. It then increases the fingerprint size of the larger levels as much as possible, moving to the next smaller level when the overflow constraint in Equation 13 is violated. The rationale for initially extending the fingerprints of larger levels is that their entries are more frequent. In this way, the algorithm follows the steepest ascent. Figure 9 shows how MF reduces the severity of non-overflow (row II) while eliminating some overflows (row III). Therefore, as shown in Figure 10, it achieves a better balance between overflow and average fingerprint size.

[0200] Fluid Alignment Coding (FAC) — Figure 9(B) shows that even with MF, non-overflow and overflow can still occur (see, row II and row IV, respectively). To further mitigate these, FAC was introduced. FAC utilizes a well-known tradeoff in information theory that the smaller some codes are set in the first code, the longer the other codes must be to remain uniquely decodable.

[0201] This trade-off is reflected in the Kraft-McMillan inequality, which states that for a given set of code lengths L, if all codes are uniquely decodable. Intuitively, code lengths are set from a budget of 1, and smaller codes consume a higher proportion of that budget.

[0202] To take advantage of this trade-off, the FAC allocates longer code that occupies no overflow bits for very frequent bucket combinations. Therefore, the code for all other bucket combinations can be shortened. This creates more space in less frequent bucket combinations, which can be used to reduce overflow and increase the fingerprint size at smaller levels. This idea is illustrated in Figure 9(C).

[0203] The combination in line II is the most frequent in the system and is now assigned longer code than before. This allows for a reduction in the code length of all other combinations, which in turn allows for longer fingerprints for entries in levels 1 and 2, and eliminates bucket overflow in line IV.

[0204] Implement FAC at the top of MF as follows. First, replace the previous overflow constraint (Equation (13)) with a new constraint, as shown in Equation \refeq:constraint3. Represented by the Kraft-McMillan inequality, it ensures that the fingerprint size remains short enough that it is still possible to construct non-overflow buckets with uniquely decodable codes for all combinations in Cfreq. Furthermore, it ensures that all other bucket combinations not in Cfreq can be uniquely identified using a unique code at most equivalent to the size of bucket B. (14)

[0205] Note that Equation 14 does not rely on a prior known Huffman code (i.e., like Equation (13)). Therefore, the Huffman encoder can be run after, rather than before, the fingerprint length is found using Algorithm 1.

[0206] Third, the Huffman encoder is run only on the combination in Cfreq, while the frequency input of the combination c is set to, instead of using its polynomial probability (in equation (11)) as before.

[0207] This ensures that the code generated by the Huffman encoder exactly fills the remaining bits B-cFP. Fourth, for all combinations not in Cfreq, a uniform binary code of size B bits is set, consisting of a common first code and a unique last code from the Huffman tree. In this way, all codes in both groups can be uniquely identified and decoded. %It consists of a common first code and a unique last code not in the Huffman tree.

[0208] Figure 10 shows that when applied together, MF and FAC eliminate the competition between overflow and fingerprint size. In fact, they keep the average fingerprint size close to the theoretical maximum (within half a bit in the figure), which is obtained by subtracting the combined entropy (in Equation (10)) from the number of bits M per entry. We presuppose the use of MF and FAC for the rest of this paper.

[0209] The execution time of Algorithm 1 is O(L * M * |C|), where L * M is the number of iterations and |C| is the cost of evaluating the constraints in Equation (14). Additionally, the time complexity of the Huffman encoder is O(|C| * log2(|C|)). This workflow is rarely invoked (i.e., only when the number of levels in the LSM tree changes), and it can be executed offline. Therefore, its execution time is practical (each point in Figure 10 takes a fraction of a second to generate).

[0210] Chucky’s FPR is difficult to analyze precisely because fingerprints have various sizes that are unknown from the beginning.

[0211] Instead, we give a conservative approximation that still allows for reasoning about the system’s behavior. First, we observe that for FAC, the average code length is always at least one bit per entry, so we use the upper bound ACL_UB in equation (9) to slightly overestimate it. Thus, we approximate the average fingerprint size as M-ACL_UB, thereby approximating the FPR of a single fingerprint as 2^-(M-ACLUB). We multiply this expression by a factor Q, which represents the average number of fingerprints searched by the underlying FF for each probe (e.g., for a cuckoo filter with four entries per bucket, Q is approximately 8). Thus, we get equation (15), which is the expected number of false positives for queries on non-existing keys. In practice, the actual FPR tends to deviate from this expression by at most 2 times. (15)

[0212] We now discuss the data structures required to decode run IDs during read operations and to re-encode them during write operations. Specifically, we show how to prevent these structures from becoming bottlenecks.

[0213] Because Huffman code is of variable length, we generally cannot decode it in constant time (e.g., using a lookup table) since we do not know the length of the given code in question from the outset. Therefore, decoding Huffman code is typically done bit-by-bit by traversing the Huffman tree from the root to a given leaf based on the code in question. One potential problem is that if the Huffman tree is large, traversing it may require up to one memory I / O per access node.

[0214] To limit this cost, we again utilize the idea that the bucket combination distribution in Equation 11 is a heavy-tailed distribution. Therefore, it is feasible to store a small Huffman tree partition in the CPU cache to allow for fast decoding of only the most common combination codes.

[0215] In order to control the size of the cached Huffman tree, we set the parameter NOV in the last subsection to 0.9999, so that the combination set Cfreq we build the Huffman tree contains 99.99% of all the combinations we expect to encounter.

[0216] Figure 11 measures the size of the corresponding tree. We continue with the LSM tree configuration in Figure 4 here. Each Huffman tree node is eight bytes. Since it occupies tens of thousands of bytes, it is small enough to fit in the CPU cache.

[0217] In fact, the attached figure highlights an important property: the size of the cached Huffman tree converges as the amount of data increases. This is because the probability of a given bucket combination (in equation (11)) converges with respect to the number of levels, and therefore any set whose size is defined by the cumulative probability of its constituent combinations also converges in size with respect to the number of levels. This property ensures that the Huffman tree does not exceed the CPU cache size as the amount of data grows.

[0218] In addition to the Huffman tree, we used a decoding table in main memory for all other combination codes not in Cfreq. To ensure fast decoding speed for DT, we utilized the property given in the previous subsection that all bucket combinations not in Cfreq are assigned a uniform code of size B bits. Since these codes are all the same size, we know from the beginning how many bits to consider, so we can map these codes to lookup arrays instead of labels in a tree. This ensures that decoding speed is within at most one memory I / O.

[0219] Figure 11 measures the size of the DT as we increase the number of levels on the x-axis (each DT entry is 8 bytes). Since the DT contains approximately |C| = () entries, its size grows slowly with the number of levels (and thus the number of runs A). We observe that even for very large LSM tree instances with ten levels, it remains less than a million bytes.

[0220] To handle bucket overflow, we use a small hash table to map the overflow bucket ID to the corresponding fingerprint. Its size is FF, which is (1-NOV) = 0.0001. It is rarely accessed, i.e., only used for infrequent bucket combinations, and it supports access in O(1) memory I / O.

[0221] To find the correct code for a given combination of run IDs while processing application write operations, a re-encoding table is used. A fixed-width format is used to represent run ID combinations, so the RT can also be constructed as a lookup array. Access costs at most one memory I / O, and its size is the same as the decode table in Figure 11. Note that the most common RT entries are in the CPU cache during execution time and therefore can be accessed without memory I / O.

[0222] Figure 11 also shows the FF size as we increase the number of levels in the LSM tree. We observe that all auxiliary data structures are relatively small, and we have seen that they require very little memory access. Therefore, Chucky prevents decoding / re-encoding from becoming a performance or space bottleneck.

[0223] Integrated with Cuckoo Filter

[0224] We will now show how to integrate Chucky with the Cuckoo Filter (CF), which we chose because it is simple in design and easy to implement.

[0225] CF consists of a bucket array, with each bucket having four fingerprint slots. During insertion, entries with key x are hashed to two buckets b1 and b2 using equations (16) and (17). The fingerprint of key x is then inserted into any bucket with available space. (16) b1 = hash(x) (17) b2 = b1

[0226] However, if both buckets are full, some fingerprints y in one of these buckets will be evicted to clear space. Fingerprints y are swapped into their alternative buckets using the equation \refeq:cuckooC, which depends not on the original key (by using an XOR operator) but only on the fingerprint and the bucket i currently containing y. (18)j=i

[0227] The swapping process continues recursively until a free bucket slot is found for all fingerprints, or until a swapping threshold is reached, at which point the original insertion fails. A query requires at most two memory I / Os because each entry is mapped to two possible buckets. In this paper, we employ a cuckoo filter where each bucket has four slots. This adjustment is known to achieve 95% capacity with a high probability without insertion failures, and only 1-2 amortized swaps per insertion.

[0228] To implement Chucky at the top of CF, we place a combinatorial code at the beginning of each CF bucket, followed by a variable-size fingerprint. We use a reserved all-zero fingerprint combined with the most frequent run ID to represent empty fingerprint slots to minimize the corresponding combinatorial code length. Additionally, we make the following adjustments.

[0229] Because the Cuckoo Filter relies on the XOR operator to locate the alternative bucket for an entry, the number of buckets must be a power of 2. This can waste up to 50% of allocation memory, especially when the capacity of the LSM tree is just a power of 2. To address this issue, borrowing the idea from the Vacuum Filter, a CF is divided into multiple independent CFs, each of which is a power of 2, but the total number of CFs is flexible. In this way, the capacity becomes adjustable by changing the number of CFs, and each key is mapped to one of the constituent CFs using hash modulo operation. We set each CF to 8MB.

[0230] When Chucky reaches its capacity, it needs to be resized to accommodate new data. However, CFs cannot be resized efficiently. The simplest approach is to rebuild Chucky from scratch when it reaches its capacity. However, this approach forces an expensive scan of the dataset to re-insert all entries into the new instance of Chucky. As an alternative, we take advantage of the fact that a merge operation at the highest level of the LSM tree will cover the entire dataset. We also use this opportunity to create a new instance of Chucky, thus avoiding the need for additional scans. The new instance of Chucky is set to be twice the size of the current data volume to accommodate data growth before the next full merge, and a 5% spare capacity is always maintained in all CFs to prevent insertion failures.

[0231] Because Chucky assigns different fingerprint sizes to entries at different levels, a problem arises where CF can map different versions of entries at different levels to more than two CF buckets.

[0232] This problem is addressed by ensuring that all fingerprints contain at least X bits, and by adjusting the CF based on the first X bits of the entry to determine the alternative bucket for the entry. This forces all versions of the same entry to reside in the same pair of CF buckets. Following the Cuckoo Filter literature, we set the minimum fingerprint size to 5 bits to ensure that the two buckets of an entry are sufficiently independent, thus achieving a 95% load factor.

[0233] Because CF maps multiple versions of the same entry from different LSM trees to the same pair of CF buckets, a bucket overflow occurs if a given entry has more than eight versions. Some FFs can solve this problem out-of-the-box using an embedded fingerprint counter (e.g., a Counting Quotient Filter). However, for our CF design, we use an Additional Hash Table (AHT) that maps bucket IDs to overflow entries. The AHT is always empty under insert-heavy workloads. Even under update-heavy workloads, the AHT remains small due to the space scaling limitations imposed by the design of the LSM tree, which limits the average number of versions per entry (e.g., at most ≤2 with hierarchical merge or lazy hierarchical merge). Each full FF bucket encountered by the AHT during a query or update adds at most O(1) additional memory accesses to them.

[0234] For each run, the fingerprint of its entry is stored in memory. During recovery, the fingerprint is read only from memory, thus avoiding a full scan of the data. Each fingerprint, along with its run ID, is inserted into a completely new CF series at a virtually constant amortized memory I / O cost per entry. In this way, recovery is efficient in terms of both memory and memory I / O.

[0235] Assessment

[0236] Now we give an expression that, in general, approximates the expected I / O caused by FF.

[0237] The machine used has 32GB of DDR memory and four 2.7 GHz cores, with 8MB of L3 cache running Ubuntu 18.04 LTS, and is connected to a 512GB SSD via PCIe.

[0238] We used our own LSM tree implementation, based on Dostoevsky's design, and are preparing to use it for commercial purposes. We added blocking caches and non-blocking BFs with uniform false positive rates (FPR) as baselines to represent design decisions in RocksDB and Cassandra, respectively.

[0239] Also supports optimal FPR.

[0240] The default settings include a lazy hierarchical merged LSM tree with a 1MB buffer, a size ratio of 5, and approximately 16GB of data across six levels. Each entry is 64B. There is a 1GB block cache, and the data structure block size is 4KB. Chucky uses 10 bits per entry and 5% over-provisioned space. Therefore, all BF baselines are allocated 1 / 0.95 times more memory to balance memory usage across the baselines.

[0241] Each point in the figure is the average of the three experiments.

[0242] When the most frequently accessed data is in the block cache, a uniform workload distribution is used to represent the worst-case performance, and a Zipfian distribution is used to create skew and clarify the performance properties.

[0243] Figure 12A compares the read / write latency using Chucky relative to blocking BF and non-blocking BF (both with optimal FPR) under a uniform workload, as data grows. Write latency is measured by dividing the total time spent on filter maintenance by the number of writes issued by the application. Read latency is measured just before the full merge operation (when there are the most runs in the system) to highlight worst-case performance.

[0244] Non-blocking BFs exhibited the fastest increasing latency because they required multiple memory I / Os per filter in an ever-increasing number of filters. Therefore, we subsequently abandoned non-blocking BFs in our evaluation because they were not contentious.

[0245] In the case of a blocked BF, the read / write latency increases more slowly because each read or write requires at most one memory I / O.

[0246] Chucky's write latency also increases slowly as the data grows because there are more levels above it that require updating the run ID.

[0247] Crucially, we observed that Chucky was the only baseline that could maintain a stable read latency with the amount of data, since each read requires a constant amount of memory I / O.

[0248] Figure 12B shows the read and write latencies of Chucky relative to blocking BF under different LSM tree variants. This is mainly due to its lower read latency. However, Chucky also increases the write cost for hierarchical LSM tree designs. This is because, when utilizing hierarchical merging, the merging is greedy.

[0249] Chucky offers a better cost balance overall, being "greedy," so the BF is quickly refactored, resulting in multiple BF insertions per entry per level. In contrast, Chucky always requires only one update per entry per level. Overall, Chucky not only improves the cost balance of filter reads / writes but also makes them independent of the underlying LSM tree variant. This makes the system easier to reason about and tune.

[0250] Figure 12C compares the FPR of Chucky with compressed and uncompressed run IDs with the FPR of a blocked BF with uniform and optimal space allocation. As the amount of data increases, the FPR of Chucky with uncompressed run IDs increases because the run ID grows and steals bits from the fingerprint.

[0251] Using a unified BF, the FPR also increases with the amount of data because more filters can produce false positives. In contrast, under the optimal BF, smaller levels are assigned exponentially reduced FPRs, so the sum of FPRs converges to a constant independent of the number of levels. Similarly, Chucky's FPR remains constant as the data increases because the average run ID code length converges, allowing most fingerprints to remain large. The figure also includes Chucky's FPR model in equation (15) to show that it gives a reasonable approximation of the FPR in practice.

[0252] Figure 12D shows that Chucky requires at least 8 bits per entry to work (i.e., code and minimum fingerprint size). However, with 11 bits or more per entry, Chucky offers a better memory / FPR tradeoff than all BF variants. The reason is that BF is known to exhibit suboptimal space usage, which effectively reduces the memory budget by a factor of \ln(2). Therefore, Chucky reduces the FPR better relative to memory.

[0253] To allow Chucky to operate on entries with fewer than 8 bits each while maintaining a low FPR, a BF can be used at the maximum level of the LSM tree, and an FF can be used for all smaller levels. Due to space limitations, we will not consider such a design for now.

[0254] Figures 12F and 12G measure end-to-end read latency with uniform and Zipfian (parameter s=1) workloads, respectively. Read latency is divided into three parts: (1) memory I / O, (2) searching across the memory through the fence pointer, buffer, and block cache, and (3) filter search. In part (F), the relevant data is typically stored in memory, so memory I / O dominates the read cost. However, since our SSD is fast, BF probing still incurs significant latency overhead, which Chucky is able to eliminate. On the other hand, in part (G), the workload is skewed, meaning the target data is typically located in the block cache. In this case, BF becomes the bottleneck because they must be searched before the relevant blocks in the cache can be identified. Chucky alleviates this bottleneck, thus significantly improving read latency.

[0255] Figure 12H illustrates how throughput shrinks as we increase the amount of data for a workload consisting of 95% Zipfian reads and 5% Zipfian writes (modeled with Workload B). The BF baselines do not shrink well because they issue memory I / O on increasing BFs. Chucky with uncompressed run IDs also exhibits deteriorating performance as its FPR increases, leading to more memory I / O. Chucky with compressed run IDs also exhibits deteriorating performance, primarily due to the increasing cost of binary searches on fence pointers. However, Chucky provides better throughput than all baselines because it reduces both the FPR of the filter and memory I / O simultaneously.

[0256] Figure 13 shows an example of method 300.

[0257] Method 300 is used to manage a log structure merge (LSM) tree of key-value (KV) pairs. The LSM tree is stored in non-volatile memory, and the method may include:

[0258] Method 300 can begin from step 310.

[0259] Step 310 may include generating or receiving a current fingerprint indicating the current KV pair. The current KV pair is included in the current run.

[0260] Step 310 can be followed by step 320, which writes the current run from the buffer to the current run position within the LSM tree. The current run may include the current key-value pair. The current run may be sorted.

[0261] Steps 310 and 320 may be followed by step 330, which performs a run writing update of the MDS by adding a mapping between the current KV pair, the current fingerprint and the current run identifier to the management data structure (MDS).

[0262] The MDS run write update reflects the execution of step 310.

[0263] Step 330 can be performed without checking for the existence of a previous version of the current KV pair within the LSM tree.

[0264] Step 330 can be executed regardless of whether the previous version of the current KV pair exists or not within the LSM tree.

[0265] Step 330 can be followed by steps 310 and / or 320.

[0266] Method 300 may include step 340 of updating the LSM tree by merging at least some runs of the LSM tree.

[0267] Step 340 may include merging the first run of the LSM, which may include the first KV pair, with the second run of the LSM tree, which may include the second KV pair.

[0268] Step 340 may include adding the second KV pair to the first run, and wherein the execution of the merge update may include updating the run identifier associated with the second KV pair while maintaining the run identifier associated with the first KV pair.

[0269] Step 340 may include writing the first KV pair and the second KV pair into the third run of the LSM tree, wherein the execution of the merge update may include updating the run identifier associated with the first KV pair and the second KV pair.

[0270] Step 340 may include deleting the previous version of the KV pair when the newer version of the KV pair may include a value representing a delete command.

[0271] Step 340 may include merging at least two runs belonging to different levels of the LSM tree.

[0272] Step 340 may include merging at least two runs belonging to the same level of the LSM tree.

[0273] Step 340 can be followed by step 350, in which step 350 performs a merge update of the MDS to indicate a merge.

[0274] Step 350 can be followed by step 340.

[0275] Method 300 may include triggering the merging of one or more levels of the LSM tree whenever a run is written to non-volatile memory.

[0276] Method 300 may include triggering the merging of the runs of one or more levels whenever one or more levels of the LSM tree reach the fullness level.

[0277] Mergers can be performed using any method, such as hierarchical merges, lazy hierarchical merges, and layered merges.

[0278] The MDS may include multiple buckets, each of which can be configured to store relay data associated with two or more KV pairs.

[0279] Method 300 may include step 360, which receives a request for accessing a requested key-value pair stored in non-volatile memory. The access request may be a request to read the requested key-value pair. The key-value pair is referred to as the requested key-value pair because it is contained in the request.

[0280] Step 360 may be followed by step 370, in which the key of the requested KV pair is used to access the MDS to obtain the location of the relevant run.

[0281] Step 370 may be followed by step 380, which retrieves the relevant run if it exists. It should be noted that a special value (tombstone) can be assigned to indicate the deletion of a previous KV pair. When the relevant run includes a key with such a special value, the response is that the requested KV pair does not exist in the LSM tree.

[0282] After step 380, you can wait to receive a new request, and when a request is received, jump to step 360.

[0283] Figure 14 shows an example of method 400.

[0284] Method 400 is used to manage a log structure merge (LSM) tree of key-value (KV) pairs, which is stored in non-volatile memory.

[0285] Method 400 may include step 410 of merging the run of the LSM tree to provide the merge run.

[0286] Method 400 may include step 420 of adding a new run to the LSM tree, wherein adding may include writing the run to non-volatile memory.

[0287] Step 410 and / or step 420 may be followed by updating at least one Management Data Structure (MDS) to reflect the merging and addition of step 430.

[0288] At least one of the MDSs is stored in a mapping between the keys of the KV pairs of the LSM tree, the fingerprints associated with the KV pairs of the LSM tree, and the compressed run identifier that identifies the run of the LSM tree.

[0289] Compressed run identifiers can be compressed using variable-length codes (such as, but not limited to, Huffman codes).

[0290] Step 430 may include step 440, which compresses the run identifier by applying variable-length encoding to provide a compressed run identifier.

[0291] An LSM tree can include a first level and a last level. The first level is smaller than the last level. There can be a factor T, which defines the ratio between a level and the previous level.

[0292] Step 440 may include assigning a compressed run identifier for the last level of the run that is shorter than the compressed run identifier of the first level of the run.

[0293] Step 430 may include step 450 of calculating the combined run identifier code representing the combination of run identifiers.

[0294] Each combined run identifier code is associated with the fingerprint of each of the run identifiers that form the combination represented by the combined run identifier.

[0295] Method 400 may include step 452 of determining the length of each fingerprint in the fingerprints of each level of the LSM tree.

[0296] Step 454 may include maximizing the product of the fingerprint length of the level multiplied by the score of that level in the LSM tree or the sum of all levels in the LSM tree.

[0297] Step 430 may include step 456, which stores multiple sets in buckets of the MDS, wherein each set includes a combined run identifier code and a fingerprint of each of the combined run identifiers represented by the combined run identifier code. These can provide aligned sets.

[0298] Step 430 may include step 458, in which overflow relay data not included in the bucket is stored in the overflow MDS.

[0299] Step 450 may include calculating a compressed combined run identifier code representing a combination of run identifiers.

[0300] Step 450 may include step 451, which imposes a constraint on the minimum length of the compressed combined run identifier code.

[0301] Step 540 may include step 453, which imposes a constraint on the minimum length of the compressed combined run identifier code and determines the length of each fingerprint in the fingerprint for each level of the LSM tree.

[0302] Method 400 may include step 460, which receives a request for accessing a requested key-value pair stored in non-volatile memory. The access request may be a request to read the requested key-value pair. The key-value pair is referred to as the requested key-value pair because it is contained in the request.

[0303] Step 460 may be followed by step 470, which uses the key of the requested KV pair to access the MDS to obtain the location of the relevant run. This may include obtaining the compressed run ID and decompressing (decoding) it to provide the uncompressed run ID.

[0304] Step 470 can be followed by step 480, which retrieves the relevant run if it exists. It should be noted that a special value (tombstone) can be assigned to indicate the deletion of a previous KV pair. When the relevant run includes a key with such a special value, the response is that the requested KV pair does not exist in the LSM tree.

[0305] After step 480, you can wait to receive a new request, and when a request is received, jump to step 460.

[0306] Figure 15 illustrates the buffer 10, SSD 30, first MDS 50, and management unit 100 for controlling writes to the run, maintaining the first MDS, etc. The management unit may be a controller, a processor, etc., and may be managed by the controller and / or processor, etc.

[0307] Assume that many runs are generated and sent to SSD 30. Figure 15 shows the generation and storage of the nth run, where n is a positive integer that can represent the nth time point.

[0308] Buffer 10 stores the contents of buffer 12.

[0309] When buffer 12 is full (or any other triggering event occurs), the current run 20(n) is sent to SSD 30. The current run 20(n) includes sorted buffer contents, which include the current KV pairs with the current key.

[0310] SSD storage SSD content 32. It includes an LSM tree 40, which includes I levels 42(1) - 42(I).

[0311] At the nth time point, the LSM tree contains R runs, run 20(1) - 20(R). R is a positive integer. The value of R can change over time.

[0312] First, the mapping between the MDS 50 storage key, fingerprint, and run ID 52.

[0313] Once the current run is written to the SSD, the first MDS is updated by adding the current entry 54. The first MDS 50 has already stored the previous entries (at the nth time point), one entry for each run's previous key (reflecting the current state of the LSM tree).

[0314] Figure 16 illustrates the merge operation.

[0315] In SSD content 32, the selected level (or part of the selected level) of the LSM tree is sent to volatile memory, and a merge operation occurs, wherein the run of the selected level 42(i) is merged to provide the modified level 42'(i).

[0316] The modified level can replace the selected level.

[0317] Merging can be performed between multiple levels of run.

[0318] The modification can be executed by running a part (or a part of a level) followed by another part.

[0319] After the modification, it is updated (52) First MDS 30.

[0320] Figure 17 shows the first MDS 50, which includes multiple buckets 52(1) - 52(S), where S is a positive integer.

[0321] Each bucket may include one or more sets of fingerprints and run IDs (RUNIDs), for example, see fingerprint FP 53(1,1), run ID 54(1,1), fingerprint FP 53(1,2), and run ID 54(1,2) for the first bucket. As another example, see, for instance, fingerprint FP 53(S,1), run ID 54(S,1), fingerprint FP 53(S,2), and run ID 54(S,2).

[0322] The number of sets for each bucket may be different from two.

[0323] Figure 18 shows the first MDS 50, which includes multiple buckets 52(1) - 52(S), where S is a positive integer.

[0324] Each bucket may include one or more sets of fingerprints and compressed run IDs (C_RUNID), see, for example, fingerprint FP 53(1,1), compressed run ID 55(1,1), fingerprint FP 53(1,2), and compressed run ID 55(1,2) for the first bucket. As another example, see, for example, fingerprint FP 53(S,1), compressed run ID 55(S,1), fingerprint FP 53(S,2), and compressed run ID 55(S,2).

[0325] Figure 19 shows the first MDS 50, which includes multiple buckets 52(1) - 52(S), where S is a positive integer.

[0326] Each bucket may include a fingerprint and one or more sets of compressed combined run IDs (CC_RUNID). The compressed combined run identifier represents a combination of run identifiers. Each compressed combined run identifier is associated with a fingerprint of each of the run identifiers that form the combination represented by the combined run identifier. The compressed combined run identifiers and these fingerprints form a set.

[0327] Each bucket may store multiple sets. For example, see the first bucket 52(1), which stores (a) a first set including fingerprints FP 53(1,1) and 53'(1,1) and compressed combined run ID 56(1,1), and (b) a second set including fingerprints FP 53(1,2) and 53'(1,2) and compressed combined run ID 56(1,2).

[0328] Figure 20 illustrates the unoverflowed and overflowed sets. A set may include fingerprints FP 53(1,1) and 53'(1,1) and a compressed combined run ID 56(1,1). Figure 20 also shows the fixed size allocated to each set for alignment purposes.

[0329] Figure 20 also shows an example of using a stretchable fingerprint (steps 61, 62 and 63), and also shows a combination of stretchable fingerprint and fluid-aligned coding (steps 61, 64 and 65).

[0330] Figures 21 and 22 show various examples of the structure and content of management data.

[0331] The first MDR 50 stores a mapping 52' between the key, fingerprint, and compressed run identifier.

[0332] The first MDR 50 stores a mapping 52'' between the key, fingerprint and compressed combined run identifier.

[0333] A combination of the first MDR 50 and the second MDR 70 (e.g., a decoding table). The first MDR 50 may be stored in a mapping 52'' between the key, fingerprint, and compressed combined run identifier, but is only used for compressed combined run identifiers not exceeding a predetermined size. The second MDR is stored in a mapping between the key, fingerprint, and combined run identifier, but is only used for combined run identifiers exceeding a predetermined size (in compressed form).

[0334] The combination of the first MDR 50 and the overflow data structure 72. The first MDR 50 may be stored in a mapping 52” between a key, fingerprint and compressed run identifier, but any content that may cause a bucket overflow may be stored in the first overflow data structure 72.

[0335] Figure 22 also shows a record table 80, which maps individual run IDs (columns 82(x)) to a single compressed combined run ID and their compressed combined run IDs (columns 84(x)). The index x ranges from 1 to X, where X is the number of entries on record table 80. X can change over time. The record table receives a request to determine the compressed combined run ID and outputs the selected CC_RUNID.

[0336] In addition to the first MDR of storage mapping 52'', a record table 80 is also provided.

[0337] While the foregoing written description of the invention enables one of ordinary skill in the art to make and use things that are currently considered to be in their optimal mode, such skill will understand and recognize the existence of variations, combinations, and equivalents of the particular embodiments, methods, and examples herein. Therefore, the invention should not be limited to the embodiments, methods, and examples described above, but rather to all embodiments and methods within the scope and spirit of the invention as claimed.

[0338] In the foregoing specification, the invention has been described with reference to specific examples of embodiments thereof. However, it will be apparent that various modifications and changes may be made therein without departing from the broader spirit and scope of the invention as set forth in the appended claims.

[0339] Those skilled in the art will recognize that the boundaries between logic blocks are merely illustrative, and alternative embodiments may combine logic blocks or circuit elements, or impose alternative decompositions of functionality on various logic blocks or circuit elements. Therefore, it should be understood that the architecture described herein is merely exemplary, and many other architectures that implement the same functionality can actually be implemented.

[0340] Any arrangement of components that perform the same function is effectively “associated” to achieve the desired function. Therefore, any two components combined in this paper to achieve a specific function can be regarded as “associated” with each other to achieve the desired function, regardless of the architecture or intermediate components. Similarly, any two components that are thus associated can also be regarded as “operably connected” or “operably coupled” to each other to achieve the desired function.

[0341] Furthermore, those skilled in the art will recognize that the boundaries between the above operations are merely illustrative. Multiple operations may be combined into a single operation, a single operation may be distributed among additional operations, and operations may be performed with at least partial overlap in time. Additionally, alternative embodiments may include multiple instances of a particular operation, and the order of operations may be varied in various other embodiments.

[0342] Additionally, for example, in one embodiment, the illustrated example can be implemented as a circuit located on a single integrated circuit or within the same device. Alternatively, the example can be implemented as any number of separate integrated circuits or separate devices interconnected with each other in a suitable manner.

[0343] However, other modifications, variations, and substitutions are also possible. Therefore, the specification and drawings are to be considered illustrative rather than restrictive.

[0344] Any reference numerals placed between parentheses in the claims should not be construed as limiting the claims. The word “comprising” does not exclude the presence of other elements or steps besides those listed in the claims. Furthermore, the terms “a(a)” or “an(an)” as used herein are defined as one or more. Furthermore, the use of introductory phrases (e.g., “at least one” and “one or more”) in the claims should not be construed as implying that the introduction of another claim element by the indefinite article “a(a)” or “an(an)” limits any particular claim containing such introduced claim element to an invention containing only one such element, even if the same claim includes the introductory phrase “one or more” or “at least one” and the indefinite article (e.g., “a(a)” or “an(an)”). The same applies to definite articles. Unless otherwise stated, terms such as “first” and “second” are used to arbitrarily distinguish the elements described by such terms. Therefore, these terms are not necessarily intended to indicate the time or other priority of such elements. The indisputable fact that certain measures are described in mutually different requests does not indicate that combinations of these measures cannot be used advantageously.

[0345] Although certain features of the invention have been illustrated and described herein, many modifications, substitutions, alterations, and equivalents will occur to those skilled in the art. Therefore, it should be understood that the appended claims are intended to cover all such modifications and alterations falling within the true spirit of the invention.

[0346] It should be understood that, for clarity, various features of embodiments of the present disclosure described in the context of different embodiments may also be provided in combination in a single embodiment. Conversely, for brevity, various features of embodiments of the present disclosure described in the context of a single embodiment may also be provided individually or in any suitable sub-combination.

[0347] Those skilled in the art will recognize that the embodiments of this disclosure are not limited to what has been specifically shown and described above. Rather, the scope of the embodiments of this disclosure is defined by the appended claims and their equivalents. [Simplified Explanation of the Diagram]

[0026] The embodiments of this disclosure will be more fully understood and appreciated from the following detailed description, taken in conjunction with the accompanying drawings, wherein:

[0027] Figure 1 shows an example of maintenance costs and data volume;

[0028] Figure 2 shows an example of an LSM tree variant;

[0029] Figure 3 shows an example of Chucky's high-level architecture;

[0030] Figure 4 shows an example of a Huffman tree;

[0031] Figure 5 shows an example of the compression result;

[0032] Figure 6 shows an example of the relationship between the average size of run ID and the LSM tree size ratio T;

[0033] Figure 7 shows examples of compressed run IDs and compressed combination run IDs;

[0034] Figure 8 shows an example of the relationship between the average size of run IDs and the number of run IDs represented by a single combined run ID;

[0035] Figure 9 shows an example of alignment problems and solutions;

[0036] Figure 10 illustrates an example of the benefits of using malleable fingerprinting and fluid alignment coding;

[0037] Figure 11 shows an example of the relationship between the size of the first Management Data Structure (MDS) and the number of levels in the LSM tree;

[0038] Figures 12A-12H show a comparison of the performance of Chucky and other methods;

[0039] Figure 13 is an example of the method;

[0040] Figure 14 is an example of the method;

[0041] Figure 15 is an example of writing to the current run and updating the first MDS;

[0042] Figure 16 is an example of merging operations and updating the first MDS;

[0043] Figure 17 is an example of the first MDS;

[0044] Figure 18 is an example of the first MDS;

[0045] Figure 19 is an example of the first MDS;

[0046] Figure 20 is an example of the bucket alignment problem and one or more solutions for solving the alignment problem;

[0047] Figure 21 includes an example of the first MDS and also an example of the second MDS; and

[0048] Figure 22 includes an example of a first MDS and one or more additional MDSs.

Claims

1. A method for managing a log structure merge (LSM) tree of key-value (KV) pairs, the LSM tree being stored in non-volatile memory, the method comprising: Write the current run from the buffer to the current run position within the LSM tree, the current run including the current key-value pair; generate or receive a current fingerprint indicating the current key-value pair by means of a management unit comprising one or more integrated circuits; and perform a run write update of the MDS by means of the management unit by adding a mapping between the current key-value pair, the current fingerprint, and the current run identifier to the management data structure (MDS). The LSM tree is updated by merging at least some runs of the LSM tree through this management unit; The merging process includes merging a first run of the LSM tree that includes a first KV pair with a second run of the LSM tree that includes a second KV pair; and by means of the management unit, performing a merge update of the MDS to indicate the merging.

2. The method as described in request item 1, wherein, The run write update is performed without checking for the existence of a previous version of the current KV pair within the LSM tree.

3. The method as described in request item 1, wherein, Regardless of whether a previous version of the current KV pair exists or not within the LSM tree, the run write update is executed.

4. The method as described in request item 1, wherein, The merging includes adding the second KV pair to the first run, and the execution of the merge update includes updating the run identifier associated with the second KV pair while maintaining the run identifier associated with the first KV pair.

5. The method as described in request item 1, wherein, The merging process includes writing the first KV pair and the second KV pair into the third run of the LSM tree, wherein the execution of the merge update includes updating the run identifiers associated with the first KV pair and the second KV pair.

6. The method as described in request item 1, wherein, The merging includes deleting the previous version of the KV pair when the newer version of the KV pair includes a value representing a delete command.

7. The method as described in request item 1, wherein, The merging includes merging at least two runs belonging to different levels of the LSM tree.

8. The method as described in request item 1, wherein, The merging includes merging at least two runs belonging to the same level of the LSM tree.

9. The method as described in Request 1, comprising triggering the merging of runs at one or more levels of the LSM tree whenever a run is written to the nonvolatile memory.

10. The method as described in Request 1, comprising triggering a merge of the runs of the one or more levels whenever one or more levels of the LSM tree reach full level.

11. The method as described in claim 1, wherein, The MDS includes multiple buckets, each configured to store relay data associated with two or more KV pairs.

12. The method as described in claim 1, comprising receiving a request for accessing a requested key-value pair stored in the non-volatile memory, accessing the MDS, using the key of the requested key-value pair to obtain the location of the relevant run, and retrieving the relevant run when it exists.

13. The method as described in claim 1, wherein, The non-volatile memory is solid-state drive (SSD) memory.

14. A non-transitory computer-readable medium for managing a log structure merge (LSM) tree of key-value (KV) pairs, the LSM tree being stored in non-volatile memory, the non-transitory computer-readable medium storing instructions that, once executed by a management unit comprising one or more integrated circuits, cause the management unit to perform the following steps: writing a current run from a buffer to a current run position within the LSM tree, the current run including a current KV pair; generating or receiving a current fingerprint indicating the current KV pair; performing a run write update of the MDS by adding a mapping between the current KV pair, the current fingerprint, and a current run identifier to a management data structure (MDS); updating the LSM tree by merging at least some runs of the LSM tree; wherein the merging includes merging a first run of the LSM tree including a first KV pair with a second run of the LSM tree including a second KV pair; and performing a merge update of the MDS to indicate the merging.

15. An apparatus including a memory controller and non-volatile memory units, the memory controller being configured to manage a log structure merging (LSM) tree of key-value (KV) pairs stored in the non-volatile memory units, the memory controller being configured to manage the LSM tree by: writing a current run from a buffer to a current run position within the LSM tree, the current run including a current KV pair; generating or receiving a current fingerprint indicating the current KV pair; performing a run write update of the MDS by adding a mapping between the current KV pair, the current fingerprint, and a current run identifier to a management data structure (MDS); and updating the LSM tree by merging at least some runs of the LSM tree; wherein, The merging includes merging a first run of the LSM tree that includes a first KV pair with a second run of the LSM tree that includes a second KV pair; and performing a merge update of the MDS to indicate the merging.

16. The device as claimed in claim 15, wherein, The run write update is performed without checking for the existence of a previous version of the current KV pair within the LSM tree.

17. The device as claimed in claim 15, wherein, Regardless of whether a previous version of the current KV pair exists or not within the LSM tree, the run write update is executed.

18. The device as claimed in claim 15, wherein, The merging includes adding the second KV pair to the first run, and the execution of the merge update includes updating the run identifier associated with the second KV pair while maintaining the run identifier associated with the first KV pair.

19. The device as claimed in claim 15, wherein, The merging process includes writing the first KV pair and the second KV pair into the third run of the LSM tree, wherein the execution of the merge update includes updating the run identifiers associated with the first KV pair and the second KV pair.

20. The device as claimed in claim 15, wherein, The merging includes deleting the previous version of the KV pair when the newer version of the KV pair includes a value representing a delete command.

21. The device as claimed in claim 15, wherein, The merging includes merging at least two runs belonging to different levels of the LSM tree.

22. The device as claimed in claim 15, wherein, The merging includes merging at least two runs belonging to the same level of the LSM tree.

23. The device as claimed in claim 15, wherein, The memory controller is configured to trigger the merging of one or more levels of the LSM tree whenever a run is written to the non-volatile memory.

24. The device as claimed in claim 15, wherein, The memory controller is configured to trigger the merging of runs at one or more levels of the LSM tree whenever one or more levels reach full level.

25. The device as claimed in claim 15, wherein, The MDS includes multiple buckets, each configured to store relay data associated with two or more KV pairs.

26. The device as claimed in claim 15, wherein, The memory controller is configured to receive a request to access a requested key-value pair stored in the non-volatile memory, access the MDS, use the key of the requested key-value pair to obtain the location of the relevant run, and retrieve the relevant run if it exists.

27. The device as claimed in claim 15, wherein, The non-volatile memory is solid-state drive (SSD) memory.

28. A method for managing a log structure merge (LSM) tree of key-value (KV) pairs, the LSM tree being stored in non-volatile memory, the method comprising: The LSM tree's runs are merged to provide a merged run by a management unit comprising one or more integrated circuits; the merged run is then written into the non-volatile memory. The management unit adds a new run to the LSM tree, wherein the addition includes writing the run to the non-volatile memory; and the management unit updates at least one management data structure (MDS) to reflect the merging and the addition; wherein the at least one MDS stores a mapping between the keys of the KV pairs of the LSM tree, the fingerprints associated with the KV pairs of the LSM tree, and the compressed run identifiers that identify the run of the LSM tree; wherein the compressed run identifiers are compressed using variable-length codes.

29. The method as described in claim 28, comprising compressing the run identifier to provide a compressed run identifier.

30. The method as described in claim 29, wherein, The LSM tree includes a first level and a last level, wherein the first level is smaller than the last level; wherein the compression includes assigning a compressed run identifier to the last level that is shorter than the compressed run identifier of the run of the first level.

31. The method as described in request 28, including calculating a combined run identifier code representing a combination of run identifiers.

32. The method as described in claim 31, wherein, Each combined run identifier code is associated with the fingerprint of each of the run identifiers that form a combination represented by the combined run identifier.

33. The method as described in claim 32, comprising determining the length of each fingerprint in the fingerprint for each level of the LSM tree.

34. The method as described in claim 33, wherein, The determination includes maximizing the product of the fingerprint length of the level multiplied by the score of that level in the LSM tree, or the sum of all levels in the LSM tree.

35. The method of claim 32, comprising storing a plurality of sets in a bucket of the MDS, wherein each set includes a combined run identifier code and a fingerprint of each of a combination of run identifiers represented by the combined run identifier code.

36. The method of claim 35, comprising storing overflow relay data not included in the bucket in an overflow MDS.

37. The method as described in request 28, comprising calculating a compressed combined run identifier code representing a combination of run identifiers.

38. The method as described in claim 37, comprising imposing a constraint on the minimum length of the compressed combined run identifier code.

39. The method as described in claim 37, comprising imposing a constraint on the minimum length of the compressed combined run identifier code and determining the length of each fingerprint in the fingerprint for each level of the LSM tree.

40. A non-transitory computer-readable medium for managing a log structure merge (LSM) tree of key-value (KV) pairs, the LSM tree being stored in non-volatile memory, the non-transitory computer-readable medium comprising: Merge the runs of the LSM tree to provide a merged run; write the merged run into the non-volatile memory; Add a new run to the LSM tree, wherein the addition includes writing the run to the non-volatile memory; and update at least one management data structure (MDS) to reflect the merging and the addition; wherein the at least one MDS stores a mapping between the keys of the KV pairs of the LSM tree, the fingerprints associated with the KV pairs of the LSM tree, and the compressed run identifiers that identify the run of the LSM tree.

41. The non-transitory computer-readable medium as described in claim 40, wherein, The compressed run identifier is compressed using variable-length code.

42. The non-transitory computer-readable medium as described in claim 40, which stores instructions for compressing a run identifier by applying variable-length encoding to provide a compressed run identifier.

43. The non-transitory computer-readable medium as described in claim 42, wherein, The LSM tree includes a first level and a last level, wherein the first level is smaller than the last level; wherein the compression includes assigning a compressed run identifier to the last level that is shorter than the compressed run identifier of the run of the first level.

44. The non-transitory computer-readable medium as described in claim 40, which stores instructions for calculating a combined run identifier code representing a combination of run identifiers.

45. The non-transitory computer-readable medium as described in claim 44, wherein, Each combined run identifier code is associated with the fingerprint of each of the run identifiers that form a combination represented by the combined run identifier.

46. ​​The non-transitory computer-readable medium as described in claim 45, which stores instructions for determining the length of each fingerprint in the fingerprints for each level of the LSM tree.

47. The non-transitory computer-readable medium as described in claim 46, wherein, The determination includes maximizing the product of the fingerprint length of the level multiplied by the score of that level in the LSM tree, or the sum of all levels in the LSM tree.

48. The non-transitory computer-readable medium of claim 45, which stores instructions for storing a plurality of sets in buckets of the MDS, wherein each set includes a combined run identifier code and a fingerprint of each of the run identifiers forming a combination represented by the combined run identifier code.

49. The non-transitory computer-readable medium as described in claim 48, which stores instructions for storing overflow relay data not included in the bucket in an overflow MDS.

50. The non-transitory computer-readable medium as described in claim 40, which stores instructions for calculating compressed combined run identifier codes representing combinations of run identifiers.

51. The non-transitory computer-readable medium as described in claim 50, which stores instructions for imposing a constraint on the minimum length of the compressed combined run identifier code.

52. The non-transitory computer-readable medium as claimed in claim 51, storing instructions for performing the following operations: imposing a constraint on the minimum length of the compressed combined run identifier code, and determining the length of each fingerprint in the fingerprint for each level of the LSM tree.

53. An apparatus including a memory controller and a non-volatile memory unit, the memory controller being configured to manage a log structure merge (LSM) tree of key-value (KV) pairs stored in the non-volatile memory unit, the memory controller being configured to manage the LSM tree by: merging runs of the LSM tree to provide a merged run; writing the merged run to the non-volatile memory; adding a new run to the LSM tree, wherein the addition includes writing a run to the non-volatile memory; and updating at least one management data structure (MDS) to reflect the merging and the addition; wherein the at least one MDS stores a mapping between keys of the KV pairs of the LSM tree, fingerprints associated with the KV pairs of the LSM tree, and compressed run identifiers identifying the runs of the LSM tree.

Citation Information

Patent Citations

  • Optimization method and device for lsm-tree index

    CN104809237B

  • A storage method suitable for key-value pair data

    CN106708427B

  • Merge tree modifications for maintenance operations

    TW201841123A

  • System and Method for Efficiently Updating a Secondary Index Associated with a Log-Structured Merge-Tree Database

    US20190332701A1