7.2.2 什么是Session共享

随着网站业务规模和访问量的逐步增大,原本由单台服务器、单个域名组成的迷你网站架构可能已经无法满足发展的需要了。此时可能需要购买更多的服务器,并且以频道化的方式启用多个二级子域名,然后根据业务功能将网站分别部署在独立的服务器上,或者通过负载均衡技术(如HAProxy、Nginx)让多个频道共享一组服务器。

如果我们把网站程序分别部署到多台服务器上,而且独立为几个二级域名,由于Session存在实现原理上的局限性(PHP中Session默认以文件的形式保存在本地服务器的硬盘上),因此使得网站用户不得不在几个频道间来回输入用户名和密码登入,导致用户体验大打折扣;另外,原本程序可以从用户Session变量中直接读取的资料(如:昵称、积分、登入时间等),因为无法跨服务器同步更新Session变量,迫使开发人员必须实时读写数据库,从而增加了数据库的负担。于是,解决网站跨服务器的Session共享问题的需求变得迫切起来,最终催生了多种解决方案,下面将列举4种较为可行的方案来进行对比和探讨。

广告:个人专属 VPN,独立 IP,无限流量,多机房切换,还可以屏蔽广告和恶意软件,每月最低仅 5 美元

1.基于Cookie的Session共享

这个方案部分读者可能会觉得比较陌生,但它在大型网站中已被普遍应用了。其原理是将全站用户的Session信息加密、序列化后以Cookie的方式统一种植在根域名下(如:.host.com)。当浏览器访问该根域名下的所有二级域名站点时,将与域名相对应的所有Cookie内容的特性都传递给它,从而实现用户的Cookie化Session在多服务器间的共享访问。

该方案的优点是无需额外的服务器资源;缺点是由于受HTTP协议头信息长度的限制,仅能够存储小部分用户的信息,同时Cookie化的Session内容需要进行安全加解密(如:采用DES、RSA等进行明文加解密;再由MD5、SHA-1等算法进行防伪认证),另外它也会占用一定的带宽资源,因为浏览器会在请求当前域名下的任何资源时将本地Cookie附加在HTTP头中传递到服务器上。

2.基于数据库的Session共享

首选当然是大名鼎鼎的MySQL数据库,并且建议使用内存表Heap,以提高Session操作的读写效率。这个方案的实用性比较强,已被普遍使用,它的缺点在于Session的并发读写能力取决于MySQL数据库的性能,同时需要我们自己来实现Session淘汰逻辑,以便定时地从数据表中更新、删除Session记录,当并发过高时容易出现表锁,虽然我们可以选择行级锁的表引擎,但不得不承认使用数据库存储Session还是有些杀鸡用牛刀的架势。

3.Session复制

熟悉Tomcat或Weblogic的朋友对Session复制应该是非常熟悉和了解了,它从字面意义上也非常好了解。Session复制,就是将用户的Session复制到Web集群内的所有服务器上,Tomcat或Weblogic自身都带有这种处理机制。但缺点也很明显:随着机器数量的增加,网络负担将成指数级上升,性能随着服务器数量的增加而急剧下降,而且很容易引起网络风暴。

4.基于Memcache/redis的Session共享

Memcache是一款基于Libevent的多路异步I/O技术的内存共享系统,简单的Key+Value数据存储模式使其代码逻辑小巧高效,因此在并发处理的能力上占据了绝对优势。

另外值得一提的是,Memcache的内存Hash表所特有的Expires数据过期淘汰机制,正好和Session的过期机制不谋而合,这就降低了删除过期Session数据的代码复杂度。但对比“基于数据库的存储方案”,仅逻辑这块就给数据表带来了巨大的查询压力。

redis作为NoSQL的后起之秀,经常被拿来与Memcached做对比。redis作为一种缓存,或者干脆称之为NoSQL数据库,提供了丰富的数据类型(list、set等),可以将大量数据的排序从单机内存解放到redis集群中进行处理,并且可以用于实现轻量级消息中间件。从性能上来说,redis在对小于100KB的数据进行读写时,其速度优于Memcached。在笔者的许多线上业务系统中,redis已经取代Memcached来存放Session数据了。