- expire key seconds
- setex(String key, int seconds, String value)
//比如向channel.1发一条消息’hello’: PUBLISH channel.1 “hello”订阅者订阅消息的命令是 SUBSCRIBE,用法: SUBSCRIBE channel [channel … ];
//比如订阅channel.1的频道: SUBSCRIBE channel.1 //执行SUBSCRIBE命令后客户端会进入订阅状态说明: PUBLISH命令的返回值表示接收到这条消息的订阅者数量。 如果发布命令时还没有订阅者订阅该频道,则返回0。另外值得注意的是 消息发送出去不会持久化,如果发送之前没有订阅者,那后续再有订阅者订阅该频道,之前的消息就收不到了; 结构图:
- RDB
1.根据配置规则进行自动快照;(自动快照采用的是异步快照操作) Redis允许用户自定义快照条件,当符合条件时自动执行快照操作。快照的条件可以由用户在配置文件中配置。配置格式如下: save [seconds] [keynum] 说明:第一个参数是时间,第二个是键个数,也就是说,在第一个时间参数配置范围内被更改的键的个数大于后面的keynum时,即符合快照条件。redis默认配置了三个规则 save 900 1 save 300 10 save 60 10000 每条快照规则占一行,每条规则之间是“或”的关系。 在900秒(15分)内有一个以上的键被更改则进行快照。 2.用户执行SAVE或者BGSAVE命令; 说明:除了让Redis自动进行快照以外,当我们对服务进行重启或者服务器迁移我们需要人工去干预备份。redis提供了两条命令来完成这个任务。通过LASTSAVE命令可以获取最近一次成功执行快照的时间; (1). save命令 当执行save命令时,Redis同步做快照操作,在快照执行过程中会阻塞所有来自客户端的请求。当redis内存中的数据较多时,通过该命令将导致Redis较长时间的不响应。所以不建议在生产环境上使用这个命令,而是推荐使用bgsave命令。 (2). bgsave命令 bgsave命令可以在后台异步地进行快照操作,快照的同时服务器还可以继续响应来自客户端的请求。执行BGSAVE后,Redis会立即返回ok表示开始执行快照操作。 3.执行FLUSHALL命令; 该命令会清除redis在内存中的所有数据。执行该命令后,只要redis中配置的快照规则不为空,也就是save的规则存在。redis就会执行一次快照操作。不管规则是什么样的都会执行。如果没有定义快照规则,就不会执行快照操作。 4.执行复制(replication)时; 该操作主要是在主从模式下,redis会在复制初始化时进行自动快照。只需要了解当执行复制操作时,即使没有定义自动快照规则,并且没有手动执行过快照操作,它仍然会生成RDB快照文件。
- AOF(append only file)
开启AOF Redis默认没有开启AOF方式的持久化,可以通过appendonly参数启用,在redis.conf中找到"appendonly yes”。开启AOF持久化后每执行一条会更改Redis中的数据的命令后,Redis就会将该命令写入硬盘中的AOF文件。AOF文件的保存位置和RDB文件的位置相同,都是通过dir参数设置的,默认的文件名是apendonly.aof;可以在redis.conf中的属性"appendfilename appendonly.aof”修改; AOF的重写 AOF文件以纯文本的形式记录Redis执行的写命令。 例如开启AOF持久化的情况下执行如下4条命令: set foo 1 set foo 2 set foo 3 get foo Redis会将前3条命令写入AOF文件中,通过vim可以看到aof文件中的内容。会发现AOF文件的内容正是Redis发送原始通信的内容,从内容中我们发现Redis只记录了3条命令。查询命令是不会写入AOF文件的。有个问题是前两条命令其实是冗余的,因为这两条命令的结果会被第三条命令覆盖。随着执行的命令越来越多,AOF文件会越来越大,但内存中实际有效的数据可能并不大,这就会造成磁盘空间浪费以及Redis数据还原时间较长。因此我们希望Redis可以自动优化AOF文件。实际上Redis也考虑到了这个问题。可以配置一个条件,每当达到一定条件时Redis就会重写AOF文件。 条件配置为: auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb 说明:auto-aof-rewrite-percentage 表示当前AOF文件大小超过上一次重写时的AOF文件大小的百分之百时会再次进行重写,如果之前没有重写过,则以启动时AOF文件大小为依据。 auto-aof-rewrite-min-size 表示允许重写的最小AOF文件大小,通常在AOF文件很小的情况下即使有很多冗余的命令我们也并不太关心。 另外,还可以通过BGREWRITEAOF命令手动执行重写AOF,执行完以后冗余的命令就会被删除。在启动时,Redis会逐个执行AOF文件中的命令来将硬盘中的数据载入到内存中,载入的速度相对于RDB会慢一些。 AOF重写原理 Redis可以在AOF文件过大时,自动在后台对AOF文件进行重写。重写后的AOF文件包含了恢复当前数据所需的最小命令集合; 重写流程:主进程会fork一个子进程来进行AOF重写,这个重写并不是基于原有的AOF文件来进行的,而是有点类似快照的方式,全量遍历内存中的数据,然后逐个序列到AOF文件中。在fork子进程重写的过程中,服务端仍然可以对外提供服务。在重写的过程中,主进程的数据更新操作会缓存在aof_rewrite_buf中,也就是单独开辟一块缓存来存储重写期间收到的命令,当子进程重写完再把缓存中的数据追加到新的AOF文件中。当所有的数据全部追加到新的AOF文件后,把新的AOF文件重命名,此后所有写操作都会被写入新的AOF文件中。如果重写过程中出现故障,不回影响原有的AOF文件,只有当rewrite完成后才会切换文件,因此这个rewrite过程还是比较可靠的。Redis内存回收策略
noeviction:默认策略,当内存使用达到阈值的时候,所有引起申请内存的命令会报错; allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰; 适合的场景:如果我们的应用对缓存的访问都是热点数据,可以使用这个策略; allkeys-random:随机移除某个key; 适合的场景:如果我们的应用对缓存的key的访问概率相等,可以使用这个策略; volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰; volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰; volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰; 适合的场景:这种策略使得我们可以向Redis提示哪些key更适合淘汰,我们可以自己控制。总结:实际上Redis实现的LRU并不是可靠的LRU,名义上使用LRU算法淘汰内存数据,但实际上被淘汰的键并不一定是真正最少使用的数据。这里涉及到一个权衡的问题,如果在所有数据中筛选最符合条件的数据,那么一定会增加系统的开销,Redis是单线程的,所以耗时的操作会谨慎一些。为了在一定成本内实现相对的LRU,早期Redis版本基于采样的LRU。Redis3.0之后,基于采样的LRU做了一些优化,目的是在一定成本内让结果更靠近真实LRU。 在Redis中使用Lua脚本 使用Redis时会面临的一些问题:
原子性问题 Redis虽然是单线程的,但是仍然会存在线程安全问题。当然,这个线程安全问题并不是来源于Redis服务器内部。而是Redis作为数据服务器,是提供给多个客户端使用的。多个客户端的操作就相当于同一进程下的多个线程。如果多个客户端之间没有做好数据的同步策略,就会产生数据不一致问题。举个例子,多个客户端的命令之间没有做请求同步,导致实际的执行顺序可能会不一致,最终无法满足原子性。 效率问题 Redis本身吞吐量是非常高的,在实际使用过程中,有一个非常重要的因素影响Redis的吞吐量,那就是网络。我们在使用Redis实现某些特定功能时,很可能需要多个命令或者多个数据类型的交互才能完成。那么这种多次网络请求对性能的影响比较大。虽然Redis做了一些优化,比如提供了pipeline管道操作,但它有一定的局限性,就是执行的多个命令和相应之间是不存在相互依赖关系的。所以我们需要一种机制能够编写一些具有业务逻辑的命令来减少网络请求。Redis内嵌了对Lua环境的支持,允许开发者使用Lua语言编写脚本传到Redis中执行。 Redis客户端可以使用Lua脚本直接在服务端 原子 的执行多个Redis命令。 使用脚本的好处:
1.减少网络开销:在Lua脚本中可以把多个命令放到同一个脚本中运行; 2.原子操作:Redis会将整个脚本作为一个整体执行,中间不回被其他命令插入; 3.复用性:客户端发送的脚本会存储在Redis中,意味着其他客户端可以复用这一脚本来完成同样的逻辑; 扩展:Lua是一个高效的轻量级脚本语言,用标准C语言编写并以源代码形式开放,其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能;在Lua脚本中调用Redis命令
在Lua脚本中调用Redis命令,可以使用redis.call函数调用。比如我们调用string类型的命令: redis.call('set’,'name','John') local value = redis.call('get’,'name')redis.call函数的返回值就是redis命令的执行结果,redis.call函数会将redis的5种类型的返回值转化为对应的Lua数据类型。在很多情况下我们需要脚本可以有返回值, redis会自动将脚本返回值的Lua数据类型转换为Redis的返回值类型。在脚本中可以使用return语句将值返回给客户端,如果没有执行return,默认返回为nil。 客户端执行lua脚本:
EVAL命令格式:[EVAL][脚本内容][key参数数量][key…][arg…] 通过key和arg两个参数向脚本中传递参数,它们的值可以在脚本中分别使用KEYS和ARGV这两个类型的全局变量访问。比如我们在Redis客户端中调用脚本实现一个set命令: lua脚本内容:return redis.call('set',KEYS[1],ARGV[1]) //KEYS和ARGV必须大写 eval “return redis.call(‘set',KEYS[1],ARGV[1])” 1 name John 注意:EVAL命令根据key的数量(例子中的参数’1’)来将后面所有参数分别存入脚本中KEYS和ARGV两个表类型的全局变量,当脚本不需要参数时也不能省略[key参数数量]这个参数,如果没有参数则为0。如:eval “return redis.call(‘get','name')” 0 EVALSHA命令格式:[EVALSHA][脚本SHA1摘要][key参数数量][key…][arg…] 通过eval执行lua脚本,在脚本较长的情况下,每次调用脚本都要把脚本传给Redis服务端,比较占用带宽。为了解决这个问题,Redis提供了EVALSHA命令允许开发者通过脚本内容的SHA1摘要来执行脚本。该命令用法和EVAL一样,只不过是将脚本内容替换成脚本内容的SHA1摘要。 1.Redis在执行EVAL命令时会计算脚本的SHA1摘要并将脚本记录在脚本缓存中。 2.执行EVALSHA命令时Redis会根据提供的摘要从脚本缓存中查找对应的脚本内容,如果找到了就执行,否则返回“NOSCRIPT No matching script,Please use EVAL”。通过以下案例来演示EVALSHA命令效果:
script load “return redis.call(‘get’,’name’)” //将脚本加入缓存并生成SHA1摘要; evalsha “a5a402e90df3eaeca2ff03d56d99982e05cf6574” 0 //在Redis客户端根据脚本摘要调用缓存的脚本;经验:我们在调用eval命令前,先执行evalsha命令,如果提示脚本不存在,则再调用eval命令。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。