redis里的key多久会过期
因此这里使用一个 my_random_value 随机值,保证客户端只会释放自己获取的锁,即只删除自己设置的 key 。2. redis术语里面,把设置了expire time的key 叫做:volatile keys。 意思就是不稳定的key。
redis查看key的创建时间 redis查看key的值命令
接下来我们创建一个临时key,并用它来 rename 掉大key test
4. redis2.1.3之前的老版本里,如果对volatile keys 做相关写入作(LPUSH,LSET),和其他一些触发修改value的作时,redis会删除 该key。 也就是说 :
用redis的时候, keys 、 hgetall 、 del 这些命令我们会多加小心,因为不合理地调用它们可能会长时间block住redis的其他请求 甚至导致CPU使用率居高不下从而卡住整个。但其实 rename 这个不起眼的命令也可能造成一样的问题,使用时也需要谨慎对待。redis.expire(key,expiration);
redis.lpush(key,field,value);
redis.get(key) //return null
redis.set(key,100);
redis.expire(key,expiration);
redis.incr(key)
//redis2.2.2 return 101; redis<2.1.3 return 1;
5. redis对过期键采用了lazy expiration:在访问key的时候判定key是否过期,如果过期,则进行过期处理。其次,每秒对volatile keys 进行抽样测试,如果有过期键,那么对所有过期key进行处理。
Redis 分布式锁详细分析
redis.get(key)锁的作用,我想大家都理解,就是让不同的线程或者进程可以安全地作共享资源,而不会产生冲突。
EXPIRE 家族命令能够把一个过期时间关联到一个给定的键,代价是这个键会使用额外的内存。当一个键设置了过期时间,Redis将会确保当指定的时间过去之后移除这个键。比较熟悉的就是 Synchronized 和 ReentrantLock 等,这些可以保证同一个 jvm 程序中,不同线程安全作共享资源。
但是在分布式系统中,这种方式就失效了;由于分布式系统多线程、多进程并且分布在不同机器上,这将使单机并发控制锁策略失效,为了解决这个问题就需要一种跨 JVM 的互斥机制来控制共享资源的访问。
比较常用的分布式锁有三种实现方式:
本篇文章主要讲解基于 Redis 分布式锁的实现。
分布式锁最主要的作用就是保证任意一个时刻,只有一个客户端能访问共享资源。
我们知道 redis 有 SET key value NX 命令,仅在不存在 key 的时候才能被执行成功,保证多个客户端只有一个能执行成功,相当于获取锁。
释放锁的时候,只需要删除 del key 这个 key 就行了。
上面的实现看似已经满足要求了,但是忘了考虑在分布式环境下,有以下问题:
的问题就是因为客户端或者网络问题,导致 redis 中的 key 没有删除,锁无法释放,因此其他客户端无法获取到锁。
使用 PX 的命令,给 key 添加一个自动过期时间(30秒),保证即使因为意外情况,没有调用释放锁的方法,锁也会自动释放,其他客户端仍然可以获取到锁。
注意给这个 key 设置的值 my_random_value 是一个随机值,而且必须保证这个值在客户端必须是的。这个值的作用是为了更加安全地释放锁。
这种实现方式,存在下面问题:
上面章节介绍了,简单实现存在的问题,下面来介绍一下 Redisson 实现又是怎么解决的这些问题的。
主要关注 tryAcquireOnceAsync 方法,有三个参数:
方法主要流程:
获取锁最终都会调用这个方法,通过 lua 脚本与 redis 进行交互,来实现分布式锁。
首先分析,传给 lua 脚本的参数:
lua 脚本的流程:
为了实现无限制持有锁,那么就需要定时刷新锁的过期时间。
这个类最重要的是两个成员属性:
使用一个静态并发 EXPIRATION_RENEWAL_MAP 来存储所有锁对应的 ExpirationEntry ,当有新的 ExpirationEntry 并存入到 EXPIRATION_RENEWAL_MAP 中时,需要调用 renewExpiration 方法,来刷新过期时间。
通过调用 unlockInnerAsync(threadId) 来删除 redis 中的 key 来释放锁。特别注意一点,当不是持有锁的线程释放锁时引起的失败,不需要调用 cancelExpirationRenewal 方法,取消定时,因为锁还是被其他线程持有。
传给这个 lua 脚本的值:
这个 lua 脚本的流程:
调用了 LockPubSub 的 subscribe 进行。
这个方法的作用就是向 redis 发起,但是对于同一个锁的同一个客户端(即 一个 jvm 系统) 只会发起一次,同一个客户端的其他等待同一个锁的线程会记录在 RedissonLockEntry 中。
方法流程:
主要属性:
这个过程对应的 redis 中的命令日志:
因为看门狗的默认时间是 30 秒,而定时刷新程序的时间是看门狗时间的 1/3 即 10 秒钟,示例程序休眠了 15 秒,导致触发了刷新锁的过期时间作。
分析源码我们了解 Redisson 模式的分布式,解决了锁过期时间和可重入的问题。但是针对 redis 本身可能存在的单点失败问题,其实是没有解决的。
基于这个问题, redisembers key 命令可以获取指定的所有成员。例如,执行 embers myset 可以获取 myset 的所有成员。 作者提出了一种叫做 Redlock 算法, 但是这种算法本身也是有点问题的,想了解更多,请看 基于Redis的分布式锁到底安全吗?
统计redis中key的数量
在改变指针的指向之前,redis会先用 if (lookupKeyWrite(c->db,c->argv[2]) != NULL) 判断 newkey 是否有对应的值,若有 则调用 dbDelete(c->db,c->argv[2]); 将newkey的值 v2 删掉。需求:测试需要统计redis中某类key的数量
在Redis 2.1.3之前的版本中,使用一个命令改变一个拥有过期时间的键的值,效果跟移除这个键一样。这种1. 在小于2.1.3的redis版本里,只能对key设置一次expire。redis2.1.3和之后的版本里,可以多次对key使用expire命令,更新key的expire time。语义是必须的,因为层的限制现在已经确定了。redis中可以使用 keys 命令来查看指定表中所有的key。因为 Redis 是单线程程序,顺序执行所有指令,其它指令必须等到当前的 keys 指令执行完了才可以继续,所以如果数目达到几百万时,keys这个命令就会导致 redis 服务卡顿。
Redis作一个设置过期时间的key
dictht ht[2];执行set命令的时候,你又重新设置了这个redis的key超时时间,如果你只是想次设置超时时间,可以把else语句里面直接更新这个key的value就可以了,不需要再设置超时时间。
EXPIRE 将会返回0,并且不会使用一个过期时间来改变一个键的过期时间。int i = Integer.parseInt(keyName_str);
i+=3、计算哈希表,根据当前字典与key进行哈希值的计算。1;
redisServ.set(keyName,i+"");
}
Redis过期时间
使用 PERSIST 命令将一个键变成持久化的键,过期时间也会被清除。1.0.0版本后可用
这就是Redis会在每秒做10次的事情:时间复杂度: O(1)
给一个 key 设置超时时间。在一个超时时间结束后,这个键将会被自动删除。一个拥有关联过期时间的键在Redis术语里通常被认为 不稳定的 。
只有删除或者覆盖键的内容的命令,包括 DEL , SET , GETSET 和所有的 STORE 命令,才会把过期时间清除。这意味着从理论上讲,所有改变键上存储的值而不是使用新的值来替换的作,都将会保持过期时间不变。例如,使用 INCR 增加一个键的值,使用 LPUSH 讲一个新的值放到列表中,或者使用 HSET 改变一个哈希的字段的值都将会使过期时间保持不变。
如果一个键被 RENAME 重命名,就像在一个已经存在的键 Key_A ,它被一个调用 RENAME Key_B Key_A 所覆盖,原始的 Key_A 是否关联过期时间是没关系的,新的键 Key_A 将会继承 Key_B 的所有特征。
注意,使用负数调用 EXPIRE / PEXPIRE ,或者使用过去的时间调用 EXPIREAT / PEXPIREAT 将会使键被删除,而不是过期(相应的,弹出的 key nt 将会是 del ,而不是 expired )。
可以使用一个已经有过期时间集的键作为参数来调用 EXPIRE 。在这种情况下,一个键的生存时间已经 更新 为一个新值。对此很多应用,下面的 Nigation ses// 类型处理函数的私有数据sion 模式一节记录了一个例子。
特别的 返回数字 :
redis> SET mykey "Hello"
redis> EXPIRE mykey 10
redis> TTL mykey
redis> SET mykey "Hello World"
redis> TTL mykey
redis>
想象你有一个网页服务,并且你对用户最近访问的N个页面有兴趣,这样每个临近的页面视图的执行时间不会超过前一个页面视图执行的60秒。理论上来讲,你可以认为用户访问的页面为 Nigation session ,其中就可以包含用户在寻找哪些他或她感兴趣的产品信息,因此你可以关联的产品。
你可以非常容易的使用下面的策略在Redis中建模这种类型:每次用户访问一个页面你就调用下面的命令:
如果用户闲置超过60秒,这个键将会被删除,只有访问时间值小于60秒的页面才会被记录。
这个模式可以很容易的修改为使用 INCR 做计数器来替代使用 RPUSH 的列表。
通常情况下创建Redis的键时不关联生存时间。这个键将会简单的一直生存,除非用户显示的删除它,例如使用 DEL 命令。
一个键的生存时间可以被 EXPIRE 命令更新,或者被 PERSIST 命令完全移除(或其他严格相关的命令)。
在Redis2.4版本中,过期时间可能不是非常的,并且它可能是在0到1秒之间的出入。从Redis2.6版本开始,过期时间误是从0到1毫秒。
键的过期信息以的Unix时间戳形式保存(Redis2.6以及更新的版本毫秒内)。这意味着甚至当Redis实例未启动时时间就流走了。
为了过期时间能工作的很好,计算机时间必须保持稳定。如果你从两个时钟巨大不同步的计算机上移动一个RDB文件,有趣的事情将会发生(像所有的键在加载时变成过期)。
实际上运行中的实例将一直会检查计算机的时钟,举例来说,如果你给一个键设置1000秒的生存时间,然后在未来将你的计算机设置在2000秒以后,这个键将会立即失效,而不是持续1000秒。
Redis键将会通过两种方式过期:一个被动的方式,和一个主动的方式。
一个键的被动过期是很简单的,当一些客户端尝试访问它,然后这个键被发现超时了。
当然,这是不够的,因为有一些键将永远不会被再次访问。这些键无论如何都应该被过期。所以,Redis会定期的在过期的中随机范围内测试少量的键。所有的已过期的键将会被从键空间被删除。
这意味着在任何一个时刻,正在使用内存的已经过期的数量的键等于每秒写作数量除以4.
为了获得正确的行为而不牺牲一致性,当一个键失效, DEL 作会同时在AOF文件和附属的副节点执行。这种方式失效进程是在主实例集中的,也不会出现一致性错误。
然而,当副本已经连接到主节点后将不会的失效键(但将会等待来自主节点的 DEL ),他们仍将会获取数据集中的全部过期状态,所以当一个副本被选举为主节点后,它将能够的失效这些键,完全像一个主节点。
如何查看redis最近使用的命令
如何查看redis最近使用的命令memcached 和 redis 的set命令都有expire参数,可以设置key的过期时间。但是redis是一个可以对数据持久化的key-value databaelse {se,它的key过期策略还是和memcached有所不同的。
使用Redis的脚本功能实现Redis中数据简单查询,有需要的朋友可以参考下。 在Redis的设计中,key是一切,对于Redis是可见的,而value对于Redis来说就是一个字节数组,Redis并不知道你的value中存储的是什么,所以要想实现比如 ‘select from users where user.location="shanghai"’ 这样的查询,在Redis是没办法通过value进行比较得出结果的。但是可以通过不同的数据结构类型来做到这一点。比如如下的数据定义 users:1 {name:Jack,age:28,location:shanghai} users:2 {name:Frank,age:30,location:beijing} users:location:shanghai [1] 其中users:1 users:2 分别定义了两个用户信息,通过Redis中的hash数据结构,而users:location:shanghai 记录了所有上海的用户id,通过数据结构实现。这样通过两次简单的Redis命令调用就可以实现我们上面的查询。 Jedis jedis = jedisPool.getResource(); Set
你的配置文件没有加载,如果配置文件修改了时候
需要重新启动redis-server重新只有当 counter >= permits 的时候,回调 listener 才会运行,起到控制 listener 运行的效果。加载配置文件
redis怎样找到key在内存中的位置
博客地址:博客地址: 一、预先需要了解的知识 1、redis 中的每一个数据库,都由一个 redisDb 的结构存储。其中,redisDb.id 存储着 redis 数据库以整数表示的号码。redisDb.dict 存储着该库所有的键对数据。red
一、预先需要了解的知识1、redis 中的每一个数据库,都由一个 redisDb 的结构存储。其中,redisDb.id 存储着 redis 数据库以整数表示的号码。redisDb.dict 存储着该库所有的键值对数据。redisDb.expires 保存着每一个键的过期时间。
2、当redis 初始化时,会预先分配 16 个数据库(该数量可以通过配置文件配置),所有数据库保存到结构 redis 的一个成员 redis.db 数组中。当我们选择数据库 select number 时,程序直接通过 redis.db[number] 来切换数据库。有时候当程序需要知道自己是在哪个数据库时,直接读取 redisDb.id 即可。
3、既然我们知道一个数据库的所有键值都存储在redisDb.dict中,那么我们要知道如果找到key的位置,就有必要了解一下dict 的结构了:
typedef struct dict {
// 特定于类型的处理函数
dictType type;
void privdata;
// 哈希表(2个)
int rehashidx;
// 当前正在运作的安全迭代器数量
int iterators;
} dict;
由上述的结构可以看出,redis 的字典使用哈希表作为其底层实现。dict 类型使用的两个指向哈希表的指针,其中 0 号哈希表(ht[0])主要用于存储数据库的所有键值,而1号哈希表主要用于程序对 0 号哈希表进行 rehash 时使用,rehash 一般是在添加新值时会触发,这里不做过多的赘述。所以redis 中查找一个key,其实就是对进行该dict 结构中的 ht[0] 进行查找作。
4、既然是哈希,那么我们知道就会有哈希碰撞,那么当多个键哈希之后为同一个值怎么办呢?redis采取链表的方式来存储多个哈希碰撞的键。也就是说,当根据key的哈希值找到该列表后,如果列表的长度大于1,那么我们需要遍历该链表来找到我们所查找的key。当然,一般情况下链表长度都为是1,所以时间复杂度可看作o(1)。
了解了上述知识之后,我们就可以来分析redis如果在内存找到一个key了。
1、当拿到一个key后, redis 先判断当前库的0号哈希表是否为空,即:if (dict->ht[0].size == 0)。如果为true直接返回NULL。
2、判断该0号哈希表是否需要rehash,因为如果在进行rehash,那么两个表中者有可能存储该key。如果正在进行rehash,将调用一次_dictRehashStep方法,_dictRehashStep 用于对数据库字典、以及哈希键的字典进行被动 rehash,这里不作赘述。
4、根据哈希值与当前字典计算哈希表的索引值。
5、根据索引值在哈希表中取出链表,遍历该链表找到key的位置lrange key start stop 命令可以获取指定列表的一部分元素。例如,执行 lrange mylist 0 -1 可以获取列表 mylist 的所有元素。。一般情况,该链表长度为1。
到此我们就找到了key在内存的中位置了。
redis内存负数
type key 命令可以查看指定键的数据类型。例如,执行 type mykey 可以查看键 mykey 的数据类型。通常Redis keys创建时没有设置相关过期时间。他们会一直二、当redis 拿到一个key 时,如果找到该key的位置。存在,除非使用显示的命令移除,例如,使用DEL命令。
EXPIRE一类命令能关联到一个有额外内存开销的key。当key执行过期作时,Redis会确保按照规定时间删除他们。
key的过期时间和有效性可以通过EXPIRE和PERSIST命令(或者其他相如果一个键被 RENAME 重命名,关联的生存时间将会被转移到新的键名上。关命令)来进行更新或者删除过期时间。
Redis随笔-rename效率问题
RENAME – Redis大部分文档在介绍 rename 的时候只将它描述成一个时间复杂度为O(1)的命令,却忘了说明它可能导致的性能问题(涉及覆盖旧值的时候 时间复杂度应该是O(1)+O(M))。
注意 rLock.tryLock(10, TimeUnit.SECONDS); 时间要设置大一点,如果等待时间太短,小于获取锁 redis 命令的时间,那么就直接返回获取锁失败了。我们先做个试验看看 rename 的问题。
先搭建一个redis,版本号为3.2,看看它的内存信息
接着用lua给redis创建一个名为 test 的大key, test 有500w个field,每个field的值都是1
这时候我们看看redis的内存占用情况
由于大key test 的创建,redis内存占用多了300多兆。
这时就能看到执行时间的异常了, rename 执行时间长达2.36秒,这是为什么呢?我们再看看redis内存占用情况:
通过 返回的信息我们可以发现在执行 rename 之后redis将大key test 大小为300多兆的值对象直接删除并回收掉了,而redis删除一个key的时间复杂度是O(M),在这里M是被删除的成员数量---500w。应该就是这个 "隐式"删除作 导致了高延迟的产生。
我们看看文档是怎么描述 rename 这一行为的:
newkey如果本就存在,redis会用key的值覆盖掉newkey的值, 而newkey原本的值会被redis隐式地删除 。我们知道大key的删除伴随着高延迟(redis是单进程服务,会在删除大key期间block住接下来其他命令的执行),这就导致时间复杂度本为O(1)的 rename 也有可能卡住redis。
这句文档的原话我没在其他文档里找到类似的翻译,看这些文档的开发者可能会误以为这是个特别安全的O(1)命令。
既然文档里已经说明了这种行为的存在,我就顺便看看源码这块逻辑是怎么走的:
正常O(1)重命名的逻辑不判断如果是当前线程持有的锁,那么就重新设置过期时间,并返回 1 即 true 。否则返回 0 即 false 。用多说,涉及到覆盖的过程可以简化成如下图:
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 12345678@qq.com 举报,一经查实,本站将立刻删除。