SlideShare una empresa de Scribd logo
1 de 27
ARE YOU REDIS?
Emanuele `mekdigital` Tozzato

Sony Computer Entertainment America
....an open-source,
networked, in-memory,
  persistent, journaled,
 key-value data store...
• no significant difference read/write
• strings, hashes, lists, sets and sorted sets.
• stable, safe, actively updated
APPLICATION #1

 large mysql collections
with sortable columns and
 ranking / ordinal index
GAME LEADERBOARD
        &
 PRODUCTS TABLE
GAME LEADERBOARD
            simplest ever

        score   player   t stamp




score ? 	 √
rank ? 	 score >= my_score
• Ordinal 1 2 3 4
• Fractional 1 2.3 2.5 3
• Dense 1 2 2 3
• Standard Competition 1 2 2 4
• Modified Competition 1 3 3 4
O      F    D   S    S’   score   player t stamp
1      2    1   1    3     150     adam     2
2      3    1   1    3     150     mike     5
3      1    1   1    3     150    ronald    1
4      4    2   4    4     121     sam      7
5      6    3   5    7     100     adam     2
6      5    3   5    7     100     mark     1
7      7    3   5    7     100    robert    3




    how is rank calculated in SQL ?
(D)
SELECT
 id, player, score,
 @prev := @curr, @curr := score,
 @rank := IF(@prev = @curr, @rank, @rank+1) AS rank
FROM
 lb,
 (SELECT @curr := NULL, @prev := NULL, @rank := 0) sel1
 ORDER BY score DESC;

(S)
SELECT a.player, a.points, COUNT(b.id) rank
FROM simple_leaderboard a, simple_leaderboard b
 WHERE a.points < b.points OR (a.points = b.points AND a.player =
b.player)
 GROUP BY a.player, a.points
 ORDER BY a.points DESC, a.player ASC;

(S’)
SELECT a.player, a.score, COUNT(b.id) rank
FROM lb a, lb b
 WHERE a.score <= b.score OR (a.score = b.score AND a.player = b.player)
 GROUP BY a.player, a.score
 ORDER BY a.score DESC, a.player ASC
REALTIME RANKING
REALTIME RANKING

• even with pagination, need for full table scans
• even with good indexes, high complexity
• large sets increased tied items or larger scores
PRECALC RANKING
PRECALC RANKING

• table locking due to increased writes
• ‘event • (ranked) players’ complexity
• oh wait! ‘event • player • column’ ...
GAME LEADERBOARD
               not the simplest ever
                no_stiuq_yldneirf ,stiuq_yldneirf ,
   9_rep_k_p ,sgninni_rep_stih_sklaw_p ,sllab_no_esab_p ,k_p
 ,snur_denrae_p ,emag_rep_pu_nevig_snur_p ,pu_nevig_snur_p ,
   9_rep_stih_b ,pu_nevig_stih_p ,dehctip_sgninni_p ,sevas_p
,stuotuhs_p ,semag_etelpmoc_p ,are_p ,tcp_dleif_f ,srorre_f
    ,stuo_tup_f ,stsissa_f ,deyalp_sgninni_f ,hctip_yb_tih_b
  ,yalp_lbd_otni_dednuorg_b ,tcp_esab_no_b ,seilf_ecifircas_b
  ,tcp_gnigguls_b ,sesab_latot_b ,sklaw_b ,gnilaets_thguac_b
      ,sesab_nelots_b ,stuoekirts_b ,emag_rep_ibr_b ,ibr_b
 ,emag_rep_rh_b ,rh_b ,b3_b ,b2_b ,emag_rep_snur_b ,snur_b
   ,stih_b ,gva_gnittab_b ,stab_ta_b ,no_stiefrof ,stiefrof
       ,no_sedecnoc ,sedecnoc ,no_stcennocsid ,stcennocsid
   ,no_stiuq ,stiuq ,semag ,kaerts ,tcp_niw ,sessol ,sniw
REDIS MEETS LEADERBOARDS
                       Sorted Set
                   key score       member

                O(1) ~ O(log(N)+M)
•   ZADD key score member


•   ZCARD key


•   Z(REV)RANK key member


•   Z(REV)RANGE key member start stop [ws]


•   Z(REV)RANGEBYSCORE key member min max [ws] [limit offset]
REDIS MEETS LEADERBOARDS
col_1      ...      col_n     player    t stamp
 123       ...      21.23       10          1100
 145       ...      19.22       12          1105




on create / update of leaderboard entry:


        zadd     col_1      123        10
        zadd     col_n      21.23      10
        zadd     col_1      145        12
        zadd     col_n      19.22      12
REDIS MEETS LEADERBOARDS
col_1       ...    col_n   player     t stamp
 123        ...    21.23     10        1100
 145        ...    19.22     12        1105



        redis> zrevrange col_1 0 -1
        1. "12"
        2. "10"
        redis> zrevrange col_n 0 -1
        1. "10"
        2. "12"
        redis> zrank col_1 12
        (integer) 1
        redis> zrank col_1 10
        (integer) 0
REDIS MEETS LEADERBOARDS
       col_1        ...         col_n       player      t stamp
        100         ...         21.23         10          1100
        100         ...         19.22         12          1105



zadd   col_1      100        10
zadd   col_1      100        12
                                     zadd      col_1      100          12
                                     zadd      col_1      100          10

               redis> zrevrange col_1 0 -1
               1. "12"
               2. "10"

  The elements having the same score are returned in (reverse)
  lexographical order. [...previously: (reverse) insertion order...]
REDIS MEETS LEADERBOARDS
 col_1       ...       col_n    player   t stamp
  100        ...       21.23        10    1100
  100        ...       19.22        12    1105




break tie composing a fractional score
 using a secondary significant column



zadd     col_1     100.1100    10
zadd     col_1     100.1105    12
QUESTIONS ??
(n.b. skip slide unless someone is still awake)
redis_leaderboard.rb                              1
after_save :dump_to_redis
after_destroy :remove_from_redis


dump to redis and remove from redis methods know™ which columns
need to be ranked and will perform the ZADD redis command that will
insert or update the score in the SortedSet it belongs.


ranked_columns.each do |col|
 # zadd(key, score, member)
 sorted_set.zadd(col.name, self.send(col.name), self.id)
end
redis_leaderboard.rb                                      2

:find_leaderboard

find leaderboard accepts pagination params to calculate START/STOP
values for Z(REV)RANGE / LIMIT/OFFSET for Z(REV)RANGEBYSCORE,
sorting column and sort order... (make sure START is >= 0)
# ZRANGE
start = params[:per_page] * (params[:page] - 1)
stop    = params[:per_page] * params[:page] - 1
ids     = params[:sort_order] == 'desc' ?
 sorted_set.zrevrange(params[:sort_column], start, stop) :
 sorted_set.zrange(params[:sort_column], start, stop)


# ZRANGEBYSCORE
limit    = params[:per_page]
offset = params[:per_page] * params[:page]
ids     = params[:sort_order] == 'desc' ?
 sorted_set.zrevrangebyscore(params[:sort_column], limit, offset) :
 sorted_set.zrangebyscore(params[:sort_column], limit, offset)
redis_leaderboard.rb                                 3

:find_leaderboard ~ cont


once the array of ids is returned by Redis, we just need to fetch those
rows from the database with no effort and `transfer` the rank from the
array to the unsorted collection:

result = all(:conditions => {:id => ids})


ids.each_with_index do |sorted_id, index|
 row = result.detect {|lb_row| lb_row.id == sorted_id.to_i}
# attr_accessor :rank
 row.rank = index + row_start + 1
end


result = result.sort {|x,y| x.rank <=> y.rank}


# assign result to a WillPaginate collection ...
redis_leaderboard.rb                                       4

:find_around_me



find around me is a classic requirement of ranking tables. It’s really easy to
accomplish this task using just the player id and the cardinality of the
items below and above. first find the rank of the player and use it to
create limit and offset for Z(REV)RANKBYSCORE

num_above_below = 5
player_rank = sorted_set.z(rev)rank(params[:sort_column], player_id)
limit    = 2*num_above_below + 1
offset = player_rank - num_above_below
ids     = params[:sort_order] == 'desc' ?
 sorted_set.zrevrangebyscore(params[:sort_column], limit, offset) :
 sorted_set.zrangebyscore(params[:sort_column], limit, offset)
redis_leaderboard.rb                                      5

:find_around_me ~ SQL
find_by_sql(["SELECT lb.* FROM
  (SELECT @numrow := @numrow+1 as rn, ldb.*
 FROM (SELECT * FROM #{table_name}
 WHERE game_type = ?
 AND platform = ?
 ORDER BY #{sort_column} #{sort_order}) ldb,
 (SELECT @numrow := 0) rown) lb,
  (SELECT rn FROM
 (SELECT @numrow := @numrow+1 AS rn, player_rn.player_id
  FROM
 (SELECT player_id
  FROM #{table_name}
  WHERE game_type = ?
  AND platform = ?
  ORDER BY #{sort_column} #{sort_order}) player_rn,
  (SELECT @numrow := 0) rown ) my_pos
   WHERE player_id = ?) p_rownum
   WHERE lb.rn >= p_rownum.rn-? AND lb.rn <= p_rownum.rn+?
   ORDER BY #{sort_column} #{sort_order}",
  game_type, platform, game_type, platform, player_id, num_above_below,
num_above_below])

Más contenido relacionado

La actualidad más candente (6)

The Ring programming language version 1.5.1 book - Part 49 of 180
The Ring programming language version 1.5.1 book - Part 49 of 180The Ring programming language version 1.5.1 book - Part 49 of 180
The Ring programming language version 1.5.1 book - Part 49 of 180
 
Game c
Game cGame c
Game c
 
Instant Dynamic Forms with #states
Instant Dynamic Forms with #statesInstant Dynamic Forms with #states
Instant Dynamic Forms with #states
 
PyCon2009_AI_Alt
PyCon2009_AI_AltPyCon2009_AI_Alt
PyCon2009_AI_Alt
 
The Ring programming language version 1.4.1 book - Part 15 of 31
The Ring programming language version 1.4.1 book - Part 15 of 31The Ring programming language version 1.4.1 book - Part 15 of 31
The Ring programming language version 1.4.1 book - Part 15 of 31
 
Swift - 혼자 공부하면 분명히 안할테니까 같이 공부하기
Swift - 혼자 공부하면 분명히 안할테니까 같이 공부하기Swift - 혼자 공부하면 분명히 안할테니까 같이 공부하기
Swift - 혼자 공부하면 분명히 안할테니까 같이 공부하기
 

Similar a Are you REDIS?

Ruby Language - A quick tour
Ruby Language - A quick tourRuby Language - A quick tour
Ruby Language - A quick tour
aztack
 

Similar a Are you REDIS? (20)

optim function
optim functionoptim function
optim function
 
Introducing maths in qbasic
Introducing maths in qbasicIntroducing maths in qbasic
Introducing maths in qbasic
 
Intro to Map Reduce
Intro to Map ReduceIntro to Map Reduce
Intro to Map Reduce
 
R for you
R for youR for you
R for you
 
Extending Structured Streaming Made Easy with Algebra with Erik Erlandson
Extending Structured Streaming Made Easy with Algebra with Erik ErlandsonExtending Structured Streaming Made Easy with Algebra with Erik Erlandson
Extending Structured Streaming Made Easy with Algebra with Erik Erlandson
 
Under the Covers of DynamoDB
Under the Covers of DynamoDBUnder the Covers of DynamoDB
Under the Covers of DynamoDB
 
Ruby Language - A quick tour
Ruby Language - A quick tourRuby Language - A quick tour
Ruby Language - A quick tour
 
Mongo db mug_2012-02-07
Mongo db mug_2012-02-07Mongo db mug_2012-02-07
Mongo db mug_2012-02-07
 
Mips1
Mips1Mips1
Mips1
 
Асинхронность и многопоточность в Яндекс.Такси — Дмитрий Курилов
Асинхронность и многопоточность в Яндекс.Такси — Дмитрий КуриловАсинхронность и многопоточность в Яндекс.Такси — Дмитрий Курилов
Асинхронность и многопоточность в Яндекс.Такси — Дмитрий Курилов
 
More than `po`: Debugging in lldb
More than `po`: Debugging in lldbMore than `po`: Debugging in lldb
More than `po`: Debugging in lldb
 
Dplyr and Plyr
Dplyr and PlyrDplyr and Plyr
Dplyr and Plyr
 
Data & Analytics - Session 3 - Under the Covers with Amazon DynamoDB
Data & Analytics - Session 3 - Under the Covers with Amazon DynamoDBData & Analytics - Session 3 - Under the Covers with Amazon DynamoDB
Data & Analytics - Session 3 - Under the Covers with Amazon DynamoDB
 
Avoid Query Pitfalls
Avoid Query PitfallsAvoid Query Pitfalls
Avoid Query Pitfalls
 
MongoDB.local Dallas 2019: Tips & Tricks for Avoiding Common Query Pitfalls
MongoDB.local Dallas 2019: Tips & Tricks for Avoiding Common Query PitfallsMongoDB.local Dallas 2019: Tips & Tricks for Avoiding Common Query Pitfalls
MongoDB.local Dallas 2019: Tips & Tricks for Avoiding Common Query Pitfalls
 
Apache Spark & Streaming
Apache Spark & StreamingApache Spark & Streaming
Apache Spark & Streaming
 
asal12 sqliii
asal12 sqliiiasal12 sqliii
asal12 sqliii
 
MongoDB .local Toronto 2019: Tips and Tricks for Effective Indexing
MongoDB .local Toronto 2019: Tips and Tricks for Effective IndexingMongoDB .local Toronto 2019: Tips and Tricks for Effective Indexing
MongoDB .local Toronto 2019: Tips and Tricks for Effective Indexing
 
Indexing thousands of writes per second with redis
Indexing thousands of writes per second with redisIndexing thousands of writes per second with redis
Indexing thousands of writes per second with redis
 
Redis begins
Redis beginsRedis begins
Redis begins
 

Último

Último (20)

The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
Tech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdfTech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdf
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed texts
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CV
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024
 
Developing An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of BrazilDeveloping An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of Brazil
 
HTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation StrategiesHTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation Strategies
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
 

Are you REDIS?

  • 1.
  • 3. Emanuele `mekdigital` Tozzato Sony Computer Entertainment America
  • 4. ....an open-source, networked, in-memory, persistent, journaled, key-value data store...
  • 5. • no significant difference read/write • strings, hashes, lists, sets and sorted sets. • stable, safe, actively updated
  • 6. APPLICATION #1 large mysql collections with sortable columns and ranking / ordinal index
  • 7. GAME LEADERBOARD & PRODUCTS TABLE
  • 8. GAME LEADERBOARD simplest ever score player t stamp score ? √ rank ? score >= my_score
  • 9. • Ordinal 1 2 3 4 • Fractional 1 2.3 2.5 3 • Dense 1 2 2 3 • Standard Competition 1 2 2 4 • Modified Competition 1 3 3 4
  • 10. O F D S S’ score player t stamp 1 2 1 1 3 150 adam 2 2 3 1 1 3 150 mike 5 3 1 1 1 3 150 ronald 1 4 4 2 4 4 121 sam 7 5 6 3 5 7 100 adam 2 6 5 3 5 7 100 mark 1 7 7 3 5 7 100 robert 3 how is rank calculated in SQL ?
  • 11. (D) SELECT id, player, score, @prev := @curr, @curr := score, @rank := IF(@prev = @curr, @rank, @rank+1) AS rank FROM lb, (SELECT @curr := NULL, @prev := NULL, @rank := 0) sel1 ORDER BY score DESC; (S) SELECT a.player, a.points, COUNT(b.id) rank FROM simple_leaderboard a, simple_leaderboard b WHERE a.points < b.points OR (a.points = b.points AND a.player = b.player) GROUP BY a.player, a.points ORDER BY a.points DESC, a.player ASC; (S’) SELECT a.player, a.score, COUNT(b.id) rank FROM lb a, lb b WHERE a.score <= b.score OR (a.score = b.score AND a.player = b.player) GROUP BY a.player, a.score ORDER BY a.score DESC, a.player ASC
  • 13. REALTIME RANKING • even with pagination, need for full table scans • even with good indexes, high complexity • large sets increased tied items or larger scores
  • 15. PRECALC RANKING • table locking due to increased writes • ‘event • (ranked) players’ complexity • oh wait! ‘event • player • column’ ...
  • 16. GAME LEADERBOARD not the simplest ever no_stiuq_yldneirf ,stiuq_yldneirf , 9_rep_k_p ,sgninni_rep_stih_sklaw_p ,sllab_no_esab_p ,k_p ,snur_denrae_p ,emag_rep_pu_nevig_snur_p ,pu_nevig_snur_p , 9_rep_stih_b ,pu_nevig_stih_p ,dehctip_sgninni_p ,sevas_p ,stuotuhs_p ,semag_etelpmoc_p ,are_p ,tcp_dleif_f ,srorre_f ,stuo_tup_f ,stsissa_f ,deyalp_sgninni_f ,hctip_yb_tih_b ,yalp_lbd_otni_dednuorg_b ,tcp_esab_no_b ,seilf_ecifircas_b ,tcp_gnigguls_b ,sesab_latot_b ,sklaw_b ,gnilaets_thguac_b ,sesab_nelots_b ,stuoekirts_b ,emag_rep_ibr_b ,ibr_b ,emag_rep_rh_b ,rh_b ,b3_b ,b2_b ,emag_rep_snur_b ,snur_b ,stih_b ,gva_gnittab_b ,stab_ta_b ,no_stiefrof ,stiefrof ,no_sedecnoc ,sedecnoc ,no_stcennocsid ,stcennocsid ,no_stiuq ,stiuq ,semag ,kaerts ,tcp_niw ,sessol ,sniw
  • 17. REDIS MEETS LEADERBOARDS Sorted Set key score member O(1) ~ O(log(N)+M) • ZADD key score member • ZCARD key • Z(REV)RANK key member • Z(REV)RANGE key member start stop [ws] • Z(REV)RANGEBYSCORE key member min max [ws] [limit offset]
  • 18. REDIS MEETS LEADERBOARDS col_1 ... col_n player t stamp 123 ... 21.23 10 1100 145 ... 19.22 12 1105 on create / update of leaderboard entry: zadd col_1 123 10 zadd col_n 21.23 10 zadd col_1 145 12 zadd col_n 19.22 12
  • 19. REDIS MEETS LEADERBOARDS col_1 ... col_n player t stamp 123 ... 21.23 10 1100 145 ... 19.22 12 1105 redis> zrevrange col_1 0 -1 1. "12" 2. "10" redis> zrevrange col_n 0 -1 1. "10" 2. "12" redis> zrank col_1 12 (integer) 1 redis> zrank col_1 10 (integer) 0
  • 20. REDIS MEETS LEADERBOARDS col_1 ... col_n player t stamp 100 ... 21.23 10 1100 100 ... 19.22 12 1105 zadd col_1 100 10 zadd col_1 100 12 zadd col_1 100 12 zadd col_1 100 10 redis> zrevrange col_1 0 -1 1. "12" 2. "10" The elements having the same score are returned in (reverse) lexographical order. [...previously: (reverse) insertion order...]
  • 21. REDIS MEETS LEADERBOARDS col_1 ... col_n player t stamp 100 ... 21.23 10 1100 100 ... 19.22 12 1105 break tie composing a fractional score using a secondary significant column zadd col_1 100.1100 10 zadd col_1 100.1105 12
  • 22. QUESTIONS ?? (n.b. skip slide unless someone is still awake)
  • 23. redis_leaderboard.rb 1 after_save :dump_to_redis after_destroy :remove_from_redis dump to redis and remove from redis methods know™ which columns need to be ranked and will perform the ZADD redis command that will insert or update the score in the SortedSet it belongs. ranked_columns.each do |col| # zadd(key, score, member) sorted_set.zadd(col.name, self.send(col.name), self.id) end
  • 24. redis_leaderboard.rb 2 :find_leaderboard find leaderboard accepts pagination params to calculate START/STOP values for Z(REV)RANGE / LIMIT/OFFSET for Z(REV)RANGEBYSCORE, sorting column and sort order... (make sure START is >= 0) # ZRANGE start = params[:per_page] * (params[:page] - 1) stop = params[:per_page] * params[:page] - 1 ids = params[:sort_order] == 'desc' ? sorted_set.zrevrange(params[:sort_column], start, stop) : sorted_set.zrange(params[:sort_column], start, stop) # ZRANGEBYSCORE limit = params[:per_page] offset = params[:per_page] * params[:page] ids = params[:sort_order] == 'desc' ? sorted_set.zrevrangebyscore(params[:sort_column], limit, offset) : sorted_set.zrangebyscore(params[:sort_column], limit, offset)
  • 25. redis_leaderboard.rb 3 :find_leaderboard ~ cont once the array of ids is returned by Redis, we just need to fetch those rows from the database with no effort and `transfer` the rank from the array to the unsorted collection: result = all(:conditions => {:id => ids}) ids.each_with_index do |sorted_id, index| row = result.detect {|lb_row| lb_row.id == sorted_id.to_i} # attr_accessor :rank row.rank = index + row_start + 1 end result = result.sort {|x,y| x.rank <=> y.rank} # assign result to a WillPaginate collection ...
  • 26. redis_leaderboard.rb 4 :find_around_me find around me is a classic requirement of ranking tables. It’s really easy to accomplish this task using just the player id and the cardinality of the items below and above. first find the rank of the player and use it to create limit and offset for Z(REV)RANKBYSCORE num_above_below = 5 player_rank = sorted_set.z(rev)rank(params[:sort_column], player_id) limit = 2*num_above_below + 1 offset = player_rank - num_above_below ids = params[:sort_order] == 'desc' ? sorted_set.zrevrangebyscore(params[:sort_column], limit, offset) : sorted_set.zrangebyscore(params[:sort_column], limit, offset)
  • 27. redis_leaderboard.rb 5 :find_around_me ~ SQL find_by_sql(["SELECT lb.* FROM (SELECT @numrow := @numrow+1 as rn, ldb.* FROM (SELECT * FROM #{table_name} WHERE game_type = ? AND platform = ? ORDER BY #{sort_column} #{sort_order}) ldb, (SELECT @numrow := 0) rown) lb, (SELECT rn FROM (SELECT @numrow := @numrow+1 AS rn, player_rn.player_id FROM (SELECT player_id FROM #{table_name} WHERE game_type = ? AND platform = ? ORDER BY #{sort_column} #{sort_order}) player_rn, (SELECT @numrow := 0) rown ) my_pos WHERE player_id = ?) p_rownum WHERE lb.rn >= p_rownum.rn-? AND lb.rn <= p_rownum.rn+? ORDER BY #{sort_column} #{sort_order}", game_type, platform, game_type, platform, player_id, num_above_below, num_above_below])