रेडिस में, वितरण की प्राथमिक इकाई हैश स्लॉट है। रेडिस के वितरित संस्करण - ओपन सोर्स रेडिस क्लस्टर, वाणिज्यिक रेडिस एंटरप्राइज और यहां तक कि एडब्ल्यूएस इलास्टी कैश सहित - एक बार में केवल डेटा 1 स्लॉट के आसपास घूम सकते हैं।
यह एक दिलचस्प समस्या की ओर जाता है - एकतरफा स्लॉट। क्या होगा यदि एक स्लॉट (या कुछ स्लॉट) में अधिकांश डेटा हो?
क्या यह संभव भी है?
रेडिस एक अच्छी तरह से प्रकाशित एल्गोरिथम का उपयोग करके एक कुंजी के लिए हैश-स्लॉट तय करता है। यह एल्गोरिथम आमतौर पर यह सुनिश्चित करेगा कि कुंजियाँ अच्छी तरह से वितरित हैं।
लेकिन डेवलपर एक हैश टैग . निर्दिष्ट करके एल्गोरिथम को प्रभावित कर सकते हैं . हैश टैग कर्ली ब्रेसिज़ में संलग्न कुंजी का एक भाग होता है {...}
. जब हैश-टैग निर्दिष्ट किया जाता है, तो इसका उपयोग हैश स्लॉट तय करने के लिए किया जाएगा।
रेडिस में हैश-टैग वह है जिसे अधिकांश डेटाबेस विभाजन कुंजी कहते हैं। यदि आप गलत विभाजन कुंजी चुनते हैं, तो आपको एकतरफा स्लॉट मिलेंगे।
उदाहरण के तौर पर, अगर आपकी कुंजियाँ {users}:1234
. जैसी हैं और {users}:5432
, रेडिस सभी उपयोगकर्ताओं को एक ही हैश स्लॉट में संग्रहीत करेगा।
क्या समाधान है?
समाधान अवधारणात्मक रूप से . है सरल - गलत हैश टैग को हटाने के लिए आपको कुंजी का नाम बदलना होगा। इसलिए नाम बदलना {users}:1234
users:{1234}
या यहां तक कि users:1234
चाल चलनी चाहिए...
... सिवाय इसके कि rename कमांड रेडिस क्लस्टर में काम नहीं करती है।
तो एकमात्र तरीका यह है कि पहले कुंजी को डंप करें और फिर इसे नए नाम के सामने पुनर्स्थापित करें।
यहां बताया गया है कि यह कोड में कैसा दिखता है:
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)