Redis Cluster 4.0 on CentOS 6.9 搭建
集群簡介
Redis 集群是一個可以在多個 Redis 節點之間進行數據共享的設施(installation)。
Redis 集群不支持那些需要同時處理多個鍵的 Redis 命令, 因為執行這些命令需要在多個 Redis 節點之間移動數據, 並且在高負載的情況下, 這些命令將降低 Redis 集群的性能, 並導致不可預測的行為。
Redis 集群通過分區(partition)來提供一定程度的可用性(availability): 即使集群中有一部分節點失效或者無法進行通訊, 集群也可以繼續處理命令請求。
Redis 集群提供了以下兩個好處:
- 將數據自動切分(split)到多個節點的能力。
- 當集群中的一部分節點失效或者無法進行通訊時, 仍然可以繼續處理命令請求的能力
Redis 集群數據共享
Redis 集群使用數據分片(sharding)而非一致性哈希(consistency hashing)來實現: 一個 Redis 集群包含 16384
個哈希槽(hash slot), 數據庫中的每個鍵都屬於這 16384
個哈希槽的其中一個, 集群使用公式 CRC16(key) % 16384
來計算鍵 key
屬於哪個槽, 其中 CRC16(key)
語句用於計算鍵 key
的 CRC16的校驗和 。
集群中的每個節點負責處理一部分哈希槽。 舉個例子, 一個集群可以有三個哈希槽, 其中:
- 節點 A 負責處理
0
號至5500
號哈希槽。 - 節點 B 負責處理
5501
11000
號哈希槽。 - 節點 C 負責處理
11001
號至16384
號哈希槽。
這種將哈希槽分布到不同節點的做法使得用戶可以很容易地向集群中添加或者刪除節點。 比如說:
- 如果用戶將新節點 D 添加到集群中, 那麽集群只需要將節點 A 、B 、 C 中的某些槽移動到節點 D 就可以了。
- 與此類似, 如果用戶要從集群中移除節點 A , 那麽集群只需要將節點 A 中的所有哈希槽移動到節點 B 和節點 C , 然後再移除空白(不包含任何哈希槽)的節點 A 就可以了。
因為將一個哈希槽從一個節點移動到另一個節點不會造成節點阻塞, 所以無論是添加新節點還是移除已存在節點, 又或者改變某個節點包含的哈希槽數量, 都不會造成集群下線。
Redis 集群中的主從復制
為了使得集群在一部分節點下線或者無法與集群的大多數(majority)節點進行通訊的情況下, 仍然可以正常運作, Redis 集群對節點使用了主從復制功能: 集群中的每個節點都有 1
個至 N
個復制品(replica), 其中一個復制品為主節點(master), 而其余的 N-1
個復制品為從節點(slave)。
在之前列舉的節點 A 、B 、C 的例子中, 如果節點 B 下線了, 那麽集群將無法正常運行, 因為集群找不到節點來處理 5501
號至 11000
號的哈希槽。
另一方面, 假如在創建集群的時候(或者至少在節點 B 下線之前), 我們為主節點 B 添加了從節點 B1 , 那麽當主節點 B 下線的時候, 集群就會將 B1 設置為新的主節點, 並讓它代替下線的主節點 B , 繼續處理 5501
號至 11000
號的哈希槽, 這樣集群就不會因為主節點 B 的下線而無法正常運作了。
不過如果節點 B 和 B1 都下線的話, Redis 集群還是會停止運作。
開始搭建
要讓集群正常工作至少需要3個主節點,在這裏我們要創建6個redis節點,其中三個為主節點,三個為從節點,對應的redis節點的ip和端口對應關系如下(為了簡單演示都在同一臺機器上面)
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
1. 獲取最新版Redis
wget http://download.redis.io/releases/redis-stable.tar.gz
2. 解壓到/usr/local/redis/安裝
# cd /usr/local/redis/ #ls -l -rw-r--r-- 1 root root 131381 Oct 1 20:41 00-RELEASENOTES -rw-r--r-- 1 root root 53 Oct 1 20:41 BUGS -rw-r--r-- 1 root root 1815 Oct 1 20:39 CONTRIBUTING -rw-r--r-- 1 root root 1487 Oct 1 20:39 COPYING drwxr-xr-x 6 root root 4096 Oct 1 20:41 deps -rw-r--r-- 1 root root 11 Oct 1 20:39 INSTALL -rw-r--r-- 1 root root 151 Oct 1 20:39 Makefile -rw-r--r-- 1 root root 4223 Oct 1 20:39 MANIFESTO -rw-r--r-- 1 root root 20530 Oct 1 20:39 README.md -rw-r--r-- 1 root root 57764 Oct 1 20:39 redis.conf -rwxr-xr-x 1 root root 271 Oct 1 20:39 runtest -rwxr-xr-x 1 root root 280 Oct 1 20:39 runtest-cluster -rwxr-xr-x 1 root root 281 Oct 1 20:39 runtest-sentinel -rw-r--r-- 1 root root 7606 Oct 1 20:39 sentinel.conf drwxr-xr-x 3 root root 4096 Oct 1 20:41 src drwxr-xr-x 10 root root 4096 Oct 1 20:41 tests drwxr-xr-x 8 root root 4096 Oct 1 20:41 utils #make && make install
3. 創建目錄
mkdir /data/redis/cluster -p cd /data/redis/cluster mkdir 7000 7001 7002 7003 7004 7005
4. 修改配置文件
cp /usr/local/redis/redis.conf /data/redis/cluster/7000/
修改配置文件中下面選項
port 7000
daemonize yes
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
文件中的 cluster-enabled 選項用於開實例的集群模式, 而 cluster-conf-file 選項則設定了保存節點配置文件的路徑, 默認值為nodes.conf 。其他參數相信童鞋們都知道。節點配置文件無須人為修改, 它由 Redis 集群在啟動時創建, 並在有需要時自動進行更新。
修改完成後,把修改完成的redis.conf復制到7001-7005目錄下,並且端口修改成和文件夾對應。
[[email protected] 7000]# cp redis.conf ../7001/ [[email protected] 7000]# cp redis.conf ../7002/ [[email protected] 7000]# cp redis.conf ../7003/ [[email protected] 7000]# cp redis.conf ../7004/ [[email protected] 7000]# cp redis.conf ../7005/
# sed -i ‘s/port 7000/port 7001/g‘ /data/redis/cluster/7001/redis.conf # sed -i ‘s/port 7000/port 7002/g‘ /data/redis/cluster/7002/redis.conf # sed -i ‘s/port 7000/port 7003/g‘ /data/redis/cluster/7003/redis.conf # sed -i ‘s/port 7000/port 7004/g‘ /data/redis/cluster/7004/redis.conf # sed -i ‘s/port 7000/port 7005/g‘ /data/redis/cluster/7005/redis.conf
5. 啟動6個redis實例
cd /data/redis/cluster/7000 redis-server redis.conf cd /data/redis/cluster/7001 redis-server redis.conf cd /data/redis/cluster/7002 redis-server redis.conf cd /data/redis/cluster/7003 redis-server redis.conf cd /data/redis/cluster/7004 redis-server redis.conf cd /data/redis/cluster/7005 redis-server redis.conf
查看進程狀態
[[email protected] 7005]# ps -ef|grep redis root 24873 1 0 21:00 ? 00:00:00 redis-server 127.0.0.1:7001 [cluster] root 24898 1 0 21:01 ? 00:00:00 redis-server 127.0.0.1:7000 [cluster] root 24905 1 0 21:01 ? 00:00:00 redis-server 127.0.0.1:7002 [cluster] root 24910 1 0 21:01 ? 00:00:00 redis-server 127.0.0.1:7003 [cluster] root 24915 1 0 21:01 ? 00:00:00 redis-server 127.0.0.1:7004 [cluster] root 24930 1 0 21:02 ? 00:00:00 redis-server 127.0.0.1:7005 [cluster]
6. 執行命令創建集群,首先安裝依賴
首先要升級Ruby 到2.2 以上的版本
yum remove ruby
wget http://cache.ruby-lang.org/pub/ruby/ruby-2.4.2.zip 按照說明編譯安裝
安裝rubygems
yum install rubygems -y
安裝gem-redis (下載地址:https://rubygems.org/gems/redis/versions/4.0.1)
這裏發現有報錯
[[email protected] /]# gem install -l redis-4.0.1.gem ERROR: Loading command: install (LoadError) ERROR: While executing gem ... (NoMethodError) undefined method `invoke_with_build_args‘ for nil:NilClass
網上Google 搜到一堆apt-get 的Ubantu 解決方案, 最後在git 上面找到解決辦法
git clone https://github.com/ruby/zlib.git
然後安裝zlib 包
[[email protected] zlib]# ruby extconf.rb checking for deflateReset() in -lz... yes checking for zlib.h... yes checking for crc32_combine() in zlib.h... yes checking for adler32_combine() in zlib.h... yes checking for z_crc_t in zlib.h... no creating Makefile [[email protected] zlib]# [[email protected] zlib]# make compiling zlib.c linking shared-object zlib.so [[email protected] zlib]# [[email protected] zlib]# make install /usr/bin/install -c -m 0755 zlib.so /usr/local/lib/ruby/site_ruby/2.4.0/x86_64-linux
解決這個問題之後就可以安裝 redis-4.0.1.gem 了
[[email protected]/]# gem install -l redis-4.0.1.gem Successfully installed redis-4.0.1 Parsing documentation for redis-4.0.1 Installing ri documentation for redis-4.0.1 Done installing documentation for redis after 2 seconds
7. 使用redis-trib 創建集群
cp /usr/local/redis/src/redis-trib.rb /usr/local/bin/redis-trib.rb
redis-trib 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
命令的意義如下:
- 給定 redis-trib.rb 程序的命令是 create , 這表示我們希望創建一個新的集群。
- 選項 --replicas 1 表示我們希望為集群中的每個主節點創建一個從節點。
- 之後跟著的其他參數則是實例的地址列表, 我們希望程序使用這些地址所指示的實例來創建新集群。
簡單來說, 以上命令的意思就是讓 redis-trib 程序創建一個包含三個主節點和三個從節點的集群。
接著, redis-trib 會打印出一份預想中的配置給你看, 如果你覺得沒問題的話, 就可以輸入 yes , redis-trib 就會將這份配置應用到集群當中:
> 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 >>> Creating cluster >>> Performing hash slots allocation on 6 nodes... Using 3 masters: 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 Adding replica 127.0.0.1:7003 to 127.0.0.1:7000 Adding replica 127.0.0.1:7004 to 127.0.0.1:7001 Adding replica 127.0.0.1:7005 to 127.0.0.1:7002 M: 2909642e736c8510a6b284139de164e5192749f4 127.0.0.1:7000 slots:0-5460 (5461 slots) master M: 67c520e08194014965474ffb6ac8e30c8f39fb63 127.0.0.1:7001 slots:5461-10922 (5462 slots) master M: d94fc8802b29df1c8601b337df927420492e7179 127.0.0.1:7002 slots:10923-16383 (5461 slots) master S: 045c473cb8e416f39444e455151eeff8168bff69 127.0.0.1:7003 replicates 2909642e736c8510a6b284139de164e5192749f4 S: 079ea3c2c8a4d75cf4e19e6cb6d4a0ccbbb6cd80 127.0.0.1:7004 replicates 67c520e08194014965474ffb6ac8e30c8f39fb63 S: 6bdaac36d8ea69968b061b44bb47657000340e4c 127.0.0.1:7005 replicates d94fc8802b29df1c8601b337df927420492e7179 Can I set the above configuration? (type ‘yes‘ to accept): yes >>> Nodes configuration updated >>> Assign a different config epoch to each node >>> Sending CLUSTER MEET messages to join the cluster Waiting for the cluster to join.. >>> Performing Cluster Check (using node 127.0.0.1:7000) M: 2909642e736c8510a6b284139de164e5192749f4 127.0.0.1:7000 slots:0-5460 (5461 slots) master 1 additional replica(s) M: d94fc8802b29df1c8601b337df927420492e7179 127.0.0.1:7002 slots:10923-16383 (5461 slots) master 1 additional replica(s) S: 045c473cb8e416f39444e455151eeff8168bff69 127.0.0.1:7003 slots: (0 slots) slave replicates 2909642e736c8510a6b284139de164e5192749f4 S: 6bdaac36d8ea69968b061b44bb47657000340e4c 127.0.0.1:7005 slots: (0 slots) slave replicates d94fc8802b29df1c8601b337df927420492e7179 S: 079ea3c2c8a4d75cf4e19e6cb6d4a0ccbbb6cd80 127.0.0.1:7004 slots: (0 slots) slave replicates 67c520e08194014965474ffb6ac8e30c8f39fb63 M: 67c520e08194014965474ffb6ac8e30c8f39fb63 127.0.0.1:7001 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.
集群的客戶端
Redis 集群現階段的一個問題是客戶端實現很少。 以下是一些網上一些資料:
- redis-rb-cluster 是antirez編寫的 Ruby 實現, 用於作為其他實現的參考。 該實現是對 redis-rb 的一個簡單包裝, 高效地實現了與集群進行通訊所需的最少語義(semantic)。
- redis-py-cluster 看上去是 redis-rb-cluster 的一個 Python 版本, 這個項目有一段時間沒有更新了(最後一次提交是在六個月之前), 不過可以將這個項目用作學習集群的起點。
- 流行的 Predis 曾經對早期的 Redis 集群有過一定的支持, 但我不確定它對集群的支持是否完整, 也不清楚它是否和最新版本的 Redis 集群兼容 (因為新版的 Redis 集群將槽的數量從 4k 改為 16k 了)。
- Redis unstable 分支中的 redis-cli 程序實現了非常基本的集群支持, 可以使用命令 redis-cli -c 來啟動。
測試 Redis 集群比較簡單的辦法就是使用 redis-rb-cluster 或者 redis-cli , 接下來我們將使用 redis-cli 為例來進行演示:
[[email protected] src]# redis-cli -c -p 7000 127.0.0.1:7000> set name ray -> Redirected to slot [5798] located at 127.0.0.1:7001 OK 127.0.0.1:7001> 127.0.0.1:7001> set name2 Tim -> Redirected to slot [742] located at 127.0.0.1:7000 OK 127.0.0.1:7000> set name3 Andy OK
關於redis 支持的數據類型和詳細的命令解釋,可以參考http://redisdoc.com
[[email protected] /]# redis-cli -p 7000 cluster nodes d94fc8802b29df1c8601b337df927420492e7179 127.0.0.1:[email protected] master - 0 1506867732433 3 connected 10923-16383 045c473cb8e416f39444e455151eeff8168bff69 127.0.0.1:[email protected] slave 2909642e736c8510a6b284139de164e5192749f4 0 1506867731000 4 connected 6bdaac36d8ea69968b061b44bb47657000340e4c 127.0.0.1:[email protected] slave d94fc8802b29df1c8601b337df927420492e7179 0 1506867730528 6 connected 079ea3c2c8a4d75cf4e19e6cb6d4a0ccbbb6cd80 127.0.0.1:[email protected] slave 67c520e08194014965474ffb6ac8e30c8f39fb63 0 1506867730427 5 connected 2909642e736c8510a6b284139de164e5192749f4 127.0.0.1:[email protected] myself,master - 0 1506867732000 1 connected 0-5460 67c520e08194014965474ffb6ac8e30c8f39fb63 127.0.0.1:[email protected] master - 0 1506867731530 2 connected 5461-10922
從上面的輸出可以看出,集群由3主3備組成,而且可以看出誰是誰的主和備,其中第一列是Redis 節點的ID,集群通信基於這個唯一的ID 而不是IP 和端口
另外info 命令可以看出集群的其他很多配置信息
127.0.0.1:7002> info # Server redis_version:4.0.2 redis_git_sha1:00000000 redis_git_dirty:0 redis_build_id:5d2f34688764aa69 redis_mode:cluster os:Linux 2.6.32-696.3.2.el6.x86_64 x86_64 arch_bits:64 multiplexing_api:epoll atomicvar_api:sync-builtin gcc_version:4.4.7 process_id:24905 run_id:cf77b38c7a0d209d5e86f0c58243b1c81b2b261b tcp_port:7002 uptime_in_seconds:5094 uptime_in_days:0 hz:10 lru_clock:13695774 executable:/data/redis/cluster/7002/redis-server config_file:/data/redis/cluster/7002/redis.conf # Clients connected_clients:1 client_longest_output_list:0 client_biggest_input_buf:0 blocked_clients:0 # Memory used_memory:2623728 used_memory_human:2.50M used_memory_rss:12177408 used_memory_rss_human:11.61M used_memory_peak:2623728 used_memory_peak_human:2.50M used_memory_peak_perc:100.04% used_memory_overhead:2538992 used_memory_startup:1423928 used_memory_dataset:84736 used_memory_dataset_perc:7.06% total_system_memory:4018335744 total_system_memory_human:3.74G used_memory_lua:37888 used_memory_lua_human:37.00K maxmemory:0 maxmemory_human:0B maxmemory_policy:noeviction mem_fragmentation_ratio:4.64 mem_allocator:jemalloc-4.0.3 active_defrag_running:0 lazyfree_pending_objects:0 # Persistence loading:0 rdb_changes_since_last_save:0 rdb_bgsave_in_progress:0 rdb_last_save_time:1506867313 rdb_last_bgsave_status:ok rdb_last_bgsave_time_sec:0 rdb_current_bgsave_time_sec:-1 rdb_last_cow_size:8572928 aof_enabled:1 aof_rewrite_in_progress:0 aof_rewrite_scheduled:0 aof_last_rewrite_time_sec:-1 aof_current_rewrite_time_sec:-1 aof_last_bgrewrite_status:ok aof_last_write_status:ok aof_last_cow_size:0 aof_current_size:0 aof_base_size:0 aof_pending_rewrite:0 aof_buffer_length:0 aof_rewrite_buffer_length:0 aof_pending_bio_fsync:0 aof_delayed_fsync:0 # Stats total_connections_received:4 total_commands_processed:702 instantaneous_ops_per_sec:0 total_net_input_bytes:85105 total_net_output_bytes:11244 instantaneous_input_kbps:0.02 instantaneous_output_kbps:0.01 rejected_connections:0 sync_full:1 sync_partial_ok:0 sync_partial_err:1 expired_keys:0 evicted_keys:0 keyspace_hits:0 keyspace_misses:1 pubsub_channels:0 pubsub_patterns:0 latest_fork_usec:4435 migrate_cached_sockets:0 slave_expires_tracked_keys:0 active_defrag_hits:0 active_defrag_misses:0 active_defrag_key_hits:0 active_defrag_key_misses:0 # Replication role:master connected_slaves:1 slave0:ip=127.0.0.1,port=7005,state=online,offset=952,lag=1 master_replid:6e3d41dcdd4c3c4767affccdd2b2150cb7d3fcd9 master_replid2:0000000000000000000000000000000000000000 master_repl_offset:966 second_repl_offset:-1 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:1 repl_backlog_histlen:966 # CPU used_cpu_sys:4.53 used_cpu_user:2.50 used_cpu_sys_children:0.01 used_cpu_user_children:0.00 # Cluster cluster_enabled:1 # Keyspace
8. Redis 故障測試
嘗試kill 掉7000實例,集群自動識別並提升對應的slave 為主
[[email protected] src]# redis-cli -c -p 7001 cluster nodes
079ea3c2c8a4d75cf4e19e6cb6d4a0ccbbb6cd80 127.0.0.1:[email protected] slave 67c520e08194014965474ffb6ac8e30c8f39fb63 0 1506868482542 5 connected
2909642e736c8510a6b284139de164e5192749f4 127.0.0.1:[email protected] master,fail - 1506868460710 1506868460595 1 disconnected
045c473cb8e416f39444e455151eeff8168bff69 127.0.0.1:[email protected] master - 0 1506868482000 7 connected 0-5460
67c520e08194014965474ffb6ac8e30c8f39fb63 127.0.0.1:[email protected] myself,master - 0 1506868480000 2 connected 5461-10922
6bdaac36d8ea69968b061b44bb47657000340e4c 127.0.0.1:[email protected] slave d94fc8802b29df1c8601b337df927420492e7179 0 1506868481000 6 connected
d94fc8802b29df1c8601b337df927420492e7179 127.0.0.1:[email protected] master - 0 1506868481096 3 connected 10923-16383
嘗試把7000重新啟動,7000並不會去搶占,而是變成新的slave 加入集群
[[email protected] src]# redis-cli -c -p 7001 cluster nodes
079ea3c2c8a4d75cf4e19e6cb6d4a0ccbbb6cd80 127.0.0.1:[email protected] slave 67c520e08194014965474ffb6ac8e30c8f39fb63 0 1506868670539 5 connected
2909642e736c8510a6b284139de164e5192749f4 127.0.0.1:[email protected] slave 045c473cb8e416f39444e455151eeff8168bff69 0 1506868672044 7 connected
045c473cb8e416f39444e455151eeff8168bff69 127.0.0.1:[email protected] master - 0 1506868671542 7 connected 0-5460
67c520e08194014965474ffb6ac8e30c8f39fb63 127.0.0.1:[email protected] myself,master - 0 1506868671000 2 connected 5461-10922
6bdaac36d8ea69968b061b44bb47657000340e4c 127.0.0.1:[email protected] slave d94fc8802b29df1c8601b337df927420492e7179 0 1506868670000 6 connected
d94fc8802b29df1c8601b337df927420492e7179 127.0.0.1:[email protected] master - 0 1506868671040 3 connected 10923-16383
Redis 除了RDB 之外還有另一種持久化的方式叫AOF,有點像MySQL 的binlog
[[email protected] 7000]# ll total 72 -rw-r--r-- 1 root root 124 Oct 1 22:28 appendonly.aof -rw-r--r-- 1 root root 205 Oct 1 22:30 dump.rdb -rw-r--r-- 1 root root 781 Oct 1 22:15 nodes.conf -rw-r--r-- 1 root root 57758 Oct 1 20:52 redis.conf [[email protected] 7000]# more appendonly.aof *2 $6 SELECT $1 0 *3 $3 set $5 name2 $3 Tim *3 $3 set $5 name3 $4 Andy *3 $3 set $5 name2 $4 Tony
最後,想說說Redis 的集群方案,目前有3大方案:
1. Redis cluster
2. Twemproxy
3. Codis
網上有很多這三種方案的比較,目前最成熟的應該是Codis 由豌豆莢開源,後面會補充Codis 的搭建手冊 https://github.com/CodisLabs/codis/
本文主要參考資料:
http://www.cnblogs.com/gomysql/p/4395504.html
http://redisdoc.com/topic/cluster-tutorial.html
Redis Cluster 4.0 on CentOS 6.9 搭建