最近sprigboot项目中,用spring-session,spring-session-data-redis插件来实现分布式session,对于redis的存储有点摸不着头脑,找了半天才解开了心中的疑惑,做个记录。

当创建一个RedisSession,然后存储在Redis中时,RedisSession的存储细节如下:
spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe
spring:session:sessions:expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe
spring:session:expirations:1439245080000
Redis会为每个RedisSession存储三个k-v。
第一个k-v用来存储Session的详细信息,包括Session的过期时间间隔、最近的访问时间、attributes等等。这个k的过期时间为Session的最大过期时间 + 5分钟。如果默认的最大过期时间为30分钟,则这个k的过期时间为35分钟
第二个k-v用来表示Session在Redis中的过期,这个k-v不存储任何有用数据,只是表示Session过期而设置。这个k在Redis中的过期时间即为Session的过期时间间隔
第三个k-v存储这个Session的id,是一个Set类型的Redis数据结构。这个k中的最后的1439245080000值是一个时间戳,根据这个Session过期时刻滚动至下一分钟而计算得出。
这里不由好奇,为什么一个RedisSession却如此复杂的存储。关于这个可以参考spring-session作者本人在github上的两篇回答:
Why does Spring Session use spring:session:expirations?
Clarify Redis expirations and cleanup task
简单描述下,为什么RedisSession的存储用到了三个Key,而非一个Redis过期Key。
对于Session的实现,需要支持HttpSessionEvent,即Session创建、过期、销毁等事件。当应用用监听器设置监听相应事件,Session发生上述行为时,监听器能够做出相应的处理。
Redis的强大之处在于支持KeySpace Notifiction——键空间通知。即可以监视某个key的变化,如删除、更新、过期。当key发生上述行为是,以便可以接受到变化的通知做出相应的处理。具体详情可以参考:
Redis Keyspace Notifications
但是Redis中带有过期的key有两种方式:
当访问时发现其过期
Redis后台逐步查找过期键
当访问时发现其过期,会产生过期事件,但是无法保证key的过期时间抵达后立即生成过期事件。具体可以参考:Timing of expired events
spring-session为了能够及时的产生Session的过期时的过期事件,所以增加了:
spring:session:sessions:expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe
spring:session:expirations:1439245080000
spring-session中有个定时任务,每个整分钟都会查询相应的spring:session:expirations:整分钟的时间戳中的过期SessionId,然后再访问一次这个SessionId,即spring:session:sessions:expires:SessionId,以便能够让Redis及时的产生key过期事件——即Session过期事件。