Redis 集群
简介由于数据量过大,单个Master复制集难以承担,因此需要对多个复制集进行集群,形成水平扩展每个复制集只负责存储整个数据集的一部分,这就是Redis的集群,其作用是提供在多个Redis节点间共享数据的程序集。
**效果:**Redis集群是一个提供在多个Redis节点间共享数据的程序集,Redis集群可以支持多个Master
作用
- Redis?集群支特多个Master,每个Masterl又可以挂载多个Slave
- 由于Cluster自带Sentinel的故障转移机制,内置了高可用的支持,无需再去使用哨兵功能
- 客户端与Rdis的节点连接,不再需要连接集群中所有的节点,只需要任意连接集群中的一个可用节点即可
- 槽位so负责分配到各个物理服务节点,由对应的集群来负责维护节点、插槽和数据之间的关系
概念
Redis 处理方案
hash 槽位 slot
Redis集群投有使用一致性hash而是引入了哈希槽的概念
Redis集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽.集群的每个节点负责部分hash槽,
举个例子,比如当前集群有3个节点那么:
分片
问 | 答 |
---|---|
分片是什么 | 使用Redis集群时我们会将存储的数据分散到多台redis机器上,这称为分片。简言之,集群中的每个Redis实例都被认为是整个数据的一个分片。 |
如何找到给定key的分片 | 为了找到给定key的分片,我们对key进行CRC16(key)算法处理并通过对总分片数量取模。然后,使用确定性哈希函数,这意味着给定的key将多次始终映射到同一个分片,我们可以推断将来读取特定key的位置。 |
为什么要这样做 | 这种结构很容易添加或者删除节点.比如如果我想新添加个节点D,我需要从节点A,B,C中得部分槽到D上.如果我想移除节点A,需要将A中的槽移到B和C节点上,然后将没有任何槽的A节点从集群中移除即可.由于从一个节点将哈希槽移动到另一个节点并不会停止服务,所以无论添加删除或者改变某个节点的哈希槽的数量都不会造成集群不可用的状态 |
其他处理方案
-
Hash 取余
更多操作2亿条记录就是2亿个k,v,我们单机不行必须要分布式多机,假设有3台机器构成一个集群,用户每次读写操作都是根据公式:hash(key) % N个机器台数,计算出哈希值,用来决定数据映射到哪一个节点上。
优点: 简单粗暴,直接有效,只需要预估好数据规划好节点,例如3台、8台、10台,就能保证一段时间的数据支撑。使用Hash算法让固定的一部分请求落到同一台服务器上,这样每台服务器固定处理一部分请求(并维护这些请求的信息),起到负载均衡+分而治之的作用。
缺点: 当 Redis 服务器扩容或是缩容的时候需要改边 计算公式 hash(key) % ? 。这个时候及可能无法与之前存储的数据所计算出来的位置保持一致。这都好说保证不改变数量就可以,但是如果 机器宕机了呢 对应位置的数据将无法进行存取。
-
一致性HASH
(1 构建hash环
一致性哈希环
一致性哈希算法必然有个hash函数并按照算法产生hash值,这个算法的所有可能哈希值会构成一个全量集,这个集合可以成为一个hash空间[0,2^32-1],这个是一个线性空间,但是在算法中,我们通过适当的逻辑控制将它首尾相连(0 = 2^32),这样让它逻辑上形成了一个环形空间。
它也是按照使用取模的方法,前面笔记介绍的节点取模法是对节点(服务器)的数量进行取模。而一致性Hash算法是对232取模,简单来说,一致性Hash算法将整个哈希值空间组织成一个虚拟的圆环,如假设某哈希函数H的值空间为0-232-1(即哈希值是一个32位无符号整形),整个哈希环如下图:整个空间按顺时针方向组织,圆环的正上方的点代表0,0点右侧的第一个点代表1,以此类推,2、3、4、……直到232-1,也就是说0点左侧的第一个点代表232-1, 0和232-1在零点中方向重合,我们把这个由232个点组成的圆环称为Hash环。
(2 节点映射
将集群中各个IP节点映射到环上的某一个位置。
将各个服务器使用Hash进行一个哈希,具体可以选择服务器的IP或主机名作为关键字进行哈希,这样每台机器就能确定其在哈希环上的位置。假如4个节点NodeA、B、C、D,经过IP地址的哈希函数计算(hash(ip)),使用IP地址哈希后在环空间的位置如下:
(3 落建服务器
当我们需要存储一个kv键值对时,首先计算key的hash值,hash(key),将这个key使用相同的函数Hash计算出哈希值并确定此数据在环上的位置,从此位置沿环顺时针“行走”,第一台遇到的服务器就是其应该定位到的服务器,并将该键值对存储在该节点上。
如我们有Object A、Object B、Object C、Object D四个数据对象,经过哈希计算后,在环空间上的位置如下:根据一致性Hash算法,数据A会被定为到Node A上,B被定为到Node B上,C被定为到Node C上,D被定为到Node D上。
(4 优缺点
优点 1健壮性
假设Node C宕机,可以看到此时对象A、B、D不会受到影响。一般的,在一致性Hash算法中,如果一台服务器不可用,则受影响的数据仅仅是此服务器到其环空间中前一台服务器(即沿着逆时针方向行走遇到的第一台服务器)之间数据,其它不会受到影响。简单说,就是C挂了,受到影响的只是B、C之间的数据且这些数据会转移到D进行存储。
优点 2易扩展
数据量增加了,需要增加一台节点NodeX,X的位置在A和B之间,那收到影响的也就是A到X之间的数据,重新把A到X的数据录入到X上即可,不会导致hash取余全部数据重新洗牌。
缺点:数据倾斜
一致性Hash算法在服务节点太少时,容易因为节点分布不均匀而造成数据倾斜(被缓存的对象大部分集中缓存在某一台服务器上)问题,
例如系统中只有两台服务器:
-
Hash槽
Redis 集群中内置了 16384 个哈希槽,redis 会根据节点数量大致均等的将哈希槽映射到不同的节点。当需要在 Redis 集群中放置一个 key-value时,redis先对key使用crc16算法算出一个结果然后用结果对16384求余数[ CRC16(key) % 16384],这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,也就是映射到某个节点上。如下代码,key之A 、B在Node2, key之C落在Node3上
在 Redis 增加删除的时候只需要对 Redis 集群中的部分槽位进行重新分配到其他的 Redis机器上面即可,宕机了使用该机器的 slaver。
为什么只有16384个槽位?
(1)如果槽位为65536,发送心跳信息的消息头达8k,发送的心跳包过于庞大。
在消息头中最占空间的是myslots[CLUSTER_SLOTS/8]。 当槽位为65536时,这块的大小是: 65536÷8÷1024=8kb
在消息头中最占空间的是myslots[CLUSTER_SLOTS/8]。 当槽位为16384时,这块的大小是: 16384÷8÷1024=2kb
因为每秒钟,redis节点需要发送一定数量的ping消息作为心跳包,如果槽位为65536,这个ping消息的消息头太大了,浪费带宽。
(2)redis的集群主节点数量基本不可能超过1000个。
集群节点越多,心跳包的消息体内携带的数据越多。如果节点过1000个,也会导致网络拥堵。因此redis作者不建议redis cluster节点数量超过1000个。 那么,对于节点数在1000以内的redis cluster集群,16384个槽位够用了。没有必要拓展到65536个。
(3)槽位越小,节点少的情况下,压缩比高,容易传输
Redis主节点的配置信息中它所负责的哈希槽是通过一张bitmap的形式来保存的,在传输过程中会对bitmap进行压缩,但是如果bitmap的填充率slots / N很高的话(N表示节点数),bitmap的压缩率就很低。 如果节点数很少,而哈希槽数量很多的话,bitmap的压缩率就很低。
注意:要使 Docker 与 Redis 集群兼容,您需要使用 Docker 的主机组网模式。
搭建集群
练习一下docker compose
-
编写 docker-compose.yml
version: '3.1' services: # redis6379配置 redis6379: image: redis:7.0.12 container_name: redis_6379 ports: - 6379:6379 # rediis端口 - 16379:16379 # 集群节点通讯接口在对外设置的端口基础上+10000 restart: always network_mode: "host" # 配置和数据的数据卷映射 volumes: - /home/redis/cluster_6379.conf:/etc/redis/cluster.conf - /home/redis/data:/data command: ["redis-server", "/etc/redis/cluster.conf"] # redis6380配置 redis6380: image: redis:7.0.12 container_name: redis_6380 ports: - 6380:6380 # rediis端口 - 16380:16380 # 集群节点通讯接口在对外设置的端口基础上+10000 restart: always network_mode: "host" # 配置和数据的数据卷映射 volumes: - /home/redis/cluster_6380.conf:/etc/redis/cluster.conf - /home/redis/data:/data command: ["redis-server", "/etc/redis/cluster.conf"] # redis6381配置 redis6381: image: redis:7.0.12 container_name: redis_6381 ports: - 6381:6381 # rediis端口 - 16381:16381 # 集群节点通讯接口在对外设置的端口基础上+10000 restart: always network_mode: "host" # 配置和数据的数据卷映射 volumes: - /home/redis/cluster_6381.conf:/etc/redis/cluster.conf - /home/redis/data:/data command: ["redis-server", "/etc/redis/cluster.conf"] # redis6382配置 redis6382: image: redis:7.0.12 container_name: redis_6382 ports: - 6382:6382 # rediis端口 - 16382:16382 # 集群节点通讯接口在对外设置的端口基础上+10000 restart: always network_mode: "host" # 配置和数据的数据卷映射 volumes: - /home/redis/cluster_6382.conf:/etc/redis/cluster.conf - /home/redis/data:/data command: ["redis-server", "/etc/redis/cluster.conf"] # redis6383配置 redis6383: image: redis:7.0.12 container_name: redis_6383 ports: - 6383:6383 # rediis端口 - 16383:16383 # 集群节点通讯接口在对外设置的端口基础上+10000 restart: always network_mode: "host" # 配置和数据的数据卷映射 volumes: - /home/redis/cluster_6383.conf:/etc/redis/cluster.conf - /home/redis/data:/data command: ["redis-server", "/etc/redis/cluster.conf"] # redis6384配置 redis6384: image: redis:7.0.12 container_name: redis_6384 ports: - 6384:6384 # rediis端口 - 16384:16384 # 集群节点通讯接口在对外设置的端口基础上+10000 restart: always network_mode: "host" # 配置和数据的数据卷映射 volumes: - /home/redis/cluster_6384.conf:/etc/redis/cluster.conf - /home/redis/data:/data command: ["redis-server", "/etc/redis/cluster.conf"]
-
编写cluster_${port}.conf 配置文件
每个配置文件都一样的,记得修改端口和日志持久化等文件名
容器数据卷的映射文件夹记得创建!!!!!!!!!! 我踩坑了,不去创建的话,容器有很大概率启动不起来
################################## NETWORK ##################################### protected-mode no #要改 port 6379 tcp-backlog 511 timeout 0 tcp-keepalive 300 ################################# GENERAL ##################################### daemonize no #要改 pidfile /data/cluster/pid/cluter_6379.pid loglevel notice #要改 logfile "/data/cluster/log/cluster_6379.log" databases 16 always-show-logo no set-proc-title yes proc-title-template "{title} {listen-addr} {server-mode}" ################################ SNAPSHOTTING ################################ save 3600 1 300 100 60 5 stop-writes-on-bgsave-error yes rdbcompression yes rdbchecksum yes dbfilename dump_6379.rdb rdb-del-sync-files yes dir /data ################################# REPLICATION ################################# #要改与${requirepass}一样 masterauth 111111 replica-serve-stale-data yes replica-read-only yes repl-diskless-sync yes repl-diskless-sync-delay 5 repl-diskless-sync-max-replicas 0 repl-diskless-load disabled repl-disable-tcp-nodelay no replica-priority 100 ################################## SECURITY ################################### acllog-max-len 128 requirepass 111111 ############################# LAZY FREEING #################################### lazyfree-lazy-eviction no lazyfree-lazy-expire no lazyfree-lazy-server-del no replica-lazy-flush no lazyfree-lazy-user-del no lazyfree-lazy-user-flush no ############################ KERNEL OOM CONTROL ############################## oom-score-adj no oom-score-adj-values 0 200 800 #################### KERNEL transparent hugepage CONTROL ###################### disable-thp yes ############################## APPEND ONLY MODE ############################### appendonly yes #看心情改 appendfilename "appendonly_cluster_6379.aof" #看心情改 appenddirname "cluster_appendonlydir" appendfsync everysec no-appendfsync-on-rewrite no auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb aof-load-truncated yes aof-use-rdb-preamble yes aof-timestamp-enabled no ################################ REDIS CLUSTER ############################### cluster-enabled yes #看心情改 cluster-config-file nodes-6379.conf ################################## SLOW LOG ################################### slowlog-log-slower-than 10000 slowlog-max-len 128 ################################ LATENCY MONITOR ############################## latency-monitor-threshold 0 ############################# EVENT NOTIFICATION ############################## notify-keyspace-events "" ############################### ADVANCED CONFIG ############################### hash-max-listpack-entries 512 hash-max-listpack-value 64 list-max-listpack-size -2 list-compress-depth 0 set-max-intset-entries 512 zset-max-listpack-entries 128 zset-max-listpack-value 64 hll-sparse-max-bytes 3000 stream-node-max-bytes 4096 stream-node-max-entries 100 activerehashing yes client-output-buffer-limit normal 0 0 0 client-output-buffer-limit replica 256mb 64mb 60 client-output-buffer-limit pubsub 32mb 8mb 60 hz 10 dynamic-hz yes aof-rewrite-incremental-fsync yes rdb-save-incremental-fsync yes jemalloc-bg-thread yes
启动成功!
-
配置集群
随便连接上一个 redis 机器 执行以下命令
–cluster-replicas 1 意思是每台机器有一台 slaver改用自己的ip 和 端口 还有密码
redis-cli -a 111111 --cluster create --cluster-replicas 1 43.138.25.182:6379 43.138.25.182:6380 43.138.25.182:6381 43.138.25.182:6382 43.138.25.182:6383 43.138.25.182:6384
验证!
使用正常连接方法连接集群时 部分 key 无法添加 因为分槽的原因 这个 key 可能不是当前 redis 机器可以存储的 使用集群方式连接则可以解决,下面测试中设置 k1 时提示槽位时 12706 需要移动到6381的机器上执行
redis-cli -a <连接密码> -p <redis机器端口> -c
后面的-c
表示集群连接
Stream On Error: Connection is closed 踩坑!
我在使用 Another Redis Desktop Manager客户端 连接redis集群报错 Stream On Error: Connection is closed
处理:修改node.conf
在node.conf中自动生成的 ip 是内网 ip 改成外网的就可以了
Redis 集群扩缩容
reids 集群常用命令
info replication
查看主从信息cluster info
查看集群状态配置信息cluster nodes
查看集群节点信息
缩容
-
移除 slaver 节点机器
# 命令:redis-cli -a 密码 --cluster del-node ip:从机端口 从机6384节点ID redis-cli -a 111111 --cluster del-node 43.138.25.182:6384 7cefe2b448b9876d7c095568a86cbbef5aaa155d
-
清空并分配 slot 数据槽
redis-cli -a 111111 --cluster reshard 43.138.25.182:6381
第一次输入的是移动的数量,第二次输入接受的id,第三次输入输出源(也就是清空的redis 的 id ,回车在输入done)
-
移除master
# 命令:redis-cli -a 密码 --cluster del-node ip:从机端口 从机6381节点ID redis-cli -a 111111 --cluster del-node 43.138.25.182:6381 0e1a6339e2dc1f9ee2e47d2a46eb8a99b22b955d
删除之后查看节点发现之前的6381 的槽位全在 6379上面
扩容
-
添加新的master
#将新增的6387作为master节点加入原有集群 6387 就是将要作为master新增节点 6381 就是原来集群节点里面的领路人,相当于6387拜拜6381的码头从而找到组织加入集群 #redis-cli -a 密码 --cluster add-node 自己实际IP地址:6387 自己实际IP地址:6381 redis-cli -a 111111 --cluster add-node 43.138.25.182:6381 43.138.25.182:6379
-
分配slot给新的master(和上面一样的)
redis-cli -a 111111 --cluster reshard 43.138.25.182:6381
-
分配新的slaver给新的master
#命令:redis-cli -a 密码 --cluster add-node ip:新slave端口 ip:新master端口 --cluster-slave --cluster-master-id 新主机节点ID redis-cli -a 111111 --cluster add-node 43.138.25.182:6384 43.138.25.182:6381 --cluster-slave --cluster-master-id 0e1a6339e2dc1f9ee2e47d2a46eb8a99b22b955d
分配完成!6379对应6382 ,6381对应6384 ,6382对应6383