redis incr与expire的配合使用 您所在的位置:网站首页 incr命令 redis incr与expire的配合使用

redis incr与expire的配合使用

2024-01-30 19:31| 来源: 网络整理| 查看: 265

「这是我参与11月更文挑战的第21天,活动详情查看:2021最后一次更文挑战」。

背景

对于失败的任务,需要定期重试或者重新处理,而有些场景重试后也会一直失败,如果不加以限制,就可能一直重试下去,频繁的更新数据库状态,处理任务,对数据库和服务器都是一种负担。因此,一般需要加最大重试次数的限制。

解决方案

这里使用golang版本的go-redis/v8进行redis命令操作。下面的代码通过redis的TxPipeline,为Incr加一、Expire设置过期时间添加事务操作,保证操作的事务安全。

var ( maxRetryCount = 3 redisTimeOut = 3 * time.Second ) func setRedisKey(key string, retryTimes int) { ctx, cFun := context.WithTimeout(context.Background(), redisTimeOut) defer cFun() redisClient := redisx.NewRedisClient() / redis 事务操作,保证原子性 pipelineRedis := redisClient.TxPipeline() // 键不存在,incr命令 先将键值初始化为0,再加1 retryCount := pipelineRedis.Incr(ctx, key).Val() if retryCount == 1 { // 当第1次写入键,键不存在,incr更新键值, 并设置过期时间 pipelineRedis.Expire(ctx, key, failedTaskRetryCntRedisKeyTTL) } else if retryCount > maxRetryCount { logrus.Errorf("current retry times=%d over maxRetryTimes", retryTimes) // 超过最大重试次数删除键(过期时间也被重置) pipelineRedis.Del(ctx, key) } _, err := pipelineRedis.Exec(ctx) if err != nil { logrus.Errorf("redis pipeline execute failed, error msg:%s", err.Error()) } }

Redis是单线程的,因此单个命令始终是原子的,但是来自不同客户端的两个给定命令可以依次执行,例如在它们之间交替执行。但是,Multi/exec能够确保在multi/exec两个语句之间的命令之间没有其他客户端正在执行命令。

在这种场景我们需要使用TxPipeline。TxPipeline总体上类似于Pipeline,但是它内部会使用MULTI/EXEC包裹排队的命令。

redis操作

Redis 中的每个Key都可以显示地设置一个过期时间,当达到过期时间的时候,这个key就会被自动删除。如果没有为Key设置过期时间,默认该Key没有过期时间,即在redis服务器内存足够时该Key将一直存在,永远不会过期。

key操作命令 获取所有键

语法:keys pattern

127.0.0.1:6379> keys * 1) "javastack" *表示通配符,表示任意字符,会遍历所有键显示所有的键列表,时间复杂度O(n),在生产环境不建议使用。 获取键总数

语法:dbsize

127.0.0.1:6379> dbsize (integer) 6

获取键总数时不会遍历所有的键,直接获取内部变量,时间复杂度O(1)。

查询键是否存在

语法:exists key [key ...]

127.0.0.1:6379> exists javastack java (integer) 2

查询查询多个,返回存在的个数。

删除键

语法:del key [key ...]

127.0.0.1:6379> del java javastack (integer) 1

可以删除多个,返回删除成功的个数。

查询键类型

语法: type key

127.0.0.1:6379> type javastack string 查询key的生命周期(秒)

秒语法:ttl key

毫秒语法:pttl key

127.0.0.1:6379[2]> ttl javastack (integer) -1

-1:永远不过期。

设置过期时间

秒语法:expire key seconds

毫秒语法:pexpire key milliseconds

127.0.0.1:6379[2]> expire javastack 60 (integer) 1 127.0.0.1:6379[2]> ttl javastack (integer) 55 设置永不过期

语法:persist key

127.0.0.1:6379[2]> persist javastack (integer) 1 更改键名称

语法:rename key newkey

127.0.0.1:6379[2]> rename javastack javastack123 OK 字符串操作命令

字符串是Redis中最基本的数据类型,单个数据能存储的最大空间是512M。

存放键值

语法:set key value [EX seconds] [PX milliseconds] [NX|XX]

nx:如果key不存在则建立,xx:如果key存在则修改其值,也可以直接使用setnx/setex命令。

127.0.0.1:6379> set javastack 666 OK 获取键值

语法:get key

127.0.0.1:6379[2]> get javastack "666" 值递增/递减

如果字符串中的值是数字类型的,可以使用incr命令每次递增,不是数字类型则报错。

语法:incr key

127.0.0.1:6379[2]> incr javastack (integer) 667

一次想递增N用incrby命令,如果是浮点型数据可以用incrbyfloat命令递增。

同样,递减使用decr、decrby命令。

批量存放键值

语法:mset key value [key value ...]

127.0.0.1:6379[2]> mset java1 1 java2 2 java3 3 OK 获取获取键值

语法:mget key [key ...]

127.0.0.1:6379[2]> mget java1 java2 1) "1" 2) "2"

Redis接收的是UTF-8的编码,如果是中文一个汉字将占3位返回。

获取值长度

语法:strlen key

127.0.0.1:6379[2]> strlen javastack (integer) 3

追加内容

语法:append key value

127.0.0.1:6379[2]> append javastack hi (integer) 5

向键值尾部添加,如上命令执行后由666变成666hi

获取部分字符

语法:getrange key start end

> 127.0.0.1:6379[2]> getrange javastack 0 4 "javas" redis常用过期时间操作

redis操作虽然简单,当时并并发操作时稍不留意也会引发一些问题,比如对于key过期时间的设置也许多加小心,因为有些操作是会把原来的key及过期时间清除的。

DEL/SET/GETSET等命令会清除过期时间

在使用DEL、SET、GETSET等会覆盖key对应value的命令操作一个设置了过期时间的key的时候,会导致对应的key的过期时间被清除。

//设置mykey的过期时间为300s 127.0.0.1:6379> set mykey hello ex 300 OK //查看过期时间 127.0.0.1:6379> ttl mykey (integer) 294 //使用set命令覆盖mykey的内容 127.0.0.1:6379> set mykey olleh OK //过期时间被清除 127.0.0.1:6379> ttl mykey (integer) -1 INCR/LPUSH/HSET等命令则不会清除过期时间

而在使用INCR/LPUSH/HSET这种只是修改一个key的value,而不是覆盖整个value的命令,则不会清除key的过期时间。 INCR:

//设置incr_key的过期时间为300s 127.0.0.1:6379> set incr_key 1 ex 300 OK 127.0.0.1:6379> ttl incr_key (integer) 291 //进行自增操作 127.0.0.1:6379> incr incr_key (integer) 2 127.0.0.1:6379> get incr_key "2" //查询过期时间,发现过期时间没有被清除 127.0.0.1:6379> ttl incr_key (integer) 277

LPUSH:

//新增一个list类型的key,并添加一个为1的值 127.0.0.1:6379> LPUSH list 1 (integer) 1 //为list设置300s的过期时间 127.0.0.1:6379> expire list 300 (integer) 1 //查看过期时间 127.0.0.1:6379> ttl list (integer) 292 //往list里面添加值2 127.0.0.1:6379> lpush list 2 (integer) 2 //查看list的所有值 127.0.0.1:6379> lrange list 0 1 1) "2" 2) "1" //能看到往list里面添加值并没有使过期时间清除 127.0.0.1:6379> ttl list (integer) 252 复制代码 PERSIST命令会清除过期时间

当使用PERSIST命令将一个设置了过期时间的key转变成一个持久化的key的时候,也会清除过期时间。

127.0.0.1:6379> set persist_key haha ex 300 OK 127.0.0.1:6379> ttl persist_key (integer) 296 //将key变为持久化的 127.0.0.1:6379> persist persist_key (integer) 1 //过期时间被清除 127.0.0.1:6379> ttl persist_key (integer) -1 复制代码 RENAME继承过期时间

使用RENAME命令,老key的过期时间将会转到新key上

在使用例如:RENAME KEY_A KEY_B命令将KEY_A重命名为KEY_B,不管KEY_B有没有设置过期时间,新的key KEY_B将会继承KEY_A的所有特性。

//设置key_a的过期时间为300s 127.0.0.1:6379> set key_a value_a ex 300 OK //设置key_b的过期时间为600s 127.0.0.1:6379> set key_b value_b ex 600 OK 127.0.0.1:6379> ttl key_a (integer) 279 127.0.0.1:6379> ttl key_b (integer) 591 //将key_a重命名为key_b 127.0.0.1:6379> rename key_a key_b OK //新的key_b继承了key_a的过期时间 127.0.0.1:6379> ttl key_b (integer) 248 复制代码 EXPIRE/PEXPIPE操作

使用EXPIRE/PEXPIRE设置的过期时间为负数或者使用EXPIREAT/PEXPIREAT设置过期时间戳为过去的时间会导致key被删除

EXPIRE:

127.0.0.1:6379> set key_1 value_1 OK 127.0.0.1:6379> get key_1 "value_1" //设置过期时间为-1 127.0.0.1:6379> expire key_1 -1 (integer) 1 //发现key被删除 127.0.0.1:6379> get key_1 (nil)

EXPIREAT:

127.0.0.1:6379> set key_2 value_2 OK 127.0.0.1:6379> get key_2 "value_2" //设置的时间戳为过去的时间 127.0.0.1:6379> expireat key_2 10000 (integer) 1 //key被删除 127.0.0.1:6379> get key_2 (nil) EXPIRE命令可以更新过期时间

对一个已经设置了过期时间的key使用expire命令,可以更新其过期时间。

//设置key_1的过期时间为100s 127.0.0.1:6379> set key_1 value_1 ex 100 OK 127.0.0.1:6379> ttl key_1 (integer) 95 //更新key_1的过期时间为300s 127.0.0.1:6379> expire key_1 300 (integer) 1 127.0.0.1:6379> ttl key_1 (integer) 295

在Redis2.1.3以下的版本中,使用expire命令更新一个已经设置了过期时间的key的过期时间会失败。并且对一个设置了过期时间的key使用LPUSH/HSET等命令修改其value的时候,会导致Redis删除该key。

Redis的过期策略

那你有没有想过一个问题,Redis里面如果有大量的key,怎样才能高效的找出过期的key并将其删除呢,难道是遍历每一个key吗?假如同一时期过期的key非常多,Redis会不会因为一直处理过期事件,而导致读写指令的卡顿。

这里说明一下,Redis是单线程的,所以一些耗时的操作会导致Redis卡顿,比如当Redis数据量特别大的时候,使用keys * 命令列出所有的key。

实际上Redis使用懒惰删除+定期删除相结合的方式处理过期的key。

懒惰删除

所谓懒惰删除就是在客户端访问该key的时候,redis会对key的过期时间进行检查,如果过期了就立即删除。

这种方式看似很完美,在访问的时候检查key的过期时间,不会占用太多的额外CPU资源。但是如果一个key已经过期了,如果长时间没有被访问,那么这个key就会一直存留在内存之中,严重消耗了内存资源。

定期删除

定期删除的原理是,Redis会将所有设置了过期时间的key放入一个字典中,然后每隔一段时间从字典中随机一些key检查过期时间并删除已过期的key。

Redis默认每秒进行10次过期扫描:

从过期字典中随机20个key 删除这20个key中已过期的 如果超过25%的key过期,则重复第一步

同时,为了保证不出现循环过度的情况,Redis还设置了扫描的时间上限,默认不会超过25ms。

参考

对于Redis中设置了过期时间的Key,你需要知道这些内容

Golang的Redis客户端

Redis 常用操作命令,非常详细! 



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有