12+Spring Session与分布式状态管理
12Spring Session与分布式状态管理标签Spring Session, 分布式会话, Redis, Java, 微服务, 会话管理, 分布式系统, 负载均衡摘要在微服务架构全面落地的今天Session管理早已不是把用户信息塞进HttpSession那么简单。当应用从单体走向分布式、从同步走向响应式Session的存储、共享、同步、安全每一个环节都可能成为压垮系统的最后一根稻草。本文基于Spring Session 3.4.x版本从SessionRepository核心抽象出发深入剖析Redis高性能集中式存储、JDBC关系型持久化以及MongoDB/Hazelcast等多后端适配方案结合Spring Boot 3.4的Reactive自动配置探讨WebFlux场景下的Session管理策略重点解读CHIPS规范下的Partitioned Cookie属性、Spring Security并发Session控制、SessionFixation攻击防护等安全机制最后从实战角度分析Gateway统一Session、跨域共享、负载均衡下的Session粘滞与无状态化权衡。全文融入多个生产级踩坑案例提供可直接落地的配置方案与性能调优建议。一、Session管理的演进从单机内存到分布式抽象十年前我刚入行时Session就是Tomcat里那个默认30分钟过期的内存对象。项目上线第一天运维把应用部署了两台节点用户就开始疯狂投诉登录一会就掉“购物车商品时有时无”。那时候所谓的解决方案要么用Nginx的ip_hash做Session粘滞要么写个简陋的JDBC Session复制。本质上都是在回避问题而不是解决问题。Spring Session的出现真正意义上把Session从容器耦合中解放出来。它不再依赖Servlet容器的HttpSession实现而是通过SessionRepository接口提供了一套统一的存储抽象。无论你后端接的是Redis、JDBC、MongoDB还是Hazelcast上层业务代码几乎无感知。这种抽象看似简单但在实际生产环境中它意味着你可以在不改动一行业务代码的情况下把Session存储从单机Redis迁移到Redis Cluster或者从Redis切到JDBC以应对某些审计合规要求。1.1 SessionRepository一切存储的起点Spring Session 3.4.x的核心接口设计非常精炼。SessionRepository定义了最基本的CRUD操作createSession、save、findById、deleteById。这个设计遵循了Spring一贯的原则——接口最小化扩展通过组合实现。publicinterfaceSessionRepositorySextendsSession{ScreateSession();voidsave(Ssession);SfindById(Stringid);voiddeleteById(Stringid);}实际开发中我们很少直接操作SessionRepository但它的存在意义在于统一了所有存储后端的行为契约。2023年我参与的一个金融项目因为监管要求需要保留用户会话日志6个月我们就是从RedisSessionRepository切换到JdbcSessionRepository整个过程只改了配置业务代码零侵入。这种灵活性在传统的容器Session实现中是不可想象的。1.2 FindByIndexNameSessionRepository被低估的索引能力很多人用了好几年Spring Session却不知道FindByIndexNameSessionRepository这个接口。它扩展了基础的SessionRepository提供了按索引名查询Session的能力。最典型的应用场景就是查找当前用户的所有活跃会话——这在并发Session控制、后台踢人、单点登录限制等场景中非常关键。publicinterfaceFindByIndexNameSessionRepositorySextendsSessionextendsSessionRepositoryS{StringPRINCIPAL_NAME_INDEX_NAMEFindByIndexNameSessionRepository.class.getName().PRINCIPAL_NAME;MapString,SfindByIndexNameAndIndexValue(StringindexName,StringindexValue);}在Redis实现中这个索引是通过Redis的Hash结构维护的一个专门保存principal_name - session_ids映射的Hash key配合每个Session的独立key实现快速查询。去年我们做一个在线教育系统需要限制同一账号同时最多3个设备登录就是靠这个索引接口配合Spring Security的ConcurrentSessionControlAuthenticationStrategy实现的。如果没有这个索引能力你需要遍历所有Session key做匹配在百万级Session场景下根本不可行。这里有个生产踩坑点Redis索引Hash的key过期策略。默认情况下Spring Session会维护Session key和索引Hash的过期一致性但在某些极端场景比如Redis内存不足触发LRU淘汰、或者手动删除Session key但未清理索引下会出现索引和实际Session数据不一致的幽灵会话。我们当时的解决方案是增加一个定时补偿任务每周扫描一次索引孤儿记录。Spring Session 3.4在这方面做了改进增加了更多的防御性清理逻辑但生产环境仍然建议做这种兜底检查。二、存储后端选型没有银弹只有权衡2.1 Redis高性能集中式Session的首选Redis作为Spring Session的默认推荐后端优势非常明显亚毫秒级的读写延迟、原生的TTL过期机制、成熟的集群方案Redis Cluster/ Sentinel、以及Pub/Sub能力带来的Session事件广播。在QPS过万的高并发场景下Redis几乎是唯一合理的选择。Spring Session 3.4.x对Redis的支持有了显著提升。配置已经非常简化ConfigurationEnableRedisHttpSession(maxInactiveIntervalInSeconds1800,redisNamespacemyapp:session)publicclassSessionConfig{BeanpublicLettuceConnectionFactoryredisConnectionFactory(){RedisStandaloneConfigurationconfignewRedisStandaloneConfiguration(redis.host,6379);// 生产环境务必配置连接池和超时LettuceClientConfigurationclientConfigLettuceClientConfiguration.builder().commandTimeout(Duration.ofSeconds(2)).shutdownTimeout(Duration.ZERO).build();returnnewLettuceConnectionFactory(config,clientConfig);}}但Redis Session的坑也不少。第一个就是序列化方式的选择。默认的JDK序列化在跨语言场景下是灾难且序列化后的体积巨大。我在2022年做过一个优化把一个金融App的Session从JDK序列化切换到JSON序列化平均每个Session从3.2KB降到了0.8KBRedis内存占用直接少了60%。Spring Session 3.4支持通过SessionSerializer自定义序列化器配合Jackson可以完美实现BeanpublicRedisSerializerObjectspringSessionDefaultRedisSerializer(){returnnewGenericJackson2JsonRedisSerializer();}第二个坑是Redis Cluster模式下的Session一致性。很多初学者以为用了Redis Cluster就高枕无忧了但实际上Spring Session的Redis实现默认使用Redis的Hash Tag机制保证相关key落在同一个slot。如果你自定义了redisNamespace但没有注意Hash Tag的兼容性在Cluster模式下会出现跨slot操作失败的错误。Spring Session 3.4在内部已经自动处理了这个问题但如果你自己直接操作RedisTemplate存取Session相关数据一定要确保key包含{namespace}这种Hash Tag格式。第三个坑是Redis连接风暴。在应用启动瞬间如果数百个实例同时连接Redis并批量创建Session很容易把Redis的maxclients打满。我们的解决方案是在应用启动时加一个随机延迟通过Spring Boot的ApplicationRunner配合Random实现把连接峰值打散到30秒窗口内。2.2 JDBC被忽视的关系型持久化方案在很多人眼里JDBC Session存储是慢的代名词。但实际上在某些特定场景下JDBC是唯一可行的方案。去年我接触的一个政府项目有严格的等保三级要求所有用户操作轨迹必须留痕且数据必须存储在通过安全认证的国产数据库中。Redis虽然快但不符合审计合规要求。这时候JdbcSessionRepository的价值就体现出来了。Spring Session 3.4.x的JDBC实现支持Oracle、MySQL、PostgreSQL、SQL Server等主流数据库且表结构非常简单只有两张核心表CREATETABLESPRING_SESSION(PRIMARY_IDCHAR(36)NOTNULL,SESSION_IDCHAR(36)NOTNULL,CREATION_TIMEBIGINTNOTNULL,LAST_ACCESS_TIMEBIGINTNOTNULL,MAX_INACTIVE_INTERVALINTNOTNULL,EXPIRY_TIMEBIGINTNOTNULL,PRINCIPAL_NAMEVARCHAR(100),CONSTRAINTSPRING_SESSION_PKPRIMARYKEY(PRIMARY_ID));CREATETABLESPRING_SESSION_ATTRIBUTES(SESSION_PRIMARY_IDCHAR(36)NOTNULL,ATTRIBUTE_NAMEVARCHAR(200)NOTNULL,ATTRIBUTE_BYTESBLOBNOTNULL,CONSTRAINTSPRING_SESSION_ATTRIBUTES_PKPRIMARYKEY(SESSION_PRIMARY_ID,ATTRIBUTE_NAME),CONSTRAINTSPRING_SESSION_ATTRIBUTES_FKFOREIGNKEY(SESSION_PRIMARY_ID)REFERENCESSPRING_SESSION(PRIMARY_ID)ONDELETECASCADE);JdbcSessionRepository的性能其实没有想象中那么差。配合数据库连接池HikariCP、合理的索引EXPIRY_TIME和PRINCIPAL_NAME必须有索引、以及Spring Session内置的定时清理任务每分钟执行一次DELETE过期Session在十万级在线用户的场景下数据库压力完全可控。我们的实测数据是MySQL 8.0 SSD存储单次Session读取平均延迟在5-10ms写入延迟在10-20ms。对于非极致性能要求的业务这个延迟完全可以接受。当然JDBC方案也有明显短板。首先是序列化膨胀问题——BLOB字段存储二进制数据无法像Redis那样做细粒度的增量更新每次Session属性变更都要整行重写。其次是数据库单点问题虽然有主从复制但写操作仍然集中在主库。我们在生产环境中采用的策略是读写分离Session读取走从库写入和过期清理走主库。2.3 MongoDB与Hazelcast特定场景的 niche 选择MongoDB作为Session存储适合已经深度使用MongoDB技术栈的团队。它的优势在于Schema-less的灵活性以及水平扩展能力。Spring Session Data MongoDB提供了MongoSessionRepository实现配置方式与Redis类似。但实际生产中使用MongoDB存Session的团队很少主要原因是运维复杂度——你很难说服团队为了Session管理单独维护一套MongoDB集群。Hazelcast的情况更有趣。它是一个内存数据网格IMDG既可以直接嵌入到应用中Embedded模式也可以作为独立集群运行Client-Server模式。在Spring Session 3.4中HazelcastSessionRepository适合那些对延迟极其敏感、且希望避免网络往返的场景。嵌入式模式下Session数据直接存储在应用进程的内存中延迟接近零。代价是Heap内存压力增大以及集群拓扑变化时的数据重平衡开销。2021年我做过一个物联网消息网关项目每秒要处理数万条设备上行消息每条消息都需要验证Session。用Redis的话网络延迟成为瓶颈最后选择了嵌入式Hazelcast。每个网关节点本地维护Session缓存同时通过Hazelcast的分布式事件机制做跨节点同步。这个方案把Session验证延迟从2-3ms降到了0.1ms以内但代价是单节点Heap增加了约500MB的Session数据。这种取舍只有在对延迟极其敏感的场景下才值得做。三、Reactive支持Spring Boot 3.4的响应式Session管理Spring Boot 3.4对Reactive Web应用的Session自动配置支持已经非常成熟。很多开发者有一个误解认为Spring Session只能用在Servlet堆栈中。实际上从Spring Session 2.x开始就有Reactive支持3.4版本进一步完善了自动配置的覆盖度。3.1 WebFlux集成机制在Reactive场景下Spring Session的核心是ReactiveSessionRepository和WebSessionStore的集成。与Servlet的Filter机制不同WebFlux通过WebFilter实现Session的透明管理ConfigurationEnableRedisWebSession(maxInactiveIntervalInSeconds1800)publicclassReactiveSessionConfig{BeanpublicReactiveRedisConnectionFactoryreactiveRedisConnectionFactory(){returnnewLettuceConnectionFactory(redis.host,6379);}}ComponentpublicclassSessionDemoHandler{publicMonoServerResponsegetUserSession(ServerRequestrequest){returnrequest.session().flatMap(session-{StringuserIdsession.getAttribute(USER_ID);// Reactive链中操作Sessionsession.getAttributes().put(LAST_ACCESS,Instant.now().toString());returnServerResponse.ok().bodyValue(Map.of(userId,userId,sessionId,session.getId()));});}}这里有一个Reactive编程模型下的关键差异Session的save操作是隐式还是显式的。在Servlet模式下Spring Session通过Filter在请求结束时自动save。但在WebFlux中由于响应式流的惰性求值特性Session的保存需要更谨慎地处理。Spring Session 3.4的WebSessionStore实现默认会在响应提交时自动持久化但如果你手动操作了Session属性且响应链出现异常可能会出现Session数据丢失。我们在生产中的做法是对于关键的Session写操作显式调用session.save()并确保它在响应返回前完成。3.2 Reactive场景的性能优化Reactive应用通常承载着更高的并发连接数C10K甚至C100K这意味着Session存储的并发压力远超传统Servlet应用。我们在一个基于WebFlux的API网关项目中使用Redis Cluster作为Session后端单机支撑了超过5万个并发WebSocket连接。核心优化点有几个第一使用ReactiveRedisTemplate的pipeline批量操作。WebFlux的非阻塞I/O模型天然适合批量请求合并。Spring Data Redis的Reactive API支持flatMap合并多个Redis命令减少网络往返。第二Session数据的冷热分离。活跃连接频繁访问的Session属性如用户ID、权限列表放在Redis中而一些低频访问的扩展属性如用户偏好设置、最后登录IP异步落库到MySQL通过懒加载机制按需回填。第三合理设置Session过期时间。Reactive应用的长连接场景WebSocket、SSE下Session不能因为30分钟无HTTP请求就过期。我们通过自定义SessionExpirationPolicy结合心跳消息刷新LastAccessTime实现了动态过期策略正常HTTP请求按30分钟过期WebSocket连接按7天过期。四、集成机制深度解析从Filter到Cookie安全4.1 Servlet Filter集成原理Spring Session在Servlet环境下的集成本质上是通过一个名为SessionRepositoryFilter的Servlet Filter接管HttpSession的生命周期。这个Filter的优先级非常高Ordered.HIGHEST_PRECEDENCE 50确保它在绝大多数业务Filter之前执行。------------------- --------------------- ------------------ | Client Request | --- | SessionRepository | --- | Security Filter | | | | Filter | | (Optional) | ------------------- --------------------- ------------------ | | | | v | | ------------------ | | | Wrap Request | | | | with Session | | | | Repository | | | ------------------ | | | | v v v --------------------------------------------------------------- | Application Filters | | (All downstream components see the wrapped HttpSession) | ---------------------------------------------------------------这个Filter的核心工作有两个一是将原生HttpServletRequest包装为SessionRepositoryRequestWrapper二是拦截请求提交事件做Session持久化。wrapper模式是Spring Session最巧妙的设计之一——下游所有调用request.getSession()的请求实际上都被重定向到了SessionRepository的实现。生产环境中有两个常见问题。第一个是Filter顺序问题。如果你自定义了Filter且也包装了Request/Response可能会与SessionRepositoryFilter冲突。我们的经验是Session相关的Filter应该尽量靠前业务Filter尽量靠后。可以通过Spring Boot的FilterRegistrationBean显式控制顺序BeanpublicFilterRegistrationBeanSessionRepositoryFilter?sessionFilterRegistration(SessionRepositoryFilter?filter){FilterRegistrationBeanSessionRepositoryFilter?registrationnewFilterRegistrationBean(filter);registration.setOrder(SessionRepositoryFilter.DEFAULT_ORDER);returnregistration;}第二个问题是异步请求AsyncContext下的Session一致性。Spring Session 3.4已经原生支持了异步请求的Session传播但在早期的2.x版本中如果在异步线程中修改了Session主线程提交时可能会覆盖这些变更。3.4版本通过改进的Session同步机制解决了这个问题但如果你有自定义的异步处理逻辑仍然建议手动确认Session在异步上下文中的可见性。4.2 Cookie属性与CHIPS规范Cookie是Session在浏览器端的载体其安全性直接影响整个Session体系。Spring Session 3.4.x全面支持了现代Cookie安全属性包括SameSite、HttpOnly、Secure以及相对较新的Partitioned属性CHIPS规范。BeanpublicCookieSerializercookieSerializer(){DefaultCookieSerializerserializernewDefaultCookieSerializer();serializer.setCookieName(MYAPP_SESSION);serializer.setCookiePath(/);serializer.setDomainNamePattern(^.?\\.(\\w\\.[a-z])$);serializer.setUseHttpOnlyCookie(true);serializer.setUseSecureCookie(true);serializer.setSameSite(Lax);// CHIPS规范支持Partitioned属性serializer.setUsePartitionedCookie(true);returnserializer;}Partitioned属性即CHIPS - Cookies Having Independent Partitioned State是应对第三方Cookie消亡的重要机制。在Chrome的Privacy Sandbox推进过程中传统的跨站Cookie正在被逐步限制。Partitioned属性允许在顶级站点隔离的上下文中使用Cookie也就是说同一个第三方服务在不同顶级站点下会有独立的Cookie分区。对于Spring Session用户这意味着什么如果你的应用被嵌入到多个不同域名的iframe中典型的SaaS场景传统的Session Cookie会被浏览器的第三方Cookie限制策略拦截。启用Partitioned属性后每个顶级站点会有独立的Session分区避免了跨站追踪的嫌疑同时保留了基本的会话能力。我在2024年初的一个SaaS平台项目中就遇到了这个问题。我们的应用被客户嵌入到各自的门户系统中Chrome 120开始限制第三方Cookie后大量用户反馈登录后跳转回客户门户就掉登录。启用Partitioned Cookie后问题解决但代价是同一个用户在不同客户门户中的Session不再共享——这在架构上其实更合理属于安全性和便利性的正确权衡。五、Spring Security集成安全不是可选项5.1 并发Session控制Spring Security的并发Session控制Concurrent Session Control是很多业务系统的刚需同一账号不能同时在多处登录或者限制最大并发数。Spring Session与Spring Security的集成让这个功能的实现变得异常简单。BeanpublicSecurityFilterChainfilterChain(HttpSecurityhttp,SessionRegistrysessionRegistry)throwsException{http.sessionManagement(session-session.maximumSessions(1).maxSessionsPreventsLogin(false)// true拒绝新登录, false踢掉旧会话.sessionRegistry(sessionRegistry));returnhttp.build();}BeanpublicSessionRegistrysessionRegistry(FindByIndexNameSessionRepository?sessionRepository){returnnewSpringSessionBackedSessionRegistry(sessionRepository);}这里的关键是SpringSessionBackedSessionRegistry它把Spring Security的SessionRegistry接口适配到了Spring Session的FindByIndexNameSessionRepository上。没有Spring Session的时候SessionRegistry默认基于内存的ConcurrentHashMap实现这在集群环境下完全不可用。有了Spring Session后Session的注册、查询、失效操作全部持久化到共享存储集群中的任意节点都能准确判断用户的并发登录状态。我强烈建议在生产环境中设置maxSessionsPreventsLogin为false踢掉旧会话而不是true拒绝新登录。原因是用户体验当用户的网络环境变化比如从WiFi切换到4G导致TCP连接断开重建时旧Session可能还在Redis中存活如果设置true用户在新网络下会被拒绝登录必须等待旧Session过期。设置为false则新登录会自动使旧Session失效虽然旧设备上会出现账号在其他地方登录的提示但整体体验更流畅。5.2 SessionFixationProtectionStrategySession Fixation会话固定攻击是一个经典的安全漏洞。攻击者先获取一个有效的Session ID然后诱使受害者使用这个Session ID登录从而劫持受害者的会话。Spring Security默认启用了SessionFixationProtectionStrategy来防御这种攻击在用户认证成功后重新生成一个新的Session ID同时把原Session的属性迁移到新Session。http.sessionManagement(session-session.sessionFixation(fixation-fixation.changeSessionId()// 默认策略Servlet 3.1支持// .migrateSession() // 创建新Session并迁移属性// .newSession() // 创建全新Session不迁移属性));在Spring Session 3.4中changeSessionId()是默认且推荐的策略。它利用了Servlet 3.1的HttpServletRequest.changeSessionId()方法在不创建新Session对象的情况下只更换Session ID性能开销最小。但如果你需要同时更换Session的存储key比如在Redis中希望旧Session key完全失效可以选择migrateSession策略。这里有一个安全细节某些敏感操作如修改密码、绑定手机号后应该强制使所有Session失效并要求重新登录。Spring Security并没有内置这个行为需要自己实现ServicepublicclassSecurityService{AutowiredprivateFindByIndexNameSessionRepository?sessionRepository;publicvoidinvalidateAllUserSessions(Stringusername){MapString,?sessionssessionRepository.findByPrincipalName(username);sessions.keySet().forEach(sessionRepository::deleteById);}}六、微服务场景实战Gateway、跨域与负载均衡6.1 Gateway统一Session架构在微服务架构中最优雅的Session方案通常是在API Gateway层统一处理。下游服务不再直接操作Session而是通过Gateway转发的请求头获取用户信息。---------------- ------------------- ------------------- | Client | --- | Spring Cloud | --- | Auth Service | | (Browser) | | Gateway | | (OAuth2/SSO) | ---------------- ------------------- ------------------- | | v v ------------------- ------------------- | Session Store | | User Service | | (Redis) | | (User Details) | ------------------- ------------------- | v ------------------- | Business Service | | (Header only, no | | Session logic) | -------------------这种架构的好处显而易见业务服务完全无状态Session管理的复杂度集中在Gateway层水平扩展毫无压力。我们在一个电商中台项目中采用了这个方案Gateway基于Spring Cloud Gateway Spring Session Redis实现认证后的用户信息脱敏后通过X-User-Id、X-User-Roles等自定义Header透传给下游服务。Gateway层的Session配置有一个关键细节Cookie域名的设置。如果Gateway和业务服务部署在不同子域名下如gateway.example.com和api.example.com必须正确设置Cookie的Domain属性为.example.com否则浏览器不会携带Cookie。Spring Session的DefaultCookieSerializer支持正则匹配域名serializer.setDomainNamePattern(^.?\\.(\\w\\.[a-z])$);这行正则的意思是从请求域名中提取根域如example.com让Cookie在整个根域下生效。6.2 跨域Session共享跨域Session共享是微服务场景中的老大难问题。浏览器的同源策略限制了Cookie的跨域发送即使服务器端Session存储是共享的浏览器端也无法自动携带Cookie。解决方案通常有几种方案一CORS Credentials如果跨域是可控的比如子域之间可以通过CORS配置允许携带凭证BeanpublicCorsConfigurationSourcecorsConfigurationSource(){CorsConfigurationconfignewCorsConfiguration();config.setAllowCredentials(true);config.addAllowedOriginPattern(https://*.example.com);config.addAllowedHeader(*);config.addAllowedMethod(*);UrlBasedCorsConfigurationSourcesourcenewUrlBasedCorsConfigurationSource();source.registerCorsConfiguration(/**,config);returnsource;}前端请求时必须设置withCredentials: true。这种方案适合子域互通的场景但完全跨域如a.com和b.com仍然无法使用。方案二Token 中心认证完全跨域的场景下建议放弃Cookie方案改用Token机制JWT或Opaque Token。用户登录后获取Token前端每次请求通过Header携带Token服务端通过Token解析用户信息。Spring Session在这种架构中退化为Token的缓存和验证层而不是Session的存储层。方案三SameSiteNone Secure如果确实需要在完全跨域场景下使用Cookie可以设置SameSiteNone和Secure属性。但这要求所有站点都使用HTTPS且Chrome对SameSiteNone的Cookie有额外的用户提示体验并不理想。我个人不推荐使用这种方式。6.3 Session粘滞与负载均衡的取舍很多运维团队在面对Session问题时第一反应是开启Nginx的ip_hash或者HAProxy的stick table让同一个用户的请求固定落在同一个后端实例上。这确实能回避分布式Session的问题但代价是巨大的负载不均某些热点IP如企业出口IP会集中打到少数节点故障恢复差某个节点宕机该节点上的所有Session瞬间丢失弹性伸缩困难扩容或缩容时Session需要重新分布微服务反模式服务间调用无法保证落在同一节点我的观点是Session粘滞是技术债不是解决方案。在2024年的技术栈中没有理由不采用集中式Session存储。Redis Cluster的延迟足够低Spring Session的集成足够简单唯一阻碍你的只是不愿意改变旧习惯。当然有一种特殊情况可以考虑Session粘滞遗留系统改造短期内无法引入Redis等外部依赖。这时候可以用Nginx的sticky模块同时把Session过期时间设短比如5分钟并配合后台异步迁移机制逐步向无状态架构过渡。但这只是过渡方案不是终态架构。七、生产环境调优 checklist最后分享一个我们在多个项目中沉淀的Spring Session生产环境调优清单连接与超时Redis连接池大小设置为应用实例数的2-3倍考虑连接风暴Redis命令超时设置为2-5秒超过则降级为无Session模式或返回503数据库连接池JDBC场景单独划分不与业务数据源混用序列化优先使用JSON序列化兼容性最好调试最方便避免在Session中存储大对象建议只存用户ID和必要元数据自定义对象必须有无参构造函数Jackson反序列化要求过期与清理根据业务场景合理设置maxInactiveInterval不要一刀切1800秒启用Spring Session的定时清理任务默认已启用但需确认cron表达式对于JDBC存储考虑在数据库层增加分区表或归档策略监控暴露Spring Session的指标到Micrometersession.created、session.deleted、session.expired监控Redis内存使用率设置80%告警对于JdbcSessionRepository监控SPRING_SESSION表的增长趋势高可用Redis必须启用持久化RDBAOF避免重启后全量Session丢失Redis Cluster模式下确保每个主节点至少有一个从节点考虑多活架构时Session存储的跨机房同步延迟必须可接受文章声明本文仅供学习参考请勿用于商业用途。