Distributed cache system Memcached hash table operations

Source: Internet
Author: User
Tags assert memcached

There are two hash tables in memcached, one is "the main hash Table" (primary_hashtable) and the other is the "original hash Table" (old_hashtable). In general, the operation is accepted in the main table, when the new item is inserted to determine whether to expand, each operation, the first to detect whether the table is in an extended (expanding) state, if it is, the original table operation, when the expansion is completed in the transfer to the main table. When scaling up, take a gradual migration strategy: to migrate only one bucket node item from the original table to the new primary table at a time.

Overall, this is almost identical to the hash operation in Redis. Therefore no more detailed explanation, detailed analysis see code comments.

Hash table initialization, parameter hashtable_init to the set Hashpower size (order), the default size is 16
void Assoc_init (const int hashtable_init) {
if (hashtable_init) {
Hashpower = Hashtable_init;
}
Create a primary table (Hashsize (hashpower): Calculate the number of bucket nodes =2 hashpower the second party)
primary_hashtable = Calloc (Hashsize (hashpower), sizeof (void *));
if (! primary_hashtable) {
fprintf (stderr, "Failed to init hashtable.\n");
Exit (Exit_failure);
}
Emcached has a lot of global statistics, for real-time access to the use of various resources,
Updates to statistics need to be locked
Stats_lock ();//Lock on global statistics, updated information
Stats.hash_power_level = Hashpower;
Stats.hash_bytes = Hashsize (hashpower) * sizeof (void *);
Stats_unlock ();//unlock
}

Find the item of the given key in the hash table: find the corresponding Hashtable, then find the corresponding bucket node, and finally traverse the list to find the item of the target key.
Item *assoc_find (const char *key, const size_t nkey, const uint32_t HV) {
Item *it;//Bucket node
unsigned int oldbucket;//The bucket node index in the original table

is expanding, and the current node is in the Wish table, not yet migrated to the main table
Note that the:i& (2^n-1) result is the remainder of I divided by 2^n
if (Expanding &&
(Oldbucket = (HV & Hashmask (hashpower-1))) >= Expand_bucket)
{
it = Old_hashtable[oldbucket];
} else {//No expansion, or has been migrated to the main table
it = PRIMARY_HASHTABLE[HV & Hashmask (Hashpower)];
}

Item *ret = NULL;
int depth = 0;//The depth of the target node in the bucket
while (IT) {//Traverse bucket node List
if ((Nkey = = It->nkey) && (memcmp (Key, Item_key (IT), nkey) = = 0)) {
ret = it;
Break
}
it = it->h_next;
++depth;
}
Memcached_assoc_find (key, Nkey, depth);
return ret;
}

/* Returns the address of the item pointer before the key. If *item = = 0,
The item wasn ' t found */
intrinsic function: Returns a pointer to the previous item of the target key item, so that when the target item is deleted, only the next pointer that returns the item pointer is directed to the next item of the target item.
Static item** _hashitem_before (const char *key, const size_t nkey, const uint32_t HV) {
Item **pos;
unsigned int oldbucket;


if (Expanding &&
(Oldbucket = (HV & Hashmask (hashpower-1))) >= Expand_bucket)
{
pos = &old_hashtable[oldbucket];
} else {
pos = &PRIMARY_HASHTABLE[HV & Hashmask (Hashpower)];
}


while (*pos && (nkey! = (*pos)->nkey) | | memcmp (KEY, Item_key (*pos), Nkey))) {
pos = & (*pos)->h_next;
}
return POS;
}

/* Grows the Hashtable to the next power of 2. */
Hash table expands to twice times the original (the original primary table is copied to the long table, and the main table is expanded)
static void Assoc_expand (void) {
old_hashtable = primary_hashtable;

    primary_hashtable = Calloc (hashsize (hashpower + 1), sizeof (void *));
    if (primary_hashtable) {
        if (Settings.verbose > 1)
    &N Bsp       fprintf (stderr, "Hash table Expansion starting\n");
        hashpower++;
        expanding = true;
        Expand_bucket = 0;
        Stats_lock ();
        stats.hash_power_level = Hashpower;
        Stats.hash_bytes + = hashsize (hashpower) * sizeof (void *);
        stats.hash_is_expanding = 1;
        Stats_unlock ();
   } else {
        primary_hashtable = old_hashtable;
       /* bad news, but we can keep running. */
   }
}

static void Assoc_start_expand (void) {
if (started_expanding)
Return
Started_expanding = true;
Pthread_cond_signal (&maintenance_cond);
}

/* note:this isn ' t an assoc_update. The key must not already exist to call this */
Insert the given item into the head of the bucket of the Hashtable note: The item cannot already exist in the hash table (HV: hash value)
int Assoc_insert (item *it, const uint32_t HV) {
unsigned int oldbucket;

ASSERT (Assoc_find (Item_key (IT), it->nkey) = = 0); /* shouldn ' t have duplicately named things defined */

is expanding and is not yet completed, the item is placed in the head of the single-linked list of the corresponding bucket of the original Hashtable
if (Expanding &&
(Oldbucket = (HV & Hashmask (hashpower-1))) >= Expand_bucket)//Note that Hashpower has doubled, so it is hashpower-1
{
It->h_next = Old_hashtable[oldbucket];
Old_hashtable[oldbucket] = it;
} else {//No expansion is being placed in the main hashtable
It->h_next = PRIMARY_HASHTABLE[HV & Hashmask (Hashpower)];
PRIMARY_HASHTABLE[HV & Hashmask (hashpower)] = it;
}

hash_items++;
Whether you need to start expanding
if (! Expanding && hash_items > (hashsize (Hashpower) * 3)/2) {
Assoc_start_expand ();
}

Memcached_assoc_insert (Item_key (IT), It->nkey, Hash_items);
return 1;
}

Delete the corresponding item (just remove the item from the bucket list)
void Assoc_delete (const char *key, const size_t nkey, const uint32_t HV) {
Item **before = _hashitem_before (key, Nkey, HV);//Find the previous item of the item

if (*before) {
Item *NXT;
The total number of item in the Hash_items--;//hash table
/* The DTrace probe cannot is triggered as the last instruction
* Due to possible tail-optimization by the compiler
*/
Memcached_assoc_delete (Key, Nkey, Hash_items);
NXT = (*before)->h_next;
(*before)->h_next = 0; /* Probably pointless, but whatever. */
*before = NXT;
Return
}
/* Note:we never actually get here. The callers don ' t delete things
They can ' t find. */
ASSERT (*before! = 0);
}

Migrate function Start_assoc_maintenance_thread (), create migration thread, call function Assoc_maintenance_thread to migrate
Thread functions: Migrating bucket nodes, migrating one bucket at a time by default
static void *assoc_maintenance_thread (void *arg) {

while (Do_run_maintenance_thread) {
int II = 0;

/* Lock the cache, and bulk move multiple buckets to the new
* Hash table. */
Item_lock_global ();
Mutex_lock (&cache_lock);

for (ii = 0; II < hash_bulk_move && expanding; ++ii) {
Item *it, *next;
int bucket;

for (it = old_hashtable[expand_bucket]; NULL! = it; it = next) {
Next = it->h_next;

Computes the hash value and calculates the value of the Bucket node index
Bucket = hash (Item_key (IT), It->nkey) & Hashmask (Hashpower);
It->h_next = Primary_hashtable[bucket];
Primary_hashtable[bucket] = it;
}

Remove the bucket from the long table after each bucket has been migrated
Old_hashtable[expand_bucket] = NULL;

            expand_bucket++;
   //expansion end
            if (Expand_bucket = = Hashsize (hashpower-1)) {
&N Bsp               expanding = false;
                free (old_hashtable);
                Stats_lock ();
                Stats.hash_bytes-= hashsize (hashpower-1) * sizeof (void *); br>                stats.hash_is_expanding = 0;
                Stats_unlock ();
                if (Settings.verbose > 1)
        &N Bsp           fprintf (stderr, "Hash table Expansion done\n");
           }
       }

Mutex_unlock (&cache_lock);
Item_unlock_global ();

if (!expanding) {
/* Finished expanding. Tell all threads to use fine-grained locks */
Switch_item_lock_type (Item_lock_granular);
Slabs_rebalancer_resume ();
/* We are doing expanding. Just wait for next invocation */
Mutex_lock (&cache_lock);
Started_expanding = false;
Pthread_cond_wait (&maintenance_cond, &cache_lock);
/* Before doing anything, tell threads to use a global lock */
Mutex_unlock (&cache_lock);
Slabs_rebalancer_pause ();
Switch_item_lock_type (Item_lock_global);
Mutex_lock (&cache_lock);
Assoc_expand ();
Mutex_unlock (&cache_lock);
}
}
return NULL;
}

Distributed cache system Memcached hash table operations

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.