sql >> Database >  >> NoSQL >> Redis

Hoe scheve hash-slots in Redis te repareren

In Redis is de primaire distributie-eenheid een hash-slot. Gedistribueerde versies van redis - inclusief de open source Redis Cluster, commerciële Redis Enterprise en zelfs AWS ElastiCache - kunnen slechts met 1 slot tegelijk worden verplaatst.

Dit leidt tot een interessant probleem - scheve slots. Wat als één slot (of een paar slots) uiteindelijk de meeste gegevens heeft?

Is dat wel mogelijk?

Redis bepaalt de hash-slot voor een sleutel met behulp van een goed gepubliceerd algoritme. Dit algoritme zorgt er meestal voor dat de sleutels goed worden verdeeld.

Maar ontwikkelaars kunnen het algoritme beïnvloeden door een hashtag op te geven . Een hash-tag is een deel van de sleutel tussen accolades {...} . Wanneer een hash-tag is opgegeven, wordt deze gebruikt om de hash-sleuf te bepalen.

De hash-tag in redis is wat de meeste databases een partitiesleutel zouden noemen. Als je een verkeerde partitiesleutel kiest, krijg je scheve slots.

Als uw sleutels bijvoorbeeld zijn zoals {users}:1234 en {users}:5432 , redis slaat alle gebruikers op in hetzelfde hashslot.

Wat is de oplossing?

De oplossing is conceptueel eenvoudig - u moet de sleutel hernoemen om de onjuiste hash-tag te verwijderen. Dus hernoemen van {users}:1234 aan users:{1234} of zelfs users:1234 zou het lukken...

… behalve dat de opdracht hernoemen niet werkt in redis-cluster.

Dus de enige uitweg is om eerst de sleutel te dumpen en vervolgens te herstellen met de nieuwe naam.

Zo ziet het eruit in code:



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. Hoe in productie te gaan met MongoDB - Top tien tips

  2. Spring data rest-toepassing krijgt geen gegevens uit de database na implementatie van redis-caching

  3. Document Lezen en invoegen met vergrendeling/transactie in nodejs met mongodb

  4. MongoDB:Combineer gegevens uit meerdere collecties in één..hoe?