Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to increment a redis sorted set's value

TL;DR I am looking for a way to store, increment and retrieve ranges of event counts by minute.

I am looking for a solution to creating an incrementing timeseries in redis. I am looking to store counts to the minute. My goal is to be able to look up a time range and get the values. So for instnace if an event occurred for a specific key 30 times a minute. I would want to do something like zrange and get their key values. I also am hoping to use something like zincrby to increment the value. I have of course looked at a sorted set which would have seemed like a perfect fit until I realized that I can only do a range scan on the score and not the value. The optimal solution would be to use the number of minutes as the score and then use the value in the sorted set as the number of events for that minute. The problem I ran into is the zincrby only increments the score and not the value. I was unable to find a way to increment the value atomically. I also looked into a hashmap using the current minute as the key and event count as the value. I was able to increment the value using hincrby but the problem is that it doesn't support fetching a range of keys.

Any help would be appreciated.

like image 714
Chris Hinshaw Avatar asked Oct 28 '15 18:10

Chris Hinshaw


People also ask

How does Redis sorted set work?

In Redis, sorted sets are a data type similar to sets in that both are non repeating groups of strings. The difference is that each member of a sorted set is associated with a score, allowing them to be sorted from the smallest score to the largest.

What is score in Redis sorted set?

Redis sorted sets use a double 64-bit floating point number to represent the score. In all the architectures we support, this is represented as an IEEE 754 floating point number, that is able to represent precisely integer numbers between -(2^53) and +(2^53) included.

What is increment in Redis?

Redis INCR command is used to increment the integer value of a key by one. If the key does not exist, it is set to 0 before performing the operation. An error is returned if the key contains a value of the wrong type or contains a string that cannot be represented as an integer.

How do you find the intersection of two sorted sets in Redis?

Redis - Sorted Set Zinterstore Command Redis ZINTERSTORE command computes the intersection of numkeys sorted sets given by the specified keys, and stores the result in the destination. It is mandatory to provide the number of input keys (numkeys) before passing the input keys and the other (optional) arguments.


1 Answers

You know, right a question already has an answer. And you already says about redis way to solve your problem:

  1. Use ZSET - key as time and value as counter.
  2. Use HSET - key as time and value as counter.
  3. Use string keys - key name as time and value as counter.

Why only this cases - becouse of only this structures (ZSET, HSET and string keys) has atomic methods to increment values.

So actualy:

  1. You should make right choise about data structure.
  2. Resolve the issue with the data selection.

The first question answer is compromise between memory and perfomance. From your question you do not need to have any types if sorting so sorted sets is not a best solution - consume lot of memory and ZINCRBY time complexity is O(log(N)) rather HINCRBY and INCRBY is O(1). So we should choose betweeh hashes and string keys. Please look at question and answer about right memory optimization in redis - according this i think you should use hashes as a data type for your solution.

The second question is common for any types of data structures becouse of all types of them do not contains select by name features or they analogs. And we may use HMGET or LUA scripting to solve this problem. In any case this solution would have time complexity O(n).

Here is sample with Jedis (i`m not an Java programmer, sorry for possible errors):

int fromMinute = 1;
int toMinute = 10;

List<String> list = new ArrayList<String>();
for(int i = fromMinute ; i < toMinute ; i++) {
    list.add(i.toString());
}

Jedis jedis = new Jedis("localhost");
List<String> values = jedis.hmget("your_set_name", list);

This solution is atomic, fast, has time complexity O(n) and consume memory as little as possible in redis.

like image 132
Nick Bondarenko Avatar answered Oct 02 '22 14:10

Nick Bondarenko