Persistent variable radix tree access system supporting lock-free concurrent access
By optimizing the variable radix tree through hash-based transformation and lock-free concurrency control mechanism, the problem of insufficient concurrency control of the variable radix tree in high-concurrency scenarios is solved, and efficient access to non-volatile memory devices and data consistency guarantee are achieved.
Patent Information
- Authority / Receiving Office
- CN · China
- Patent Type
- Patents(China)
- Current Assignee / Owner
- Filing Date
- 2023-04-17
- Publication Date
- 2026-03-24
AI Technical Summary
Existing variable radix tree storage systems lack sufficient concurrency control in high-concurrency scenarios and have limited adaptability to non-volatile memory devices, making it difficult to achieve lock-free concurrent access.
The variable radix tree is optimized by using prefix nodes modified by hashing and hash hashing method. Combined with a lock-free concurrency control mechanism, the request queue is processed through a background processing module to ensure data consistency and system stability.
It reduces the amount of memory access during a single request processing, reduces read/write latency, improves the system request processing throughput in high-concurrency scenarios, and ensures data consistency and power failure recoverability.
Smart Images

Figure CN116401416B_ABST
Abstract
Description
TECHNICAL FIELD
[0001] The present application relates to the field of computer storage systems, and in particular, to a persistent variable radix tree access system that supports lock-free concurrent access. BACKGROUND
[0002] New persistent memory (PM) technology combines the characteristics of both DRAM memory and disk / solid state disk (SSD) / flash. It has received extensive attention and research due to its byte-addressable capability, low latency and data persistence. On the one hand, it is similar to disk / solid state disk (SSD) / flash, with characteristics such as large capacity, non-volatility, low power consumption, etc. On the other hand, it is similar to DRAM memory, with characteristics such as low latency, high bandwidth, byte-addressable, etc. Compared with traditional DRAM memory, a storage system based on PM can directly store data persistently, saving the overhead of refreshing to the next level of persistent storage. Compared with a disk, a persistent storage system based on PM can directly access the data stored in PM through load / store, saving the overhead of loading into a memory buffer. Therefore, PM provides an opportunity for the construction of a new type of efficient persistent storage system, and research results on the design of high-performance persistent data structures based on the memory access characteristics of non-volatile memory are also being continuously proposed. In addition, commercial products such as Intel Optane DC persistent memory module (DCPMM) with byte-addressable capability have also been introduced.
[0003] A radix trie (also called a compressed prefix tree) is a data structure that is a more space-efficient prefix tree in which each node that has only one child is merged with its parent, and edges can be represented as either a sequence of elements or as a single element. Thus, the number of children of each internal node is at most the radix r of the radix trie, where r is a positive integer and x is a power of 2, x >= 1, which makes the radix trie more suitable for smaller sets (especially when the strings are long) and sets of strings with long common prefixes. The way of searching in a radix trie is also different from that of a regular tree (the regular tree compares the entire key at the beginning until it is not the same), and when searching in a radix trie, the key in the node is compared in blocks, where the length of the block in the node is the radix r; when r is 2, the radix trie is binary (i.e., the length of the key in the node is 1 bit), which can minimize the depth of the tree to minimize sparsity (maximally merge nodes without branching in the key). When r >= 4 and is an integer power of 2, the radix trie is an r-ary radix tree, which can reduce the depth of the radix tree at the cost of potential sparsity. As an important tree-based data indexing structure, compared with a B+ tree, the overall structure of the radix tree is determined by the inserted Key value and insertion does not need to perform any comparison on the Key value, effectively reducing the flushing of cache lines and necessary log records, and thus the radix tree is more suitable for data indexing in non-volatile memory.
[0004] The adaptive radix tree is different from the traditional radix tree in that the number of prefix bytes that can be accommodated by each node is dynamically changed, and the size of the node can be dynamically adjusted through node promotion / demotion, thereby improving the space utilization and cache locality. The nodes of the adaptive radix tree include prefix nodes for prefix indexing and leaf nodes for storing Key-Value key-value pairs.
[0005] Patent document CN1613073A (application number: 03802024.6) discloses an improved multi-radix tree method and device for assigning a logical level number to a symbol in a key comprising a string of symbols, and storing an entry of the key in a node level of a multi-radix tree based at least in part on the logical level number, rather than on a path between nodes representing each symbol in the key. The patent does not improve the concurrency control of the radix tree, and cannot be well applied in a high concurrency scenario. And the existing adaptive radix tree storage system mainly uses DRAM memory storage devices, and has very limited adaptation and support for non-volatile memory devices. SUMMARY
[0006] In view of the defects in the prior art, the purpose of the present application is to provide a persistent adaptive radix tree access system supporting lock-free concurrent access.
[0007] According to the application, a persistent variable radix tree access system supporting lock-free concurrent access is provided, comprising:
[0008] A data storage module: organizes and manages the persistent storage of prefix nodes, leaf nodes and data of the variable radix tree, and supports a general variable radix tree read-write access interface;
[0009] A local access module: receives read-write requests of local applications to the variable radix tree and puts them into a processing queue for processing by a background processing module;
[0010] A background processing module: processes read-write requests in the processing queue, updates prefix nodes and leaf nodes of the variable radix tree, and ensures that the metadata and data of the variable radix tree are in a consistent state;
[0011] A data recovery module: detects fault consistency after the persistent variable radix tree access system is restarted, and recovers all data in the variable radix tree to a consistent state when the data is inconsistent.
[0012] Preferably, the prefix nodes have different node sizes and capacities according to different load sizes, store prefix information of data key values, and are used for indexing data key values;
[0013] The prefix nodes include N8 prefix nodes, N32 prefix nodes, N64 prefix nodes, N128 prefix nodes and N256 prefix nodes, and respectively contain information of 8, 32, 64, 128 and 256 child nodes for indexing prefix values of the corresponding number of child nodes;
[0014] The leaf nodes are used for storing complete information of data Key-Value values.
[0015] Preferably, the prefix nodes use a hash partitioning method to divide different sizes of prefix nodes in the variable radix tree into N persistent memory access granules with a size of XPLine, and the Buckets store all information required for accessing the corresponding prefix byte values in the current prefix node; wherein the N32 prefix nodes, the N64 prefix nodes and the N128 prefix nodes respectively contain 1, 2 and 4 Buckets, and the N8 prefix nodes and the N256 prefix nodes use a child node information list to store index information of child node prefix bytes;
[0016] The prefix nodes use a hash partitioning method to hash partition the prefix byte values stored in the current node and the corresponding child node information, so that different prefix byte values are uniformly distributed in different Buckets of the prefix node.
[0017] Preferably, the Bucket comprises 32 sub-node context fields, each of which corresponds to one sub-node of the current prefix node and contains all metadata of the sub-node; each sub-node context field is 8 bytes, supporting atomic update;
[0018] The sub-node context field comprises a sub-node type, a sub-node version number, a prefix byte, a sub-node pointer, and a sub-node prefix length.
[0019] Preferably, in the data storage module, the entire persistent variable radix tree is stored in persistent memory, allowing different threads of the background processing module to initiate concurrent non-blocking read-write requests, and realizing processing of requests received by the local access module.
[0020] Preferably, in the background processing module,
[0021] Module M3.1: The background processing thread obtains a to-be-processed request from the request queue, and parses the corresponding key and request type;
[0022] Module M3.2: The background processing thread divides the key into a prefix byte sequence according to the parsed key.
[0023] Module M3.3: Using the current prefix byte sequence, the layer sequence index is performed from the variable radix tree root prefix node until the index reaches a leaf node or the lowest prefix node or a prefix mismatch.
[0024] Module M3.4: After the prefix index process is completed, according to the current request type, the corresponding action is performed to complete the main action of the current request.
[0025] Module M3.5: After the main action of the request is completed, it is checked whether the version in the sub-node context field of the index endpoint corresponding to the prefix node has changed to determine whether the prefix node has undergone or is undergoing node upgrade / downgrade; if the node upgrade / downgrade has occurred, the last level of layer sequence index is retriggered to execute, and modules M3.4 to M3.5 are retriggered to execute at the new index endpoint, until the version number does not change at one time, the current read-write request processing process is completed, and the result is returned; if the node upgrade / downgrade is being performed, the version number of the sub-node context field is read in a loop until the prefix node completes the node upgrade / downgrade, and then the last level of layer sequence index is retriggered to execute, and modules M3.4 to M3.5 are retriggered to execute at the new index endpoint, until the version number does not change at one time, the current read-write request processing process is completed, and the result is returned.
[0026] Preferably, in the module M3.3,
[0027] Module M3.3.1: Hash the current prefix byte to determine the Bucket to which the prefix byte belongs in this prefix node;
[0028] Module M3.3.2: In a given Bucket, perform a traversal search on the context field of the child nodes contained in the Bucket, compare the prefix bytes in the context field of the child nodes with the current prefix bytes, and determine the next-level prefix node in the variable radix tree corresponding to the key being processed;
[0029] Module M3.3.3: Based on the found child node context field, read and record the version number of the child node to ensure data concurrency consistency;
[0030] Module M3.3.4: Based on the found child node context field, read the prefix length of the child node, skip the prefix bytes of the corresponding length in the prefix byte sequence corresponding to the currently processed key, and determine the prefix bytes used for indexing in the next level prefix node;
[0031] Module M3.3.5: Based on the found child node context field, read the pointer of the child node and determine the address of the next level child node;
[0032] Module M3.3.6: Based on the found child node context field, read the node type of the child node, determine the size of the next-level child node and the number of Buckets, hash the determined next prefix byte, determine the Bucket to which the prefix byte belongs in the next-level child node, and read the child node Bucket according to the determined child node address. Repeatedly trigger modules M3.3.1 to M3.3.6 to execute the prefix indexing process of the next level until the index endpoint is reached;
[0033] The index endpoints include: the leaf node and prefix node corresponding to the key of the current request, where no corresponding leaf node or prefix node is found in the prefix node and its prefix does not match the key of the current request.
[0034] Preferably, the node upgrade / downgrade uses the version number in the child node context field to identify the initiation and completion status of the node upgrade / downgrade, thus avoiding concurrency anomalies such as update loss and read inconsistency during this process;
[0035] Module M3.5.1: Atomically update the context field of the child node corresponding to the prefix node of the node being upgraded / downgraded in this execution, and increment its version number by 1;
[0036] Module M3.5.2: Create a new prefix node of a different size than the original prefix node, based on upgrade or downgrade requirements;
[0037] Module M3.5.3: Begins copying the sequence of child node context fields of each Bucket in the original prefix node to the corresponding Bucket in the new prefix node;
[0038] Module M3.5.4: Performs an atomic update on the context field of the child node corresponding to the original prefix node in the parent node, increments its version number by 1, and updates the node type at the same time.
[0039] Preferably, the update of the version number of the child node context field during the node upgrade / downgrade process will serve as the basis for other concurrent threads to check whether the current prefix node has undergone or is undergoing a node upgrade / downgrade.
[0040] The prefix node version number is initialized to 0;
[0041] If a check finds that the prefix node version number is odd, it means that the prefix node is undergoing node upgrade / downgrade. At this time, the concurrent background processing thread will read the context field of the child node in a loop until the prefix node version number becomes even during a certain read, which means that the node upgrade / downgrade of the prefix node has been completed. The background processing thread can then read the address of the new prefix node again and start executing the target operation.
[0042] If a check finds that the prefix node version number is even, but has changed since the initial read, it means that the prefix node has undergone a complete node upgrade / downgrade process during the execution of the target operation by the background processing thread. In this case, the concurrent background processing thread will reread the child node context field, obtain the address of the new prefix node, and restart the execution of the target operation.
[0043] Preferably, in the data recovery module, the variable radix tree is traversed in a level order starting from the root prefix node. The context fields of all child nodes in all Buckets of all prefix nodes are checked in turn. The version number of the child node is checked. If the version number is odd, it means that the node was in an incomplete node upgrade / downgrade state before the power failure of the persistent variable radix tree access system. The version number needs to be incremented by one to restore it to an even number.
[0044] After the above process is completed for all prefix nodes, the persistent variable radix tree access system can begin to receive and concurrently process read and write requests normally.
[0045] Compared with the prior art, the present invention has the following beneficial effects: by performing hash-based transformation on the prefix nodes of the variable radix tree, the present invention reduces the memory access volume of a single request processing process and reduces the read and write latency of the persistent variable radix tree access system; in addition, by performing lock-free transformation on the variable radix tree concurrency control mechanism, the present invention improves the throughput of system request processing in high-concurrency scenarios and ensures data consistency and power failure recoverability. Attached Figure Description
[0046] Other features, objects, and advantages of the present invention will become more apparent from the following detailed description of non-limiting embodiments with reference to the accompanying drawings:
[0047] Figure 1 This is a block diagram of the persistent variable radix tree access system that supports lock-free concurrent access according to an embodiment of the present invention.
[0048] Figure 2 This is a data layout diagram of a variable radix tree according to an embodiment of the present invention.
[0049] Figure 3 This is the core data structure of a variable radix tree in one embodiment of the present invention. Detailed Implementation
[0050] The present invention will now be described in detail with reference to specific embodiments. These embodiments will help those skilled in the art to further understand the present invention, but do not limit the invention in any way. It should be noted that those skilled in the art can make several changes and improvements without departing from the concept of the present invention. These all fall within the protection scope of the present invention.
[0051] Example 1
[0052] To address the shortcomings of existing technologies, the purpose of this invention is to provide a persistent variable radix tree access system that supports lock-free concurrent access.
[0053] A persistent variable radix tree access system supporting lock-free concurrent access according to the present invention includes:
[0054] Data storage module: Organizes and manages the prefix nodes, leaf nodes, and persistent storage of data in the variable radix tree, and supports a general variable radix tree read / write access interface;
[0055] Local access module: Receives read and write requests from local applications to the variable radix tree and puts them into a processing queue to wait for processing by the background processing module;
[0056] The backend processing module processes read and write requests (add, delete, query, and modify requests) in the processing queue, updates the prefix and leaf nodes of the variable radix tree, and ensures that the metadata and data of the variable radix tree are in a consistent state.
[0057] Data recovery module: After the persistent variable radix tree access system restarts, it detects the consistency of the fault and restores all data in the variable radix tree to a consistent state when the data is inconsistent.
[0058] Specifically, the data used to organize and manage the variable cardinality tree includes: prefix node data and leaf node data;
[0059] Prefix nodes can have different sizes and capacities depending on the load, storing prefix information for data key values for indexing. Prefix node types include N8, N32, N64, N128, and N256, containing information for 8, 32, 64, 128, and 256 child nodes respectively, allowing indexing of the corresponding number of child node prefix values.
[0060] Leaf nodes store complete information about the key-value pairs of the data.
[0061] Specifically, the prefix nodes employ a hash-based partitioning scheme to reduce the number of persistent memory reads and writes required for a single node access. Prefix nodes of different sizes in the variable radix tree consist of N buckets of persistent memory access granularity (XPLine, 256 bytes). Each bucket stores all the information needed to access the prefix byte value corresponding to that region within the prefix node. Specifically, N32, N64, and N128 prefix nodes contain 1, 2, and 4 buckets respectively. N8 and N256 prefix nodes use a child node information list to store the index information of the prefix bytes of their child nodes. N8 and N256 are special cases; they do not require bucket partitioning for storage but can be stored directly using a more efficient list method. In essence, the purpose of bucket partitioning is to allow the location of a child node to be completed by accessing only 256 bytes of space for prefix nodes of different sizes. 256 bytes correspond to one bucket, which contains 32 child node information entries; therefore, N32 is the smallest prefix node that can be stored at the bucket level. The entire size of node N8 is only 64 bytes, so accessing all the content of N8 will not exceed 256 bytes. Therefore, there's no need to use bucket storage; a list can be used to store the information of these eight child nodes. For N256, in a variable radix tree, each level of prefix node actually indexes a 1-byte prefix value of a child node. This 1-byte prefix value consists of 8 bits. If these 8 bits are considered an integer, the maximum range they can represent is 0-255, or 256 possible values. Therefore, in N256, we can directly construct a child node information array of length 256 and store the child node information directly in the position corresponding to the integer part of its prefix byte. For example, if the prefix byte of a node translates to the integer 15, then this child node information is stored in the 15th position of the N256 list. In this way, if we want to find the child node information corresponding to the prefix value of a certain child node in N256, we don't need to compare and search one by one in other nodes (or other buckets). Instead, we can directly search at the corresponding position. In this way, N256 does not need to be stored in the bucket mode to achieve the effect of accessing a range of no more than 256 bytes.
[0062] The prefix node uses a hash hashing scheme to hash the prefix byte value stored in the node and its corresponding child node information, so that different prefix byte values are evenly distributed in different buckets of the prefix node.
[0063] Specifically, the Bucket consists of 32 child node context fields. Each child node context field corresponds one-to-one with a child node of the current prefix node and contains all the metadata of that child node. Each child node context field is 8 bytes and supports atomic updates.
[0064] Specifically, the child node context field comprises five parts:
[0065] NodeType: Indicates the type of child node corresponding to the context field of this child node, with a size of 3 bits. Where 1 represents an N8 prefix node, 2 represents an N32 prefix node, 3 represents an N64 prefix node, 4 represents an N128 prefix node, 5 represents an N256 prefix node, and 6 represents a leaf node;
[0066] NodeVersion: Represents the version number of the child node corresponding to the context field of this child node, with a size of 5 bits. This version number records the upgrade and downgrade information of this child node, and is incremented by 1 at the beginning and end of each upgrade and downgrade, respectively, to assist in the concurrent access of the variable radix tree;
[0067] KeyByte: This represents the prefix byte value of the child node corresponding to the context field of this child node in the variable radix tree. It is 8 bits in size and is used to assist in the execution of the level order indexing process of the variable radix tree.
[0068] Pointer: Represents the address of the child node corresponding to the context field of this child node in the variable radix tree system. It is 48 bits in size and is used to assist in the execution of the level order indexing process of the variable radix tree.
[0069] PrefixLength: This indicates the number of prefix bytes that were compressed after path compression was performed on the child node corresponding to the context field of this child node in the variable radix tree system. It is 6 bits in size and is actually the last 6 bits of the child node pointer segment. It is used to skip the prefix bytes that were compressed during the path compression process when performing the level order index of the variable radix tree.
[0070] Specifically, the data storage module stores the entire persistent variable radix tree in persistent memory, allowing different threads of the background processing module to initiate concurrent, non-blocking data read and write requests, thereby enabling the processing of requests received by the local access module.
[0071] Specifically, the background processing module uses a request queue to accept read and write requests initiated by multiple local upper-layer applications. The background processing module performs the following actions to process the read and write requests in the request queue:
[0072] More specifically, the background processing module includes:
[0073] Module M3.1: The background processing thread retrieves the next pending request from the request queue and parses the corresponding Key and request type.
[0074] Module M3.2: The background processing thread divides the parsed Key into a prefix byte sequence.
[0075] Module M3.3: Uses the prefix byte sequence to perform level-order indexing starting from the root prefix node of the variable radix tree, until the leaf node, the lowest-level prefix node, or a prefix mismatch is reached.
[0076] The prefix indexing process at each prefix node, based on the current prefix byte and the current prefix node content, includes:
[0077] Module M3.3.1: Hash the current prefix byte to determine the Bucket to which the prefix byte belongs in this prefix node.
[0078] Module M3.3.2: In the Bucket determined above, perform a traversal search on the context field of the child nodes contained in the Bucket, and compare whether the prefix byte KeyByte in the context field of the child node is the same as the current prefix byte, so as to determine the next-level prefix node corresponding to the Key being processed in the variable radix tree.
[0079] Module M3.3.3: Based on the found child node context field, read and record the child node version number NodeVersion to assist in subsequent processing and ensure data concurrency consistency.
[0080] Module M3.3.4: Based on the found child node context field, read the prefix length of the child node, skip the prefix bytes of the corresponding length in the prefix byte sequence corresponding to the Key being processed, and determine the prefix bytes to be used for indexing in the next level prefix node.
[0081] Module M3.3.5: Based on the found child node context field, read the pointer of the child node to determine the address of the next level child node.
[0082] Module M3.3.6: Based on the found child node context field, it reads the child node's node type (NodeType), determines the size of the next-level child nodes and the number of buckets, and hashes the next prefix byte determined in S3.4 to determine the bucket to which the prefix byte belongs in the next-level child node. It then reads the child node's bucket based on the child node address determined in S3.5, repeating the above process to execute the next-level prefix indexing process until the index endpoint is reached. Possible index endpoints include: the leaf node corresponding to the requested key, a prefix node where no corresponding leaf node is found, and a prefix node whose prefix does not match the requested key.
[0083] Module M3.4: After the prefix indexing process is completed, the corresponding action is executed according to the request type to complete the main action of the request;
[0084] Module M3.4 includes:
[0085] Module M3.4.1: For query, modification, and deletion requests, depending on the index endpoint type, the following situations apply:
[0086] If the index endpoint is the leaf node corresponding to the key of this request, then the corresponding query, modification, and deletion actions are performed directly on that leaf node.
[0087] If the index endpoint is a prefix node and the target leaf node is not found in that prefix node, or the prefix of that prefix node does not match the key of this request, then the result of this request is that the target key does not exist.
[0088] Module M3.4.2: For insert requests, depending on the index endpoint type, the following situations apply:
[0089] If the index endpoint is the leaf node corresponding to the key in this request, then the result of this request is that the target key already exists.
[0090] If the index endpoint is a prefix node, and the target leaf node is not found in that prefix node, then a leaf node consisting of the target key and value, along with its corresponding child node context field, is constructed, and the child node context field is inserted into the corresponding Bucket within that prefix node. If the Bucket is full, a node upgrade operation will also be triggered.
[0091] If the index endpoint is a prefix node and its prefix does not match the key of this request, a node split operation will be triggered. The specific actions include creating a new prefix node, setting its prefix to the common prefix of the original prefix node and the key of this request, inserting the new prefix node into the original parent node of the original prefix node, and updating the prefix of the original prefix node.
[0092] Module M3.5: After the main action of the above request is completed, check whether the version number NodeVersion in the context field of the child node of the prefix node corresponding to the index endpoint has changed to determine whether the prefix node has undergone or is undergoing a node upgrade / downgrade. If a node upgrade / downgrade has already occurred, the last level of the sequence index in Module M3.3 is re-triggered, and the operations of Modules M3.4 and M3.5 are re-triggered at the new index endpoint until the version number is found to be unchanged. At this point, the current read / write request processing process ends and the result is returned. If a node upgrade / downgrade is currently occurring, the version number NodeVersion in the context field of the child node is read in a loop until the prefix node completes the node upgrade / downgrade. Then, the last level of the sequence index in the S3 process is re-executed, and the operations of Modules M3.4 and M3.5 are re-triggered at the new index endpoint until the version number is found to be unchanged. At this point, the current read / write request processing process ends and the result is returned.
[0093] Specifically, the node upgrade / downgrade process uses the version number in the child node context field to identify the initiation and completion status of the node upgrade / downgrade, avoiding concurrency anomalies such as update loss and read inconsistency during this process. When performing a node upgrade / downgrade, the background processing thread executes the following actions:
[0094] Module M3.5.1: Performs an atomic update on the context field of the child node corresponding to the prefix node of the node being upgraded / downgraded in this execution, and increments its version number NodeVersion by 1.
[0095] Module M3.5.2: Create a new prefix node of a different size than the original prefix node, based on upgrade or downgrade requirements.
[0096] Module M3.5.3: Starts copying the child node context field sequence of each Bucket in the original prefix node to the corresponding Bucket in the new prefix node.
[0097] Module M3.5.4: Performs an atomic update on the context field of the child node corresponding to the original prefix node in the parent node, increments its version number NodeVersion by 1, and updates the node type NodeType at the same time.
[0098] Specifically, the update of the version number NodeVersion in the context field of the child node during the node upgrade / downgrade process will serve as the basis for other concurrent threads to check whether the current prefix node has undergone or is undergoing a node upgrade / downgrade.
[0099] Specifically, the prefix node version number NodeVersion is initialized to 0.
[0100] If a check finds that the prefix node version number (NodeVersion) is odd, it indicates that the prefix node is undergoing a node upgrade / downgrade. At this point, the concurrent background processing thread will continuously read the context field of this child node until the prefix node version number (NodeVersion) becomes even during a read, indicating that the node upgrade / downgrade is complete. The background processing thread can then reread the address of the new prefix node and begin executing the target operation.
[0101] If a check finds that the prefix node version number NodeVersion is even, but has changed since the initial read, it means that the prefix node has undergone a complete node upgrade / downgrade process during the execution of the target operation by the background processing thread. In this case, the concurrent background processing thread will reread the child node context field, obtain the address of the new prefix node, and restart the execution of the target operation.
[0102] Specifically, the data recovery module includes:
[0103] Starting from the root prefix node, perform a level-order traversal of the variable radix tree, and check the context field of all child nodes in all Buckets of all prefix nodes in turn. Check the NodeVersion of the child node. If the version number is odd, it means that the node was in an incomplete node upgrade / downgrade state before the system power failure. The version number needs to be incremented by one to restore it to an even number.
[0104] After performing the above process on all prefix nodes, the persistent variable radix tree access system can begin to receive first-level concurrent read and write requests normally.
[0105] The persistent variable radix tree access system supporting lock-free concurrent access provided by this invention can be implemented through the steps of the persistent variable radix tree access method supporting lock-free concurrent access provided by this invention. Those skilled in the art can understand the persistent variable radix tree access method supporting lock-free concurrent access as a preferred example of the persistent variable radix tree access system supporting lock-free concurrent access.
[0106] Example 2
[0107] Example 2 is a preferred example of Example 1.
[0108] Before introducing the embodiments of the present invention, the terminology used in the present invention will be explained.
[0109] The novel persistent memory (PM) technology, also known as non-volatile memory (NVM), combines the characteristics of DRAM and disks / solid-state drives (SSDs) / flash memory. Due to its byte-addressing capability, low latency, and data persistence, it has attracted widespread attention and research. On the one hand, it is similar to disks / SSDs / flash memory, featuring large capacity, non-volatility, and low power consumption; on the other hand, it is similar to DRAM, featuring low latency, high bandwidth, and byte-addressability. Compared to traditional DRAM, storage systems built on PM can directly and persistently store data, eliminating the overhead of refreshing to the next level of persistent storage. Compared to disks, persistent storage systems built on PM can directly access data stored in PM via load / store, eliminating the overhead of loading to the memory buffer. Therefore, PM provides an opportunity to build new, efficient persistent storage systems, and research results on designing high-performance persistent data structures based on the access characteristics of non-volatile memory are constantly being proposed. In addition, commercial products with byte-addressable persistent memory capabilities, such as the Intel Optane DCpersistent memory module (DCPMM), are now available.
[0110] A radix tree (also called a radix-trie or compressed prefix tree) is a data structure that is a more space-efficient prefix tree where each node with a unique child node is merged with its parent node. Edges can be represented as either sequences of elements or single elements. Therefore, the number of children of each internal node is at most the radix *r* of the radix tree, where *r* is a positive integer, *x* is a power of 2, and *x* ≥ 1. This makes radix trees more suitable for smaller sets (especially long strings) and sets of strings with long common prefixes. The search method in a radix tree also differs from that of a regular tree (where the entire key is compared at the beginning until they are different). When searching a node in a radix tree, the keys at that node are compared block by block, where the length of the blocks in that node is the radix *r*. When *r* is 2, the radix tree is binary (i.e., the key length of that node is 1 bit), which minimizes the tree depth to reduce sparsity (by merging nodes with no branches in the key to the maximum extent). When r ≥ 4 and is an integer power of 2, the radix tree is an r-ary radix tree, which can reduce the depth of the radix tree at the cost of potential sparsity. As an important tree-based data indexing structure, compared to B+ trees, the overall structure of the radix tree is determined by the inserted key value, and the insertion does not require any comparison of the key value, effectively reducing the flushing of cache lines and necessary log recording. Therefore, it is more suitable for data indexing in non-volatile memory.
[0111] The biggest difference between an adaptive radix tree and a traditional radix tree is that the prefix bytes that each node can hold are dynamically changing. The size of a node can be dynamically adjusted through node promotion / demotion, thereby improving space utilization and cache locality.
[0112] Figure 1 This is a module composition diagram of a persistent variable radix tree access system supporting lock-free concurrent access according to an embodiment of the present invention. Figure 1 The persistent variable radix tree access system shown, which supports lock-free concurrent access, consists of four main modules: local access module, background processing module, data storage module, and data recovery module.
[0113] The data storage module organizes and manages the prefix nodes, leaf nodes, and persistent data of the variable radix tree, and supports a general variable radix tree read / write access interface. The local access module receives read / write requests from local applications to the variable radix tree and places them in a processing queue to await processing by the background processing module. The background processing module processes add, delete, query, and modify requests in the processing queue, updates the prefix nodes and leaf nodes of the variable radix tree, and ensures that the metadata and data of the variable radix tree are in a consistent state. The data recovery module detects fault consistency after the persistent variable radix tree access system restarts, and restores all data in the variable radix tree to a consistent state when the data is inconsistent.
[0114] The persistent variable radix tree's data storage module divides the variable radix tree data into two parts: prefix node data and leaf node data. Prefix nodes, with varying sizes and capacities depending on the load, store prefix information for the data keys, used for indexing these keys. Prefix node types include N8, N32, N64, N128, and N256 prefix nodes, containing information for 8, 32, 64, 128, and 256 child nodes respectively, allowing indexing of the corresponding number of child node prefix values. Leaf nodes store complete key-value information.
[0115] like Figure 2 As shown, the prefix nodes employ a hash-based partitioning scheme to reduce the number of persistent memory reads and writes caused by a single node access. Prefix nodes of different sizes in the variable radix tree consist of N buckets of persistent memory access granularity (XPLine, 256 bytes). Each bucket stores all the information needed to access the prefix byte value corresponding to that region within the prefix node. Specifically, N32, N64, and N128 prefix nodes contain 1, 2, and 4 buckets respectively, while N8 and N256 prefix nodes use a child node information list to store the index information of the prefix bytes of their child nodes. The prefix nodes use a hash-based partitioning scheme to distribute the stored prefix byte values and their corresponding child node information evenly across the different buckets of the prefix node.
[0116] Furthermore, persistent variable radix trees are... Figure 3The data structure shown uses the child node context fields to organize the prefix node content. The Bucket consists of 32 child node context fields. Each child node context field corresponds one-to-one with a child node of the current prefix node and contains all the metadata of that child node. Each child node context field is 8 bytes and supports atomic updates. The child node context field includes five parts: NodeType, which indicates the type of child node corresponding to this context field, and is 3 bits in size. Here, 1 represents an N8 prefix node, 2 represents an N32 prefix node, 3 represents an N64 prefix node, 4 represents an N128 prefix node, 5 represents an N256 prefix node, and 6 represents a leaf node; and NodeVersion, which indicates the version number of the child node corresponding to this context field, and is 5 bits in size. The version number records the upgrade / downgrade information of this child node, incrementing by 1 at the beginning and end of each upgrade and downgrade, respectively, to assist in concurrent access to the variable radix tree; the prefix byte KeyByte represents the prefix byte value of the child node corresponding to the context field in the variable radix tree, with a size of 8 bits, to assist in the level-order indexing process of the variable radix tree; the child node pointer Pointer represents the address of the child node corresponding to the context field in the variable radix tree system, with a size of 48 bits, to assist in the level-order indexing process of the variable radix tree; the child node prefix length PrefixLength represents the number of prefix bytes that were compressed after path compression in the variable radix tree system, with a size of 6 bits, which is actually the last 6 bits of the child node pointer segment, used to skip the prefix bytes compressed during the path compression process during the level-order indexing process of the variable radix tree.
[0117] The data storage module stores the entire persistent variable radix tree in persistent memory, allowing different threads of the background processing module to initiate concurrent, non-blocking data read and data modification requests, thereby enabling the processing of requests received by the local access module.
[0118] The background processing module uses a request queue to accept read and write requests from multiple local upper-layer applications. Specifically, the background processing module performs the following actions to process the read and write requests in the request queue:
[0119] Module S3.1: The background processing thread retrieves the next pending request from the request queue and parses the corresponding Key and request type.
[0120] Module S3.2: The background processing thread divides the parsed Key into a prefix byte sequence.
[0121] Module S3.3: Uses the prefix byte sequence to perform level-order indexing starting from the root prefix node of the variable radix tree, until a leaf node, the lowest-level prefix node, or a prefix mismatch is reached. The process of performing prefix indexing at each prefix node based on the current prefix byte and the current prefix node content is as follows:
[0122] Module S3.3.1: Hash the current prefix byte to determine the Bucket to which the prefix byte belongs in this prefix node.
[0123] Module S3.3.2: In the Bucket determined above, perform a traversal search on the context field of the child nodes contained in the Bucket, and compare whether the prefix byte KeyByte in the context field of the child node is the same as the current prefix byte, so as to determine the next-level prefix node corresponding to the Key being processed in the variable radix tree.
[0124] Module S3.3.3: Based on the found child node context field, read and record the child node version number NodeVersion to assist in subsequent processing and ensure data concurrency consistency.
[0125] Module S3.3.4: Based on the found child node context field, read the prefix length PrefixLength of the child node, skip the corresponding length of prefix bytes in the prefix byte sequence corresponding to the Key being processed, and determine the prefix bytes to be used for indexing in the next level prefix node.
[0126] Module S3.3.5: Based on the found child node context field, read the pointer of the child node to determine the address of the next level child node.
[0127] Module S3.3.6: Based on the found child node context field, read the child node's node type (NodeType), determine the size of the next-level child nodes and the number of buckets, and hash the next prefix byte determined in S3.4 to determine the bucket to which the prefix byte belongs in the next-level child node. Then, read the child node's bucket based on the child node address determined in S3.5. Repeat the above process to execute the next-level prefix indexing process until the index endpoint is reached. Possible index endpoints include: the leaf node corresponding to the requested key, a prefix node where no corresponding leaf node is found, and a prefix node whose prefix does not match the requested key.
[0128] Module S3.4: After the prefix indexing process is completed, the corresponding action is executed according to the request type to complete the main action of this request. Specifically, the following situations apply:
[0129] Module S3.4.1: For query, modification, and deletion requests, depending on the index endpoint type, the following situations apply:
[0130] Module S3.4.1.1: If the index endpoint is the leaf node corresponding to the Key of this request, then the corresponding query, modification, and deletion actions are performed directly on that leaf node.
[0131] Module S3.4.1.2: If the index endpoint is a prefix node and the target leaf node is not found in that prefix node, or the prefix of that prefix node does not match the key of this request, then the result of this request is that the target key does not exist.
[0132] Module S3.4.2: For insert requests, depending on the index endpoint type, the following situations apply:
[0133] Module S3.4.2.1: If the index endpoint is the leaf node corresponding to the Key of this request, then the result of this request is that the target Key already exists.
[0134] Module S3.4.2.2: If the index endpoint is a prefix node and no target leaf node is found in that prefix node, then construct the leaf node consisting of the target key and value and its corresponding child node context field, and insert the child node context field into the corresponding Bucket within that prefix node. If the Bucket is full, a node upgrade operation will also be triggered.
[0135] Module S3.4.2.3: If the index endpoint is a prefix node and its prefix does not match the key of this request, a node split operation will be triggered. The specific actions include creating a new prefix node, setting its prefix to the common prefix of the original prefix node and the key of this request, inserting the new prefix node as the parent node of the original prefix node into the original parent node of the original prefix node, and updating the prefix of the original prefix node.
[0136] Module S3.5: After the main action of the above request is completed, check whether the version number NodeVersion in the context field of the child node of the prefix node corresponding to the index endpoint has changed to determine whether the prefix node has undergone or is undergoing a node upgrade / downgrade. If a node upgrade / downgrade has already occurred, re-execute the last level order index of the S3.3 process, and re-execute the S3.4 and S3.5 operations on the new index endpoint until the version number is unchanged in a certain check. The current read / write request processing process ends and the result is returned. If a node upgrade / downgrade is in progress, read the version number NodeVersion in the context field of the child node in a loop until the prefix node completes the node upgrade / downgrade. Then re-execute the last level order index of the S3.3 process, and re-execute the S3.4 and S3.5 operations on the new index endpoint until the version number is unchanged in a certain check. The current read / write request processing process ends and the result is returned.
[0137] During the node upgrade / downgrade process, the version number in the child node's context field identifies the initiation and completion status of the upgrade / downgrade, preventing concurrency exceptions such as lost updates and read inconsistencies. When executing a node upgrade / downgrade, the background processing thread performs the following actions:
[0138] Module S3.5.1: Performs an atomic update on the context field of the child node corresponding to the prefix node of the node being upgraded / downgraded in this execution, and increments its version number NodeVersion by 1.
[0139] Module S3.5.2: Create a new prefix node of a different size than the original prefix node based on upgrade or downgrade requirements.
[0140] Module S3.5.3: Starts copying the child node context field sequence of each Bucket in the original prefix node to the corresponding Bucket in the new prefix node.
[0141] Module S3.5.4: Performs an atomic update on the context field of the child node corresponding to the original prefix node in the parent node, increments its version number NodeVersion by 1, and updates the node type NodeType at the same time.
[0142] The update of the NodeVersion field in the context field of the child node during the node upgrade / downgrade process will serve as the basis for other concurrent threads to check whether the current prefix node has undergone or is undergoing a node upgrade / downgrade.
[0143] Specifically, the prefix node version number NodeVersion is initialized to 0.
[0144] If a check finds that the prefix node version number (NodeVersion) is odd, it indicates that the prefix node is undergoing a node upgrade / downgrade. At this point, the concurrent background processing thread will continuously read the context field of this child node until the prefix node version number (NodeVersion) becomes even during a read, indicating that the node upgrade / downgrade is complete. The background processing thread can then reread the address of the new prefix node and begin executing the target operation.
[0145] If a check finds that the prefix node version number NodeVersion is even, but has changed since the initial read, it means that the prefix node has undergone a complete node upgrade / downgrade process during the execution of the target operation by the background processing thread. In this case, the concurrent background processing thread will reread the child node context field, obtain the address of the new prefix node, and restart the execution of the target operation.
[0146] The data recovery module performs the following actions: starting from the root prefix node, it traverses the variable radix tree in level order, checking the context fields of all child nodes within all buckets of all prefix nodes, and verifying the node version number (NodeVersion). If the version number is odd, it indicates that the node was in an incomplete node upgrade / downgrade state before the power outage, and the version number needs to be incremented to restore it to an even number. After performing the above process on all prefix nodes, the persistent variable radix tree access system can begin to normally receive and process first-level concurrent read and write requests.
[0147] Those skilled in the art will understand that, in addition to implementing the system, apparatus, and their modules provided by this invention in purely computer-readable program code, the same program can be implemented in the form of logic gates, switches, application-specific integrated circuits, programmable logic controllers, and embedded microcontrollers by logically programming the method steps. Therefore, the system, apparatus, and their modules provided by this invention can be considered a hardware component, and the modules included therein for implementing various programs can also be considered structures within the hardware component; alternatively, modules for implementing various functions can be considered both software programs implementing the method and structures within the hardware component.
[0148] Specific embodiments of the present invention have been described above. It should be understood that the present invention is not limited to the specific embodiments described above, and those skilled in the art can make various changes or modifications within the scope of the claims, which do not affect the essence of the present invention. Unless otherwise specified, the embodiments and features described in this application can be arbitrarily combined with each other.
Claims
1. A persistent variable radix tree access system supporting lock-free concurrent access, characterized in that, include: Data storage module: Organizes and manages the prefix nodes, leaf nodes, and persistent storage of data in a variable radix tree, and supports a general variable radix tree read / write access interface; Local access module: Receives read and write requests from local applications to the variable radix tree and puts them into a processing queue to wait for processing by the background processing module; The backend processing module processes read and write requests in the processing queue, updates the prefix and leaf nodes of the variable radix tree, and ensures that the metadata and data of the variable radix tree are in a consistent state. Data recovery module: After the persistent variable radix tree access system restarts, it detects the consistency of the fault and restores all data in the variable radix tree to a consistent state when the data is inconsistent; In the background processing module, Module M3.1: The background processing thread retrieves requests to be processed from the request queue and parses them to obtain the corresponding key and request type; Module M3.2: The background processing thread divides the parsed key into a prefix byte sequence; Module M3.3: Performs level-order indexing from the root prefix node of the variable radix tree using the current prefix byte sequence until a leaf node, the lowest-level prefix node, or a prefix mismatch is reached; Module M3.4: After the prefix indexing process is completed, the corresponding action is executed according to the current request type to complete the main action of the current request; Module M3.5: After the main action of the request is completed, check whether the version in the context field of the child node of the prefix node corresponding to the index endpoint has changed to determine whether the prefix node has undergone or is undergoing node upgrade / downgrade; if the node upgrade / downgrade has already occurred, then re-trigger the execution of the last level of the hierarchical index, and re-trigger the execution of the operations of Module M3.4 to Module M3.5 on the new index endpoint, until the version number is no longer changed, the current read / write request processing process ends and the result is returned; If a node upgrade / downgrade is in progress, the version number of the child node's context field is read repeatedly until the prefix node completes the upgrade / downgrade. Then, the execution of the last level of the hierarchical index is triggered again, and the operation of modules M3.4 to M3.5 is triggered again at the new index endpoint. This continues until the version number is checked and found to be unchanged. At this point, the current read / write request process ends and the result is returned.
2. The persistent variable radix tree access system supporting lock-free concurrent access according to claim 1, characterized in that, The prefix node has different node sizes and capacities depending on the load size, and stores the prefix information of the data key value for indexing the data key value; The prefix nodes include: N8 prefix nodes, N32 prefix nodes, N64 prefix nodes, N128 prefix nodes, and N256 prefix nodes, which respectively contain information on 8, 32, 64, 128, and 256 child nodes, and index the prefix values of the corresponding number of child nodes; The leaf nodes are used to store complete information about the data key-value pairs.
3. The persistent variable radix tree access system supporting lock-free concurrent access according to claim 2, characterized in that, The prefix nodes are divided into N buckets of different sizes in the variable radix tree using a hash-based partitioning method. The bucket stores all the information required to access the prefix byte value corresponding to the region in this prefix node. Among them, the N32 prefix node, N64 prefix node, and N128 prefix node contain 1, 2, and 4 buckets respectively. The N8 prefix node and N256 prefix node use a child node information list to store the index information of the prefix bytes of the child nodes. The prefix node uses a hash function to hash the prefix byte value stored in the node and its corresponding child node information, so that different prefix byte values are evenly distributed in different buckets of the prefix node.
4. The persistent variable radix tree access system supporting lock-free concurrent access according to claim 3, characterized in that, The Bucket includes 32 child node context fields. Each child node context field corresponds one-to-one with a child node of the current prefix node and contains all the metadata of that child node. Each child node context field is 8 bytes long and supports atomic updates. The child node context fields include: child node type, child node version number, prefix byte, child node pointer, and child node prefix length.
5. The persistent variable radix tree access system supporting lock-free concurrent access according to claim 1, characterized in that, In the data storage module, the entire persistent variable radix tree is stored in persistent memory, allowing different threads of the background processing module to initiate concurrent, non-blocking read and write requests, thereby processing the requests received by the local access module.
6. The persistent variable radix tree access system supporting lock-free concurrent access according to claim 1, characterized in that, In module M3.3, Module M3.3.1: Hash the current prefix byte to determine the Bucket to which the prefix byte belongs in this prefix node; Module M3.3.2: In a given Bucket, perform a traversal search on the context field of the child nodes contained in the Bucket, compare the prefix bytes in the context field of the child nodes with the current prefix bytes, and determine the next-level prefix node in the variable radix tree corresponding to the key being processed; Module M3.3.3: Based on the found child node context field, read and record the version number of the child node to ensure data concurrency consistency; Module M3.3.4: Based on the found child node context field, read the prefix length of the child node, skip the prefix bytes of the corresponding length in the prefix byte sequence corresponding to the currently processed key, and determine the prefix bytes used for indexing in the next level prefix node; Module M3.3.5: Based on the found child node context field, read the pointer of the child node and determine the address of the next level child node; Module M3.3.6: Based on the found child node context field, read the node type of the child node, determine the size of the next-level child node and the number of Buckets, hash the determined next prefix byte, determine the Bucket to which the prefix byte belongs in the next-level child node, and read the child node Bucket according to the determined child node address. Repeatedly trigger modules M3.3.1 to M3.3.6 to execute the prefix indexing process of the next level until the index endpoint is reached; The index endpoints include: the leaf node and prefix node corresponding to the key of the current request, where no corresponding leaf node or prefix node is found in the prefix node and its prefix does not match the key of the current request.
7. The persistent variable radix tree access system supporting lock-free concurrent access according to claim 1, characterized in that, The node upgrade / downgrade uses the version number in the child node context field to identify the initiation and completion status of the node upgrade / downgrade, thus avoiding concurrency anomalies such as update loss and read inconsistency during this process; Module M3.5.1: Atomically update the context field of the child node corresponding to the prefix node of the node being upgraded / downgraded in this execution, and increment its version number by 1; Module M3.5.2: Create a new prefix node of a different size than the original prefix node, based on upgrade or downgrade requirements; Module M3.5.3: Begins copying the sequence of child node context fields of each Bucket in the original prefix node to the corresponding Bucket in the new prefix node; Module M3.5.4: Performs an atomic update on the context field of the child node corresponding to the original prefix node in the parent node, increments its version number by 1, and updates the node type at the same time.
8. The persistent variable radix tree access system supporting lock-free concurrent access according to claim 7, characterized in that, The update of the version number of the child node context field during the node upgrade / downgrade process will serve as the basis for other concurrent threads to check whether the current prefix node has undergone or is undergoing a node upgrade / downgrade. The prefix node version number is initialized to 0; If a check finds that the prefix node version number is odd, it means that the prefix node is undergoing node upgrade / downgrade. At this time, the concurrent background processing thread will read the context field of the child node in a loop until the prefix node version number becomes even during a certain read, which means that the node upgrade / downgrade of the prefix node has been completed. The background processing thread can then read the address of the new prefix node again and start executing the target operation. If a check finds that the prefix node version number is even, but has changed since the initial read, it means that the prefix node has undergone a complete node upgrade / downgrade process during the execution of the target operation by the background processing thread. In this case, the concurrent background processing thread will reread the child node context field, obtain the address of the new prefix node, and restart the execution of the target operation.
9. The persistent variable radix tree access system supporting lock-free concurrent access according to claim 1, characterized in that, In the data recovery module, the variable radix tree is traversed in a level order starting from the root prefix node. The context fields of all child nodes in all Buckets of all prefix nodes are checked in turn. The version number of the child node is checked. If the version number is odd, it means that the node was in an incomplete node upgrade / downgrade state before the power failure of the persistent variable radix tree access system. The version number needs to be incremented by one to restore it to an even number. After the above process is completed for all prefix nodes, the persistent variable radix tree access system can begin to receive and concurrently process read and write requests normally.
Citation Information
Patent Citations
Enhanced multiway radix tree
CN1613073A
Cardinal number tree access system based on RDMA and nonvolatile memory
CN111400306A