工具下載:
https://github.com/eyjian/redis-tools/blob/master/move_redis_slot.sh
支持遷移已有的keys。
#!/bin/sh# 遷移 slot 工具,但一次只能遷移一個 slot # # 使用時,需要指定如下幾個參數: # 1)參數1:必選參數,用於指定被遷移的 slot # 2)參數2:必選參數,用於指定源節點(格式為:ip:port) # 3)參數3:必選參數,用於指定目標節點(格式為:ip:port) # 6)參數4:可選參數,用於指定訪問 redis 的密碼 # # 使用示例(將2020從10.9.12.8:1383遷移到10.9.12.9:1386): # move_redis_slot.sh 2020 10.9.12.8:1383 10.9.12.9:1386 # # 執行本腳本時,有兩個“確認”, # 第一個“確認”是提示參數是否正確, # 第二個“確認”是提示是否遷移已有的keys, # 如果輸入非yes則只遷移slot,不遷移已有keys。 # 確保redis-cli可用 REDIS_CLI=${REDIS_CLI:-redis-cli} which "$REDIS_CLI" > /dev/null 2>&1 if test $? -ne 0; then echo "\`redis-cli\` not exists or not executable" exit 1 fi # 參數檢查 if test $# -ne 3 -a $# -ne 4; then echo -e "Usage: `basename $0` \033[1;33mslot\033[m source_node destition_node redis_password" echo -e "Example1: `basename $0` \033[1;33m2020\033[m 127.0.0.1:6379 127.0.0.1:6380" echo -e "Example2: `basename $0` \033[1;33m2020\033[m 127.0.0.1:6379 127.0.0.1:6380 password123456" exit 1 fi SLOT=$1 SRC_NODE="$2" DEST_NODE="$3" REDIS_PASSOWRD="$4" # 得到指定節點的 nodeid function get_node_id() { node="$1" node_ip="`echo $node|cut -d':' -f1`" node_port=`echo $node|cut -d':' -f2` # 得到對應的 nodeid $REDIS_CLI --raw --no-auth-warning -a "$REDIS_PASSOWRD" \ -h $node_ip -p $node_port \ CLUSTER NODES | awk -v node=$node -F'[ @]' '{if ($2==node) printf("%s",$1);}' } SRC_NODE_ID="`get_node_id $SRC_NODE`" SRC_NODE_IP="`echo $SRC_NODE|cut -d':' -f1`" SRC_NODE_PORT=`echo $SRC_NODE|cut -d':' -f2` DEST_NODE_ID="`get_node_id $DEST_NODE`" DEST_NODE_IP="`echo $DEST_NODE|cut -d':' -f1`" DEST_NODE_PORT=`echo $DEST_NODE|cut -d':' -f2` echo -e "\033[1;33mSource\033[m node: $SRC_NODE_IP:$SRC_NODE_PORT($SRC_NODE_ID)" echo -e "\033[1;33mDestition\033[m node: $DEST_NODE_IP:$DEST_NODE_PORT($DEST_NODE_ID)" if test -z "$SRC_NODE_ID"; then echo -e "Can not get the source node ID of \033[1;33m$SRC_NODE\033[m" exit 1 fi if test -z "$DEST_NODE_ID"; then echo -e "Can not get the destition node ID of \033[1;33m$DEST_NODE\033[m" exit 1 fi echo -en "Confirm to continue? [\033[1;33myes\033[m/\033[1;33mno\033[m]" read -r -p " " input if test "$input" != "yes"; then exit 1 fi echo "........." # 目標節點上執行 IMPORTING 操作 # 如果 $SLOT 已在目標節點,則執行時報錯“ERR I'm already the owner of hash slot 1987” echo -e "\033[1;33mImporting\033[m $SLOT from $SRC_NODE($SRC_NODE_ID) to $DEST_NODE($DEST_NODE_ID) ..." err=`$REDIS_CLI --raw --no-auth-warning -a "$REDIS_PASSOWRD" \ -h $DEST_NODE_IP -p $DEST_NODE_PORT \ CLUSTER SETSLOT $SLOT IMPORTING $SRC_NODE_ID` if test "X$err" != "XOK"; then echo "[destition://$DEST_NODE_IP:$DEST_NODE_PORT] $err" exit 1 fi # 源節點上執行 MIGRATING 操作 # 如果 $SLOT 並不在源節點上,則執行時報錯“ERR I'm not the owner of hash slot 1987” echo -e "\033[1;33mMigrating\033[m $SLOT from $SRC_NODE($SRC_NODE_ID) to $DEST_NODE($DEST_NODE_ID) ..." err=`$REDIS_CLI --raw --no-auth-warning -a "$REDIS_PASSOWRD" \ -h $SRC_NODE_IP -p $SRC_NODE_PORT \ CLUSTER SETSLOT $SLOT MIGRATING $DEST_NODE_ID` if test "X$err" != "XOK"; then echo "[source://$SRC_NODE_IP:$SRC_NODE_PORT] $err" exit 1 fi # 是否遷移已有的keys? echo -en "Migrate keys in slot://$SLOT? [\033[1;33myes\033[m/\033[1;33mno\033[m]" read -r -p " " input if test "$input" = "yes"; then first=1 # 是否第一輪keys遷移操作 batch=100 # 一次批量遷移的keys數 timeout_ms=60000 # 超時時長(單位:毫秒) destination_db=0 # 對於redis集群,取值總是為0 num_keys=0 echo "........." echo -e "Migrating keys in slot://$SLOT ..." while true do # 在源節點上執行: # 借助命令“CLUSTER GETKEYSINSLOT”和命令“MIGRATE”遷移已有的keys keys="`$REDIS_CLI --raw --no-auth-warning -a "$REDIS_PASSOWRD" \ -h $SRC_NODE_IP -p $SRC_NODE_PORT \ CLUSTER GETKEYSINSLOT $SLOT $batch | tr '\n' ' ' | xargs`" if test -z "$keys"; then if test $first -eq 1; then echo -e "No any keys to migrate in slot://$SLOT" else echo -e "Finished migrating all keys (\033[1;33m$num_keys\033[m) in slot://$SLOT" fi break fi first=0 n=`echo "$keys" | tr -cd ' ' | wc -c` num_keys=$(($num_keys + $n)) #echo -e "(\033[1;33m$n\033[m)$keys" # 在源節點上執行命令“MIGRATE”遷移到目標節點 # MIGRATE returns OK on success, # or NOKEY if no keys were found in the source instance if test -z "$REDIS_PASSOWRD"; then err=`$REDIS_CLI --raw \ -h $SRC_NODE_IP -p $SRC_NODE_PORT \ MIGRATE $DEST_NODE_IP $DEST_NODE_PORT "" $destination_db $timeout_ms \ REPLACE KEYS $keys` else err=`$REDIS_CLI --raw --no-auth-warning -a "$REDIS_PASSOWRD" \ -h $SRC_NODE_IP -p $SRC_NODE_PORT \ MIGRATE $DEST_NODE_IP $DEST_NODE_PORT "" $destination_db $timeout_ms \ REPLACE AUTH "$REDIS_PASSOWRD" KEYS $keys` fi if test "X$err" = "XNOKEY"; then break fi echo -e "\033[1;33m$n\033[m keys migratd" done fi # 取得所有 master 節點 nodes=(`$REDIS_CLI --raw --no-auth-warning -a "$REDIS_PASSOWRD" -h $DEST_NODE_IP -p $DEST_NODE_PORT \ CLUSTER NODES | awk '{if (match($3,"master")) printf("%s\n",$2);}'`) # 在所有 master 節點上執行 NODE 操作 # 實際上,只可對源節點和目標節點執行 NODE 操作, # 新的配置會自動在集群中傳播。 echo "........." for node in ${nodes[*]}; do node_ip="`echo $node|cut -d':' -f1`" node_port=`echo $node|cut -d':' -f2` echo -en "NODE: $node_ip:$node_port " err=`$REDIS_CLI --raw --no-auth-warning -a "$REDIS_PASSOWRD" \ -h $node_ip -p $node_port \ CLUSTER SETSLOT $SLOT NODE $DEST_NODE_ID` echo -e "\033[1;33m$err\033[m" done