NoSQL数据库(三)02-Redis进阶与实战——EXPIRE命令设置过期时间-实现定期检测删除过期数据 & EXPIRE实现和优化访问服务器频率限制
过期时间
在实际的开发中经常会遇到一些有时效的数据,比如限时优惠活动、缓存或验证码等,过了一定的时间就需要删除这些数据。在关系数据库中一般需要额外的一个字段记录到期时间,然后定期检测删除过期数据。而在Redis中可以使用 EXPIRE命令设置一个键的过期时间,到时间后Redis会自动删除它。
EXPIRE 命令的使用方法为 EXPIRE key seconds
,其中 seconds 参数表示键的过期时间,单位是秒。如要想让session:29e3d键在15分钟后被删除:
redis> SET session:29e3d uid1314 OK redis> EXPIRE session:29e3d 900 (integer) 1
EXPIRE命令返回1表示设置成功,返回0则表示键不存在或设置失败。例如:
redis> DEL session:29e3d (integer) 1 redis> EXPIRE session:29e3d 900 (integer) 0
如果想知道一个键还有多久的时间会被删除,可以使用TTL命令。返回值是键的剩余时间(单位是秒):
redis> SET foo bar OK redis> EXPIRE foo 20 (integer) 1 redis> TTL foo (integer) 15 redis> TTL foo (integer) 7 redis> TTL foo (integer) –2
可见随着时间的不同,foo键的过期时间逐渐减少,20秒后foo键会被删除。当键不存在时TTL命令会返回−2。 那么没有为键设置过期时间(即永久存在,这是建立一个键后的默认情况)的情况下会返回什么呢?答案是返回−1:
如果想取消键的过期时间设置(即将键恢复成永久的),则可以使用PERSIST
命令。如果过期时间被成功清除则返回1;否则返回0(因为键不存在或键本来就是永久的):
redis> SET foo bar OK redis> EXPIRE foo 20 (integer) 1 redis> PERSIST foo (integer) 1 redis> TTL foo (integer) –1
除了PERSIST命令之外,使用SET或GETSET命令为键赋值也会同时清除键的过期时间,例如:
redis> EXPIRE foo 20 (integer) 1 redis> SET foo bar OK redis> TTL foo (integer) –1
其他只对键值进行操作的命令(如INCR、LPUSH、HSET、ZREM)均不会影响键的过期时间。
EXPIRE命令的seconds参数必须是整数,所以最小单位是1秒。如果想要更精确的控制键的过期时间应该使用 PEXPIRE命令,PEXPIRE命令与 EXPIRE的唯一区别是前者的时间单位是毫秒,即 PEXPIRE key 1000 与 EXPIRE key 1 等价。对应地可以用 PTTL命令以毫秒为单位返回键的剩余时间。
提示 如果使用 WATCH命令监测了一个拥有过期时间的键,该键时间到期自动删除并不会被WATCH命令认为该键被改变。
另外还有两个相对不太常用的命令:EXPIREAT
和 PEXPIREAT
。
EXPIREAT命令与EXPIRE命令的差别在于前者使用Unix时间作为第二个参数表示键的过期时刻。PEXPIREAT命令与EXPIREAT命令的区别是前者的时间单位是毫秒。如:
redis> SET foo bar OK redis> EXPIREAT foo (integer) 1 redis> TTL foo (integer) 142 redis> PEXPIREAT foo 00 (integer) 1
ex: 实现访问频率限制
需求:为了减轻服务器的压力,需要限制每个用户(以IP计)一段时间的最大访问量。
实现: 通过EXPIRE
。
分析思路:
- 根据用户ID新建一个字段
rate.limite:$IP
, 存储的结果是访问量 - 用户每次访问的时候使用incr进行+1,并且EXPIRE设置一个过期时间
- 如果没过期则+1, 过期了则新建字段初始化访问次数0
var redis = require('redis'); var client = new redis({
// 配置 }); var isKeyExists = client.exists(`rate:limite:$ip`); if (isKeyExists) {
var time = client.incr('rate:limite:$ip'); if (time > 100) {
client.exit('你已经不能访问了,请休息下') } } else {
client.incr('rate:limite:$ip'); client.expire('rate:limite:$ip 60'); }
这段代码存在一个不太明显的问题:假如程序执行完倒数第二行后突然因为某种原因退出了,没能够为该键设置过期时间,那么该键会永久存在,导致使用对应的IP的用户在管理员手动删除该键前最多只能访问100次博客,这是一个很严重的问题。
修改思路:使用事务包裹else
如果一个用户在一分钟的第一秒访问了一次博客,在同一分钟的最后一秒访问了9次,又在下一分钟的第一秒访问了10次,这样的访问是可以通过现在的访问频率限制的,但实际上该用户在2秒内访问了19次博客,这与每个用户每分钟只能访问10次的限制差距较大。尽管这种情况比较极端,但是在一些场合中还是需要粒度更小的控制方案。
修改思路:将散列结构改造为列表
- 新建一个
rate:limit:$ip
的列表字段 - 每次访问向列表中存入当前时间戳
- 如果 列表长度小于10则存入
- 如果等于10,则讲列表的第一个时间戳和现在的时间对比。
- 如果小于60秒则拒绝访问
- 反之存入,并删除第一条时间
var redis = require('redis'); var client = new redis({
// 配置 }); var listLength = client.llen('rate:limit:$ip'); if (listLength < 10) {
client.lpush(`rate:limit:$ip ${
new Date().valueOf()}`); } else {
var time = client.lindex('rate:limit:$ip -1'); if(new Date().valueOf() - time < 60) {
client.exit('访问超过限制'); } else {
client.lpush(`rate:limit:$ip ${
new Date().valueOf()}`); client.ltrim(`rate:limit:$ip 0 9`); } }
实例1-expire实现访问频率限制
expire.js
var redis = require('redis'); var client = new redis({
// 配置 }); // 第一次访问,redis有你的字段吗? var isKeyExists = client.exists(`rate:limit:$ip`); if (isKeyExists) {
// 假设1分钟对多访问10次 var times = client.incr(`rate:limit:$ip`); if (times > 10) {
client.exit('你不能访问'); } } else {
// 没有访问过的情况 client.multi(); client.incr('rate:limit:$ip'); // 假如出现意外,则永远不会设置过期时间 client.expire('rate:limit:$ip 60'); client.exec(); }
实例2-expire实现访问频率限制优化
list_expire.js
var redis = require('redis'); var client = new redis({
// 配置 }); // 判断列表的长度, 代表你的访问次数 var listLength = clien.llen('rate:limit:$ip'); if (listLength < 10) {
clien.lpush(`rate:limit:$ip ${
new Date().valueOf()}`); // 存储时间戳 } else{
var time = clien.lindex(`rate:limit:$ip -1`); if (new Date().valueOf() - time < 60 ) {
// 当前时间 - 你第一访问的时间(rate:limit:$ip 列表里面最早的时间 ) < 60秒 clien.exit('不能访问'); } else {
clien.lpush(`rate:limit:$ip ${
new Date().valueOf()}`); clien.ltrim(`rate:limit:$ip 0 9`); } }
到此这篇NoSQL数据库(三)02-Redis进阶与实战——EXPIRE命令设置过期时间-实现定期检测删除过期数据 & EXPIRE实现和优化访问服务器频率限制的文章就介绍到这了,更多相关内容请继续浏览下面的相关推荐文章,希望大家都能在编程的领域有一番成就!
版权声明:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权、违法违规、事实不符,请将相关资料发送至xkadmin@xkablog.com进行投诉反馈,一经查实,立即处理!
转载请注明出处,原文链接:https://www.xkablog.com/sjkxydsj/10804.html