Trong Redis, đơn vị phân phối chính là một rãnh băm. Các phiên bản phân tán của redis - bao gồm Redis Cluster mã nguồn mở, Redis Enterprise thương mại và thậm chí AWS ElastiCache - chỉ có thể di chuyển quanh vùng dữ liệu tại một thời điểm.
Điều này dẫn đến một vấn đề thú vị - các khe cắm mặt lop. Điều gì sẽ xảy ra nếu một vùng (hoặc một vài vùng) có hầu hết dữ liệu?
Điều đó có khả thi không?
Redis quyết định vị trí băm cho một khóa bằng cách sử dụng một thuật toán được xuất bản tốt. Thuật toán này thường sẽ đảm bảo rằng các khóa được phân phối tốt.
Nhưng các nhà phát triển có thể tác động đến thuật toán bằng cách chỉ định một thẻ băm . Thẻ băm là một phần của khóa được đặt trong dấu ngoặc nhọn {...}
. Khi một thẻ băm được chỉ định, nó sẽ được sử dụng để quyết định vị trí băm.
Thẻ băm trong redis là thứ mà hầu hết các cơ sở dữ liệu gọi là khóa phân vùng. Nếu bạn chọn một khóa phân vùng sai, bạn sẽ nhận được các khe bị lệch.
Ví dụ:nếu các khóa của bạn giống như {users}:1234
và {users}:5432
, redis sẽ lưu trữ tất cả người dùng trong cùng một vùng băm.
Cách khắc phục là gì?
Cách khắc phục là về mặt khái niệm đơn giản - bạn cần đổi tên khóa để xóa thẻ băm không chính xác. Vì vậy, đổi tên {users}:1234
tới người dùng users:{1234}
hoặc thậm chí users:1234
nên thực hiện thủ thuật…
… Ngoại trừ việc lệnh đổi tên không hoạt động trong cụm redis.
Vì vậy, cách duy nhất là trước tiên hãy kết xuất khóa và sau đó khôi phục nó với tên mới.
Đây là cách nó trông như thế nào trong mã:
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)