天天看點

Centos6 安裝 Redis 和叢集配置

Redis安裝

先确認gcc和tcl已經安裝

sudo yum install gcc-c++
sudo yum install tcl      

解壓, 編譯和安裝

tar zxvf redis-3.0.5.tar.gz
sudo mv redis-3.0.5 /usr/src/
cd /usr/src/redis-3.0.5/

sudo make
sudo make test
sudo make install

# 如果不想安裝到 /usr/local/bin, 用這個語句會安裝到/opt/redis/redis-3.0.5/bin下, 然後再到/usr/bin去建好軟鍊
sudo make PREFIX=/opt/redis/redis-3.0.5 install      

安裝很自由, 在哪裡編譯都可以, 有用的就是最後生成的那幾個binary. Redis 由四個可執行檔案:redis-benchmark、redis-cli、redis-server、redis-stat 這四個檔案,加上一個redis.conf就構成了整個redis的最終可用包。它們的作用如下:

  • redis-server:Redis伺服器的daemon啟動程式
  • redis-cli:Redis指令行操作工具。當然,你也可以用telnet根據其純文字協定來操作
  • redis-benchmark:Redis性能測試工具,測試Redis在你的系統及你的配置下的讀寫性能
  • redis-stat:Redis狀态檢測工具,可以檢測Redis目前狀态參數及延遲狀況

現在就可以啟動redis了,redis隻有一個啟動參數,就是他的配置檔案路徑。源碼中提供了一個redis.conf作為參考

redis-server /etc/redis.conf      

目錄下預設的redis.conf檔案的daemonize參數為no,是以redis不會在背景運作。修改為yes則為背景運作redis。另外配置檔案中規定了pid檔案,log檔案和資料檔案的位址,如果有需要先修改,預設log資訊定向到stdout.

下面是redis.conf的主要配置參數的意義:

  • daemonize:是否以背景daemon方式運作
  • pidfile:pid檔案位置
  • port:監聽的端口号
  • timeout:請求逾時時間
  • loglevel:log資訊級别
  • logfile:log檔案位置
  • databases:開啟資料庫的數量
  • save * *:儲存快照的頻率,第一個*表示多長時間,第三個*表示執行多少次寫操作。在一定時間内執行一定數量的寫操作時,自動儲存快照。可設定多個條件。
  • rdbcompression:是否使用壓縮
  • dbfilename:資料快照檔案名(隻是檔案名,不包括目錄)
  • dir:資料快照的儲存目錄(這個是目錄)
  • appendonly:是否開啟appendonlylog,開啟的話每次寫操作會記一條log,這會提高資料抗風險能力,但影響效率。
  • appendfsync:appendonlylog如何同步到磁盤(三個選項,分别是每次寫都強制調用fsync、每秒啟用一次fsync、不調用fsync等待系統自己同步)

為Redis建立使用者和日志目錄

shell> groupadd redis
shell> useradd -g redis -s /bin/bash redis
mkdir -p /var/redis/data
chown redis:redis /var/redis/data      

修改資料快照的儲存目錄,需要修改redis.conf

# The working directory.
#
# The DB will be written inside this directory, with the filename specified
# above using the 'dbfilename' configuration directive.
#
# Also the Append Only File will be created inside this directory.
#
# Note that you must specify a directory here, not a file name.
dir /var/redis/data      

修改log檔案位置

mkdir -p /var/redis/log
chown redis:redis /var/redis/log

# Specify the log file name. Also 'stdout' can be used to force
# Redis to log on the standard output. Note that if you use standard
# output for logging but daemonize, logs will be sent to /dev/null
logfile /var/redis/log/redislog      

添加使用者密碼, 取消注釋, 将 foobared 修改為32位以上的長字元串, 因為redis的效率, 短密碼很容易被破解

# requirepass foobared      

指令行測試

~]$./bin/redis-cli      
127.0.0.1:6379> auth foobared 
OK
127.0.0.1:6379> set foo bar
OK
127.0.0.1:6379> get foo
"bar"
127.0.0.1:6379> exit      

指令行中, 亦可使用 -a 參數, 如 redis-cli -a foobared

性能測試

/opt/redis/redis-3.2.6/bin/redis-benchmark -l -p 6379 -a foobared      

使用-l參數會循環運作性能測試樣例

日志中的warning

3477:M 10 Nov 00:30:57.460 # WARNING: The TCP backlog setting of 511 cannot be e
nforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
3477:M 10 Nov 00:30:57.460 # Server initialized
3477:M 10 Nov 00:30:57.460 # WARNING overcommit_memory is set to 0! Background s
ave may fail under low memory condition. To fix this issue add 'vm.overcommit_me
mory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.over
commit_memory=1' for this to take effect.
3477:M 10 Nov 00:30:57.460 # WARNING you have Transparent Huge Pages (THP) suppo
rt enabled in your kernel. This will create latency and memory usage issues with
 Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transpare
nt_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retai
n the setting after a reboot. Redis must be restarted after THP is disabled.      

解決方法: 在/etc/sysctl.conf 中增加

net.core.somaxconn = 32768
vm.overcommit_memory = 1      

然後sysctl -p

用于添加服務的 /etc/init.d/redis.

Update 2017-11-10 在redis-4.0.2的源代碼中有init.d例子, 位于 utils/redis_init_script

#!/bin/bash
#
# redis          Start up the Redis server daemon
#
# chkconfig: 2345 55 25
# description: 
#
# processname: /opt/redis/redis-3.2.6/bin/redis-server
# pidfile: /var/redis/redis.pid

PATH=/sbin:/bin:/usr/bin:/usr/sbin

USER=redis
REDISPORT=16379
EXEC=/opt/redis/redis-3.2.6/bin/redis-server
REDIS_CLI=/opt/redis/redis-3.2.6/bin/redis-cli
SECURE=foobar

PIDFILE=/var/redis/redis.pid
CONF=/opt/redis/redis-3.2.6/conf/redis.conf

case "$1" in
  start)
    if [ -d /sys/kernel/mm/transparent_hugepage ]; then
      echo 'never' > /sys/kernel/mm/transparent_hugepage/enabled
      echo 'never' > /sys/kernel/mm/transparent_hugepage/defrag
    elif [ -d /sys/kernel/mm/redhat_transparent_hugepage ]; then
      echo 'never' > /sys/kernel/mm/transparent_hugepage/enabled
      echo 'never' > /sys/kernel/mm/transparent_hugepage/defrag
    fi
if [ -f $PIDFILE ]
    then
      echo "$PIDFILE exists, process is already running or crashed"
    else
      echo "Starting Redis server..."
      su - $USER -c "$EXEC $CONF"
    fi
    if [ "$?"="0" ]
    then
      echo "Redis is running..."
    fi
    ;;
  stop)
    if [ ! -f $PIDFILE ]
    then
      echo "$PIDFILE does not exist, process is not running"
    else
      PID=$(cat $PIDFILE)
      echo "Stopping ..."
      su - $USER -c "$REDIS_CLI -p $REDISPORT -a $SECURE SHUTDOWN"
      while [ -x ${PIDFILE} ]
      do
          echo "Waiting for Redis to shutdown ..."
          sleep 1
      done
      echo "Redis stopped"
    fi
    ;;
  restart|force-reload)
    ${0} stop
    ${0} start
    ;;
  *)
    echo "Usage: /etc/init.d/redis {start|stop|restart|force-reload}" >&2
      exit 1
esac      

Ubuntu16.04下的/etc/init.d/redis

#!/bin/sh
#
### BEGIN INIT INFO
# Provides:          redis
# Required-Start:    $remote_fs $syslog
# Required-Stop:     $remote_fs $syslog
# Should-Start:      $network $time
# Should-Stop:       $network $time
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Start/ Stop Redis Server daemon
# Description:       This service script facilitates startup and shutdown of
#                    redis daemon
### END INIT INFO
#
# Simple Redis init.d script conceived to work on Linux systems
# as it does use of the /proc filesystem.

REDISPORT=6034
EXEC=/opt/redis/redis-4.0.2/bin/redis-server
CLIEXEC=/opt/redis/redis-4.0.2/bin/redis-cli

PIDFILE=/var/run/redis_${REDISPORT}.pid
CONF="/opt/redis/redis-4.0.2/conf/${REDISPORT}.conf"
SECURE=biggbang

case "$1" in
    start)
        if [ -f $PIDFILE ]
        then
                echo "$PIDFILE exists, process is already running or crashed"
        else
                echo "Starting Redis server..."
                $EXEC $CONF
        fi
        ;;
    stop)
        if [ ! -f $PIDFILE ]
        then
                echo "$PIDFILE does not exist, process is not running"
        else
                PID=$(cat $PIDFILE)
                echo "Stopping ..."
                $CLIEXEC -p $REDISPORT -a $SECURE shutdown
                while [ -x /proc/${PID} ]
                do
                    echo "Waiting for Redis to shutdown ..."
                    sleep 1
                done
                echo "Redis stopped"
        fi
        ;;
    *)
        echo "Please use start or stop as first argument"
        ;;
esac      

添加入ubuntu的系統服務:

sudo update-rc.d redis defaults 
# 如果需要删除則使用
sudo update-rc.d redis remove      

加入服務後, 可以用 start, stop, status 檢視服務狀态

Redis Cluster 叢集的配置

Redis叢集支援多節點資料集自動分片, 提供一定程度的分區可用性, 部分節點挂掉或者無法連接配接其他節點後, 服務可以正常運作.

叢集中的每個Redis節點需要2個TCP連接配接端口,如6379端口用于Client連接配接,16379端口用于叢集資料通信.

叢集的實作機制

叢集采用Hash Slot方案, 而不是一緻性哈希. Redis使用的是16384個Hash slot (16x32x32, 即2的14次方, 214). 每個key通過CRC16校驗後對16384取模來決定放置哪個slot. 叢集的每個節點負責一部分hash slot.  如果有3台機器, 那麼Node A 負責 0-5500, Node B 負責 5501-11000, Node C 負責 11001-16384. 這種設計下, 添加, 删除新Node比較友善. 例如:

添加新節點D, 隻需移動A, B, C上的slot到節點D即可.

移除節點A, 将A上的slot遷移到B和C即可

由于Hash slot在節點間的遷移無需停止操作, 叢集新增或者删除節點, 改變叢集内部節點占用的Slot比例等都可線上完成. 

Redis Cluster Master-Slave Model

為保證某些節點挂掉或無法連接配接其他節點的情況下可正常提供服務,Redis Cluster提供了主從模式的資料副本機制,每個Hash Slot都可以設定1-N個從節點。

由A,B,C三節點組成的叢集中,可以設定A1,B1,C1分别為A,B,C的從節點,如B挂掉後,原B1從節點被提升為主節點,進而保證叢集服務正常。

叢集在一緻性方面的問題, 可能存在寫資料丢失情況發生, 如:

- Client寫入資料到NodeA

- NodeA答複Client寫入成功

- NodeA在寫入從節點A1時蹦潰掉了

- A1被提升為Master節點後,但是資料丢失

為解決這個問題,Redis叢集提供了同步寫支援

叢集配置參數

所有的叢集配置參數都存在于redis.conf中,主要幾個如下:

Cluster-enabled:是否開啟叢集模式
Cluster-config-file:叢集配置變更後會自動寫入改檔案
Cluster-node-timeout:節點逾時時間,超過該時間無法連接配接主要Master節點後,會停止接受查詢服務
Cluster-slave-validity-factor:控制從節點FailOver相關的設定. 設為0, 從節點會一直嘗試啟動FailOver;設為正數, 則失聯大于一定時間(factor*節點TimeOut)後不再進行FailOver
Cluster-migration-barrier:最小從節點連接配接數
Cluster-require-full-coverage:預設為Yes, 丢失一定比例Key後(可能Node無法連接配接或者挂掉)叢集停止接受寫操作; 設定為No, 叢集丢失Key的情況下仍提供查詢服務      

搭建方式一

叢集環境至少需要3個節點, 推薦使用6個節點配置, 即3個主節點3個從節點, 首先啟動6個獨立Redis節點, 然後進行叢集關聯配置,

最小化的Redis配置檔案, 實際使用中, 需要指定不同的資料檔案名和日志檔案名

port 7000
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes      

建立6個空的檔案夾

mkdir cluster-test  
cd cluster-test  
mkdir 7000 7001 7002 7003 7004 7005      

分别建立配置檔案并啟動6個單點服務

cd 7000
../redis-server ./redis.conf      

啟動後會看到以下日志資訊,提示Node.conf不存在,并且每個節點建立了一個NodeID

[82462] 26 Nov 11:56:55.329 * No cluster configuration found, I'm 97a3a64667477371c4479320d683e4c8db5858b1      

組建叢集配置

源碼src檔案下提供了一個叫做redis-trib.rb的腳本檔案,是一個Ruby腳本用于建立叢集,檢測及重新分片等

./redis-trib.rb create --replicas 1 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005
# create表示建立一個新的叢集
# replicas 1表示指定叢集中每個節點的副本個數為1        

出現以下消息,表示隻有一個主節點完成了HashSlot的初始化并可以提供服務

[OK] All 16384 slots covered        

搭建方式二

使用Create-Cluster腳本, 腳本位于utils/create-cluster目錄西,執行以下指令

create-cluster start
create-cluster create      

啟動後預設第一個節點端口為30001,完成後執行以下指令  

create-cluster stop      

驗證叢集是否搭建成功

$ redis-cli -c -p 7000
redis 127.0.0.1:7000> set foo bar
-> Redirected to slot [12182] located at 127.0.0.1:7002
OK
redis 127.0.0.1:7002> set hello world
-> Redirected to slot [866] located at 127.0.0.1:7000
OK
redis 127.0.0.1:7000> get foo
-> Redirected to slot [12182] located at 127.0.0.1:7002
"bar"
redis 127.0.0.1:7000> get hello
-> Redirected to slot [866] located at 127.0.0.1:7000
"world"      

對Redis Cluster的操作

添加一個新的主節點 Adding a new node

添加一個新節點基本上就是添加一個空節點, 然後将一些資料移動到其中, 在這種情況下它是一個新的master. 或者你明确的設定它作為副本, 那麼這種情況下它就是一個slave. 

[[email protected]0002 cluster-test]# cp -R 7005 7006
[[email protected]-d6b3-0002 cluster-test]# vi 7006/redis.conf
[[email protected]-d6b3-0002 cluster-test]# cd 7006
[[email protected]-d6b3-0002 7006]# ../redis-server redis.conf
# 用redis-trib來添加一個節點到已存在的叢集
./redis-trib.rb add-node 127.0.0.1:7006 127.0.0.1:7000      

第一個位址是新節點位址, 第二個位址是叢集中任意節點位址.

添加一個新的從節點 Adding a new node as a replica

./redis-trib.rb add-node --slave 127.0.0.1:7006 127.0.0.1:7000      

删除一個節點Removing a node

./redis-trib del-node 127.0.0.1:7000 `<node-id>`
./redis-trib.rb del-node 127.0.0.1:7006 7c7b7f68bc56bf24cbb36b599d2e2d97b26c5540      

重新分片(Resharding the cluster)

./redis-trib.rb reshard 127.0.0.1:7000
# --from 的參數是node id, 可以有多個, 用逗号隔開, 也可以是all; --to 目的節點的node id, 隻能填一個
./redis-trib.rb reshard --from <node-id> --to <node-id> --slots <number of slots> --yes <host>:<port>      

在删除節點前, 必須通過reshard将節點資料清空, 執行上面的第一條指令, 會進入互動式的指令行操作, 第二個是直接執行的指令格式, 下面是一個執行的指令行記錄

$ruby redis-trib.rb reshard --from all --to 80b661ecca260c89e3d8ea9b98f77edaeef43dcd --slots 11 10.180.157.199:6379
>>> Performing Cluster Check (using node 10.180.157.199:6379)
S: b2506515b38e6bbd3034d540599f4cd2a5279ad1 10.180.157.199:6379
   slots: (0 slots) slave
   replicates 460b3a11e296aafb2615043291b7dd98274bb351
S: d376aaf80de0e01dde1f8cd4647d5ac3317a8641 10.180.157.205:6379
   slots: (0 slots) slave
   replicates e36c46dbe90960f30861af00786d4c2064e63df2
M: 15126fb33796c2c26ea89e553418946f7443d5a5 10.180.157.201:6379
   slots:10923-16383 (5461 slots) master
   1 additional replica(s)
S: 59fa6ee455f58a5076f6d6f83ddd74161fd7fb55 10.180.157.208:6379
   slots: (0 slots) slave
   replicates 15126fb33796c2c26ea89e553418946f7443d5a5
M: 460b3a11e296aafb2615043291b7dd98274bb351 10.180.157.202:6379
   slots:0-5460 (5461 slots) master
   1 additional replica(s)
M: 80b661ecca260c89e3d8ea9b98f77edaeef43dcd 10.180.157.200:6380
   slots: (0 slots) master
   0 additional replica(s)
M: e36c46dbe90960f30861af00786d4c2064e63df2 10.180.157.200:6379
   slots:5461-10922 (5462 slots) master
   1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

Ready to move 11 slots.
  Source nodes:
    M: 15126fb33796c2c26ea89e553418946f7443d5a5 10.180.157.201:6379
   slots:10923-16383 (5461 slots) master
   1 additional replica(s)
    M: 460b3a11e296aafb2615043291b7dd98274bb351 10.180.157.202:6379
   slots:0-5460 (5461 slots) master
   1 additional replica(s)
    M: e36c46dbe90960f30861af00786d4c2064e63df2 10.180.157.200:6379
   slots:5461-10922 (5462 slots) master
   1 additional replica(s)
  Destination node:
    M: 80b661ecca260c89e3d8ea9b98f77edaeef43dcd 10.180.157.200:6380
   slots: (0 slots) master
   0 additional replica(s)
  Resharding plan:
    Moving slot 5461 from e36c46dbe90960f30861af00786d4c2064e63df2
    Moving slot 5462 from e36c46dbe90960f30861af00786d4c2064e63df2
    Moving slot 5463 from e36c46dbe90960f30861af00786d4c2064e63df2
    Moving slot 5464 from e36c46dbe90960f30861af00786d4c2064e63df2
    Moving slot 0 from 460b3a11e296aafb2615043291b7dd98274bb351
    Moving slot 1 from 460b3a11e296aafb2615043291b7dd98274bb351
    Moving slot 2 from 460b3a11e296aafb2615043291b7dd98274bb351
    Moving slot 10923 from 15126fb33796c2c26ea89e553418946f7443d5a5
    Moving slot 10924 from 15126fb33796c2c26ea89e553418946f7443d5a5
    Moving slot 10925 from 15126fb33796c2c26ea89e553418946f7443d5a5
Do you want to proceed with the proposed reshard plan (yes/no)? yes
Moving slot 5461 from 10.180.157.200:6379 to 10.180.157.200:6380:
Moving slot 5462 from 10.180.157.200:6379 to 10.180.157.200:6380:
Moving slot 5463 from 10.180.157.200:6379 to 10.180.157.200:6380:
Moving slot 5464 from 10.180.157.200:6379 to 10.180.157.200:6380:
Moving slot 0 from 10.180.157.202:6379 to 10.180.157.200:6380:
Moving slot 1 from 10.180.157.202:6379 to 10.180.157.200:6380:
Moving slot 2 from 10.180.157.202:6379 to 10.180.157.200:6380:
Moving slot 10923 from 10.180.157.201:6379 to 10.180.157.200:6380:
Moving slot 10924 from 10.180.157.201:6379 to 10.180.157.200:6380:
Moving slot 10925 from 10.180.157.201:6379 to 10.180.157.200:6380:      

使用Java連接配接Redis叢集

Jedis連接配接Redis叢集

JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(20);
config.setMaxIdle(2);

# 可以隻添加一兩個主節點, 不必添加所有的節點
HostAndPort hp0 = new HostAndPort("localhost", 7000);
HostAndPort hp1 = new HostAndPort("localhost", 7001);
HostAndPort hp2 = new HostAndPort("localhost", 7002);
HostAndPort hp3 = new HostAndPort("localhost", 7003);
HostAndPort hp4 = new HostAndPort("localhost", 7004);
HostAndPort hp5 = new HostAndPort("localhost", 7005);

Set<HostAndPort> hps = new HashSet<HostAndPort>();
hps.add(hp0);
hps.add(hp1);
hps.add(hp2);
hps.add(hp3);
hps.add(hp4);
hps.add(hp5);

JedisCluster jedisCluster = new JedisCluster(hps, 5000, 10, config);

long start = System.currentTimeMillis();
for (int i = 0; i < 100; i++) {
jedisCluster.set("sn" + i, "n" + i);
}
long end = System.currentTimeMillis();

System.out.println("Time : " + (end - start) / 10000);

for (int i = 0; i < 1000; i++) {
    System.out.println(jedisCluster.get("sn" + i));
}

jedisCluster.close();