北京、廣州兩套redis集群環境
我們的需求是將北京redis集群數據同步廣州redis
廢話不多說 直接上代碼
1、導出腳本sync_export_redis30_aof.sh
sync_export_redis30_aof.sh

#行數 offset='' #端口號 port='' #redis安裝路徑-測試 #install_dir="/opt/web_app/redis-3.0.0" #生產 install_dir="/opt/supp_app/redis3.0.0" #遷移臨時存放數據路徑 result_dir="${install_dir}/data/export" #tmp 臨時文件 aof_tmp_dir="${install_dir}/tmp" #備份數據存在路徑 aof_bak_dir="${install_dir}/bak" #依賴redis工具-測試 #redis_cli_command="/usr/local/bin/redis-cli" #redis_check_aof="/usr/local/bin/redis-check-aof" #生產 redis_cli_command="${install_dir}/src/redis-cli" redis_check_aof="${install_dir}/src/redis-check-aof" cur_time=`date "+%Y%m%d%H%M%S"` #腳本執行日志 log="${install_dir}/aof.log" printlog() { sysdate=`date "+%Y-%m-%d %H:%M:%S"` msg="[$1] $sysdate - $port - $2" echo $msg echo $msg >> $log 2>&1 } #create directory if it doesn't exist if [ ! -d ${result_dir} ]; then mkdir -p $result_dir fi if [ ! -d ${aof_tmp_dir} ]; then mkdir -p $aof_tmp_dir fi if [ ! -d ${aof_bak_dir} ]; then mkdir -p $aof_bak_dir fi #獲取當前服務器eth0 IP hostip=`cat /etc/sysconfig/network-scripts/ifcfg-eth0 |grep IPADDR|cut -d '=' -f2` #1、獲取當前服務器redis啟動端口集合 ports=` ps -ef | grep redis-server| grep -v grep|awk -F: '{print $NF}' |cut -d " " -f1` printlog "info" "redis_ports : $ports" #2、連接任意一個端口 port=`echo $ports|cut -d ' ' -f1` #獲取redis集群信息 redis_masters=`${redis_cli_command} -c -h ${hostip} -p ${port} cluster nodes | grep master |cut -d ' ' -f2,9` printlog "info" "redis_masters : $redis_masters" arr=($redis_masters) #3、判斷主節點 是否在本機 在執行備份 不在 打印日志 for(( i=0;i<${#arr[@]};i=$i+2)) do printlog "info" "redis export aof start ###################" ip=`echo ${arr[$i]}|cut -d ':' -f1` ip_port=`echo ${arr[$i]}|cut -d ':' -f2` partition=${arr[$i+1]} printlog "info" "redis_IP : $ip , redis_port:$ip_port , redis_partition :$partition" #判斷服務器IP 是否與集群 IP一致 if [ "$ip" == "$hostip" ] then port=$ip_port #aof 日志文件存在位置 #測試 #aof_data_file="${install_dir}/redis-node-${port}/appendonly.aof" #生產 aof_data_file="${install_dir}/redis-node-${port}/appendonly-${port}.aof" redis_cluster_type="redis30_cluster" tmp_aof_file1="${aof_tmp_dir}/${redis_cluster_type}_${port}_tmp1" tmp_aof_file2="${aof_tmp_dir}/${redis_cluster_type}_${port}_tmp2" #獲取文件總行數 total_line=`wc -l $aof_data_file | grep -o '[0-9]\+ '` tmp_key="REDIS:DATA:CLUSTE:"$partition tmp_value=`${redis_cli_command} -c -h ${hostip} -p ${port} get $tmp_key ` if [ -z "$tmp_value" ] then redis_key='' else redis_key=`echo $tmp_value |base64 -d -i` fi printlog "info" "tmp_key : $tmp_key ; tmp_value : $tmp_value ; redis_key:$redis_key" if [ -z "$redis_key" ] then #分區key為空,手動執行重新 全量同步 echo "befor $port seting:" echo `${redis_cli_command} -p ${port} config get auto-aof-rewrite-min-size` echo `${redis_cli_command} -p ${port} config set auto-aof-rewrite-min-size 3000000000` echo "after $port seting:" echo `${redis_cli_command} -p ${port} config get auto-aof-rewrite-min-size` echo `${redis_cli_command} -p ${port} BGREWRITEAOF` offset=0 else #通過aof文件 查找key所在行數 line_str=`cat -n $aof_data_file|grep -a $redis_key` printlog "info" "line_str : $line_str " line_num=`echo $line_str|cut -d ' ' -f1` printlog "info" "line_num : $line_num" offset=`expr $line_num + 2` fi printlog "info" "total_line:$total_line,offset:$offset" if [ $total_line -le $offset ] ; then printlog "info" "aof_data_file length is less than offset reset offsetsize = 0 " offset=$total_line fi #get good byte for increament aof file tail -n +"$(($offset + 1))" $aof_data_file > "$tmp_aof_file1" ok_bytes=`$redis_check_aof $tmp_aof_file1 | grep -o 'ok_up_to=[0-9]\+'` printlog "info" "ok_bytes:$ok_bytes,offset:$offset" if [[ -z $ok_bytes ]] ; then printlog "info" "could't get ok bytes from temp aof file" continue fi ok_bytes=${ok_bytes//ok_up_to=} if [[ $ok_bytes -le 0 ]] ; then printlog "info" "no ok bytes for skip line $offset" continue fi #create good aof file head -c $ok_bytes $tmp_aof_file1 > $tmp_aof_file2 ok_lines=`wc -l $tmp_aof_file2 | cut -d' ' -f1` offset=$((offset + ok_lines)) aof_bak_name="${partition}_${cur_time}.aof" mv $tmp_aof_file2 "${result_dir}/${aof_bak_name}" start=`echo $partition|cut -d '-' -f1` end=`echo $partition|cut -d '-' -f2` printlog "info" "redis_partition_start: $start,redis_partition_end:$end" #生成新key寫入文件 同時刪除 舊key new_key='REDIS:DATA:SYNC:' while [ "1" = "1" ] do #獲取linux uuid 的校驗碼 uuid=`cat /proc/sys/kernel/random/uuid| cksum | cut -f1 -d " "` new_key="$new_key""$uuid" result=$(java CRC16 $new_key) #printlog "info" "$result >=$start,$result<=$end" if [ $result -ge $start -a $result -le $end ] then break fi done printlog "info" "new_key:$new_key,redis_key:$redis_key" #插入新 key echo `${redis_cli_command} -c -h ${hostip} -p ${port} set ${new_key} "$cur_time"` base64_encode_key=`echo $new_key |base64 -i` printlog "info" "tmp_key:$tmp_key,base64_encode_key:$base64_encode_key" #更新加密vlalue echo `${redis_cli_command} -c -h ${hostip} -p ${port} set ${tmp_key} "$base64_encode_key"` #舊key存在 刪除 if [ -n "$redis_key" ]; then echo `${redis_cli_command} -c -h ${hostip} -p ${port} del ${redis_key}` fi fi printlog "info" "redis export aof end ###################" done; #壓縮遷移aof文件 for one_aof_file in `ls -1 ${result_dir}/* | sort`; do bname=`basename $one_aof_file` dname=`dirname $one_aof_file` cur_pwd=`pwd` tmp_name="/tmp/${bname}.tar.gz" cd $dname tar zcf $tmp_name $bname cd $cur_pwd printlog 'info', "rsync -avz --progress --bwlimit=4000 --password-file=/etc/rsyncd.secrets $tmp_name content_sync@10.230.40.54::content_sync/data" echo "rsync -avz --progress --bwlimit=2500 --password-file=/etc/rsyncd.secrets $tmp_name content_sync@10.230.40.54::content_sync/data" rsync -avz --progress --bwlimit=2500 --password-file=/etc/rsyncd.secrets $tmp_name content_sync@10.230.40.54::content_sync printlog "info" " finish rsync,tmpname:$tmp_name" if [ $? -eq 0 ] ; then full_bak_dir="${aof_bak_dir}/`date +%Y%m%d`" if [ ! -d $full_bak_dir ]; then mkdir -p "$full_bak_dir" fi if [[ $one_aof_file =~ $aof_bak_name ]] ; then echo "mv ${one_aof_file}/ ${full_bak_dir}" mv $one_aof_file $full_bak_dir fi else sleep 1 fi rm $tmp_name done
CRC16.java

/** * CRC-CCITT 算法校驗類 * * @author amadowang * @version [版本號, Aug 29, 2011] * @see [相關類/方法] * @since [產品/模塊版本] * @date 2012-10-24 */ public class CRC16 { private char[] crc_tb = { 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 }; public char caluCRC(byte[] pByte) { int len = pByte.length; char crc; byte da; crc = 0x0; int i = 0; while (len-- != 0) { da = (byte) (crc / 256); crc <<= 8; int num = da ^ pByte[i]; if (num < 0) num += 256; crc ^= crc_tb[num]; ++i; } return crc; } public static void main(String[] args) { CRC16 crc = new CRC16(); // byte[] test = "SSO_MOBILE-SSO_APP-C487C9139AC6D1CD889853B598747222".getBytes(); byte[] test = args[0].getBytes(); char ch = crc.caluCRC(test); // 以下是為了測試方便進行的處理,因為char數據類型很多是不可顯示的 int i = (int) ch; // String str = Integer.toHexString(i); // System.out.println("CRC十六進制字符串是:" + str); int resut = i % 16384; System.out.println(resut); // System.exit(resut); } }
2、導入腳本import_redis30_aof

#install_dir="/opt/supp_app/redis_3.0" install_dir="/opt/supp_app/sylredis/redis-3.0.7" data_dir="${install_dir}/data/import" bak_dir="${install_dir}/bak" #redis_cli_command="${install_dir}/src/redis-cli" redis_cli_command="/usr/local/bin/redis-cli" log="${install_dir}/aof.log" #cur_time=`date "+%Y%m%d%H%M%S"` printlog() { sysdate=`date "+%Y-%m-%d %H:%M:%S"` msg="[$1] $sysdate - $port - $2" echo $msg echo $msg >> $log 2>&1 } #獲取當前服務器IP hostip=`cat /etc/sysconfig/network-scripts/ifcfg-eth0 |grep IPADDR|cut -d '=' -f2` while true; do for one_tar_file in `ls -1 ${data_dir}/* 2>/dev/null | sort`; do printlog "info", "redis import aof start ####################" #1、獲取當前服務器redis啟動端口第一個 tmp_port=` ps -ef | grep redis-server| grep -v grep|awk -F: '{print $NF}' |cut -d " " -f1 |head -n 1` printlog "info" "redis_port : $tmp_port" #2、通過文件名獲取redis分區范圍 bname=`basename $one_tar_file` aof_fname=${bname%%.tar.gz} name_start=${aof_fname%%_*} #3、連接任意一個端口 獲取該分區的IP以及端口 printlog "info", "$redis_cli_command -c -h $hostip -p ${tmp_port} cluster nodes|grep ${name_start} |cut -d' ' -f2|cut -d ':' -f1| head -n 1" ip=`$redis_cli_command -c -h $hostip -p ${tmp_port} cluster nodes|grep ${name_start} |cut -d' ' -f2|cut -d ':' -f1| head -n 1` printlog "info", "$redis_cli_command -c -h $hostip -p ${tmp_port} cluster nodes|grep ${name_start} |cut -d' ' -f2|cut -d ':' -f2| head -n 1" port=`$redis_cli_command -c -h $hostip -p ${tmp_port} cluster nodes|grep ${name_start} |cut -d' ' -f2|cut -d ':' -f2| head -n 1` tname="/tmp/${aof_fname}" printlog "info", "starting processing $one_tar_file " printlog "info", "name_start : $name_start , ip : $ip , port : $port" tar -zxvf $one_tar_file -C /tmp >> /tmp/tar.log if [ $? -eq 0 ] ; then echo "good bname:$bname, tname:$tname" filesize=$(stat -c '%s' ${tname}) if [[ $filesize -gt 300000000 ]]; then echo "start flushall" $redis_cli_command -h ${ip} -p ${port} flushall fi printlog "info", "cat $tname | $redis_cli_command --pipe -h ${ip} -p ${port}" cat $tname | $redis_cli_command --pipe -h ${ip} -p ${port} #mv $name $bak full_bak_dir="${bak_dir}/`date +%Y%m%d`" if [ ! -d $full_bak_dir ]; then mkdir -p "$full_bak_dir" fi mv $one_tar_file $full_bak_dir rm $tname printlog "info", "process $one_tar_file ok" else printlog "Error", "tar -zxvf $one_tar_file -C /tmp" $? fi printlog "info", "redis import aof end ####################" done sleep 10 done
使用步驟
(1)導出腳本 sync_export_redis30_aof.sh :
定時間隔十分鍾執行 sync_export_redis30_aof.sh 該腳本 每次通過redis 分區 生成aof日志 並進行壓縮傳輸到廣州機房指定的服務器上
部署:sync_export_redis30_aof.sh CRC16.class 兩個部署在一起
修改:修改該腳本中 install_dir=redis安裝目錄
檢查:腳本中aof_data_file aof文件所在位置是否正確
注:北京機房使用,redis集群服務器 都需要安裝
(2)導入腳本 import_redis30_aof :
后台執行 import_redis30_aof.sh 該腳本 ,每次檢查執行目錄是否存在aof壓縮文件 存在這執行導入功能。
部署: 只部署 import_redis30_aof.sh
修改:修改該腳本中 install_dir=redis安裝目錄
檢查:腳本中data_dir= 需要導入的文件所在位置是否正確
注: 廣州機房使用, 部署在redis集群中的任意一台即可。