1. 程式人生 > 實用技巧 >Redis排行榜的實現

Redis排行榜的實現

場景

- 使用者邀請排行榜

users測試表結構

- 基本的資料結構

欄位 備註
id 使用者 id
name 暱稱
invite_count 邀請人數
created_at 建立時間
updated_at 更新時間

程式碼說明

return $this->response->paginator($data, new UserTransformer())->setMeta($userInfo);

- 使用了Dingo API
- https://learnku.com/docs/dingo-api/2.0.0/Responses/1446

常規的做法

SQL排序 --》存Redis(設定過期時間)

/**
* 走資料庫 [排行榜]
* @return \Dingo\Api\Http\Response
*/
public function rankingList()
{
  $currentUserId = 1;
  if (!\Cache::has('ranking-list')) { 
    \Cache::remember('ranking-list', 1, function (){ //laravel5.6是1分鐘, laravel5.8之後是1s
    // 先取排名
    return User::selectRaw('id, name, email, invite_count, if(@rowNum,@rowNum := @rowNum +1,@rowNum := 1) as rank_num')
      
->where('invite_count', '>=', 1)       ->orderBy('invite_count', 'desc')       ->orderBy('updated_at', 'asc') //invite_count和updated_at建聯合索引       ->paginate();     });   }   $data = \Cache::get('ranking-list');   // 集合生成陣列   $result = $data->pluck('rank_num', 'id')->toArray();   
// 使用者名稱次   $currentUser = User::find($currentUserId);   $userInfo = [     'user_id' => $currentUserId,     'user_rank' => $result[$currentUserId] ?? '--', //取使用者名稱次,注意下標沒有的情況     'phone' => $currentUser->phone,     'invite_count' => $currentUser->invite_count,   ];   return $this->response->paginator($data, new UserTransformer())->setMeta($userInfo); }

利用Zset有序集合

/**
* Rides的zSet [排行榜]
* $invite_count[邀請次數] , $user_id[使用者ID]
* @return \Dingo\Api\Http\Response
*/
public function rankingList()
{
  // 使用者建立時記錄 zAdd($key, $invite_count, $user_id)
  // 邀請成功時記錄 zIncrBy($key, 1, $user_id)
  // 檢視總的條數 zCard($key)
  // 查詢所有的記錄 zRevRange($key, 0, $num-1 , false);
  // 檢視自己的名次 zRevRank($key, $user_id)
  // 得到排序好的 user_ids
  $redis_store = \Cache::store('redis');
  $store_key = $redis_store->getPrefix(); // 獲取字首
  $redis = $redis_store->getRedis(); // 例項化redis
  $key = $store_key . 'test-ranking-list:';
  
  /**
  * Mock加資料
  */
  $users = User::all()->toArray();
  foreach ($users as $item){
    $redis->zAdd($key, $item['invite_count'], $item['id']);
  }

  /**
  * 總排行(從大到小排序好)
  */
  $user_ids = $redis->zRevRange($key, 0, 9 , false); // 取前10名,全部的話end為num-1,zCard($key)
  $ids_ordered = implode(',', $user_ids);
  $rankingBoard = User::whereIn('id', $user_ids)
    ->orderByRaw(DB::raw("FIELD(id, $ids_ordered)")) // whereIn按user_id排序返回
    ->paginate();

  /**
  * 使用者所屬排名
  */
  $currentUserId = 1; //當前使用者
  $rank = $redis->zRevRank($key, $currentUserId);
  $myInfo = [
    'rank' => $rank + 1,
    'user_id' => $currentUserId,
  ];
  return $this->response->paginator($rankingBoard, new UserTransformer())->setMeta($myInfo);
}