分布式环境下 Session 怎么共享?Session 复制、粘性会话、集中存储各有什么优劣?
简化版
单机时 Session 存在服务器内存里没问题,但集群(多台服务器)时,用户第一次请求在服务器 A 创建了 Session,第二次请求被负载均衡分到服务器 B——B 上没有这个 Session,用户就「掉登录」了。这就是分布式 Session 问题。解决方案有几种:① 粘性会话(Sticky Session)——负载均衡按用户「固定分配」到同一台服务器(如按 IP hash),保证一个用户的请求都落到同一台;简单但服务器宕机会丢 Session、负载可能不均;② Session 复制(Replication)——每台服务器把自己的 Session 复制同步给其他所有服务器(每台都有全量 Session);不丢 Session 但复制开销大、占内存、服务器多了不可扩展;③ 集中存储(推荐)——把 Session 存到外部的共享存储(如 Redis),所有服务器都从 Redis 读写 Session;任何服务器都能拿到 Session、服务器无状态、可水平扩展,是主流方案(Spring Session + Redis 一行配置搞定);④ 客户端存储(JWT)——干脆不用服务端 Session,用 JWT 把状态放客户端(无状态、天然分布式,但有 JWT 的失效难题)。核心:粘性会话简单但不可靠、复制不可扩展、集中存储(Redis)是主流、JWT 是无状态替代。
详细版
分布式 Session 方案对比:
| 方案 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| 粘性会话 | 同用户固定一台服务器 | 简单、无额外存储 | 宕机丢 Session、负载不均 |
| Session 复制 | 各服务器互相同步 Session | 不丢 Session | 复制开销大、不可扩展 |
| 集中存储(Redis) | Session 存 Redis,共享 | 无状态、可扩展、可靠 | 依赖 Redis、有网络开销 |
| 客户端(JWT) | 状态放客户端 Token | 无状态、天然分布式 | JWT 失效难、Token 变大 |
// Spring Session + Redis(集中存储,最主流,几乎零代码)
// 1. 引入依赖
// spring-session-data-redis + spring-boot-starter-data-redis
// 2. 配置
// spring.session.store-type=redis
// 3. 加注解(或 Spring Boot 自动配置)
@EnableRedisHttpSession // 开启 Redis 存储 Session
public class SessionConfig { }
// 之后代码不变,还是用 HttpSession
session.setAttribute("user", user); // 底层存到 Redis(所有服务器共享)
User u = (User) session.getAttribute("user"); // 从 Redis 读
// → 用户请求到任何一台服务器,都能从 Redis 拿到 Session
分布式 Session 问题:
用户 → 负载均衡 → 服务器A(创建 Session,存 A 的内存)
用户再请求 → 负载均衡 → 服务器B(B 内存里没这个 Session)
→ 用户"掉登录"(Session 找不到)
集中存储解决:
服务器A、B 都从 Redis 读写 Session
→ 用户请求到 A 或 B,都能从 Redis 拿到 Session(共享)
⚠️ 分布式 Session 的主流答案是「集中存储(Redis)」——它让服务器「无状态」,是水平扩展的关键。粘性会话虽然简单,但有致命缺陷:服务器宕机,落在它上面的所有用户 Session 都丢了(用户全部掉登录),且负载可能不均(某台被分到很多长会话用户)。Session 复制在服务器少时能用,但每台都存全量 Session(占内存)、每次 Session 变化要同步给所有服务器(复制风暴),服务器越多越不可扩展(N 台要 N-1 次复制)。而集中存储把 Session 从「服务器内存」搬到「外部共享存储(Redis)」——服务器本身不存 Session(无状态),任何服务器都能从 Redis 读写,所以可以随意加减服务器(水平扩展)、某台宕机不影响 Session。Spring Session + Redis 让这个方案几乎零成本(加个依赖、加个注解、配一下 Redis,代码里还是用
HttpSession,底层自动存 Redis)。所以现代分布式系统要么用 Redis 集中存 Session、要么用 JWT 无状态,很少用粘性会话或复制。
完整版教学
一、问题:集群下 Session 找不到
先理解分布式 Session 问题:
单机时的 Session(没问题):
用户登录 → 服务器创建 Session(存在服务器内存)
浏览器存 Session ID(Cookie: JSESSIONID)
后续请求带 Session ID → 服务器用它找到 Session → 认得用户
集群时的问题(多台服务器):
用户 → 负载均衡 → 服务器A(登录,Session 存 A 的内存)
用户再请求 → 负载均衡 → 分到服务器B
→ B 的内存里没有这个 Session!
→ B 找不到 Session → 用户"掉登录"(以为没登录)
根源:
Session 存在单台服务器的内存里
但负载均衡可能把同一用户的请求分到不同服务器
→ 其他服务器没有这个 Session
所以分布式 Session 问题 = Session 存单机内存、集群下其他服务器拿不到
→ 用户请求分到没有 Session 的服务器 → 掉登录
分布式 Session 问题——单机时 Session 存服务器内存没问题(用 Session ID 找到)。集群时:用户在服务器 A 登录(Session 存 A 内存)、再请求被分到服务器 B、B 内存里没这个 Session、找不到、用户掉登录。根源:Session 存单台服务器内存、负载均衡可能把同一用户分到不同服务器、其他服务器没有这个 Session。理解「分布式 Session 问题:单机 Session 存内存没问题、集群时用户在 A 登录 Session 存 A 内存、再请求分到 B 找不到 Session 掉登录;根源 Session 存单机内存集群下其他服务器拿不到」,就理解了问题。
二、粘性会话
粘性会话(Sticky Session)——把用户固定到一台服务器:
粘性会话(Sticky Session / Session Affinity):
负载均衡"按用户固定分配"到同一台服务器
→ 保证一个用户的所有请求都落到同一台
→ 那台服务器一直有这个用户的 Session
实现(负载均衡的策略):
① 按 IP hash:同一 IP 的请求分到同一台
② 按 Cookie:负载均衡记住"这个用户分到哪台"(如加个 route cookie)
→ Nginx 的 ip_hash、或基于 cookie 的会话保持
优点:
① 简单——不用额外存储(Session 还在服务器内存)
② 无 Session 同步/存储开销
缺点(致命):
① ★ 服务器宕机 → 落在它上面的所有用户 Session 都丢了
→ 那些用户全部掉登录
② 负载可能不均——某台被分到很多长会话用户
③ 扩缩容时 Session 会乱(加减服务器,用户重新分配)
适用:
简单场景、能容忍宕机丢 Session、服务器稳定
→ 不推荐用于要求高可用的系统
所以粘性会话简单但不可靠(宕机丢 Session、负载不均)
粘性会话(Sticky Session)——负载均衡按用户固定分配到同一台服务器(保证一个用户所有请求落同一台、那台一直有 Session)。实现:按 IP hash(Nginx ip_hash)或按 Cookie(会话保持)。优点:简单(不用额外存储)、无同步开销。缺点(致命):① 服务器宕机落在它上面的所有用户 Session 都丢(全部掉登录)、② 负载可能不均、③ 扩缩容 Session 乱。适用简单场景、能容忍宕机丢 Session、不推荐高可用系统。理解「粘性会话:负载均衡按用户固定分配到同一台(IP hash/Cookie);优点简单无同步开销;缺点致命:服务器宕机丢所有 Session+负载不均+扩缩容乱;不推荐高可用」,就掌握了粘性会话。
三、Session 复制
Session 复制(Replication)——服务器间互相同步 Session:
Session 复制(Session Replication):
每台服务器把自己的 Session 复制/同步给其他所有服务器
→ 每台服务器都有"全量 Session"(所有用户的)
→ 用户请求到任何一台,都能找到 Session
实现:
Tomcat 的集群 Session 复制(DeltaManager / BackupManager)
→ 通过组播/单播在服务器间同步 Session 变化
优点:
① 不丢 Session——每台都有全量,某台宕机其他还有
② 用户请求到任何一台都能找到
缺点(不可扩展):
① ★ 复制开销大——每次 Session 变化要同步给所有服务器
N 台服务器 → 每次变化要复制 N-1 次
→ 服务器越多,复制流量越大(复制风暴)
② 占内存——每台都存全量 Session(N 台存 N 份)
③ 不可扩展——服务器数量增加,复制成本急剧上升
→ 只适合"少量服务器"(如 2-4 台)
适用:
服务器数量少、Session 数据小、要不丢 Session
→ 服务器多了就不行(复制风暴)
所以 Session 复制不丢 Session 但复制开销大、不可扩展
Session 复制(Replication)——每台服务器把自己的 Session 复制同步给其他所有服务器(每台都有全量 Session、用户请求到任何一台都能找到)。实现:Tomcat 集群 Session 复制(DeltaManager/BackupManager,组播/单播同步)。优点:不丢 Session(每台都有全量)、任何一台都能找到。缺点(不可扩展):① 复制开销大(每次 Session 变化同步给所有服务器、N 台复制 N-1 次、复制风暴)、② 占内存(每台存全量)、③ 不可扩展(服务器多复制成本急剧上升)。适用服务器少(2-4 台)。理解「Session 复制:每台把 Session 同步给其他所有服务器(每台全量);优点不丢 Session;缺点不可扩展:复制开销大(N 台复制 N-1 次复制风暴)+占内存+服务器多不行;只适合少量服务器」,就掌握了 Session 复制。
四、集中存储(推荐)
集中存储(Redis)——把 Session 存外部共享存储,主流方案:
集中存储(Centralized Session Storage):
把 Session 从"服务器内存"搬到"外部共享存储"(如 Redis)
→ 所有服务器都从这个共享存储读写 Session
→ 服务器本身不存 Session(无状态)
原理:
用户请求到服务器A → A 从 Redis 读/写 Session(用 Session ID 找)
用户请求到服务器B → B 也从 Redis 读/写同一个 Session
→ 所有服务器共享 Redis 里的 Session
优点(主流原因):
① ★ 服务器无状态——不存 Session,可以随意加减(水平扩展)
② 可靠——某台服务器宕机不影响 Session(Session 在 Redis)
③ 可扩展——加服务器不增加 Session 存储成本(都用一个 Redis)
④ Redis 快、支持过期(Session 过期自动清理)
缺点:
① 依赖 Redis(Redis 挂了 Session 都没了 → Redis 要高可用)
② 有网络开销(每次读写 Session 访问 Redis,但 Redis 快、可忽略)
Spring Session + Redis(几乎零成本):
引入 spring-session-data-redis
配置 spring.session.store-type=redis
→ 代码不变(还是用 HttpSession),底层自动存 Redis
→ 一行配置,Session 就集中存储了
所以集中存储(Redis)是主流:无状态、可扩展、可靠、Spring Session 零成本
集中存储(Redis)——把 Session 从服务器内存搬到外部共享存储(Redis),所有服务器从 Redis 读写(服务器本身不存 Session、无状态)。原理:用户请求到任何服务器都从 Redis 读写同一个 Session。优点(主流原因):① 服务器无状态可随意加减(水平扩展)、② 可靠(某台宕机不影响 Session)、③ 可扩展(加服务器不增加存储成本)、④ Redis 快支持过期。缺点:依赖 Redis(要高可用)、有网络开销(可忽略)。Spring Session + Redis 几乎零成本(引入依赖+配置、代码不变还是用 HttpSession、底层自动存 Redis)。理解「集中存储 Redis:Session 存 Redis 所有服务器共享(服务器无状态);优点无状态可水平扩展+可靠(宕机不影响)+可扩展+Redis 快;缺点依赖 Redis;Spring Session+Redis 零成本(代码不变底层存 Redis);主流方案」,就掌握了集中存储。
五、客户端存储(JWT)
客户端存储(JWT)——不用服务端 Session,状态放客户端:
客户端存储(JWT):
干脆不用服务端 Session——把状态放客户端(JWT Token)
→ 用户信息 + 签名都在 Token 里,客户端存、每次请求带
→ 服务端不存 Session(无状态、天然分布式)
原理:
登录 → 服务端签发 JWT(含用户信息)→ 客户端存
后续请求带 JWT → 服务端验签 + 查过期 → 认得用户
→ 服务端不存任何东西,任何服务器都能验 JWT(无状态)
优点:
① 无状态——服务端不存 Session,天然分布式(任何服务器都能验)
② 无需共享存储(不用 Redis 存 Session)
③ 易水平扩展
缺点:
① JWT 失效难——签发后无法主动失效(见登出题)
要黑名单(又引入状态)或短过期
② Token 变大——用户信息都在 Token 里,每次请求带(带宽)
③ 安全——Token 泄露风险(要 HTTPS、短过期)
JWT vs Redis 集中存储:
Redis:有状态(存 Redis),能主动失效,但依赖 Redis
JWT:无状态,天然分布式,但失效难
→ 看是否需要"主动失效"和"服务端存储"
所以 JWT 是无状态的分布式方案(不用 Session),但有失效难题
客户端存储(JWT)——不用服务端 Session,把状态放客户端(JWT Token,用户信息+签名在 Token 里、客户端存、每次请求带、服务端不存 Session、无状态、天然分布式)。原理:登录签发 JWT、后续请求验签查过期认得用户(服务端不存、任何服务器都能验)。优点:无状态天然分布式、无需共享存储、易扩展。缺点:JWT 失效难(签发后无法主动失效、要黑名单或短过期)、Token 变大、安全风险。JWT vs Redis:Redis 有状态能主动失效但依赖 Redis、JWT 无状态天然分布式但失效难。理解「客户端存储 JWT:不用服务端 Session、状态放客户端 Token(服务端不存无状态天然分布式);优点无状态易扩展无需共享存储;缺点 JWT 失效难(要黑名单/短过期)+Token 变大;vs Redis:Redis 有状态能失效、JWT 无状态但失效难」,就掌握了客户端存储。
六、选择与实践
总结分布式 Session 的选择:
选择(按需求):
要简单、能容忍宕机丢 Session、服务器少 → 粘性会话(不推荐)
服务器少(2-4 台)、要不丢 Session → Session 复制(有限场景)
★ 主流推荐 → 集中存储(Redis + Spring Session)
→ 无状态、可扩展、可靠、零成本
无状态、REST API、天然分布式 → JWT(客户端存储)
现代主流做法:
① 传统 Session 场景(要用 HttpSession)→ Spring Session + Redis
(代码不变、底层存 Redis、集中存储)
② 前后端分离 / REST API → JWT(无状态)
→ 很少用粘性会话或 Session 复制
实践建议:
① 用 Spring Session + Redis(集中存储,主流、零成本)
② 或用 JWT(无状态,前后端分离场景)
③ Redis 要高可用(Session 依赖它)
④ 别用粘性会话(宕机丢 Session)或复制(不可扩展)
核心总结:
分布式 Session 问题:Session 存单机、集群下其他服务器拿不到
方案:粘性会话(简单不可靠)/复制(不可扩展)/集中存储 Redis(主流)/JWT(无状态)
主流:Redis 集中存储(Spring Session 零成本)或 JWT
分布式 Session 选择:要简单能容忍宕机丢 Session→粘性会话(不推荐)、服务器少要不丢→复制(有限场景)、主流推荐→集中存储(Redis+Spring Session,无状态可扩展可靠零成本)、REST API 无状态→JWT。现代主流:传统 Session 场景用 Spring Session+Redis(代码不变底层存 Redis)、前后端分离用 JWT、很少用粘性会话或复制。实践:用 Spring Session+Redis 或 JWT、Redis 要高可用、别用粘性会话或复制。理解「选择:粘性会话(不推荐)/复制(有限)/集中存储 Redis(主流)/JWT(无状态);现代主流 Spring Session+Redis 或 JWT、很少用粘性会话或复制;Redis 要高可用」,就掌握了选择和实践。
记忆钩子:「分布式 Session 问题:Session 存单机内存、集群下用户请求分到其他服务器拿不到 Session 掉登录;方案:①粘性会话 Sticky Session(负载均衡按用户固定一台 IP hash/Cookie,简单但服务器宕机丢所有 Session+负载不均,不推荐)②Session 复制(每台同步给所有服务器每台全量,不丢 Session 但复制开销大 N 台复制 N-1 次复制风暴+占内存+不可扩展,只适合少量服务器)③★集中存储 Redis(Session 存 Redis 所有服务器共享、服务器无状态可水平扩展+可靠宕机不影响+Spring Session+Redis 零成本代码不变底层存 Redis,主流)④客户端 JWT(状态放客户端 Token 服务端不存无状态天然分布式,但失效难 Token 变大);主流:Redis 集中存储或 JWT,别用粘性会话/复制」。
七、常见误区与追问
- 误区:粘性会话是分布式 Session 的好方案。 有致命缺陷——服务器宕机会导致落在它上面的所有用户 Session 丢失(全部掉登录)、负载可能不均、扩缩容时 Session 会乱;只适合简单、能容忍宕机丢 Session 的场景;高可用系统应该用集中存储(Redis)或 JWT。
- 误区:Session 复制适合大集群。 不适合——Session 复制每次 Session 变化要同步给所有服务器,N 台服务器每次变化要复制 N-1 次,服务器越多复制流量越大(复制风暴),且每台都存全量 Session(占内存);只适合少量服务器(2-4 台);大集群要用集中存储(一个 Redis 共享)。
- 误区:用 Redis 存 Session 要大改代码。 不用——Spring Session + Redis 几乎零成本:引入 spring-session-data-redis 依赖、配置 spring.session.store-type=redis(或加 @EnableRedisHttpSession),代码里还是用 HttpSession(session.setAttribute/getAttribute 不变),底层自动把 Session 存到 Redis、所有服务器共享。
- 误区:JWT 和 Redis 集中存储都是有状态的。 不同——Redis 集中存储是有状态的(Session 存在 Redis,服务端能主动删除/失效);JWT 是无状态的(状态在客户端 Token 里,服务端不存,任何服务器验签即可),天然分布式但 JWT 签发后无法主动失效(要黑名单或短过期);一个有状态能失效、一个无状态但失效难。
- 追问:分布式环境下 Session 为什么会有问题,怎么解决? 单机时 Session 存在服务器内存里没问题;集群时,用户第一次请求在服务器 A 创建了 Session(存 A 内存),第二次请求被负载均衡分到服务器 B,B 内存里没有这个 Session,找不到就导致用户掉登录;解决方案:① 粘性会话(负载均衡把同用户固定到一台,简单但宕机丢 Session);② Session 复制(服务器间互相同步,不丢但不可扩展);③ 集中存储(Session 存 Redis 等外部共享存储,所有服务器共享,服务器无状态、可扩展、可靠,主流,Spring Session + Redis 零成本);④ JWT(状态放客户端,无状态、天然分布式)。
- 追问:为什么集中存储(Redis)是分布式 Session 的主流方案? 因为它让服务器「无状态」——Session 从服务器内存搬到外部共享存储(Redis),服务器本身不存 Session,任何服务器都能从 Redis 读写同一个 Session;好处:① 可以随意加减服务器(水平扩展,加服务器不增加 Session 存储成本);② 某台服务器宕机不影响 Session(Session 在 Redis,其他服务器照样能拿到);③ Redis 快、支持过期(Session 自动清理);④ Spring Session + Redis 几乎零成本(代码不变);相比粘性会话(宕机丢 Session)和复制(不可扩展),集中存储可靠且可扩展。
- 追问:Session 集中存储和 JWT 怎么选? Session 集中存储(Redis):有状态(Session 存 Redis),能主动失效(服务端删 Session)、支持并发会话控制、和传统 Session 编程模型一致(用 HttpSession),但依赖 Redis(要高可用);JWT:无状态(状态在客户端 Token),天然分布式(不用共享存储)、易水平扩展,但 JWT 签发后无法主动失效(要黑名单或短过期)、Token 变大;如果需要主动失效、并发会话控制、传统 Session 模型 → Redis;如果追求无状态、REST API、前后端分离、不想依赖 Redis → JWT;很多系统混用(JWT + Redis 黑名单)。
八、加强记忆
分布式 Session 问题:单机时 Session 存服务器内存没问题,但集群时用户在服务器 A 创建 Session(存 A 内存),再请求被分到服务器 B,B 内存里没这个 Session、用户掉登录。解决方案:① 粘性会话(Sticky Session)——负载均衡把同用户固定到一台(IP hash/Cookie),简单但服务器宕机丢所有 Session、负载不均(不推荐);② Session 复制(Replication)——每台服务器把 Session 同步给其他所有(每台全量),不丢 Session 但复制开销大(N 台复制 N-1 次、复制风暴)、占内存、不可扩展(只适合少量服务器);③ 集中存储(Redis,主流推荐)——Session 存 Redis,所有服务器共享,服务器无状态、可水平扩展、可靠(宕机不影响)、Spring Session + Redis 零成本(代码不变、底层存 Redis);④ 客户端存储(JWT)——状态放客户端 Token,服务端不存(无状态、天然分布式),但 JWT 失效难、Token 变大。现代主流:传统 Session 场景用 Spring Session + Redis、前后端分离用 JWT,很少用粘性会话或复制。一句话「分布式 Session 问题:Session 存单机集群下其他服务器拿不到掉登录;粘性会话(固定一台,宕机丢 Session 不推荐)/Session 复制(互相同步,不可扩展复制风暴)/集中存储 Redis(共享,服务器无状态可扩展可靠,Spring Session 零成本,主流)/JWT(状态放客户端,无状态天然分布式但失效难);主流 Redis 集中存储或 JWT」。