Implementation method of high-concurrency extensible hash table in multi-thread environment

By locking each bucket and using two bucket arrays, combined with extension threads and worker threads, the high concurrency and scalability issues of hash tables in multi-threaded environments are solved, achieving efficient data access and expansion, and avoiding excessively large lock granularity and deadlocks.

CN120821732APending Publication Date: 2025-10-21崔茂前 +1
View PDF 0 Cites 2 Cited by

Patent Information

Application Number
CN202410443597.0
Authority / Receiving Office
CN · China
Patent Type
Applications(China)
Current Assignee / Owner
Filing Date
2024-04-13
Publication Date
2025-10-21

AI Technical Summary

Technical Problem

In a multi-threaded environment, existing hash tables struggle to balance high concurrency and scalability. When the load factor exceeds a certain threshold, the number of elements in some buckets increases significantly, leading to decreased access efficiency. Furthermore, expanding the hash table requires changing the bucket corresponding to the element key, and existing methods cannot effectively solve this problem.

Method used

Add a lock to each bucket, remove the overall locking, add two bucket arrays, and dynamically adjust the load factor by expanding threads and worker threads to achieve high concurrency and scalability. During expansion, a combination of mutex locks and shared locks is used to avoid deadlocks and optimize the element migration process.

Benefits of technology

It achieves high concurrency access and scalability of hash tables in a multi-threaded environment, avoids concurrency queuing caused by excessively large lock granularity, optimizes access efficiency, adapts to changes in data scale, and reduces the waste of CPU computing resources.

✦ Generated by Eureka AI based on patent content.

Smart Images

  • Figure FT_1
    Figure FT_1
  • Figure FT_2
    Figure FT_2
  • Figure FT_3
    Figure FT_3
Patent Text Reader

Abstract

The invention provides a method, a device and equipment for realizing a high-concurrency extensible hash table (hash table) in a multi-thread environment and a medium. The implementation content of the method comprises two parts: 1) realizing high concurrency by a hash table fine-grained lock: realizing by adding the fine-grained lock to the hash table, locking each bucket of a bucket array of the hash table, and improving the concurrency of accessing the hash table in a multi-thread environment; 2) expandability of the hash table double-bucket array structure: two bucket arrays are used, the original bucket array is used for storing hash table elements in a non-expansion state, the expansion bucket array is used for storing newly added hash table elements and elements migrated from the original bucket array in an expansion state, the expansion bucket array is used as the original bucket array in an expansion state after migration is completed, and the expansion bucket array is used as the original bucket array; according to the method, the problem of realizing the high-concurrency extensible hash table in the multi-thread environment is solved, the high-concurrency access of the hash table in the multi-thread environment is ensured, the expandability of the hash table in the multi-thread environment is also ensured, the high concurrency and the expandability are carried out in parallel without conflict, and the hash table can adapt to the change of the data scale.
Need to check novelty before this filing date? Find Prior Art

Description

Technical Field

[0001] The implementation technology of hash table in multi-threaded and high-concurrency environment of computer software system. Background Art

[0002] Hash tables are a common data structure in computer software systems. Their strengths lie in their high data access efficiency, but their disadvantage is their low space utilization. While these inherent characteristics are unchangeable, achieving high concurrency and scalability in multithreaded environments presents room for improvement. Currently, high concurrency is achieved by locking the hash table as a whole and serializing access to it. However, since each hash table only has one lock, concurrency is significantly limited. A bigger problem is scalability. If the load factor of the hash table (load factor = number of hash table elements / number of hash table buckets) exceeds a certain threshold, the number of elements in some buckets will increase significantly, resulting in a decrease in the access efficiency of some elements in the hash table. To maintain the access efficiency of the hash table, the load factor is generally not greater than 0.75. If it exceeds 0.75, the hash table needs to be expanded by increasing the number of buckets. Increasing the number of buckets will cause the buckets corresponding to the element keys of the original hash table to change. When expanding, the elements in the buckets that have changed need to be moved to the expanded buckets. However, in a multi-threaded and highly concurrent environment, there is currently no feasible way to ensure normal access to the hash table while expanding the number of buckets of the hash table.

[0003] The present invention is aimed at this problem and discloses a method for realizing a highly concurrent and scalable hash table in a multi-threaded environment, thereby solving the problem of realizing a highly concurrent and scalable hash table in a multi-threaded environment. The method ensures both highly concurrent access to the hash table in a multi-threaded environment and scalability of the hash table in a multi-threaded environment, so that high concurrency and scalability do not conflict with each other and the hash table can adapt to changes in data scale. Summary of the Invention

[0004] The method for accessing elements in a hash table is generally to first calculate the hash value of the element's key through a hash function, and then use this hash value to take the remainder of the number of buckets in the hash table (the number of buckets in the hash table is the size of the array that makes up the hash table). The remainder value is the array index that stores the hash table element. The bucket is found by the index, and the bucket stores the hash table element, which is a key-value pair. Buckets are generally implemented by lists (linked lists). When the amount of data in the bucket is too large, the bucket can also be implemented by maps (mapping tables). Maps have lower space utilization than lists, and the list structure is simple. Since there are generally not too many elements in the bucket when the load factor is not too large, lists can fully meet performance requirements.

[0005] To achieve high concurrency in hash tables, a single lock cannot be used to implement mutually exclusive access to the hash table. This would result in excessive lock granularity. As the number of hash table elements increases and the number of concurrently accessing threads increases, concurrent queuing becomes more pronounced. To achieve high concurrency, we eliminate the need for global locking on the hash table and instead apply a lock to each bucket. When a bucket is locked by one thread, it does not affect other buckets locked by other threads. As the number of hash table elements increases, high concurrency performance in a multi-threaded environment remains unaffected, as long as the load factor is kept within a certain range.

[0006] As the number of elements in a hash table increases, the load factor gradually increases. As the load factor increases, the number of elements in some buckets increases. As the number of elements in a bucket increases, the performance of the hash table decreases. In this case, the hash table needs to be expanded to increase the number of buckets. As the number of buckets in a hash table increases, the elements in the original hash table buckets need to be moved to the expanded buckets. After the elements are moved, the expanded buckets can be accessed as if they were the original hash table.

[0007] A conventional hash table has only one bucket array. In order to achieve scalability in a multi-threaded and high-concurrency environment, the hash table needs to add a bucket array. The two bucket arrays are named bucket array 0 and bucket array 1 respectively. Bucket array 0 and bucket array 1 may be the original bucket array or the extended bucket array. If the original bucket array is bucket array 0, the extended bucket array is bucket array 1. Similarly, if the original bucket array is bucket array 1, the extended bucket array is bucket array 0. The extended bucket array is the bucket array being expanded. After the expansion is completed, the extended bucket array immediately becomes the original bucket array. In the non-extended state, the extended bucket array is meaningless.

[0008] The data structure diagram of the multi-threaded high-concurrency scalable hash table is shown in the attached figure of the specification. Figure 1 (Hash table data structure diagram).

[0009] In order to determine which bucket array is the original bucket array and which bucket array is the extended bucket array, a bucket array identifier is added. The bucket array identifier value is 0 or 1. When the bucket array identifier is 0, the original bucket array is bucket array 0 and the extended bucket array is bucket array 1; when the bucket array identifier is 1, the original bucket array is bucket array 1 and the extended bucket array is bucket array 0.

[0010] To achieve scalability in a multi-threaded, highly concurrent environment, after the hash table is constructed, it starts an expansion thread to perform operations on the hash table. The threads that perform add, delete, modify, and query operations on the hash table are called worker threads. When the expansion thread is expanding the hash table, the hash table is in the expanding state; otherwise, it is in the non-expanding state. The hash table is always in one of these two states.

[0011] When the hash table is in the extended state, the relationship between the working thread and the extended thread is shown in the figure attached to the specification. Figure 2 (Multi-threaded high-concurrency scalable hash table thread architecture diagram - expansion state).

[0012] When the hash table is in a non-expanded state, the relationship between the working thread and the expansion thread is shown in the figure attached to the specification. Figure 3 (Multi-threaded high-concurrency scalable hash table thread architecture diagram - non-expanded state).

[0013] The expansion thread periodically traverses the original bucket array. When it finds that the load factor of the original array is greater than the threshold, it starts the hash table expansion operation. Before starting to expand the hash table, you need to set an expansion flag. The expansion flag value is 0 or 1. When the load factor is less than or equal to the threshold and the hash table does not need to be expanded, the expansion flag value is 0. When the load factor is greater than the threshold, the expansion thread starts the expansion operation and changes the expansion flag value to 1. The hash table is in the expansion state. After the expansion operation is completed, the expansion flag value is changed to 0, and the hash table is in the non-expanded state. When the hash table is in different states, the operations of the worker thread to add, delete, modify and query hash table elements are also different.

[0014] Before determining whether the hash table needs to be expanded, the expansion thread needs to first calculate the load factor. Based on the value of the bucket array identifier, it obtains the original bucket array, traverses the original bucket array, adds a shared lock to each bucket and obtains the number of bucket elements, accumulates the number of bucket elements, releases the shared lock on the bucket, obtains the total number of elements in the original bucket array, and divides the total number of elements by the size of the original bucket array to obtain the load factor of the hash table. If the load factor is greater than the threshold, it is determined that the hash table needs to be expanded. The size of the extended bucket array of the hash table is determined by the expansion ratio, which is generally between 1.1 and 2, that is, the number of hash table buckets after expansion is 1.1 to 2 times the number of original hash table buckets. The size of the expansion ratio is determined by the growth rate of the elements. The greater the rate, the greater the multiple. The size of the extended bucket array is calculated according to the expansion ratio, and the extended bucket array is determined based on the bucket array identifier. The extended bucket array is first cleared, and the array capacity of the extended bucket array is reset according to the size of the extended bucket array.

[0015] After the array capacity of the extended bucket array is reset, the bucket array identifier value must be modified. The bucket array identifier is changed from 0 to 1, or vice versa. The extension identifier value is also changed to 1. This notifies the worker thread that the hash table is being expanded. When the extension identifier value is 1, the bucket array specified by the bucket array identifier is the extended bucket array. When the extension identifier value is 0, the worker thread only accesses the bucket array specified by the bucket array identifier, which is the original bucket array. When the extension identifier value is 1, the bucket array specified by the bucket array identifier is the extended bucket array. The worker thread prioritizes accessing the extended bucket array. If access fails, it accesses the original bucket array. When the extension thread moves elements during the expansion process, it also prioritizes accessing the extended bucket array before accessing the original bucket array. During the expansion process, no matter which bucket array is accessed, the bucket must be locked. Since the locking order is to lock the extended bucket first, then the original bucket, this locking order prevents deadlock.

[0016] The extension thread begins to traverse the original bucket array, adds a shared lock to each bucket in the original bucket array, and obtains the element key list of the original bucket. It traverses the element key list of the original bucket, obtains the extension bucket from the extension bucket array based on the element key and adds a mutex lock to the extension bucket, obtains the original bucket from the original bucket array and adds a mutex lock to the original bucket, queries the element from the original bucket, and if there is no value, it means that the element has been migrated to the extension bucket array (it may be that the worker thread has added, deleted, or modified the element before the extension thread); if there is a value, the key-value pair of the element is added to the extension bucket, and then the element corresponding to the key is deleted from the original bucket. The mutex locks added to the extension bucket and the original bucket are released to move the element from the original bucket array to the extension bucket array. At this time, due to the protection of the mutex lock, the normal access of the worker thread to the hash table is not affected.

[0017] After the extension thread traverses the original bucket array, it moves all elements of the original bucket array to the extended bucket array, modifies the extension identifier value to 0 (the bucket array identifier remains unchanged), and notifies the worker thread that the extension operation of the extension thread has been completed. The extended bucket array specified by the bucket array identifier is also automatically restored to the original bucket array. In the non-extended state (the extension identifier value is 0), the worker thread only needs to access the original bucket array, thus completing the expansion of the hash table without affecting the normal access of the worker thread.

[0018] After the expansion thread completes a round of expansion, the hash table's load factor will significantly decrease, eliminating the need for an immediate next round of expansion. Instead, the thread can actively sleep to free up CPU resources. The sleep duration can be tailored to the specific hash table usage scenario and can be adjusted accordingly with the hash table's growth rate. In short, avoid excessively short sleep times, which lead to frequent load factor calculations and increased CPU resource consumption, and avoid excessively long sleep times, which lead to excessive load factors and affect hash table access efficiency. A preliminary recommendation is a starting sleep duration of 10 seconds and a maximum sleep duration of 10 minutes (600 seconds). If the load factor is below the threshold, the sleep duration is increased to 1.1 times the previous sleep duration, until the maximum duration reaches 10 minutes. If the load factor is above the threshold, the sleep duration is the interval between the start time of the previous expansion round and the start time of the previous expansion round. If the number of hash table elements increases significantly in a short period of time (load factor greater than 1), the sleep duration is reduced to half the previous sleep duration. By adjusting the sleep time in this way, the load changes of the hash table can be adapted in a timely manner.

[0019] See the flowchart of extending the number of hash table buckets in the attached figure of the specification for the flow chart of extending the number of hash table buckets Figure 4 (Extended thread extended hash table flow chart).

[0020] When a worker thread adds an element (key-value pair) to a hash table, it first uses the array identifier to determine the current original bucket array and the expanded bucket array. When the expanded identifier is 0, the bucket array specified by the bucket array identifier is the original bucket array, and the worker thread only needs to access the original bucket array. When the expanded identifier is 1, the bucket array specified by the bucket array identifier is the expanded bucket array, and the other bucket array is the original bucket array. For example, when the expanded identifier is 1 and the bucket array identifier is 0, the expanded bucket array is bucket array 0 and the original bucket array is bucket array 1. When the bucket array identifier is 1, the expanded bucket array is bucket array 1 and the original bucket array is bucket array 0. If the expanded identifier is 0, the hash table is not in the expanded state, and the worker thread only needs to add the new element to the original bucket array. It first finds the bucket in the original bucket array corresponding to the key, applies a mutex lock to the bucket, adds the element to that bucket, and releases the mutex lock on the bucket. If the extension flag is 1, the hash table is in the extended state. First, the bucket corresponding to the element in the extended bucket array is obtained, a mutex lock is placed on the extended bucket, and then the bucket corresponding to the element in the original bucket array is obtained. The mutex lock is placed on the original bucket, and the new element is searched for in the original bucket. If the new element is not found in the original bucket, the new element is added to the extended bucket. If the new element is found in the original bucket, the key-value pair in the original bucket is inserted into the extended bucket, and the element in the original bucket is deleted. When adding a new element, if the key already exists in the bucket array, the old value is not overwritten with the new value.

[0021] The flowchart of adding hash table elements to the worker thread is shown in the attached figure of the manual. Figure 5(Flowchart of adding hash table elements to the worker thread).

[0022] When a worker thread modifies a hash table element, it first uses the bucket array identifier to determine the current original bucket array and the expanded bucket array. If the expanded identifier is 0, the hash table is not in the expanded state. The worker thread only needs to modify the element corresponding to the key in the original bucket array. It first finds the bucket in the original bucket array corresponding to the key, applies a mutex lock to the bucket, updates the element value, and releases the mutex lock on the bucket. If the extension flag value is 1, the hash table is in the extended state. At this time, first get the bucket corresponding to the key in the extended bucket array, add a mutex lock on the extended bucket, query the element corresponding to the key in the extended bucket, and modify the value of the element in the extended bucket (indicating that the element corresponding to the update key has been moved from the original bucket array to the extended bucket array, or the worker thread has added the element when the hash table is in the extended state); if the element corresponding to the update key cannot be found in the extended bucket, get the bucket corresponding to the key in the original bucket array, add a mutex lock on the original bucket, and query the element corresponding to the update key in the original bucket. If the element is found (indicating that the extension thread has not yet moved the element from the original bucket array to the extended bucket array), add the modified element to the extended bucket, delete the element from the original bucket, release the mutex lock of the original bucket, and release the mutex lock of the extended bucket. The operation here is to move the element from the original bucket array to the extended bucket array while updating.

[0023] The flowchart of the working thread modifying the hash table element is shown in the attached figure of the specification. Figure 6 (Flowchart of worker thread modifying hash table elements).

[0024] When a worker thread deletes an element from a hash table, it first uses the bucket array identifier to determine the current original bucket array and the expanded bucket array. If the expanded identifier is 0, the hash table is not in the expanded state. The worker thread simply deletes the element corresponding to the key from the original bucket array. It first finds the bucket in the original bucket array corresponding to the key, then locks the bucket with a mutex, deletes the element from that bucket, and releases the mutex on the bucket. If the expanded identifier is 1, the hash table is in the expanded state. The worker thread first obtains the bucket corresponding to the key in the expanded bucket array, locks the expanded bucket with a mutex, and searches for the element corresponding to the deleted key in the expanded bucket. If the element is found, it deletes the element directly from the expanded bucket (indicating that the element corresponding to the deleted key has been moved from the original bucket array to the expanded bucket array, or that the worker thread has added the element while the hash table is in the expanded state). If the element corresponding to the deleted key is not found in the expanded bucket, the worker thread obtains the bucket in the original bucket array corresponding to the key, locks the original bucket with a mutex, and searches for the element corresponding to the deleted key in the original bucket. If the element is found (indicating that the expansion thread has not yet moved the element from the original bucket array to the expanded bucket array), the worker thread deletes the element from the original bucket. If the extension thread traverses the original bucket earlier than the worker thread, the element cannot be found in the original bucket. If the extension thread traverses the original bucket later than the worker thread access time, the worker thread will delete the element in advance and the extension thread does not need to perform the deletion operation.

[0025] The flowchart of the worker thread deleting hash table elements is shown in the attached figure of the specification. Figure 7 (Flowchart of worker thread deleting hash table elements).

[0026] When a worker thread queries an element from the hash table, it first uses the array identifier to determine the current original bucket array and the expanded bucket array. If the expanded identifier is 0, the hash table is not in the expanded state. The worker thread queries the original bucket array for the element corresponding to the key. It first finds the bucket in the original bucket array corresponding to the key, applies a shared lock to the bucket, queries the element value, and releases the shared lock on the bucket. If the expanded identifier is 1, the hash table is in the expanded state. The worker thread first obtains the bucket in the expanded bucket array corresponding to the key, applies a shared lock to the expanded bucket, queries the element value in the expanded bucket, and returns the result if the element is found. If the element is not found, the worker thread obtains the bucket in the original bucket array corresponding to the key, applies a shared lock to the original bucket, queries the element value in the original bucket, returns the result, and releases the shared locks on both the expanded bucket and the original bucket. If the element is found in the original bucket, the element corresponding to the query key has not yet been moved to the expanded bucket array. The worker thread must wait for the expanded thread to move it to the expanded bucket array. The expanded thread will eventually move the element to the expanded bucket array after traversing the original bucket array. Therefore, element movement is not a concern when querying an element.

[0027] The flowchart of the working thread querying the hash table elements is shown in the attached figure of the specification. Figure 8 (Flowchart of worker thread querying hash table elements). BRIEF DESCRIPTION OF THE DRAWINGS

[0028] Figure 1 It is a diagram of the hash table data structure, including bucket array 0, bucket array 1, bucket array identifier, extension identifier, and bucket structure diagram. Figure 1 The instructions are as follows: Bucket Array Description: 1) Bucket Array 0 and Bucket Array 1: Both bucket arrays 0 and 1 can be the original bucket array or the extended bucket array. If the original bucket array is bucket array 0, the extended bucket array can be bucket array 1. Conversely, if the original bucket array is bucket array 1, the extended bucket array can be bucket array 0. The extended bucket array is the bucket array used when the hash table is in the extended state. After the expansion is complete, the extended bucket array serves as the original array. 2) The original bucket array and the extended bucket array are the two bucket arrays used when the hash table is in the extended state. In the non-extended state, only the original bucket array is used.

[0029] Bucket array identifier and extension identifier explanation: 1) The bucket array identifier value is 0 or 1. 2) The extension identifier value is 0 or 1, 0 indicates that the hash table is in a non-extended state, and 1 indicates that the hash table is in an extended state. 3) The original bucket array and the extended bucket array are determined by the bucket array identifier and the extension identifier. When the extension identifier value is 0, the hash table is in a non-extended state, the bucket array identifier value points to the original bucket array, and the worker thread only accesses the original bucket array; when the extension identifier value is 1, the hash table is in an extended state, the bucket array identifier value points to the extended bucket array, and the worker thread gives priority to accessing the extended bucket array. If no elements are accessed, the worker thread accesses the original bucket array; after the extension thread completes the extension operation, the extension identifier value is changed to 0, and the extended bucket array pointed to by the bucket array identifier value is accessed as the original bucket array. At this time, there is no concept of an extended bucket array.

[0030] Bucket structure description: 1) Buckets are elements that make up the bucket array. Each bucket contains a lock and a linked list of stored elements. 2) When querying an element, a shared lock must be applied to the lock on the bucket. Query operations do not modify elements, so multiple threads can simultaneously apply a shared lock to the bucket. 3) When adding, deleting, or modifying elements, a mutex lock must be applied to the lock on the bucket to ensure that only one thread holds the lock at any given time.

[0031] Figure 2This is a diagram of the thread architecture for a multi-threaded, highly concurrent, scalable hash table in its expanded state. In this state, the expansion flag is 1, and the bucket array specified by the bucket array flag is the expanded bucket array. Worker threads prioritize accessing the expanded bucket array. If there are no elements to be accessed in the expanded bucket array, they access the original bucket array. If the operation is a modification, the element must be moved from the original bucket array to the expanded bucket array. The expansion thread then iterates over the original bucket array, applying a shared lock to each original bucket. After acquiring an element from the original bucket, it releases the shared lock. It then iterates over each acquired element in the original bucket, applying a mutex lock to both the expansion bucket and the original bucket corresponding to each original bucket element. The element is moved from the original bucket to the expansion bucket, and the mutex locks on both the expansion bucket and the original bucket are released. After all elements in the original bucket array are moved, the expansion flag is changed (from 1 to 0), and the bucket array specified by the bucket array flag automatically becomes the original bucket array. Worker threads then automatically access the expanded original bucket array.

[0032] Figure 3 This is a diagram of the thread architecture of a multi-threaded, high-concurrency, extensible hash table in an extended state. At this time, the extended identifier value is 0, and the bucket array specified by the bucket array identifier value is the original bucket array. The worker thread only accesses the original bucket array. The extended thread will periodically traverse the original bucket array and calculate the load factor of the hash table. When the load factor is less than the threshold, the extended thread will sleep for a while and then recalculate the load factor of the hash table. If the load factor is greater than the threshold, the extended thread will calculate the number of array elements of the extended bucket based on the expansion ratio, expand the capacity of the extended bucket array based on the new number of elements, modify the bucket array identifier value (0 to 1, or 1 to 0) and the extended identifier value (0 to 1), and the hash table state will change to an extended state. The array pointed to by the bucket array identifier value will become an extended bucket array.

[0033] Figure 4 This is a flowchart of extending the thread and hash table. For details, please refer to the extended thread description part of the invention content.

[0034] Figure 5 This is a flowchart of adding hash table elements to the working thread. For specific content, please refer to the description of new elements added to the working thread in the invention content.

[0035] Figure 6 This is a flowchart of the working thread modifying the hash table elements. For specific content, please refer to the description part of the working thread modifying the elements in the invention content.

[0036] Figure 7 This is a flowchart of the working thread deleting hash table elements. For specific content, please refer to the description part of the working thread deleting elements in the invention content.

[0037] Figure 8 This is a flowchart of the working thread querying the hash table elements. For details, please refer to the description of the working thread query elements in the invention content.

Claims

1. A method for implementing a highly concurrent and scalable hash table in a multi-threaded environment, characterized in that Two parts: 1) Hash table fine-grained lock to achieve high concurrency; 2) Hash table double bucket array structure to achieve high concurrency scalable algorithm. Hash table fine-grained lock locks each bucket in the bucket array to achieve hash table fine-grained lock; The double-bucket array structure of the hash table uses two bucket arrays to implement a scalable hash table algorithm in a multi-threaded and high-concurrency environment.

2. The high-concurrency and scalable algorithm implemented by using a fine-grained hash table lock and a double-bucket array structure for a hash table according to claim 1 is a general multi-threaded, high-concurrency, and scalable hash table implementation method that optimizes high concurrency and scalability. The method is applicable to all computer software systems that use multi-threaded, high-concurrency, and scalable hash tables, such as software running on various operating systems such as Unix, Linux, Windows, and Apple, as well as various embedded software systems.

Citation Information

Cited By

  • Metadata object column storage method and system based on slot management

    CN121029764A

  • Metadata object column storage method and system based on slot management

    CN121029764B