データベースを用いたセッションデータ管理について

Web アプリケーションとは切っても切れないセッション機構。DB ベースでセッション管理を行なって得られた知見と、それを元に考察した結果をまとめてみます。

セッションデータの特性

DB で管理される他のデータに比べ、セッションデータはかなり特殊です。主な特徴は次のような感じ。

  1. データが増加するのが速い
    • 定期的な削除が必要
  2. 頻繁に更新される
  3. リクエスト毎に読みに行く必要がある
    • このデータを読めないとアプリケーション全体にアクセスできない


アクセス頻度が高いということです。あと、1つ目の特徴からセッションデータについては意識的に管理してやる必要があります。

現在の環境

アプリケーションの領域が少し特殊で、セッションデータがやたらたまります(ユーザ数何百万のサービスとかそういうのではないです)。

  • RDBMS
  • ストレージエンジン
  • レコード数
    • 6千万
  • テータサイズ
    • 25G
  • 新規セッション数/day
    • 100万
  • 有効期限
    • 2ヶ月

テーブル設計上の注意

InnoDB を使う

これはmust。InnoDB でなくてもいいですが、「行ロック」をサポートしているのが重要。

  • insert/update が多いのでMyISAM だとテーブルロックが発生しまくる。これらの処理はwrite ロック がかかるので、read 処理もブロックされて最悪
  • 行ロックなのでinsert やupdate とdelete が競合することがない。当然、select とは競合しない
    • delete と競合しないというのは削除処理を行う上で重要
  • mysqldump 時にテーブルをロックする必要がない
    • --single-transaction オプション
  • 行ロック機構を使うには、プライマリーキーかユニーク制約を持つカラムが必要

メモ: InnoDBMyISAM

セッションテーブルだからというより、大抵のテーブルの場合InnoDB を使って良さそう。トランザクションや外部キー制約もあるし。
MyISAM は速いと言われますが、ロックがテーブルレベルなのは結構痛い。この辺りについてはnaoya さんの記事やkazuhooku さんの記事が参考になる。


実際にストレージエンジンを選択する際にはアプリケーションで実際に使うデータを入れてベンチマークを取りながら選定するのがいい。
管理の面から見ても、InnoDB もテーブル毎にデータ(+インデックス)ファイルを持つようにすれば、MyISAM と比較してもそれほど苦にならないと思われる。

サーバ構成

  • セッション専用のDB サーバを設置
    • マスターDB 内にセッションデータがあると、他のデータにも負荷の影響が及ぶ可能性があるため
  • セッションについてはレプリケーションはしてない
    • した方がより良いと考えている(後述)
  • メモリ
    • 10G
  • innodb_buffer_pool_size
    • 2G
  • innodb_log_file_size
    • 128M
  • innodb_log_files_in_group
    • 2
メモリ10G に対してinnodb_buffer_pool_size 2G な理由

innodb_buffer_pool_size はメモリの80% くらい割りあてても言いと言われている。それでも今2G しかあててない理由は次の通り。

警告: Linux x86 では、メモリ使用率の設定を高くし過ぎないように注意してください。glibc はプロセスヒープがスレッドスタックよりも大きくなることを許可しており、その場合にサーバがクラッシュします。次の計算式を見てください。

innodb_buffer_pool_size + key_buffer_size +
max_connections * (sort_buffer_size + read_buffer_size) + max_connections * 2 MB

この値が、2 GB に近いか、2 GB を超えていると危険です。各スレッドはスタックを使用し(通常は 2 MB。ただし MySQL AB バイナリでは 256 KB のみ)、最悪の場合、sort_buffer_size + read_buffer_size の大きさの追加メモリも使用します。


実はここに関する理解はかなり曖昧な部分があって、調査して追記します*1。とくにメモリ利用量について。mysqld のプロセスが利用できるメモリの上限が、32bit OS の場合は3G 程度ということは、innodb_buffer_pool_size もこの制限を受け、これについての警告が、先に紹介したリファレンスマニュアルのものという理解だけどいいのだろうかというのが1つ。
2つ目は、この理解があっているとすると、4G 以上のクラスのメモリをつんだサーバをDB サーバとして利用する場合、64 bit OS でないとリソースの有効活用ができないか。それとも、先に書いたとおり、OS レベルのキャッシュとして利用できるから、結果としてデータファイルを読み込む際のI/O 負荷軽減になるか。

運用

定期的なデータの削除
DELETE FROM sessions where timestamp =< '2009-03-01 00:00:00' LIMIT 1000;
    • 一気に削除するとI/O 負荷が高いので、トランザクションを1000 ずつに分割
    • このSQL を条件にマッチするレコードがなくなるまでループ処理
    • ループ間にsleep 1 入れる
  • この設定で、現在スクリプトの実行に1時間弱かかる
  • 流石に6千万レコードから昔のレコードを削除するのは負荷が高い
    • mytop で監視していると、一回のdelete に5秒かかる時もある
  • 削除中のI/0 負荷はsar で見るとiowaite 30% くらい。LA が2〜2.5

InnoDB のおかげで削除中も、insert やupdate がブロックされることはない。仮にセッションDB にクエリを投げているweb サーバのプロセスに影響が及ぶとしたら単純にI/O 負荷のせい。
現在削除中の負荷は高めだけど、深夜に実行していることもあり、サービスのパフォーマンスに影響がでるレベルには至っていない。

データ削除処理を改善するには
  • 1. sleep 時間を長くする

対処療法。
ロックによるブロックはないので、削除処理には長めの時間をとっても問題ないと思われる(流石に日中にやるのは問題があると思うけど)。1回のdelete が走る間隔を長めにとればI/O 負荷は軽減できるはず。

  • 2. セッションライフタイムを短かくする

これも対処療法。例えばライフタイムを現在の半分の1ヶ月 にすれば、データ量も半分になる。削除処理の負荷は軽くなる。

漢(オトコ)のコンピュータ道: パーティショニングの使用例 - http session情報参考。

MySQL を使わないけど、セッション管理に要求される機能をかなりのレベルで満たしている。mixi Engineers’ Blog » DBMによるテーブルデータベース その五参考。

    • memcachedのように高速かつ並列に操作できる
    • メモリでなくファイルにデータを格納することでデータを永続化でき、実メモリ容量以上のデータも扱える
    • 不要な(古い)データは勝手に消え、データサイズを一定に保つことができる
バックアップ

mysqldump でバックアップ。

$ mysqldump --single-transaction --flush-logs --disable-keys --quick --extended-insert session_db sessions > session_dump.sql
  • --single-transaction オプションを利用してdump 中にテーブルにロックをかけない
  • dump に10分弱かかる
  • フルリストアする場合30 分ほどかかる
バックアップを改善するには

レプリケーションを使う。スレーブがあれば、マスタ障害時にも迅速に対応できるし、dump もスレーブ側で実行できて便利。


その他参考