使用Redis實現關注好友的功能
現在很多社交都有關注或者新增粉絲的功能, 類似於這樣的功能我們如果採用資料庫做的話只是單純得到使用者的一些粉絲或者關注列表的話是很簡單也很容易實現, 但是如果我想要查出兩個甚至多個使用者共同關注了哪些人或者想要查詢兩個或者多個使用者的共同粉絲的話就會很麻煩, 效率也不會很高. 但是如果你用redis去做的話就會相當的簡單而且效率很高. 原因是redis自己本身帶有專門針對於這種集合的交集,並集, 差集的一些操作。
設計思路如下:
總體思路我們採用redis裡面的zset完成整個功能, 原因是zset有排序(我們要按照關注時間的倒序排列), 去重(我們不能多次關注同一使用者)功能. 一個使用者我們存貯兩個集合, 一個是儲存使用者關注的人 另一個是儲存關注使用者的人.
用到的命令是:
1、 zadd 新增成員:命令格式:zadd key score member [[score][member] …]
2、zrem 移除某個成員:命令格式:zrem key member [member …]
3、 zcard 統計集合內的成員數:命令格式:zcard key
4、 zrange 查詢集合內的成員:命令格式:ZRANGE key start stop [WITHSCORES]
描述:返回指定區間的成員。其中成員位置按 score 值遞增(從小到大)來排序。 WITHSCORES選項是用來讓成員和它的score值一併返回.
5、 zrevrange跟zrange作用相反
6、zrank獲取成員的排名:命令格式:zrank key member
描述:返回有序集key中成員member的排名。成員按 score 值遞增(從小到大)順序排列。排名以0開始,也就是說score 值最小的為0。返回值:返回成員排名,member不存在返回nil.
7、 zinterstore 取兩個集合的交集:命令格式:ZINTERSTORE destination numkeys key [key …][WEIGHTS weight [weight …]] [AGGREGATE SUM|MIN|MAX]
描述:計算給定的一個或多個有序集的交集。其中給定 key 的數量必須以 numkeys 引數指定,並將該交集(結果集)儲存到 destination 。預設情況下,結果集中某個成員的 score 值是所有給定集下該成員 score 值之 和 。
返回值:儲存到 destination 的結果整合員數。
下面我用Java寫了一個簡單的例子 maven構建
第一步: 新增Redis客戶端
<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> </dependency>
第二步: 封裝一個簡單的redis工具類
import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; public final class RedisUtil { //Redis伺服器IP private static String ADDR = "localhost"; //Redis的埠號 private static int PORT = 6379; //訪問密碼 private static String AUTH = "admin"; //控制一個pool最多有多少個狀態為idle(空閒的)的jedis例項,預設值也是8。 private static int MAX_IDLE = 200; private static int TIMEOUT = 10000; //在borrow一個jedis例項時,是否提前進行validate操作;如果為true,則得到的jedis例項均是可用的; private static boolean TEST_ON_BORROW = true; private static JedisPool jedisPool = null; static { try { JedisPoolConfig config = new JedisPoolConfig(); config.setMaxIdle(MAX_IDLE); config.setTestOnBorrow(TEST_ON_BORROW); jedisPool = new JedisPool(config, ADDR, PORT, TIMEOUT, AUTH); } catch (Exception e) { e.printStackTrace(); } } public synchronized static Jedis getJedis() { try { if (jedisPool != null) { Jedis resource = jedisPool.getResource(); return resource; } else { return null; } } catch (Exception e) { e.printStackTrace(); return null; } } @SuppressWarnings("deprecation") public static void returnResource(final Jedis jedis) { if (jedis != null) { jedisPool.returnResource(jedis); } } }
第三步: 封裝簡單的Follow類
import java.util.HashSet; import java.util.Set; import redis.clients.jedis.Jedis; import com.indulgesmart.base.util.RedisUtil; public class FollowUtil { private static final String FOLLOWING = "FOLLOWING_"; private static final String FANS = "FANS_"; private static final String COMMON_KEY = "COMMON_FOLLOWING"; // 關注或者取消關注 public static int addOrRelease(String userId, String followingId) { if (userId == null || followingId == null) { return -1; } int isFollow = 0; // 0 = 取消關注 1 = 關注 Jedis jedis = RedisUtil.getJedis(); String followingKey = FOLLOWING + userId; String fansKey = FANS + followingId; if (jedis.zrank(followingKey, followingId) == null) { // 說明userId沒有關注過followingId jedis.zadd(followingKey, System.currentTimeMillis(), followingId); jedis.zadd(fansKey, System.currentTimeMillis(), userId); isFollow = 1; } else { // 取消關注 jedis.zrem(followingKey, followingId); jedis.zrem(fansKey, fansKey); } return isFollow; } // 驗證兩個使用者之間的關係 // 0=沒關係 1=自己 2=userId關注了otherUserId 3= otherUserId是userId的粉絲 4=互相關注 public int checkRelations (String userId, String otherUserId) { if (userId == null || otherUserId == null) { return 0; } if (userId.equals(otherUserId)) { return 1; } Jedis jedis = RedisUtil.getJedis(); String followingKey = FOLLOWING + userId; int relation = 0; if (jedis.zrank(followingKey, otherUserId) != null) { // userId是否關注otherUserId relation = 2; } String fansKey = FANS + userId; if (jedis.zrank(fansKey, userId) != null) {// userId粉絲列表中是否有otherUserId relation = 3; } if ((jedis.zrank(followingKey, otherUserId) != null) && jedis.zrank(fansKey, userId) != null) { relation = 4; } return relation; } // 獲取使用者所有關注的人的id public static Set<String> findFollwings(String userId) { return findSet(FOLLOWING + userId); } // 獲取使用者所有的粉絲 public static Set<String> findFans(String userId) { return findSet(FANS + userId); } // 獲取兩個共同關注的人 public static Set<String> findCommonFollowing(String userId, String otherUserId) { if (userId == null || otherUserId == null) { return new HashSet<>(); } Jedis jedis = RedisUtil.getJedis(); String commonKey = COMMON_KEY + userId + "_" + otherUserId; // 取交集 jedis.zinterstore(commonKey + userId + "_" + otherUserId, FOLLOWING + userId, FOLLOWING + otherUserId); Set<String> result = jedis.zrange(commonKey, 0, -1); jedis.del(commonKey); return result; } // 根據key獲取set private static Set<String> findSet(String key) { if (key == null) { return new HashSet<>(); } Jedis jedis = RedisUtil.getJedis(); Set<String> result = jedis.zrevrange(key, 0, -1); // 按照score從大到小排序 return result; } }