Redis zset實現多條件排序思路

2021-10-25 15:36:57 字數 2115 閱讀 3028

某應用使用者排行規則:

第一排行維度:使用者積分;(積分越高,排行越靠前)

第二排行維度:使用者是否為會員;(積分相同時,會員排前面)

第三排行維度:使用者最近一次登入時間;(前兩個維度值相同時,使用者最後一次登入時間越近越靠前)

以下是原始資料:

使用者  積分  是否會員  最近一次登入時間戳

a 100 1 1612754184997

b 200 0 1612754184997

c 200 1 1612754184997

d 400 0 1612754184997

e 200 1 1612754184998

期望的排序結果:

使用者  積分  是否會員  最近一次登入時間戳

d 400 0 1612754184997

e 200 1 1612754184998

c 200 1 1612754184997

b 200 0 1612754184997

a 100 1 1612754184997

#score 儲存格式

redis的zset的score是用 double 儲存的,

乙個 64 位浮點數儲存時分為3段:

double 儲存的有效資料是第三段的52位,超出會損失精度,由於標準規定小數點是在有效數字最前面,所以實際可以儲存 53 位數字。

#通過「二進位制拆分」,將三個維度合併生成score

利用二進位制 64 位 long 分段儲存各個維度

首先時間戳如果全儲存就太長了,可以通過一些計算縮小一些,先忽略毫秒,然後和乙個大數計算差值。

long ts =

1609430400000

; 0:0:0

int max_second =

1893427200

;// 2030.1.1 0:0:0

int sts =

(max_second -

(int

)(ts /

1000))

;// 283996800

這樣時間就縮小到了300000000以內。

接下來進行劃分,首位標誌位不用,剩下63位,然後我劃分高33位存分數,1位存標誌位,最後29位存時間戳,儲存結構是這樣的:

如果要不損失精度,第一段可儲存位數 = 53 - 1 - 29 = 23;

第一段最大值 = 2^33 = 8589934591,不損失精度 = 2^23 = 8388607;

第三段最大值 = 2^29 = 536870911;

使用時可以適當縮短第三段時間戳的長度,或者不追求時間戳一定精確的話,第一段分數可以超出不損失精度的長度,也只會損失一點時間戳的精度而已。

具體生成score的**就不列舉了。

#直接拆分十進位制數

可以用二進位制拆分當然也可以直接拆分十進位制數,為了方便,還可以用小數劃分維度,比如將積分放在整數字,標誌位和時間戳放在小數字。

a    100.11612754184

b 200.01612754184

c 200.11612754184

d 400.01612754184

e 200.11612754184

不過這樣不丟精度儲存的分數比二進位制拆分小。

資料量不大的情況下直接使用 讀資料庫 就可以了,比較方便。用 redis 的時候,如果排序維度多,可以使用拆分二進位制或十進位制的方法儲存,二進位制的優點是儲存的數比較大,而且可以用位運算,十進位制的優點是計算簡單,可讀性比較好。各個維度的長度還可以做成配置項,這樣就可以滿足不同的業務需求了。

redis zset支援多條件排序

Jedis 實現多條件查詢

這裡面訪問redis的key和value全部都先序列化了的 public class defaultjedispoolclient return sinter finally public void sadd string key,string.macs finally public void hm...

Mysql實現多條件排名

其中重要的知識點有兩個 currank if prevrank s.sum and userrank c1.rank,currank incrank as rank5,incrank incrank 1 prevrank s.sum,userrank c1.rank 判斷 sum是否相同,如果相同,...

多條件篩選的實現

conditions array price color metal 要進行篩選的字段放在這裡 price color metal 先給需要篩選的字段賦空值,這些值將輸出到頁面的hidden fileds中 以下迴圈給已經進行的篩選賦值,以便能夠在下一次篩選中保留 foreach condition...