Redis安装配置、内存维护、数据持久化、远程与GUI、Docker安装、基本数据类型和命令、springboot与Jedis和Lettuce、事务、集群

1、安装和启动

使用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

2、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

3、内存维护策略

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

4、关闭redis数据是否持久化的问题

正常情况下,数据要持久化的话要满足我们看的那个redis配置文件中的规定时间内达到一定变更次数的规则要求,所以如果正常满足的话,那么就会正常持久化。

(1)如果遇到非正常关闭的话,比如断电,我们可以用杀掉进程的方式模拟:

ps -ef | grep redis
# 得到redis-server的PID后kill
kill -9 PID
# 然后再次启动redis-server后在客户端keys * 查看发现之前的数据没有持久化

(2)如果正常关闭的话,会持久化数据。正常关闭是在客户端输入:

shutdown
# 然后再次启动redis-server后在客户端keys * 查看发现之前的数据可以查询到,说明关闭之前做了持久化操作

5、远程连接

RedisDestopManager现在收费了。网上貌似可以搜索到老旧的版本,这些老旧的版本还继续可以使用。基础的使用应该是没有问题的。

这里远程连接的时候,需要开放一下centos的防火墙。

# 添加一个放行端口
firewall-cmd --zone=public --add-port=6379/tcp --permanent
# 重启防火墙
firewall-cmd --reload
# 查看开放的端口
firewall-cmd --list-ports

远程连接后,可以发现默认有16个库。

6、使用Docker部署redis

安装好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的话设置一下,如果之前已经设置过了,那么久不用设置了,直接在外部就可以远程连接了。

7、常见命令

# 设置一个键值对
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 keyexpire key seconds使用。

key的命名建议,不太太长或者杂乱,一般如下为key设置某种规律来命名:

set user:eric:1111 1
set user:tom:2222 2

8、基本数据类型

(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存储对象没有什么优势,比如:

  • 如果用json字符串存储对象的话,如果需要修改这个对象中某个属性的值,那么需要取出整个字符串然后反序列化,修改后再序列化成json字符串,开销太大
  • 如果把一个对象的多个属性分开用多个key-value存在redis中,那恐怕是疯了。

所以,hash目前看来是最适合存储对象的一种数据类型。

9、springboot+jedis

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中是否有数据。

10、springboot+lettuce

初始化项目时选择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明显不同,它封装了很多方法提供给我们,所以在具体使用时需要学习一下,我们也可以通过一些设置或者创建一些工具类,方便每次的调用。

11、Set数据类型

# 以下命令只会添加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等信息。

12、Zset数据类型

# 添加。不同点是每个元素前有个分数,排序就是根据这个分数来的
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的应用场景可以是排行榜,获取其他可以使用权重排行的场景。

13、HyperLogLog结构

# 创建一个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。

14、订阅发布功能

# 订阅sms频道channel
subscribe sms
# 打开另一个终端,向这个频道发布信息,上面那个订阅的终端会显示收到这条信息
publish sms hello,world
# 还有批量订阅,根据pattern订阅以及退订
psubscribe pattern
unsubscribe sms
punsubscribe pattern

订阅发布功能在现代软件中使用的很多,但是redis的发布订阅功能被使用的较少,毕竟有其他各种消息队列(MQ)可以使用。

15、多数据库

默认有16个数据库,下标索引从0开始。可以在配置文件中设置如下指定使用哪个数据库:

spring.redis.database=0

可以使用如下命令切换数据库:

# 切换数据库
select 1
# 移动某个key到哪个库,比如move name 15
move key databaseindex

多数据库可以提供给不同的应用微服务来使用。比如支付的、用户信息的等使用不同的数据库。也可以根据不同开发阶段使用不同的库,比如开发时用0,测试用1,生成用2等。

16、Redis事务

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)

17、list数据类型

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"

18、集群

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

你可能感兴趣的:(其他)