Redis在Rails中的應用實例
Redis是一個非常快速的,原子性的key-value存儲。它允許字符串,集合,排序集合,列表和散列存儲。 Redis把數據持久化在RAM中,很像Memcached但又不全是Memcached的,因爲Redis的週期性寫入磁盤來維持它的持久性。
1. 數據類型
Redis支持五種數據類型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合),詳情請看這裏
2. 啓動服務
運行redis-server,啓動redis服務器,得到以下
PENG-MacBook-Pro:~ PENG-mac$ redis-server
31998:C 02 Nov 13:58:45.177 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
31998:M 02 Nov 13:58:45.178 * Increased maximum number of open files to 10032 (it was originally set to 256).
_._
_.-``__ ''-._
_.-`` `. `_. ''-._ Redis 3.0.6 (00000000/0) 64 bit
.-`` .-```. ```\/ _.,_ ''-._
( ' , .-` | `, ) Running in standalone mode
|`-._`-...-` __...-.``-._|'` _.-'| Port: 6379
| `-._ `._ / _.-' | PID: 31998
`-._ `-._ `-./ _.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' | http://redis.io
`-._ `-._`-.__.-'_.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' |
`-._ `-._`-.__.-'_.-' _.-'
`-._ `-.__.-' _.-'
`-._ _.-'
`-.__.-'
31998:M 02 Nov 13:58:45.179 # Server started, Redis version 3.0.6
31998:M 02 Nov 13:58:45.184 * DB loaded from disk: 0.004 seconds
31998:M 02 Nov 13:58:45.184 * The server is now ready to accept connections on port 6379
以上未指定配置文件,使用默認的配置文件,若要自定義請在後面指定參數redis-server /path/to/redis.conf
然後用redis-cli
本地連接到redis服務器
Rails and Redis
在Rails中爲了避免Sql的反覆查詢,可以考慮把一部分數據用Redis儲存(緩存),例如Web服務器中的Session儲存就可以利用redis來緩存,這裏用個簡單的例子來說明Redis的一些基本方法和如何連接rails和redis
1.創建新的rails應用rails new redis-test
2.在Gemfile中加入ruby的redis庫
gem 'redis'
3.運行bundle install
安裝依賴庫
4.在Rails中配置與redis的連接:在初始化文件中創建config/initializers/redis.rb
,並填入一下:
$redis = Redis.new(:host => 'localhost', :port => 6379)
以上會創建一個新的redis客戶端實例,與localhost:6379
連接(默認),這個實例會被儲存在全局變量$redis
中,能在其他任何地方直接調用
5.運行rails console
進入Rails的控制檯,檢查與redis的連接:
PENG-MacBook-Pro:redis-test PENG-mac$ rails console
Loading development environment (Rails 4.2.5.2)
2.2.4 :001 > $redis
=> #<Redis client v3.2.2 for redis://localhost:6379/0>
2.2.4 :002 > $redis.set('chunky', 'bacon')
=> "OK"
2.2.4 :003 > $redis.get('chunky')
=> "bacon"
2.2.4 :004 >
6.運行rails generate model user name:string
建立一個用戶模型,並指定字段name的類型爲字符串
7.下面考慮用戶之間的關注問題:比如,在微博中用戶之間可以互相關注(follow),那麼就有followers和followings,followings表示我關注的人,followers表示關注我的人。這種功能的實現可以通過傳統的關係數據庫實現:多對多的關聯關係,通過建立額外的關聯表,儲存雙方的id即可,請戳這裏獲得詳細的描述
現在我們通過redis來實現這個,編輯user.rb
:
class User < ActiveRecord::Base
# follow a user
def follow!(user)
$redis.multi do
$redis.sadd(self.redis_key(:following), user.id)
$redis.sadd(user.redis_key(:followers), self.id)
end
end
# unfollow a user
def unfollow!(user)
$redis.multi do
$redis.srem(self.redis_key(:following), user.id)
$redis.srem(user.redis_key(:followers), self.id)
end
end
# users that self follows
def followers
user_ids = $redis.smembers(self.redis_key(:followers))
User.where(:id => user_ids)
end
# users that follow self
def following
user_ids = $redis.smembers(self.redis_key(:following))
User.where(:id => user_ids)
end
# users who follow and are being followed by self
def friends
user_ids = $redis.sinter(self.redis_key(:following), self.redis_key(:followers))
User.where(:id => user_ids)
end
# does the user follow self
def followed_by?(user)
$redis.sismember(self.redis_key(:followers), user.id)
end
# does self follow user
def following?(user)
$redis.sismember(self.redis_key(:following), user.id)
end
# number of followers
def followers_count
$redis.scard(self.redis_key(:followers))
end
# number of users being followed
def following_count
$redis.scard(self.redis_key(:following))
end
# helper method to generate redis keys
def redis_key(str)
"user:#{self.id}:#{str}"
end
end
如何使用?
> %w[Alfred Bob].each{|name| User.create(:name => name)}
=> ['Alfred', 'Bob']
> a, b = User.all
=> [#<User id: 1, name: "Alfred">, #<User id: 2, name: "Bob">]
> a.follow!(b)
=> [1, 1]
> a.following?(b)
=> true
> b.followed_by?(a)
=> true
> a.following
=> [#<User id: 2, name: "Bob">]
> b.followers
=> [#<User id: 1, name: "Alfred">]
> a.friends
=> []
> b.follow!(a)
=> [1, 1]
> a.friends
=> [#<User id: 2, name: "Bob">]
> b.friends
=> [#<User id: 1, name: "Alfred">]
我們使用了上述redis的集合(set)數據類型,使用了以下方法:
方法 | 說明 | 示例 |
---|---|---|
sadd | 向集合中添加新的成員(不能重複) | SADD myset "Hello" |
srem | 移除集合中一個或多個成員 | SREM myset "Hello" |
smembers | 返回集合中的所有成員 | SMEMBERS myset |
sinter | 返回給定所有集合的交集 | SINTER set1 set2 |
scard | 獲取集合的成員數 | SCARD myset |
multi | 標誌着一個事務塊的開始(多條語句執行) |
結束語
上述通過一個實際例子,介紹瞭如何在Rails中使用redis儲存,以及redis的集合的一些操作,可以看出來,用key-value的儲存方式設計起來要比傳統的多對多關係要簡單的多。
當然,當Web服務器中的登錄人數過多時,僅靠內存來儲存session會話信息可能不太夠用(<4k),這時就可以考慮用redis來緩存,因爲redis可以週期性將數據部分儲存在硬盤裏。但是如果直接將sessions會話信息直接儲存在數據庫中,隨着時間的增加,這些冗餘數據會佔據一部分空間,因此需要週期對數據庫進行清理。所以相對來說,使用redis緩存會成爲一個折中的方案。
reference:http://www.justinweiss.com/articles/how-rails-sessions-work/
http://jimneath.org/2011/03/24/using-redis-with-ruby-on-rails.html