ケース:Redisによる関心関係の保存
6402 ワード
Redisは、リレーショナル・データベースや簡単なKey-Valueストレージ(Memcachedなど)よりも、実際に適用されているデータ・モデルに近い豊富なデータ型を提供しています.たとえば以下に述べるような親友関係の記憶では,原作者はRedisのSets(集合)データ構造を用いている.
具体的な記憶方式は、各ユーザについて、注目関係には、2つのリスト、1つのユーザに注目するユーザのUIDリスト、もう1つのユーザファンのUIDリストが格納され、この2つのリストはいずれもSets(集合)を使用する.例えばユーザID 123のユーザに対してgraph:user:123:followingはその注目者のリストを保存し、graph:user:1:followed_byは彼に注目している人のリストを保存しています.
以下はPHPコードの関心関係クラスで、通常の関心関係操作クエリーなどの方法が含まれています.具体的には注釈を参照してください.
次に、このクラスを使用して注目関係を操作する例を示します.
具体的な記憶方式は、各ユーザについて、注目関係には、2つのリスト、1つのユーザに注目するユーザのUIDリスト、もう1つのユーザファンのUIDリストが格納され、この2つのリストはいずれもSets(集合)を使用する.例えばユーザID 123のユーザに対してgraph:user:123:followingはその注目者のリストを保存し、graph:user:1:followed_byは彼に注目している人のリストを保存しています.
以下はPHPコードの関心関係クラスで、通常の関心関係操作クエリーなどの方法が含まれています.具体的には注釈を参照してください.
<?
/*
* This example would probably work best if you're using
* an MVC framework, but it can be used standalone as well.
*
* This example also assumes you are using Predis, the excellent
* PHP Redis library available here:
* https://github.com/nrk/predis
*/
class UserNode {
// The user's ID, probably loaded from MySQL
private $id;
// The redis server configuration
private $redis_config = array(
array('host' => 'localhost', 'port' => 6379 )
);
// Stores the redis connection resource so that
// we only need to connect to Redis once
private $redis;
public function __construct($userID) {
$this->id = $userID;
}
private function redis() {
if (!$this->redis) {
$this->redis = new Predis\Client($redis_config);
}
return $this->redis;
}
/*
* Makes this user follow the user with the given ID.
* In order to stay efficient, we need to make a two-way
* directed graph. This means when we follow a user, we also
* say that that user is followed by this user, making a forward
* and backword directed graph.
*/
public function follow($user) {
$this->redis()->sadd("graph:user:{$this->id}:following", $user);
$this->redis()->sadd("graph:user:$user:followed_by", $this->id);
}
/*
* Makes this user unfollow the user with the given ID.
* First we check to make sure that we are actually following
* the user we want to unfollow, then we remove both the forward
* and backward references.
*/
public function unfollow($user) {
if ($this->is_following()) {
$this->redis()->srem("graph:user:{$this->id}:following", $user);
$this->redis()->srem("graph:user:$user:followed_by", $this->id);
}
}
/*
* Returns an array of user ID's that this user follows.
*/
public function following() {
return $this->redis()->smembers("graph:user:{$this->id}:following");
}
/*
* Returns an array of user ID's that this user is followed by.
*/
public function followed_by() {
return $this->redis()->smembers("graph:user:{$this->id}:followed_by");
}
/*
* Test to see if this user is following the given user or not.
* Returns a boolean.
*/
public function is_following($user) {
return $this->redis()->sismember("graph:user:{$this->id}:following", $user);
}
/*
* Test to see if this user is followed by the given user.
* Returns a boolean.
*/
public function is_followed_by($user) {
return $this->redis()->sismember("graph:user:{$this->id}:followed_by", $user);
}
/*
* Tests to see if the relationship between this user and the given user is mutual.
*/
public function is_mutual($user) {
return ($this->is_following($user) && $this->is_followed_by($user));
}
/*
* Returns the number of users that this user is following.
*/
public function follow_count() {
return $this->redis()->scard("graph:user:{$this->id}:following");
}
/*
* Retuns the number of users that follow this user.
*/
public function follower_count() {
return $this->redis()->scard("graph:user:{$this->id}:followed_by");
}
/*
* Finds all users that the given users follow in common.
* Returns an array of user IDs
*/
public function common_following($users) {
$redis = $this->redis();
$users[] = $this->id;
$keys = array();
foreach ($users as $user) {
$keys[] = "graph:user:{$user}:following";
}
return call_user_func_array(array($redis, "sinter"), $keys);
}
/*
* Finds all users that all of the given users are followed by in common.
* Returns an array of user IDs
*/
public function common_followed_by($users) {
$redis = $this->redis();
$users[] = $this->id;
$keys = array();
foreach ($users as $user) {
$keys[] = "graph:user:{$user}:followed_by";
}
return call_user_func_array(array($redis, "sinter"), $keys);
}
}
次に、このクラスを使用して注目関係を操作する例を示します.
<?
// create two user nodes, assume for this example
// they're users with no social graph entries.
$user1 = UserNode(1);
$user2 = UserNode(2);
$user1->follows(); // array()
// add some followers
$user1->follow(2);
$user1->follow(3);
// now check the follow list
$user1->follows(); // array(2, 3)
// now we can also do:
$user2->followed_by(); // array(1)
// if we do this...
$user2->follow(3);
// then we can do this to see which people users #1 and #2 follow in common
$user1->common_following(2); // array(3)