redis集群之REDIS CLUSTER
1. Linux系统配置
1.1. vm.overcommit_memory设置
overcommit_memory文件指定了内核针对内存分配的策略,其值可以是0、1、2。
0, 表示内核将检查是否有足够的可用内存供应用进程使用;如果有足够的可用内存,内存申请允许;否则,内存申请失败,并把错误返回给应用进程。
1, 表示内核允许分配所有的物理内存,而不管当前的内存状态如何。
2, 表示内核允许分配超过所有物理内存和交换空间总和的内存
jenkins_service@jenkinsservice:~/redis-3.0.1$ sudo sysctl vm.overcommit_memory=1
vm.overcommit_memory = 1
1.2. Transparent Huge Pages开启
THP(Transparent Huge Pages)是一个使管理Huge Pages自动化的抽象层。
目前需要注意的是,由于实现方式问题,THP会造成内存锁影响性能,尤其是在程序不是专门为大内内存页开发的时候,简单介绍如下:
操作系统后台有一个叫做khugepaged的进程,它会一直扫描所有进程占用的内存,在可能的情况下会把4kpage交换为Huge Pages,在这个过程中,对于操作的内存的各种分配活动都需要各种内存锁,直接影响程序的内存访问性能,并且,这个过程对于应用是透明的,在应用层面不可控制,对于专门为4k page优化的程序来说,可能会造成随机的性能下降现象。
Redis Cluster 命令行
//集群(cluster)
- CLUSTER INFO 打印集群的信息
- CLUSTER NODES 列出集群当前已知的所有节点(node),以及这些节点的相关信息。
- //节点(node)
- CLUSTER MEET <ip> <port> 将 ip 和 port 所指定的节点添加到集群当中,让它成为集群的一份子。
- CLUSTER FORGET <node_id> 从集群中移除 node_id 指定的节点。
- CLUSTER REPLICATE <node_id> 将当前节点设置为 node_id 指定的节点的从节点。
- CLUSTER SAVECONFIG 将节点的配置文件保存到硬盘里面。
- //槽(slot)
- CLUSTER ADDSLOTS <slot> [slot ...] 将一个或多个槽(slot)指派(assign)给当前节点。
- CLUSTER DELSLOTS <slot> [slot ...] 移除一个或多个槽对当前节点的指派。
- CLUSTER FLUSHSLOTS 移除指派给当前节点的所有槽,让当前节点变成一个没有指派任何槽的节点。
- CLUSTER SETSLOT <slot> NODE <node_id> 将槽 slot 指派给 node_id 指定的节点,如果槽已经指派给另一个节点,那么先让另一个节点删除该槽>,然后再进行指派。
- CLUSTER SETSLOT <slot> MIGRATING <node_id> 将本节点的槽 slot 迁移到 node_id 指定的节点中。
- CLUSTER SETSLOT <slot> IMPORTING <node_id> 从 node_id 指定的节点中导入槽 slot 到本节点。
- CLUSTER SETSLOT <slot> STABLE 取消对槽 slot 的导入(import)或者迁移(migrate)。
- //键 (key)
- CLUSTER KEYSLOT <key> 计算键 key 应该被放置在哪个槽上。
- CLUSTER COUNTKEYSINSLOT <slot> 返回槽 slot 目前包含的键值对数量。
- CLUSTER GETKEYSINSLOT <slot> <count> 返回 count 个 slot 槽中的键。
2. 配置文件
开启cluster的redis必须是空服务器
修改redis.conf开启cluster
设置3个cluster
10.24.6.7:6379
10.24.6.4:6379
10.24.6.6:6379
3. 查看初始redis集群状态
启动三个节点上的Redis服务器。此时,三个Redis服务器节点均会以Redis Cluster的方式开始运行,但并没有自动构建集群,因为三者还处于“我不认识你,你不属于我”的状态,它们每个都是孤零零的Redis节点,或者是只包含了一个节点的集群。我们可以通过Redis客户端连接到服务器查看它们的状态,图一给出了状态查询方法和查询结果,其中cluster nodes命令用于查看当前Redis节点所属的Redis集群中的所有节点,而cluster info则用于查看当前Redis节点所属的Redis集群的整体状态。由图中我们可以看到,Redis集群中仅包含一个Redis节点,也就是当前节点,整个集群的状态是fail。
4. 分配hash slot
通过上面的操作,我们已经将三个各自为政的Redis节点规划到一个相同的集群中,那么我们现在是否就已经完成了集群搭建的所有工作了呢?非也!通过图二中对集群状态的查看我们可以知道,当前集群的状态还是fail,此时的Redis集群是不工作的,无法处理任何Redis命令。那么集群的状态为什么还是fail呢?本博主通过查看官方文档说明找到了原因所在,现摘录原文如下:
The FAIL state for the cluster happens in two cases.
1) If at least one hash slot is not served as the node serving it currently is in FAIL state.
2) If we are not able to reach the majority of masters (that is, if the majorify of masters are simply in PFAIL state, it is enough for the node to enter FAIL mode).
很明显,导致我们的集群处于fail状态的原因不是第二个条,也就是说至少有一个hash slot没有被服务!稍微考虑一下,可不是!何止有一个hash slot没有被服务,压根儿就没有Redis节点为任何hash slot服务!众所周知,Redis Cluster通过hash slot将数据根据主键来分区,所以一条key-value数据会根据算法自动映射到一个hash slot,但是一个hash slot存储在哪个Redis节点上并不是自动映射的,是需要集群管理者自行分配的。那么我们需要为多少个hash slot分配Redis节点呢?根据源码可知是16384个,即我们要将16384个hash slot分配到集群内的三个节点上。Redis中用于分配hash slot的命令有很多,其中包括cluster addslots、cluster delslots和cluster setslot。鉴于我们现在是集群的初始化阶段,所以我们可以选择cluster addslots来分配hash slot,该命令的语法为cluster addslots slot1 [slot2] ... [slotN]。
4.1. nodes-6379.conf分配
每个redis客户端单独分配自己负责的部分
修改内容如下:cda76a0a094d2ce624e33bed7f3c75689a4128fd :0 myself,master - 0 0 connected 0-5000 (注意是在自身节点的描述,也就是包含了myself那一行的后面追加hash slot的范围)。类似的,Redis Cluster Node2上nodes-6379.conf文件中追加 5001-10000 ,Redis Cluster Node3上nodes-6379.conf文件中追加 10001-16383。经过这样的配置后,Redis Cluster Node1负责存储0至5000之间的所有hash slots,Redis Cluster Node2负责存储5001至10000之间的所有hash slots,Redis Cluster Node3负责存储10001至16383的所有hash slots。
4.2. Redis cluster 命令分配
5. 搭建Redis集群
这里所谓的搭建集群,说白了就是让之前启动的三个Redis节点彼此连通,意识到彼此的存在,那么如何做到这一点呢?答案就是cluster meet命令。该命令的作用就是将当前节点主动介绍给另外一个节点认识,图二给出了cluster meet命令的执行方法和执行效果,由图中可知我们使用cluster meet命令分别将Redis Cluster Node1介绍给了Redis Cluster Node2(节点IP地址为192.168.32.3,运行端口为6379)和Redis Cluster Node3(节点IP地址为192.168.32.4,运行端口为6379),之后我们再次查看集群节点和集群状态就可以知道,三个节点已经成功合并到了同一个集群中。
这里找到了3个节点
查看10.24.6.6客户端
至此,3个结点
搭建完成标识:
在集群状态显示为ok之后,我们就可以像在Redis单机版上一样执行Redis命令了。
6. 测试
6.1. 非集群模式
6.1.1. Redis-cli
非集群模式客户端在cluster跳转时会提示MOVED错误
6.1.2. Redis客户端库
Sentinel
sentinel = Sentinel([(
'10.24.6.7' , 26379 )], socket_timeout= 0.1
)
master = sentinel.master_for(
'10.24.6.5master' , socket_timeout= 0.1
)
master
master.set(
'aaaaaaaaaaaaaaa' , 'bar'
)
master.set(
'bbbbbbbbbbbbbbbb' , 'bar'
)
print master.get( 'aaaaaaaaaaaaaaa'
)
print master.get( 'bbbbbbbbbbbbbbbb' )
Traceback (most recent call last):
File "E:/HomeInternet/server/utest_workspace/utest_utils/utest_unit/__init__.py", line 23, in <module>
master.set('bbbbbbbbbbbbbbbb', 'bar')
File "build\bdist.win32\egg\redis\client.py", line 1055, in set
File "build\bdist.win32\egg\redis\client.py", line 565, in execute_command
File "build\bdist.win32\egg\redis\client.py", line 577, in parse_response
File "build\bdist.win32\egg\redis\sentinel.py", line 55, in read_response
File "build\bdist.win32\egg\redis\connection.py", line 574, in read_response
redis.exceptions.ResponseError: MOVED 9577 10.24.6.6:6379
6.2. 集群模式(-C)
6.2.1. Redis-cli
集群模式客户端在跳转时会自动进行结点转向
6.2.2. Redis Cluster
https://github.com/Grokzen/redis-py-cluster
StrictRedisCluster
startup_nodes = [{
"host" : "10.24.6.7" , "port" : "6379"
}]
rc = StrictRedisCluster(startup_nodes=startup_nodes, decode_responses=True)
print rc.set( "bbbbbbbbbbbbbbbb" , "bar"
)
True
print rc.get( "bbbbbbbbbbbbbbbb" )
6.2.3. Redis Sentinel + Cluster
Sentinel
from rediscluster import
StrictRedisCluster
sentinel = Sentinel([(
'10.24.6.7' , 26379 )], socket_timeout= 0.1
)
ip, port = sentinel.discover_master(
'10.24.6.5master'
)
rc = StrictRedisCluster(host=ip, port=port, decode_responses=True)
print rc.set( "bbbbbbbbbbbbbbbb" , "bar"
)
print rc.get( "bbbbbbbbbbbbbbbb" )