Rails SQL Session Store优化版

问题根源

原始的ActiveRecord会话仓库很慢。对于低流量的网站而言没有什么问题,但是对于大一点的而言就慢了。首先,它的慢是因为ActiveRecord本身比较慢。虽然这是一个强大的ORM框架,但对于像会话管理这种简单的任务而言就是杀鸡用牛刀了。

还有其他的解决方案如cookie会话仓库(会话长度有限,不能在会话中存放敏感数据),memcached(无法持久化+难以实现高可用性方案)。

这就是为何要创建SqlSession仓库的原因。它直接操作mysql的数据库API,要比原始的AR会话仓库快很多。不过有时候它还是比较慢,因为:

  • 每次访问都会创建、更新会话 – 任何机器人或者偶然的访客都会在数据库中创建一条会话记录,最后导致会话表里面会有成千上万的无用记录,但其中99%的访问其实是不需要任何会话更新的。
  • 它使用32位字符串作为会话记录的键 – 所有RDBMS在处理字符串索引都要比整数慢很多,所以使用整数更好,然而我们的会话ID非常长,同时这些会话仓库都直接使用他们作为表索引。
  • 使用了auto_increment 主键,对于MySQL 5.1.21之前的版本都会导致InnoDB使用表级锁。在不必要的插入上使用表级锁会给大型网站造成奇怪的问题。

解决方案

FastSessions Rails插件便是作为对于以上几个问题的解决方案而诞生的。

首先,我们从会话表中去掉了id字段,所以我们无须用到auto-increment锁。接下来为了让查找更快,我们使用了以下这些技术:不使用(session_id)作为查找索引,使用(CRC32(session_id), session_id)——双列键,可以帮助MySQL更快地找到会话记录因为键的基数更高(这样,mysql可以更快地找到记录而不用检查很多索引行)。我们测试过这种方法,在大会话表中显示了10-15%的性能提升

最后,也是最强的优化是,对于空会话不创建数据库记录,同时如果数据没有在请求处理过程中没有更新过,则会话数据不会存回数据库。这个更改基本上可以减少50-90%的插入数量(根据应用程序的情况)。

那么,你肯定在想,用了这个插件之后会话究竟能快多少?这很难说。结果要看你是怎么操作会话的:如果你很少更改会话数据(如登录时储存用户id),那么可能会有90%的性能提升,但如果每次访问,你都要给用户会话写入些信息(比如最后一次访问时间last_visit_time),那么根据服务器的负载和会话表的大小,可能有5-15%的性能提升。

你只需要安装一个简单的插件,便可以自动实现上面这些更改。

我们解决了AUTO_INC锁的问题并去掉auto-increment键之后,又引入了一个新的问题,我在这里想说一下。这个问题是这样的。InnoDB会根据主键将所有数据分组。这意味着当我们使用自增长主键并向表插入记录时,会话记录会被组到一起,顺序地存储在磁盘上。然而如果使用比较随机的值(如一个随机会话id的crc32值)作为主键,那么每一个会话记录会被插入到属于它自己的不同的地方,那么会产生一些随机I/O,这对于I/O有限制的服务器不是很理想。所以,我们决定让用户自己选择在部署的时候使用何种主键,当你想在MySQL 5.1.22+上使用这个模块的话,可以设置

CGI::Session::ActiveRecordStore::FastSessions.use_auto_increment = true

这样在InnoDB中会连贯地插入数据。另一个情况当你的MySQL服务器的I/O有限制,不想因为随机主键而增加随机I/O,也可以这样设置。

如果你不想丢失使用AR会话插件创建的旧会话数据,你可以设置

CGI::Session::ActiveRecordStore::FastSessions.fallback_to_old_table = true

这样当某些session_id在新的会话表中找不到的时候,就会回去访问旧的会话表。旧会话表的名字可以使用CGI::Session::ActiveRecordStore::FastSessions.old_table_name 变量进行设置.

这个选项会使会话变慢所以我建议只要在升级到新会话表的时候才使用。在这种情况下,新的会话数据就会存入新表中,当到了会话超时期限的时候,就可以删除旧表了(我们用了两个月的期限,过了两个月之后,我们就可以删除旧表,并将此选项关闭。)。

安装

安装FastSessions插件十分简单,只需以下几个步骤:

  1. 将该插件代码从我们的SVN库中安装到vendor/plugins目录中(可以使用./script/plugin install安装,或者piston import命令进行安装——看你喜欢)例如:

    $ piston import http://rails-fast-sessions.googlecode.com/svn/trunk/ vendor/plugins/fast_sessions
  2. 在config/environment.rb文件中启用ActiveRecord会话仓库:

    Rails::Initializer.run do |config|
    ......
    config.action_controller.session_store = :active_record_store
    ......
    end
  3. 为新的会话表创建数据库迁移:

    $ ./script/generate fast_session_migration AddFastSessions
  4. 如果需要,可以打开新创建的迁移脚本并更改表名table_name和插件use_auto_increment参数。
  5. 运行数据库迁移:

    $ rake db:migrate
  6. 启动应用程序并尝试进行一定会保存数据到会话的操作。然后检查fast_sessions会话表(如果你没有改名字的话)有没有这条记录。

下载

该插件的最新版本可以在它的项目网站或在 SVN仓库中找到。该插件由Alexey KovyrinPercona的MySQL性能专家)制作。开发由Scribd.com赞助。

“Rails SQL Session Store优化版”的5个回复

  1. 貌似这个plugin从08年2月份上线后就没有在更新过了,在issues列表里面写到,跟rails 2.2.2不兼容….
    说实话,无法认同对cookiebase session的看法,这些都不是什么问题,或者是可以轻松解决的问题,其实cookie session提供的空间足够了,如果不够用,还有一大堆办法可以解决…其次是将session存放到文件系统,最次才是存放session到数据库…偶这么认为…

  2. Daniel lv 很高兴这里也能见到你的足迹. 有内涵.哈哈哈

    我觉得session放在数据库里肯定是要考虑他的有点. 比如:我最近希望能够 通过用户的session_id 传递给其他的应用 ,让别的应用 反正能够通过方法来鉴别 这个session_id的用户是谁. 这种时候,数据库的优势就出来了.—— 可以方便查询阿~~
    文件的那个加密我就不知道怎么解密, 查起来也麻烦. cookie根本不用说, 服务端没有记录.

    是吧.. 我也很苦恼~被你这么你说好像又没有一个好的方案了
    2.2.2后面有个人发了个补丁. 可惜现在是2.3.2 今天一试, 上手就出错.哈哈.

发表评论

电子邮件地址不会被公开。 必填项已用*标注