12
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

[Rails]Redisの導入と使い方

Last updated at Posted at 2022-01-09

Redisとは

特徴

非RDB(Relational Database)。マシンのメモリ上へデータを保存する。
※対比として、RDBであるMySQLではファイルへデータを保存している。

メリット

  • メモリ上でデータを扱うため処理が早い(HDDとメモリでは数万倍の差がある)
  • すべての処理が完了しないとロールバックされる機能がデフォである
  • 直列処理であるため、データの不整合は発生しない
  • 複数のDBを持てる

デメリット

  • マシン上のメモリの消費が大きく、データの保存量上限は少ない
    • メモリ上限に達すると指定したルールに従ってデータが削除される
  • マシンが落ちたときデータが揮発してしまう
  • シングルスレッドでの動作であり、並列処理ができない & 1コアしか使えないためCPUのコア数を上げても無駄
  • マスタ-スレーブ構成が可能だが、非同期であるため不整合が発生する可能性あり

データの揮発対策

2パターンある

スナップショットからの復旧

「Redis Database」というファイルへRedisのスナップショットを作成できる、これを定期的に行うことである程度の防止が可能である。
直前のデータは復旧不可。

AOF

DB操作のログからデータをバックアップする方法。
ログを取る分、DB操作時の実行速度は落ちるが、直前のデータは復旧できる。

用途

  • セッション管理: RDSでセッション情報を扱うより高速に動作させられる
  • Webページのキャッシュ
  • リアルタイム性を求められる機能(ランキングとか): リスト化した状態で保存して随時ソートを行う

類似のソフトウェアMemcached

うーん。目立ったメリットもないので、Redisにしとけばまぁ間違いなさそうである。

参考

利用手順

当然だが、Redisを利用するには、そのサーバーとクライアントソフトウェアが必要。
docker-composeでローカル開発環境を構築する際の手順

Redisサーバー

docker-compose.yml

  # redisコンテナはデフォルトで0〜15のインデックスのDBが作成され、リッスンポート6379で起動し、 
  # /dataに60秒に1回スナップショット(Redis Databaseファイル)を作成するようになっている
  redis:
    container_name: redis
    # dockerホスト起動時にコンテナが起動するよう設定
    restart: always
    image: "redis:latest"
    ports:
      - "6379:6379"
    volumes:
      - "./data/redis:/data"

Redisクライアント

gem(redis)のセットアップ

①Gemfileに記載

gem 'redis'

②インストール

bundle install

③Redisクライアントの設定

③-1. 各RAILS_ENVごとに切り替えるため、config/settings/<環境名>.yml に以下を記載する

config/settings/<環境名>.yml
# 例. development.yml で ローカルのredisサーバーへ接続する
redis:
  host: redis
  port: 6379
  db: 0 # DBのindex. Redisはデフォで16のDBを持ち、0〜15のindexがある。

③-2. config/initializers/redis.rbを作成し、以下のように設定しておく

# RAILS_ENV別のredisクライアント設定読み込み
redis = Settings.redis

# redisクライアントのインスタンスを生成してRedis.currentでアクセスできるようにする
Redis.current = Redis.new(
  host: redis.host,
  port: redis.port,
  db: redis.db
)

④動作確認

# 前提: redisコンテナが起動している

# 1. railsサーバーが起動しているコンテナへログイン
docker exec -it <railsコンテナID> bash

# 2. rails console起動
bundle exec rails c

# 3. 適当なキー、値をセットしてみる
Redis.current.set("a", "b")
Redis.current.get("a")
=>
"b" と表示されれば、Redisサーバー & Redisクライアントともに正常に動作している

参考

利用方法

redisクライアントのインスタンスに対してキー・バリューのset系メソッド(データの書き込み), get系メソッド(データの読み込み)を実行する、というだけ。
ただし、バリューは以下5つのデータ型が取れるが、それぞれで使うべきメソッドが違う。

String型

参考: リファレンス

# 保存されるデータ型のイメージ
{'key': "value"}

# set
## 期限なし
Redis.current.set('en', 'Hello')
## 期限あり(秒で設定)
Redis.current.setex('en', 10, 'Hello')


# get
Redis.current.get('en')
=> "Hello" ※setexでセットした場合は、指定した秒数後はnilで返ってくる。


List型
先頭 or 末尾にデータの追加が可能.

# 保存されるデータ型のイメージ
{"key": ["value1", "value2",...]}

# set
## 末尾追加
Redis.current.rpush("en", "Hello")

## 先頭追加
Redis.current.lpush("en", "Hello2")

# get
## i番目の値を取得
Redis.current.lindex("en", 0)
=>"Hello2"

## リストのi番目からj番目までの値を配列で取得(先頭インデックスは0)
Redis.current.lrange("en", 0, 1)
=>["Hello2", "Hello"]

Set型
valueの配列に同じ値の要素は登録不可能同。
順不同で保存。
集合演算メソッドが利用できる、というより集合演算が目的っぽい

# 保存されるデータ型のイメージ
{"key": ["value1", "value2",...]}

# set
## 追加
Redis.current.sadd("en", "Hello")
Redis.current.sadd("en", "Hello2")

Redis.current.sadd("en2", "Hello2")
Redis.current.sadd("en2", "Hello3")

# get
## 全要素を取得
Redis.current.smembers("en")
=>["Hello2", "Hello"]

## 和を出す
Redis.current.sunion("en", "en2")
=>["Hello2", "Hello3", "Hello",]

SortedSet型
ソート済みセット型。同じ値は登録不可能。
配列要素は「スコア」でソートされ、「順位」を持つ。スコア/順位で範囲検索が可能。

# 保存されるデータ型のイメージ
{"key": [["value1", score1], ["value2", score2]]}

# set
Redis.current.zadd("score_ranking", 10, "user_id_1")
Redis.current.zadd("score_ranking", 500, "user_id_2")
Redis.current.zadd("score_ranking", 600, "user_id_3")
Redis.current.zadd("score_ranking", 1, "user_id_4")

# get
## 特定要素の順位を取得
Redis.current.zrank("score_ranking", "user_id_3")
=>3

## スコア上位iからjまでの値を取得。iは0始まり
Redis.current.zrevrange("score_ranking", 0, 2)
=>["user_id_3", "user_id_2", "user_id_1"]

## スコア上位iからjまでの値とスコアを取得。iは0始まり
Redis.current.zrevrange("score_ranking", 0, 2, with_scores: true)
=> [["user_id_3", 600.0], ["user_id_2", 500.0], ["user_id_1", 10.0]]"user_id_3"], [500, "user_id_2"], [10, "user_id_1"]]

Hash型

文字列型のキー & バリューのハッシュ。キーでの検索は可能だが、値での検索は不可能。
RDBっぽい管理方法。

参考: リファレンス

# 保存されるデータ型のイメージ
{"key": {"value_key": value_value}}
# 1つのカラム・値で構成されるレコードをset,getする例
## set
### すでに「"record"」が存在する場合は上書きされる
Redis.current.hset("record", "column_1_key", "column_1_value")
### すでに「"record"」が存在する場合は上書きされない 
Redis.current.hsetnx("record", "column_1_key", "column_1_value")
## get
Redis.current.hget("record", "column_1_key")
=>
"column1_value"

# 複数のカラム・値で構成されるレコードをset,getする例
## set
Redis.current.hmset("record",
  [
    "column1_key", "column1_value",
    "column2_key", "column2_value",
  ]
)

## get
Redis.current.hmget("record", ["column_1_key", "column_2_key"])
=> ["column_1_value", "column2_value"]

参考

応用編

ActiveRecordなどのオブジェクトをString型にしてRedisへ保存する場合には、

  • Marshal.dump(<オブジェクト>)
  • Marshal.load(<オブジェクト>)

を使うといい感じにいける。

キー: "key", 値: ActiveRecord型 でset, getする例

# set
user = User.first
Redis.current.set("key", Marshal.dump(user))

# get
user_str = Redis.current.get("key")
Marshal.load(user_str)

メモ

Redisクライアントのインスタンスを保存するようのクラスメソッド「Redis.current」がgem 「redis」に存在するので、各RailsソースにまたがってRedisを利用する際にはこれを使うべき

参考

参考

12
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?