当前位置:网站首页 > 数据科学与大数据 > 正文

NoSQL数据库(三)02-Redis进阶与实战——EXPIRE命令设置过期时间-实现定期检测删除过期数据 & EXPIRE实现和优化访问服务器频率限制

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

分析思路:

  1. 根据用户ID新建一个字段 rate.limite:$IP, 存储的结果是访问量
  2. 用户每次访问的时候使用incr进行+1,并且EXPIRE设置一个过期时间
  3. 如果没过期则+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次的限制差距较大。尽管这种情况比较极端,但是在一些场合中还是需要粒度更小的控制方案。

修改思路:将散列结构改造为列表

  1. 新建一个 rate:limit:$ip的列表字段
  2. 每次访问向列表中存入当前时间戳
  3. 如果 列表长度小于10则存入
  4. 如果等于10,则讲列表的第一个时间戳和现在的时间对比。
    1. 如果小于60秒则拒绝访问
    2. 反之存入,并删除第一条时间
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实现和优化访问服务器频率限制的文章就介绍到这了,更多相关内容请继续浏览下面的相关推荐文章,希望大家都能在编程的领域有一番成就!

版权声明


相关文章:

  • NoSQL数据库(三)03-Redis进阶与实战——EXPIRE实现服务器缓存数据 & sort实现排序之对列表类型、有序集合和非数字类型进行排序 & Redis的底层通信协议对管道提供支持2024-11-30 17:00:09
  • NoSQL数据库(三)04-Redis进阶与实战——nodejs操作redis数据库之ioredis更新属于node_redis改良版 & ioredis的可视化工具安装、基本语法、管道与事务2024-11-30 17:00:09
  • NoSQL数据库(三)05-Redis进阶与实战——总结之事务-错误处理和watch、过期时间、sort排序、by排序 & noedjs操作redis数据库2024-11-30 17:00:09
  • NoSQL数据库(四)-memcached——介绍-分布式内存对象缓存系统、安装、api之set设置、add新增、replace替换、append追加2024-11-30 17:00:09
  • NoSQL数据库(五)01-mongoDB入门——介绍与应用场景、安装与可视化工具 & 使用node-mongodb-native进行增删改查2024-11-30 17:00:09
  • NoSQL数据库(三)01-Redis进阶与实战——redis事务命令与错误处理、事务中的watch命令2024-11-30 17:00:09
  • NoSQL数据库(二)06——redis总结之发展、特点、安装、操作 & 数据类型之字符串数据类型、散列、列表、集合、有序集合2024-11-30 17:00:09
  • NoSQL数据库(二)04-Redis数据类型——集合类型之介绍、命令-增加和删除元素、获得集合中的所有元素、判断元素是否在集合中、集合间运算2024-11-30 17:00:09
  • NoSQL数据库(二)03-Redis数据类型——列表类型之介绍、命令-向列表两端增加元素、从列表两端弹出元素、获取列表中元素的个数、删除列表中指定的值2024-11-30 17:00:09
  • NoSQL数据库(二)02-Redis数据类型——实践-散列类型命令之散列存储逻辑、获取id、修改缩略名2024-11-30 17:00:09
  • 全屏图片