一、expire 生存时间

Redis中可以使用expire命令设置一个键的生存时间,到时间后Redis会自动删除它。

它的一个典型应用场景是:手机验证码

我们平时在登录或者注册的时候,手机会接收到一个验证码,上面会提示验证码的过期时间,过了这个时间之后这个验证码就不能用了。

expire支持以下操作

命令		格式					解释
expire		expire key seconds		设置key的过期时间(单位:秒)
ttl			ttl key					获取key的剩余有效时间
persist		persist key				取消key的过期时间
expireat	expireat key timestamp	设置UNIX时间戳的过期时间

1、设置key的过期时间

127.0.0.1:6379> set abc 123
OK
127.0.0.1:6379> expire abc 200
(integer) 1

2、获取key的剩余有效时间

127.0.0.1:6379> ttl abc
(integer) 192

3、取消key的过期时间

127.0.0.1:6379> persist abc
(integer) 1

此时再查看这个key的剩余有效时间,返回的值是-1,-1表示这个key是一个永久存在的key

127.0.0.1:6379> ttl abc
(integer) -1

4、还可以通过expireat指定key在指定时间点过期

先获取当前时间戳。

[root@bigdata04 ~]# date +%s
1768618628

127.0.0.1:6379> expireat abc 1768618638
(integer) 1

过一会再查看这个key的剩余有效时间,返回的是-2,表示这个key被删除了,不存在了。

127.0.0.1:6379> ttl abc
(integer) -2
127.0.0.1:6379> exists abc
(integer) 0

总结一下:

当key永久存在的时候,执行ttl返回的是-1,
当key被设置了过期时间之后,执行ttl返回的就是这个key剩余的有效时间
当key已经被删除了,不存在的时候,执行ttl返回的是-2

二、pipeline 管道

针对批量操作数据或者批量初始化数据的时候使用,效率高
Redis的pipeline功能在命令行中没有实现,在Java客户端(jedis)中是可以使用的

它的原理是这样的

redis setex expire单位 redis expired_keys_缓存


不使用管道的时候,我们每执行一条命令都需要和redis服务器交互一次

使用管道之后,可以实现一次提交一批命令,这一批命令只需要和redis服务器交互一次,所以就提高了性能。

这个功能就类似于mysql中的batch批处理。

接下来看一个案例
案例:初始化10万条数据
需求:使用普通方式一条一条添加和使用管道批量初始化进行对比分析

代码如下:

package com.imooc.redis;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.Pipeline;

/**
 * pipeline(管道)的使用
 * 
 */
public class PipelineOp {
    public static void main(String[] args) {
        //1:不使用管道
        Jedis jedis = RedisUtils.getJedis();
        long start_time = System.currentTimeMillis();
        for(int i=0;i<100000;i++){
            jedis.set("a"+i,"a"+i);
        }
        long end_time = System.currentTimeMillis();
        System.out.println("不使用管道,耗时:"+(end_time-start_time));

        //2:使用管道
        Pipeline pipelined = jedis.pipelined();
        start_time = System.currentTimeMillis();
        for(int i=0;i<100000;i++){
            pipelined.set("b"+i,"b"+i);
        }
        pipelined.sync();
        end_time = System.currentTimeMillis();
        System.out.println("使用管道,耗时:"+(end_time-start_time));

        RedisUtils.returnResource(jedis);
    }

}

结果如下:

不使用管道,耗时:40887
使用管道,耗时:180

在代码执行的过程中,我们可以使用info命令观察数据库中的数据条数

127.0.0.1:6379> info
# Keyspace
db0:keys=200000,expires=1,avg_ttl=389945

从这可以看出来,针对海量数据的初始化,管道可以显著提高初始化性能。

三、info命令

这里面参数比较多
在这我们主要关注几个重点的参数

# Redis 服务器版本
redis_version:5.0.9
# Redis服务的可执行文件路径
executable:/data/soft/redis-5.0.9/redis-server
# 启动Redis时使用的配置文件路径
config_file:/data/soft/redis-5.0.9/redis.conf
# 已连接客户端的数量
connected_clients:1
# Redis目前存储数据使用的内容
used_memory_human:15.01M
# Redis可以使用的内存总量,和服务器的内存有关
total_system_memory_human:1.78G
# db0表示0号数据库,keys:表示0号数据库的key总量,expires:表示0号数据库失效被删除的key总量
db0:keys=200001,expires=1,avg_ttl=389945

四、Redis的持久化

Redis持久化简单理解就是把内存中的数据持久化到磁盘中 可以保证Reids重启之后还能恢复之前的数据。
Redis支持两种持久化,可以 单独使用 或者 组合使用。

RDB 和 AOF。
RDB是Redis默认的持久化机制。

1、Redis持久化之RDB

RDB持久化是通过快照完成的,当符合一定条件时Redis会自动将内存中的所有数据执行快照操作并存储到硬盘上,默认存储在dump.rdb文件中。

[root@bigdata04 redis-5.0.9]# ll
....
-rw-r--r--.  1 root root 2955661 Jan 17 12:12 dump.rdb
......

Redis什么时候会执行快照?
Redis执行快照的时机是由以下参数控制的,这些参数是在redis.conf文件中的。

save 900 1
save 300 10
save 60 10000

save 900 1 表示900秒内至少一个key被更改则进行快照
这里面的三个时机哪个先满足都会执行快照操作。

RDB的优点:由于存储的有数据快照文件,恢复数据很方便
RDB的缺点:会丢失最后一次快照以后更改的所有数据,因为两次快照之间是由一个时间差的,这一段时间之内修改的数据可能会丢。

2、Redis持久化之AOF

AOF持久化是通过日志文件的方式,默认情况下没有开启,可以通过appendonly参数开启。

[root@bigdata04 redis-5.0.9]# vi redis.conf 
....
appendonly yes
....

AOF日志文件的保存位置和RDB文件相同,都是dir参数设置的,默认的文件名是appendonly.aof

注意:dir参数的值为. 表示当前目录,也就是说我们在哪个目录下启动redis,rdb快照文件和aof日志文件就产生在哪个目录下。

可以试验一下,换一个目录启动redis,发下redis中的数据是空的。关闭之后,重新在之前的目录启动redis,数据又回来了。

AOF方式只会记录用户的写命令,添加、修改、删除之类的命令,查询命令不会记录,因为查询命令不会影响数据的内容。

那redis什么时候会把用户的写命令同步到aof文件中呢?

# appendfsync always
appendfsync everysec
# appendfsync no

默认是每秒钟执行一次同步操作。appendfsync everysec
也可以实现每执行一次写操作就执行一次同步操作,appendfsync always,但是这样效率会有点低。
或者使用appendfsync no,表示不主动进行同步,由操作系统来做,30秒执行一次。

如果大家对数据的丢失确实是0容忍的话,可以使用always。
不过一般情况下,redis中存储的都是一些缓存数据,就算丢了也没关系,程序还会继续往里面写新数据,不会造成多大影响。

五、Redis 的安全策略

1、设置数据库密码

默认情况下访问redis只要网络能通就可以直接访问,这样其实是有一些不安全的,不过我们一般会限制只能在内网访问,这样其实问题也不大。

redis针对这个问题,也支持给数据库设置密码,在redis.conf中配置。

[root@bigdata04 redis-5.0.9]# vi redis.conf 
....
requirepass admin
....

重启redis服务

[root@bigdata04 redis-5.0.9]# redis-cli shutdown
[root@bigdata04 redis-5.0.9]# redis-server redis.conf

重新连接redis

[root@bigdata04 redis-5.0.9]# redis-cli 
127.0.0.1:6379> get imooc
(error) NOAUTH Authentication required.
127.0.0.1:6379> auth admin
OK
127.0.0.1:6379>get imooc
hello bigdata!

在代码层面,以后在使用的时候时候就需要使用auth方法先校验权限了。

package com.imooc.redis;

import redis.clients.jedis.Jedis;

/**
 * 单连接方式操作redis
 * 
 */
public class RedisSingle {
    /**
     * 注意:此代码能够正常执行的前提是
     * 1:redis所在服务器的防火墙需要关闭
     * 2:redis.conf中的bind参数需要指定192.168.18.100
     * @param args
     */
    public static void main(String[] args) {
        //获取jedis连接
        Jedis jedis = new Jedis("192.168.18.100",6379);
        //使用密码
        jedis.auth("admin");
        //向redis中添加数据,key=imooc,value=hello bigdata!
        jedis.set("imooc","hello bigdata!");
        //从redis中查询key=imooc的value的值
        String value = jedis.get("imooc");

        System.out.println(value);

        //关闭jedis连接
        jedis.close();

    }
}

注意:在实际工作中一般不会设置密码,因为我们在这设置的密码是明文的,其实意义也不大,针对别有用心的人,你这样设置是没有意义的。

所以在实际工作中我们一般只需要控制好redis服务器的访问权限就可以了,redis服务器的访问权限其实就是使用bind参数来设置的。

所以再把刚才设置的密码取消掉,直接把对应的配置注释掉即可。

[root@bigdata04 redis-5.0.9]# vi redis.conf 
#requirepass admin

2、bind参数的应用

在实际工作中,我们的服务器至少会有3个ip地址

1、127.0.0.1 这个是本机回环地址
2、192.168.182.103 这个是本机的内网地址
3、还有一个是外网地址

我们一般会使用bind绑定内网ip,这样其实就限制了redis服务器的访问范围,不会暴露在外网,只需要运维同学做好网络的访问限制就可以了,此时我们就可以认为redis是安全的了。

3、命令重命名

咱们前面讲过一个命令是flushall,这个命令是很危险的,它可以把redis中的所有数据全部清空
所以在实际工作中一般需要将这个命令给禁用掉,防止误操作。

在redis.conf配置文件中进行设置

[root@bigdata04 redis-5.0.9]# vi redis.conf 
....
rename-command flushall ""
....

这样修改之后,就把flushall命令给禁用掉了。
重启redis服务。

[root@bigdata04 redis-5.0.9]# redis-cli shutdown
[root@bigdata04 redis-5.0.9]# redis-server redis.conf

重新连接redis

[root@bigdata04 redis-5.0.9]# redis-cli 
127.0.0.1:6379> flushall
(error) ERR unknown command `flushall`, with args beginning with: 
127.0.0.1:6379>

此时会提示未知命令。

其实我们还可以选择,在重命名的时候给这个命令起一个别名,这样后期如果想使用的时候也是可以使用的。
我们现在在后面直接指定的是空字符串 就是直接禁用了,如果指定一个其它字符串,就相当于起别名了。

六、一个Redis实例最多能存放多少key?

一个Redis实例最多能存放多少key?
有没有限制?

Redis本身是不会限制存储多少key的,但是Redis是基于内存的,它的存储极限是系统中的可用内存值,如果内存存满了,那就无法再存储key了。

七、Redis监控命令-monitor

这个monitor命令是一把双刃剑。
在实际工作中要慎用。
先演示一下:

[root@bigdata04 redis-5.0.9]# redis-cli 
127.0.0.1:6379> monitor
OK

执行代码RedisSingle.java中的代码

然后会发现monitor监控到我们对redis的所有操作

[root@bigdata04 redis-5.0.9]# redis-cli 
127.0.0.1:6379> monitor
OK
1768628815.007443 [0 192.168.182.1:60633] "SET" "imooc" "hello bigdata!"
1768628815.007797 [0 192.168.182.1:60633] "GET" "imooc"

monitor可以监控我们对redis的所有操作,如果在线上的服务器上打开了这个功能,这里面就会频繁打印出来我们对redis数据库的所有操作,这样会影响redis的性能,所以说要慎用。

但是在某些特殊的场景下面它是很有用的。

之前在工作中我遇到过一个很奇怪的问题,redis中的一个key总是会莫名其妙的消失
我的一个程序会定时向redis中写入一个key,但是我发现这个key刚写进去,然后一会就没了,很奇怪,当时我仔细排查了我的代码,里面既没有设置失效时间,也没有使用删除功能。

所以这个key不是我的代码删的,肯定是有其它的代码会删除这个key,但是到底是哪的代码?
这个时候就不好排查了,我们的业务机有几十台,根本无从下手。

这个时候我突然想到了monitor这个命令,虽然开启monitor会影响redis的性能,但是这个时候需要排查问题,使用一会也是可以接受的。
所以就打开了monitor,打开之后屏幕上就打印出来很多命令,这样根本就看不清,没办法追踪,数据太多了。
所以又想到了这个办法,结合grep命令来操作,这样就可以过滤出来对指定key的所有操作了。

[root@bigdata04 redis-5.0.9]# redis-cli monitor | grep key
1768629282.484868 [0 192.168.182.1:52364] "del" "key"

通过这条数据我们可以分析出来到底是哪台机器上的程序删除了这个key。然后再排查这台机器上都有哪些程序,对应的去排查代码,这样就快多了,最终发现是有一个代码里面会定时删除这个key。

这个就是monitor的典型应用。