sql >> Database teknologi >  >> NoSQL >> Redis

Sådan repareres skæve Hash-slots i Redis

I Redis er den primære distributionsenhed en hash-slot. Distribuerede versioner af redis - inklusive open source Redis Cluster, kommercielle Redis Enterprise og endda AWS ElastiCache - kan kun flytte rundt på data 1 slot ad gangen.

Dette fører til et interessant problem - skæve slots. Hvad hvis et slot (eller nogle få pladser) ender med at have de fleste data?

Er det overhovedet muligt?

Redis bestemmer hash-slot for en nøgle ved hjælp af en veludgivet algoritme. Denne algoritme vil normalt sikre, at nøgler er godt fordelt.

Men udviklere kan påvirke algoritmen ved at angive et hash-tag . Et hash-tag er en del af nøglen omgivet af krøllede klammeparenteser {...} . Når et hash-tag er angivet, vil det blive brugt til at bestemme hash-pladsen.

Hash-tagget i redis er, hvad de fleste databaser ville kalde en partitionsnøgle. Hvis du vælger en forkert partitionsnøgle, får du skæve slots.

Som et eksempel, hvis dine nøgler er som {users}:1234 og {users}:5432 , vil redis gemme alle brugere i den samme hash-plads.

Hvad er løsningen?

Rettelsen er konceptuelt simpelt - du skal omdøbe nøglen for at fjerne det forkerte hashtag. Så omdøb {users}:1234 til users:{1234} eller endda users:1234 burde gøre tricket...

… bortset fra at omdøb-kommandoen ikke virker i redis cluster.

Så den eneste udvej er først at dumpe nøglen og derefter gendanne den mod det nye navn.

Sådan ser det ud i kode:



from redis import StrictRedis
try:
    from itertools import izip_longest
except:
    from itertools import zip_longest as izip_longest


def get_batches(iterable, batch_size=2, fillvalue=None):
    """
    Chunks a very long iterable into smaller chunks of `batch_size`
    For example, if iterable has 9 elements, and batch_size is 2,
    the output will be 5 iterables - each of length 2. 
    The last iterable will also have 2 elements, 
    but the 2nd element will be `fillvalue`
    """
    args = [iter(iterable)] * batch_size
    return izip_longest(fillvalue=fillvalue, *args)


def migrate_keys(allkeys, host, port, password=None):
    db = 0
    red = StrictRedis(host=host, port=port, password=password)

    batches = get_batches(allkeys)
    for batch in batches:
        pipe = red.pipeline()
        keys = list(batch)
        for key in keys:
            if not key:
                continue
            pipe.dump(key)
            
        response = iter(pipe.execute())
        # New pipeline to run the restore command
        pipe = red.pipeline(transaction=False)
        for key in keys:
            if not key:
                continue
            obj = next(response)
            new_key = "restored." + key
            pipe.restore(new_key, 0, obj)

        pipe.execute()


if __name__ == '__main__':
    allkeys = ['users:18245', 'users:12328:answers_by_score', 'comments:18648']
    migrate_keys(allkeys, host="localhost", port=6379)


  1. Tjek, om hvert element i arrayet matcher tilstanden

  2. MongoDB-CR-godkendelse mislykkedes

  3. Spring data mongo bruger OR i Query

  4. Placering i mongoose, mongoDB