使用VMware创建一个CentOS7的系统环境。到Redis官网可以查看下载、编译和运行的操作命令:
wget http://download.redis.io/releases/redis-5.0.7.tar.gz
tar xzf redis-5.0.7.tar.gz
# 解压到指定目录可以如下指令
tar zxvf redis-5.0.7.tar.gz -C /opt
cd redis-5.0.7
make
# 指定安装目录可以用以下命令
make PREFIX=/usr/local/redis install
# 但最好加上 MALLOC=libc这个设置,不然安装会报错找不到文件jemalloc/jemalloc.h
make PREFIX=/usr/local/redis MALLOC=libc install
redis是C语言开发的,编译需要gcc,所以如果没有安装gcc的话,make命令会报错,那么就安装一下(标准版的CentOS是没有gcc的需要安装):
# 养成习惯,第一次使用yum的时候先更新一下
yum -y update
yum -y install gcc automake autoconf libtool make
然后进入到安装redis的目录,启动redis。
# 启动服务端
./redis-server
# 启动客户端时-h -p可省略,默认是127.0.0.1和6379
# 如果没有启动服务端直接启动客户端的话会提示连接被拒绝
./redis-cli
# 启动客户端后可以使用如下命令看看是否成功连接了服务端
set name eric
get name
# 当然也可以使用如下命令查看redis相关进程是否开启
ps -ef | grep redis
redis的配置文件在安装目录中是没有的,用户可以自己创建一个,但是redis的源文件中有个配置文件,我们可以基于这个配置来自定义成我们自己需要的样子。所以我们可以把解压缩后源文件中的配置文件复制到安装目录中,和bin文件夹同目录。
cp /opt/redis-5.0.7/redis.conf /usr/local/redis/
可以大概看一下核心的几个配置:
# 默认只允许本地访问
bind 127.0.0.1
# 端口设置
port 6379
# 默认不以守护进程方式运行,所以启动redis服务端后占用终端,实际中我们可以设置成yes
daemonize no
# 如果以守护进程方式运行,进程信息记录在如下文件
pidfile /var/run/redis_6379.pid
# 日志相关
loglevel notice
# 数据库数量,以0开始,可以切换
databases 16
# 这是快照的规则,60秒内检查是否有10000次变更,如果有就存一次快照;300秒内是否有10次变更,如果有就...
save 900 1
save 300 10
save 60 10000
# 默认开启持久化文件的压缩算法
rdbcompression yes
# 持久化文件,默认在/bin目录下的dump.rdb
dbfilename dump.rdb
# 还有一些master主从集群的设置,具体使用到时再说
# 默认是不需要密码的
# requirepass foobared
# 最大客户端连接
# maxclients 10000
# 最大内存使用
# maxmemory
还有很多配置,redis的配置文件中针对每一项都有详细的说明,如果要搜索的话,可以在非编辑状态下,输入/
和关键字进行搜索,搜索到多个的话,可以使用n
切换到下一个进行查看。
我们自己需要设置的3个主要配置就是:
# 注释掉允许所有用户访问,开发时用
# bind 127.0.0.1
# 设置成yes
daemonize yes
# 设置密码,不设置密码的话可能远程无法连接,所以建议开启
requirepass 123456
那我们启动redis-server的时候需要带上我们自定义的配置文件:
./bin/redis-server ./redis.conf
设置了密码后,客户端连接的话,也要设置密码,也就是认证:
./redis-cli -a 123456
redis的使用是基于内存的,如果内存不能有效维护好,那么内存很容易被爆满掉,发生内存泄露等问题。所以可以有以下2个方式来维护内存。
(1)合理利用key的有效性,部分key应该设置有效期,这样可以不需要一直占用内存。
set name eric
# ttl查看key的剩余时间,-1表示永久,-2表示过期,时间单位秒
ttl name
# 设置过期时间10秒
expire name 10
# 10秒后发现没有这个key了
keys *
# ttl查看的话发现返回-2表示过期
ttl name
# 如果要设置不过期
persist name
(2)有一个内存维护策略,就是按照什么规则删除一些补偿应的数据。最前面2种是比较常用的。
# MAXMEMORY POLICY: how Redis will select what to remove when maxmemory
# is reached. You can select among five behaviors:
#
# volatile-lru -> Evict using approximated LRU among the keys with an expire set.
# allkeys-lru -> Evict any key using approximated LRU.
# volatile-lfu -> Evict using approximated LFU among the keys with an expire set.
# allkeys-lfu -> Evict any key using approximated LFU.
# volatile-random -> Remove a random key among the ones with an expire set.
# allkeys-random -> Remove a random key, any key.
# volatile-ttl -> Remove the key with the nearest expire time (minor TTL)
# noeviction -> Don't evict anything, just return an error on write operations.
#
# LRU means Least Recently Used
# LFU means Least Frequently Used
正常情况下,数据要持久化的话要满足我们看的那个redis配置文件中的规定时间内达到一定变更次数的规则要求,所以如果正常满足的话,那么就会正常持久化。
(1)如果遇到非正常关闭的话,比如断电,我们可以用杀掉进程的方式模拟:
ps -ef | grep redis
# 得到redis-server的PID后kill
kill -9 PID
# 然后再次启动redis-server后在客户端keys * 查看发现之前的数据没有持久化
(2)如果正常关闭的话,会持久化数据。正常关闭是在客户端输入:
shutdown
# 然后再次启动redis-server后在客户端keys * 查看发现之前的数据可以查询到,说明关闭之前做了持久化操作
RedisDestopManager现在收费了。网上貌似可以搜索到老旧的版本,这些老旧的版本还继续可以使用。基础的使用应该是没有问题的。
这里远程连接的时候,需要开放一下centos的防火墙。
# 添加一个放行端口
firewall-cmd --zone=public --add-port=6379/tcp --permanent
# 重启防火墙
firewall-cmd --reload
# 查看开放的端口
firewall-cmd --list-ports
远程连接后,可以发现默认有16个库。
安装好docker,设置好阿里云或国内其他的仓库镜像后:
docker pull redis:5.0.7
# -d后台运行,--requirepass设置密码,-p设置端口映射
docker run -d --name redis6379 -p 6379:6379 redis:5.0.7 --requirepass "123456"
# 如果是docker开启的,那么我们想进入docker并打开客户端的话可以操作
docker exec -it redis6379 redis-cli -a 123456
同样的如果centos的防火墙没有开放6379的话设置一下,如果之前已经设置过了,那么久不用设置了,直接在外部就可以远程连接了。
# 设置一个键值对
set key value
# 获取一个key的value
get key
# 查询所有key
keys *
# 查看匹配na*的key,比如name
keys na*
# 是否存在key,存在返回1,不存在返回0
exists key名
# 设置剩余过期时间
expire key名 多少秒
# 删除key
del key名
# 查看过期剩余时间,-1永不过期,-2已过期,正整数表示秒数
ttl key名
# 设置永不过期
persist key名
# 选择数据库,默认是index为0的数据库中操作
select index
# 重命名key
rename 旧的key名 新的key名
# 设置过期时间单位为毫秒
PEXPIRE key milliseconds
# 以毫秒为单位返回剩余过期时间
PTTL key
# 将key移动到数据库index为x的库中
MOVE key dbx
# 返回key的数据类型
type key
一些限时优惠、验证码、数据缓存、限制访问频率等场景下,可以结合exists key
和expire key seconds
使用。
key的命名建议,不太太长或者杂乱,一般如下为key设置某种规律来命名:
set user:eric:1111 1
set user:tom:2222 2
(1)string
一个key对应的value最大可以达到512M,所以这个string类型的值可以存很多种类的数据,设置序列化的字符串都可以存在里面。
# 如果这个key不存在,则赋值,因为直接使用set的话如果存在该key,它的值会被覆盖
setnx key value
# 赋值并设置过期时间
setex key seconds value
# 修改key的值,比如原先name是eric,执行后变成etic
setrange name 1 t
# 获取值得一个范围子字符串也是同理
# 比如value是eric,start是1,end是2,那么结果是ri,包含start和end,如果是1,1,那么返回的就是r
getrange key start end
# 设置新值返回旧值,如果之前不存在,则返回nil
getset key value
# 长度
strlen key
# 批量写读
mset key1 value1 key2 value2 ...
mget key1 key2 ...
# 加1,如果一开始不存在key,值创建key并设置value为1,以后每次执行就为value+1
incr key
# 减1
decr key
# 以下自定义自增
incrby key 自增值
decyby key 自增值
# 把value追加在原来value值后面
append key value
string数据类型的使用场景包括:常用字符串或者json字符串;因为是二进制安全的,所以可以把图片的内容当做字符串存储;常见的计数器,比如关注数粉丝数等,因为redis的incr和decr操作具有原子性,不会发生现成不安全的问题。
(2)hash
操作命令和之前的string操作命令类似,大都是在命令之前加了一个h
字母,表示对hash的操作。
# 单个field
hset key field value
# 多个field
hmset key field1 value1 field2 value2 ...
# 查看所有
hgetall key
# 查看某个field值
hget key field
# 删除所有
del key
# 删除某个field
hdel key field
# 查看所有fields
hkeys key
# 查看字段数量
hlen key
# 赋值前检查是否存在
hsetnx key field value
# 自增
hincrby key field 自增量
# 自增浮点
hincrfloat key field 自增量
# 字段是否存在
hexists key field
一般用hash来存储对象。因为string存储对象没有什么优势,比如:
所以,hash目前看来是最适合存储对象的一种数据类型。
jedis是很早的一个对redis封装的客户端,供java开发时使用。主要的使用包括(使用Lombok的话,entity上加个注解@Data即可):
springboot2.0的话默认的redis客户端时Lettuce,所以我们要使用jedis的话不能选择默认的,所以需要自己增加一个依赖:
redis.clients
jedis
配置:
spring.redis.host=172.16.25.137
spring.redis.port=6379
spring.redis.password=123456
spring.redis.timeout=2000
spring.redis.jedis.pool.min-idle=2
spring.redis.jedis.pool.max-idle=6
spring.redis.jedis.pool.max-active=10
设置pool,创建一个JedisConfig:
@Configuration
public class JedisConfig {
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private int port;
@Value("${spring.redis.password}")
private String password;
@Value("${spring.redis.timeout}")
private int timeout;
@Value("${spring.redis.jedis.pool.max-active}")
private int maxActive;
@Value("${spring.redis.jedis.pool.min-idle}")
private int minIdle;
@Value("${spring.redis.jedis.pool.max-idle}")
private int maxIdle;
@Bean
public JedisPool getJedisPool(){
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxIdle(maxIdle);
jedisPoolConfig.setMinIdle(minIdle);
jedisPoolConfig.setMaxTotal(maxActive);
return new JedisPool(jedisPoolConfig, host, port, timeout, password);
}
}
然后就可以用这个Pool获取Jedis客户端来使用了,下面直接写在Controller中。可以看到,jedis中的方法基本上都是对应着redis的基础命令的。
@RestController
public class UserController {
@Autowired
JedisPool jedisPool;
@GetMapping("/user")
public String getUser(Integer id){
Jedis jedis = jedisPool.getResource();
String stringKey = "name:" + id;
String hashKey = "user:" + id;
StringBuilder res = new StringBuilder();
if (jedis.exists(stringKey) && jedis.exists(hashKey)){
res.append("从redis查询的数据:");
res.append(jedis.get(stringKey));
res.append(jedis.hget(hashKey, "name"));
}else{
// 模拟从数据库查询出这个user的全部信息
User user = new User();
user.setId(id);
user.setName("eric");
user.setAge(30);
// 设置string数据类型
jedis.set(stringKey, user.getName());
// 设置hash数据类型
Map map = new HashMap<>();
map.put("name", user.getName());
map.put("age", String.valueOf(user.getAge()));
map.put("id", String.valueOf(user.getId()));
jedis.hmset(hashKey, map);
res.append("从数据库查询的数据");
}
jedis.close();
return res.toString();
}
}
我们再浏览器中,访问localhost:8080/user?id=123
可以看到效果,并结合rdm客户端查看redis中是否有数据。
初始化项目时选择spring data redis或者增加如下依赖:
org.springframework.boot
spring-boot-starter-data-redis
配置:
spring.redis.host=172.16.25.138
spring.redis.port=6379
spring.redis.password=123456
spring.redis.lettuce.pool.max-active=10
spring.redis.lettuce.pool.min-idle=2
spring.redis.lettuce.pool.max-idle=6
spring.redis.lettuce.shutdown-timeout=200
自己编写一个User类,同上。然后使用默认的RedisTemplate,全部写在Controller测试一下:
@RestController
public class UserController {
@Autowired
RedisTemplate redisTemplate;
@GetMapping("/user")
public String getUser(Integer id){
String stringKey = "name:" + id;
String hashKey = "user:" + id;
StringBuilder res = new StringBuilder();
if (redisTemplate.hasKey(stringKey) && redisTemplate.hasKey(hashKey)){
res.append("从redis查询的数据:");
res.append(redisTemplate.opsForValue().get(stringKey));
res.append(redisTemplate.opsForHash().get(hashKey, "name"));
}else{
// 模拟从数据库查询出这个user的全部信息
User user = new User();
user.setId(id);
user.setName("eric");
user.setAge(30);
// 设置string数据类型
redisTemplate.opsForValue().set(stringKey, user.getName());
// 设置hash数据类型
Map map = new HashMap<>();
map.put("name", user.getName());
map.put("age", String.valueOf(user.getAge()));
map.put("id", String.valueOf(user.getId()));
redisTemplate.opsForHash().putAll(hashKey, map);
res.append("从数据库查询的数据");
}
return res.toString();
}
}
启动发现报错,因为spring data redis需要commons-pool2依赖。
org.apache.commons
commons-pool2
我们再浏览器中,访问localhost:8080/user?id=123
可以看到效果,并结合rdm客户端查看redis中是否有数据。
但这里发现一个问题,就是我们在代码中读取没有问题,但是在rdm中查看的时候发现有乱码,key和value都有乱码(在key的前面增加了\xac\xed\x00\x05t\x00\b一串字符),导致开发的时候不方便,在redis-cli查询时也会有问题。
可以先删除之前的所有的key,也就是删除当前数据库或者所有数据库:
# 删除当前数据库
flushdb
# 删除所有数据库
flushall
这个时候我们就需要自定义配置RedisTemplate,也就是不用默认的。创建一个RedisConfig类:
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate getRedisTemplate(LettuceConnectionFactory factory){
RedisTemplate redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(factory);
// 以下就是自定义的部分,主要是客户端查看时的乱码问题,替换了原先默认的序列化方式
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(stringRedisSerializer);
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.setHashKeySerializer(stringRedisSerializer);
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
然后一切就好了。当然,这里可以做其他自定义的配置,不仅仅是序列化的配置,其他的还包括缓存策略等。
RedisTemplate的操作方法和之前的Jedis明显不同,它封装了很多方法提供给我们,所以在具体使用时需要学习一下,我们也可以通过一些设置或者创建一些工具类,方便每次的调用。
# 以下命令只会添加1 2 3 4 6,因为是不可重复的
sadd scores 1 2 3 4 4 6
# 所以查询数量时返回5
scard scores
# 检查是否是集合中元素,以下返回0,如果是则返回1
sismember scores 5
# 返回集合中所有元素
smembers scores
# 随机返回几个元素,比如随机返回2个元素
srandmember scores 2
# 删除某个元素,比如2,如果2存在就返回1,如果2不存在返回0,可以删除多个
srem scores 2
# 把scores中元素5移动到number集合中,如果目标集合中已有了,那移动过去就自动合并了
smove scores number 5
# 返回减去nums2中元素的nums1剩余的元素,即在num1中不在num2中的元素
sdiff nums1 nums2
# 把差集结果存在nums3中
sdiffstore nums3 nums1 nums2
# 返回交集
sinter nums1 nums2
# 直接存储交集结果到新的集合
sinter nums4 nums1 nums2
# 下面是丙级和存储
sunion nums1 nums2
sunionstore nums5 nums1 nums2
使用场景可以结合集合的特点:集合之间可以做并集交集等操作,可以查询共同好友共同兴趣等;集合的唯一性,可以记录独立ip等信息。
# 添加。不同点是每个元素前有个分数,排序就是根据这个分数来的
zadd z1 99 eric 88 tom 77 jerry 66 jason
# 返回几个元素,4
zcard z1
# 返回最小最大之间个数,2
zcount z1 70 90
# 返回索引,是3.如果元素不在,则返回nil
# 内部默认从小打到排列,所以eric是最后一个,索引是3
zrank z1 eric
# 从小到大返回索引起止间的元素,0 -1表示全部
zrange z1 0 -1
# 降序排列,0 -1仍然是起止索引
zrevrange z1 0 -1
# 按照分数从小到大
zrangebyscore z1 70 90
# 按照分数降序
zrevrangebyscore z1 70 90
# 删除key
del key
# 删除元素
zrem key member
# 按照索引删除,zremrangebyrank z1 0 1 删除0和1的共2个
zremrangebyrank key start stop
# 按照分数删除,zremrangebyscore z1 80 90 删除分数在80到90之间的
zremrangebyscore key min max
# 给某一个member增加分数
zincrby key 增加量 menber
zset的应用场景可以是排行榜,获取其他可以使用权重排行的场景。
# 创建一个HyperLogLog
pfadd p1 1 2 2 3 3 3 4 4 4 4 5 5 5 5 5
# 它返回的是基数的个数,5
pfcount p1
# 合并
pfmerge destkey sourcekey1 sourcekey2 ...
应用场景可以统计每天独立ip等信息,但是HyperLogLog的缺陷是只能获得基数,不能获取具体的信息,所以可以配合其他数据结构一起使用。还可以统计文章的阅读数,比如存储的元素是日期+ip
的形式,那么同一天同一ip虽然pfadd增加到了里面,但是统计基数的时候只算1个。其实有点类似于set的用法。它和set的区别就是,如果数据量很大而且不需要知道详细信息,那么可以用HyperLogLog,如果需要知道详细信息则可以使用set。
# 订阅sms频道channel
subscribe sms
# 打开另一个终端,向这个频道发布信息,上面那个订阅的终端会显示收到这条信息
publish sms hello,world
# 还有批量订阅,根据pattern订阅以及退订
psubscribe pattern
unsubscribe sms
punsubscribe pattern
订阅发布功能在现代软件中使用的很多,但是redis的发布订阅功能被使用的较少,毕竟有其他各种消息队列(MQ)可以使用。
默认有16个数据库,下标索引从0开始。可以在配置文件中设置如下指定使用哪个数据库:
spring.redis.database=0
可以使用如下命令切换数据库:
# 切换数据库
select 1
# 移动某个key到哪个库,比如move name 15
move key databaseindex
多数据库可以提供给不同的应用微服务来使用。比如支付的、用户信息的等使用不同的数据库。也可以根据不同开发阶段使用不同的库,比如开发时用0,测试用1,生成用2等。
redis的事务是把所有命令序列化按顺序放在队列中,可以发现有QUEUED提示,直到遇到exec开始执行,或者遇到discard放弃取消事务和队列。不可以加塞,也不可以回滚。
在一个终端上模拟设置两个账户,模拟转账:
set account:a 100
set account:b 50
# 然后启动事务
multi
# a账户减少10元
decrby account:a 10
# b账户增加10元
incrby account:b 10
# 此时可以在另一个终端查询这两个key的数据发现没有变化
# 提交执行。discard是取消事务
exec
# 此时可以在另一个终端查询这两个key的数据发现有变化了
事务的报错:
(1)在终端中一般情况下multi开启事务后,输入的每一条指令redis都会做检查,如果被检查出来有问题,那么这个事务就需要重写,主要是检查命令;
(2)但是有些问题是我们程序的问题,不是命令的错误。比如给非数字的字符串加1这样的程序在事务中。那么事务执行的时候,只有这条语句不被执行,其他的语句还是正常执行,而且不回滚。比如下面的,我们新建了一个key是name,事务中的这句命令得到了执行。
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set name hello
QUEUED
127.0.0.1:6379> incr name
QUEUED
127.0.0.1:6379> exec
1) OK
2) (error) ERR value is not an integer or out of range
watch监视:
127.0.0.1:6379> set a 100
OK
# 监视a,如果a被其他线程变动了,那么此线程的事务就取消掉
127.0.0.1:6379> watch a
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incr a
QUEUED
# 在另一个终端上执行一个incr a之类的操作变动一下a
# 返回nil就是被取消了
127.0.0.1:6379> exec
(nil)
127.0.0.1:6379> lpush names eric tom jerry
(integer) 3
127.0.0.1:6379> lrange names 0 -1
1) "jerry"
2) "tom"
3) "eric"
127.0.0.1:6379> lpush names jason
(integer) 4
127.0.0.1:6379> lrange names 0 -1
1) "jason"
2) "jerry"
3) "tom"
4) "eric"
127.0.0.1:6379> rpush names hello
(integer) 5
127.0.0.1:6379> lrange names 0 -1
1) "jason"
2) "jerry"
3) "tom"
4) "eric"
5) "hello"
127.0.0.1:6379> lpop names
"jason"
127.0.0.1:6379> lrange names 0 -1
1) "jerry"
2) "tom"
3) "eric"
4) "hello"
127.0.0.1:6379> rpop names
"hello"
127.0.0.1:6379> lrange names 0 -1
1) "jerry"
2) "tom"
3) "eric"
redis集群不是简单的一主多从模式,而是无中心结构,也就是说所有的节点都是master节点,每个master节点对应一个或多个slave节点(这里的才是一主多从),相当于多个“一主多从”。至少需要3个master和3个slave才能建立集群,因为要投票半数以上,所以至少3个。这也就是说至少要部署6个独立的节点。
master节点之间相互连接,半数以上的master如果连不上该master,那么该master就被认为fail了。
数据在redis集群中的存储是按照hash分布在不同的节点中的,并不是所有的节点都是完整的数据。比如一共有16384个hash槽,如果有3个master节点,那么0-5500的在master1上,5500-11000在master2上以此类推。这样有一个key过来之后,hash过后根据值分配存储在对应的master上。
(1)准备好6个节点
在/usr/local
下mkdir一个redis_cluster
文件夹,名称随意,然后在下面新建6个文件夹,可以分别命名为7000到7005。把redis安装包中的conf文件拷贝到7000中,做好一些配置后,分别复制到7001-7005中去。主要修改如下(可参考官网):
# 以下是官网要求的必须
port 7000
cluster-enabled yes
cluster-config-file nodes-7000.conf
cluster-node-timeout 5000
appendonly yes
# 以下是其他一些配置
# 关闭保护模式可用于公网访问
protected-mode no
daemonize yes
pidfile /var/run/redis_7000.pid
logfile "7000.log"
# dir /redis/data
# bind 127.0.0.1
# 连接主节点的密码
masterauth 123456
# 各个节点保持一致
requirepass 123456
搜索在非编辑状态下使用/
。
复制到其他文件夹下的时候,可以非编辑状态下使用如下命令全部替换后保存。其中的冒号是要自己输入的。
:%s/7000/7001/g
分别启动这6个节点。启动redis的时候需要带上我们上面配置的6个文件。
./redis-server /usr/local/redis_cluster/7000/redis.conf
./redis-server /usr/local/redis_cluster/7001/redis.conf
./redis-server /usr/local/redis_cluster/7002/redis.conf
./redis-server /usr/local/redis_cluster/7003/redis.conf
./redis-server /usr/local/redis_cluster/7004/redis.conf
./redis-server /usr/local/redis_cluster/7005/redis.conf
可以通过ps -ef | grep redis
查看是否启动了6个redis进程。
(2)启动集群
./redis-cli --cluster create -a 123456 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 --cluster-replicas 1
可以看到因为我们启动集群的时候用了6个节点,所以自动分配成3主3从,并且3主的hash槽也分配好了。再输入yes
同意设置后就好了。
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 127.0.0.1:7004 to 127.0.0.1:7000
Adding replica 127.0.0.1:7005 to 127.0.0.1:7001
Adding replica 127.0.0.1:7003 to 127.0.0.1:7002
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: 191649ea7bd94e91e3978372ada3280a18e679bd 127.0.0.1:7000
slots:[0-5460] (5461 slots) master
M: a5f2c009a564725a582a6b912a0a7807460c5856 127.0.0.1:7001
slots:[5461-10922] (5462 slots) master
M: 395b0a62b241bf9e0a7376e31f0af77ebdaac84d 127.0.0.1:7002
slots:[10923-16383] (5461 slots) master
S: f585f1c5a500577b031b92a64cb010db9712f167 127.0.0.1:7003
replicates 191649ea7bd94e91e3978372ada3280a18e679bd
S: 64dd7f28856589d60537fdae2343ecbe69c7d0c4 127.0.0.1:7004
replicates a5f2c009a564725a582a6b912a0a7807460c5856
S: 65748dd6826ea267fe355ddf8345489c6ed497e7 127.0.0.1:7005
replicates 395b0a62b241bf9e0a7376e31f0af77ebdaac84d
(3)集群的验证
因为集群是去中心化的,意味着我们随便连接哪个节点都可以。连接命令有一些不同,多了一个-c
,表示连接的是集群:
# 因为在本机,所以省略了-h xxx.xxx.xxx.xxx
redis-cli -p 7000 -c -a 123456
输入以下命令,查看当前连接节点的信息,比如是否主从节点,对应的主从节点是哪个等:
127.0.0.1:7000> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=127.0.0.1,port=7003,state=online,offset=462,lag=0
master_replid:d3d306c18c918f09d7c40206bb69ede376ca9564
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:462
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:462
以下命令是查看整个集群的节点信息:
127.0.0.1:7000> cluster nodes
395b0a62b241bf9e0a7376e31f0af77ebdaac84d 127.0.0.1:7002@17002 master - 0 1581420073532 3 connected 10923-16383
64dd7f28856589d60537fdae2343ecbe69c7d0c4 127.0.0.1:7004@17004 slave a5f2c009a564725a582a6b912a0a7807460c5856 0 1581420073834 5 connected
191649ea7bd94e91e3978372ada3280a18e679bd 127.0.0.1:7000@17000 myself,master - 0 1581420072000 1 connected 0-5460
65748dd6826ea267fe355ddf8345489c6ed497e7 127.0.0.1:7005@17005 slave 395b0a62b241bf9e0a7376e31f0af77ebdaac84d 0 1581420073000 6 connected
f585f1c5a500577b031b92a64cb010db9712f167 127.0.0.1:7003@17003 slave 191649ea7bd94e91e3978372ada3280a18e679bd 0 1581420072529 4 connected
a5f2c009a564725a582a6b912a0a7807460c5856 127.0.0.1:7001@17001 master - 0 1581420073000 2 connected 5461-10922
myself表示当前登录的节点。
slave后面跟着的就是对应master的id,这样就可以知道主从关系。
数据读取的时候可以看到,它会切换到某一个slot上。如果set时正好hash计算后可以存在当前slot上那么久不会切换,如果需要存在其他slot上,它会先切换过去再存。读取数据也是一样的,如果在当前slot中直接返回,如果不在,则会切换slot后读取数据返回。keys *
只返回当前slot的keys数据。
主节点负责写数据,从节点负责读数据。
127.0.0.1:7000> set aa 123
OK
127.0.0.1:7000> get aa
"123"
127.0.0.1:7000> set bb 456
-> Redirected to slot [8620] located at 127.0.0.1:7001
OK
127.0.0.1:7001> get bb
"456"
127.0.0.1:7001> keys *
1) "bb"
127.0.0.1:7001> get aa
-> Redirected to slot [1180] located at 127.0.0.1:7000
"123"
如果有ABC三个主节点,分别对应A1、B1、C1三个从节点,如果B节点挂掉了,那么B1从节点会顶替B成为主节点,保证可用性,如果B开启后,B成为B1的从节点。但如果B没有启动而B1也挂掉了,那么整个集群就fail了不可用。
(4)关闭集群节点
就是关闭每个节点,但是如果节点多的话,可以写成一个shutdown.sh文件然后执行这个文件来执行关闭。
/usr/local/redis/bin/redis-cli -c -h 127.0.0.1 -p 7000 -a 123456 shutdown
/usr/local/redis/bin/redis-cli -c -h 127.0.0.1 -p 7001 -a 123456 shutdown
/usr/local/redis/bin/redis-cli -c -h 127.0.0.1 -p 7002 -a 123456 shutdown
/usr/local/redis/bin/redis-cli -c -h 127.0.0.1 -p 7003 -a 123456 shutdown
/usr/local/redis/bin/redis-cli -c -h 127.0.0.1 -p 7004 -a 123456 shutdown
/usr/local/redis/bin/redis-cli -c -h 127.0.0.1 -p 7005 -a 123456 shutdown
然后给这个文件执行权限
chmod u+x shutdown.sh
最后执行即可。
同理,开启这6个节点的命令也可以放在一个startup.sh文件中:
/usr/local/redis/bin/redis-server /usr/local/redis_cluster/7000/redis.conf
/usr/local/redis/bin/redis-server /usr/local/redis_cluster/7001/redis.conf
/usr/local/redis/bin/redis-server /usr/local/redis_cluster/7002/redis.conf
/usr/local/redis/bin/redis-server /usr/local/redis_cluster/7003/redis.conf
/usr/local/redis/bin/redis-server /usr/local/redis_cluster/7004/redis.conf
/usr/local/redis/bin/redis-server /usr/local/redis_cluster/7005/redis.conf
然后给这个文件执行权限
chmod u+x shutdown.sh
执行开启节点后,再使用我们上面提到的命令开启集群即可:
./redis-cli --cluster create -a 123456 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 --cluster-replicas 1