ユーザーの閲覧履歴をDB(MySQL)からBigQueryに移すにあたり、既存のDBを使っている「最近見たお仕事」を表示する機能も変更が必要になりました。
「最近見たお仕事」は文言は異なるものの、よくECなどでも「最近チェックした商品」、不動産系では「最近見た物件」などと表示されてるやつです。
BigQueryに入っている履歴を使うのも考えましたが大きく以下の3点から別の実装を検討しました。
- スキャン料金を抑えたい
- クエリに時間がかかる
- タイムラグがあるためさっき見たページが表示されない可能性がある
セッションも考えたけど、同じくDBに入ってしまうので、負荷がかかるのでいまいち。
そこで考えたのがRedis。ずっと残しておく必要も無いデータなのでちょうど良さそう。
ハッシュ型でもできそうですが、いろいろ使える型を見ているとSorted Setが今回の用途にぴったりでした。
RedisのSorted Setとは
一つのキーに対してスコアを持った複数の値(メンバーと呼ばれる)を保存でき、常にスコア順に入っているのでスコア順で高速に取得ができます。
またSetなので、重複は入りません。最近みたページには丁度いい
その特徴からよくランキングに使われています。
qiita.com
今回はスコアにタイムスタンプ値を使うことで「最近見たお仕事」を実装しました。
保存する処理
ZADDコマンドを使います。
使用する言語、ライブラリなどにより異なりますが、生のRedisコマンドで書くとこんな感じ。
redis> ZADD recenrtyview:{ユーザーID} タイムスタンプ値 "{ページID}"
適当なprefix (上記だとrecenrtyview)とユーザーIDをキーとして、事前に出しておいたタイムスタンプ値と、ページのIDを入れておきます。
この処理をページを見たときの処理に入れておけばOK。
有効期限を設定する
今回はさらにその追加した値に有効期限を付けて、ゴミが溜まらないようにしました。
下記を参考に念のため、MULTIを使ってトランザクションもしておきます。
qiita.com
EXPIREは秒で指定するため、今回は30日、 60*60*24*30 = 2592000を使います。
redis> MULTI redis> ZADD recenrtyview:{ユーザーID} タイムスタンプ値 "{ページID}" redis> EXPIRE recenrtyview:{ユーザーID} 2592000 redis> EXEC
もし同じページIDを見た場合はスコアのタイムスタンプ値と、有効期限の両方が更新されます。
取得する処理
今回は新しい順、タイムスタンプ値が大きい順に取得したいので、ZRANGEの逆版、ZREVRANGEを使います。
redis> ZREVRANGE recenrtyview:{ユーザーID} {start} {stop}
start, stopのindexは0始まりなので、最新10件であれば下記のような感じ。
redis> ZREVRANGE recenrtyview:{ユーザーID} 0 9
これで、保存されたページIDが欲しい順番で返ってくるので、それぞれの情報を取得して、表示すれば完了です。
今回実装したのは下記のサイトのそれぞれのお仕事詳細ページの下の方で使われています。
シニア・中高年の求人・仕事探しなら【シニアジョブ】
情報の入れ替わりが激しいのでぼかしといた
ということで、ランキング以外にもSorted Setが使うのがぴったりな用途を見つけたので記事にしました。
RedisのSorted Setを使うことで、高速で、かつ構造もシンプルでバグも少ない実装が出来た気がします。
- 作者:Josiah L. Carlson
- 発売日: 2013/12/27
- メディア: 大型本