Rumah > Artikel > pangkalan data > Contoh terperinci tentang cara Redis melaksanakan kedudukan dan fungsi pengisihan mata yang sama mengikut masa
Pembelajaran yang disyorkan: Tutorial video Redis
Dalam pembangunan harian, kami sering menghadapi keperluan untuk menjaringkan pengguna, dsb. Menyusun, sebagai contoh, dalam permainan, keberkesanan pertempuran perlu diberi kedudukan, dalam aktiviti pasukan, nilai sumbangan setiap pasukan perlu disenaraikan, dalam WeChat, bilangan langkah setiap rakan perlu disenaraikan, dalam kes ini, susunan redis biasanya dipilih. Koleksi ini menyimpan markah pengguna untuk memenuhi keperluan ranking Walau bagaimanapun, kaedah penarafan dalam senario yang berbeza juga sedikit berbeza.
Keperluan: Susun nilai sumbangan setiap pasukan dalam aktiviti pasukan.
Set Isih Redis ialah set tertib jenis Rentetan. Ahli set adalah unik, yang bermaksud bahawa data pendua tidak boleh muncul dalam set.
Setiap elemen dikaitkan dengan skor jenis berganda. Redis menggunakan markah untuk mengisih ahli koleksi daripada kecil kepada besar.
Ahli set yang ditempah adalah unik, tetapi markah boleh diulang.
Mengabaikan situasi mempunyai skor yang sama, yang berikut melaksanakan senarai kedudukan:
// 准备数据,其中value为每个队伍的ID,score为队伍的贡献值 > zadd z1 5 a 6 b 1 c 2 d 10 e (integer) 5 // 分页查询排行榜所有的队伍和贡献值,要使用zrevrange,而不是zrange,贡献值越大越排在前面 > zrevrange z1 0 2 withscores 1) "e" 2) "10" 3) "b" 4) "6" 5) "a" 6) "5" // 增加某个队伍的贡献值 > zincrby z1 3 d "5" > zincrby z1 4 c "5" // 查询排行榜所有的队伍 > zrevrange z1 0 -1 withscores 1) "e" 2) "10" 3) "b" 4) "6" 5) "d" 6) "5" 7) "c" 8) "5" 9) "a" 10) "5" // 查询某个队伍的排名 > zrevrank z1 d (integer) 2
Pelaksanaan lalai Redis ialah ahli yang mempunyai skor yang sama diisih mengikut susunan kamus (09 , AZ, a~z), Di atas menggunakan zrevrange, jadi ia adalah dalam susunan terbalik, jadi pengisihan dengan skor yang sama tidak boleh diisih mengikut keutamaan masa.
Dalam pelaksanaan di atas, jika nilai sumbangan dua pasukan adalah sama, iaitu nilai mata adalah sama, mereka tidak boleh disusun mengikut masa.
Jadi, kita perlu mereka bentuk skor = cap masa nilai sumbangan Sesiapa yang mendapat markah yang lebih akan diletakkan pada kedudukan pertama, nilai sumbangan mesti dianalisis berdasarkan skor.
Gunakan integer untuk menyimpan nilai skor Skor itu sendiri dalam redis ialah jenis berganda, dan nombor integer maksimum yang boleh disimpan dengan tepat ialah 2^53=9007199254740992 (16 bit. ). Cap masa yang tepat kepada milisaat memerlukan 13 digit, hanya meninggalkan 3 digit untuk nilai sumbangan storan Pada masa ini, jika masa adalah tepat kepada saat, hanya 10 digit diperlukan, meninggalkan 6 digit untuk nilai sumbangan.
Reka bentuk keseluruhan: 3 digit tinggi mewakili nilai sumbangan, dan 13 digit rendah mewakili cap masa.
Jika kita hanya menggabungkan struktur skor: 贡献值 * 10^13 时间戳
, kerana semakin besar skor, semakin dekat dan semakin kecil cap masa, semakin hampir, jadi peraturan penghakiman kedua-dua bahagian adalah bertentangan dan tidak boleh digabungkan begitu sahaja Kedua-duanya digabungkan bersama untuk menjadi skor.
Tetapi kita boleh berfikir secara terbalik dan menolak cap masa daripada nombor Integer yang cukup besar yang sama. MAX Semakin kecil cap masa, semakin besar perbezaan yang kita dapat, jadi kita boleh menukar struktur skor Untuk: 贡献值 * 10^13 (Integer.MAX-时间戳)
, ini akan memenuhi keperluan kita.
Memandangkan nilai skor redis adalah jenis berganda, anda boleh menggunakan bahagian integer untuk menyimpan nilai sumbangan, bahagian perpuluhan untuk menyimpan cap masa dan menggunakan nilai maksimum untuk menolaknya daripada bahagian cap masa yang sama.
Dengan cara ini, reka bentuk keseluruhan menjadi: 分数=贡献值 (Integer.MAX-时间戳) * 10^-13
Kelemahan: Memandangkan nilai skor dikira oleh dua pembolehubah, ia tidak boleh digunakan hanya apabila menambah nilai sumbangan kepada pasukan zincrby sebelumnya telah digunakan untuk menukar nilai skor, jadi meningkatkan nilai sumbangan kepada pasukan di bawah keadaan serentak akan membawa kepada nilai skor yang tidak tepat.
Simulasi situasi ralat:
Andaikan nilai sumbangan semasa pasukan A ialah 10. Pemain X dalam pasukan A menambah 1 nilai sumbangan kepada pasukan 11.xxx dalam pasukan A Pemain Y pasukan A menambah nilai sumbangan 1 kepada pasukan dan markah dikira dalam program menjadi 11.yyy Pemain X pasukan A memanggil perintah zadd redis untuk menetapkan sumbangan pasukan nilai kepada 11.xxx Pemain Y pasukan A memanggil perintah zadd redis Tetapkan nilai sumbangan pasukan kepada 11.yyy Akhirnya, nilai sumbangan pasukan A dikira sebagai 11. Keatomisan operasi meningkatkan nilai sumbangan tidak boleh dijamin.
Pada masa ini, anda perlu menggunakan skrip Lua untuk memastikan keatoman dua operasi pengiraan dan penetapan nilai sumbangan:
// 其中KEYS[1]为排行榜key,KEYS[2]为队伍ID // 其中ARGV[1]为增加的贡献值,ARGV[2]为Integer.MAX-时间戳 local score = redis.call('zscore', KEYS[1], KEYS[2]) if not(score) then score=0 end score=math.floor(score) + tonumber(ARGV[1]) + tonumber(ARGV[2]) redis.call('zadd', KEYS[1], score, KEYS[2]) return 1
Memandangkan fungsi masa tidak boleh digunakan dalam redis, bahagian (Integer.MAX-时间戳) * 10^-13
terdiri daripada Program di luar skrip mengira yang masuk.
Anda boleh terus menggunakan arahan di atas untuk fungsi seperti halaman senarai kedudukan dan pertanyaan kedudukan pasukan.
Apa yang dipanggil kedudukan seri ialah kedudukan dengan situasi kedudukan yang sama.
Hasil yang kami jangkakan adalah seperti berikut:
队伍ID | 贡献值 | 排名 |
---|---|---|
a | 100 | 1 |
b | 99 | 2 |
c | 99 | 2 |
d | 88 | 4 |
e | 87 | 5 |
当然现实中也有排名不跳过的情况,我这里考虑的是排名跳过的情况。
redis中score的设计还是采用上面的分数=贡献值 + (Integer.MAX-时间戳) * 10^-13
,只是在查询排名时需要进行计算。
比如要查上表中队伍b的排名,思路如下:
使用命令实现上面的步骤如下:
> zscore 排行榜key teamId > zrevrangebyscore(排行榜key, 上一步得到的score+1, 上一步得到的score, limit, 0 , 1) > zrevrank(排行榜key, 上一步得到的teamId)
为了性能考虑,可以使用下面的脚本一次查出来:
// KEYS[1]表示排行榜key // KEYS[2]表示要查询的队伍的ID local rank = 0 local score = redis.call('zscore', KEYS[1], KEYS[2]) if not(score) then score=0 else score=math.floor(score) local firstScore = redis.call('zrevrangebyscore', KEYS[1], score+1, score, 'limit', 0, 1) rank=redis.call('zrevrank', KEYS[1], firstScore[1]) end return {score,rank}
下面附上分页查询排行榜的脚本,假如一页10条,不用下面的脚本需要查询10次上面的脚本,如果连上面的脚本都没有使用的话就要查询30次redis。
// 排行榜key // ARGV[1]分页起始偏移 // ARGV[2]分页结束偏移 local list = redis.call('zrevrange', KEYS[1], ARGV[1], ARGV[2], 'withscores') local result={} local i = 1 for k,v in pairs(list) do if k%2 == 0 then local teamId = list[k-1] local score = math.floor(v) local firstScore = redis.call('zrevrangebyscore', KEYS[1], score+1, score, 'limit', 0, 1) local rank=redis.call('zrevrank', KEYS[1], firstScore[1]) local l = {teamId=teamId, contributionValue=score, teamRank=rank+1} result[i] = l i = i + 1 end end return cjson.encode(result)
此脚本使用了cjson库,返回的是一个json。
推荐学习:Redis视频教程
Atas ialah kandungan terperinci Contoh terperinci tentang cara Redis melaksanakan kedudukan dan fungsi pengisihan mata yang sama mengikut masa. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!