Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Redis - Alternative to check existence of multiple values in a set

Tags:

java

redis

In my application I need have a set of values and I need to check how many of these values are present in a set in Redis.

Just to make it simple, What I want to do is something like :

> Sadd myset field1
(integer) 1
> Sadd myset field2
(integer) 1
> Sadd myset field4
(integer) 1

> Sismember myset field1 field4 // which is not possible as of now.

Since I can not give multiple parameters for SISMEMBER, I might have to multiple redis call which is very time consuming.

I was thinking about alternatives like pipelining but then I thought this will be a good (hacky) way of achieving it :

> Hset myhash field1 "true"
(integer) 0
> Hset myhash field2 "true"
(integer) 0
> Hset myhash field4 "true"
(integer) 1

> Hmget myhash field1 field2 field3
1) "true"
2) "true"
3) (nil)

> Hmget myhash field1 field2 field3 field4
1) "true"
2) "true"
3) (nil)
4) "true"

Redis HMGET page says the following :

Available since 2.0.0.

Time complexity: O(N) where N is the number of fields being requested.

Which is really good when compared to making multiples calls for SADD, But I am really not sure If I am 100% correct and I also don't know if there is any serious drawback of using hmget this way.

So I just wanted to the drawbacks of using hmget this way and any better approaches to solve this.

like image 784
Karthik Avatar asked Sep 28 '22 03:09

Karthik


2 Answers

This is certainly a valid solution, although a little wasteful as you'll be maintaining a bunch of true values - a RAM overhead. FYI, Redis' Sets are implemented internally using the same hash table structs as Hashes are, so you're not that far off :)

While there is no variadic form of SISMEMBER, it as an easily scriptable flow with Lua so you may want to consider that as well. For example something like the following:

local r = {}

for _, m in pairs(ARGV) do
  r[#r+1] = redis.call('SISMEMBER', KEYS[1], m)
end

return r
like image 67
Itamar Haber Avatar answered Nov 03 '22 19:11

Itamar Haber


Following @itamar 's answer, I was able to do this using lua script.

I used the following script:

 local r = {}
 for i, m in pairs(KEYS) do
    r[i] = redis.call('SISMEMBER',ARGV[1],m)
 end
 return r

May be this will be useful for someone in future, so just writing how to call this script from java (Spring Data Redis 1.5.0 & jedis 2.6.2)

    redisTemplate.opsForSet().add("mySet","3");
    redisTemplate.opsForSet().add("mySet","4");
    redisTemplate.opsForSet().add("mySet","35");
    redisTemplate.opsForSet().add("mySet", "6");
    List<String> list = new LinkedList<>();
    list.add("1"); 
    list.add("2");
    list.add("3");
    list.add("35");
    list.add("6");
    System.out.println(redisTemplate.execute(script, list,"mySet"));

prints the following :

   [0, 0, 1, 1, 1]

EDIT I am not sure everybody is saying that mySet is KEYS and list is ARGV, but in SPRING DATA REDIS, execute function is defined in the following way :

public <T> T execute(RedisScript<T> script, List<K> keys, Object... args) {
    return this.scriptExecutor.execute(script, keys, args);
}
like image 33
Karthik Avatar answered Nov 03 '22 18:11

Karthik