Similar to this, but needing a solution for hashes instead of plain keys: How to atomically delete keys matching a pattern using Redis
I have a bunch of hashes with prefix like: "prefix:"
Under each hash are a bunch of keys like: "cc_XX", where "XX" is a 2 letter code.
I need to some how loop through all my redis hashes, and delete each of the cc_XX sub keys some how, and am looking for a cli/lua way to do this (not great with either).
Any advice would be greatly appreciated.
The following EVAL script should do what you want:
local keys = redis.call('KEYS',KEYS[1])
for i,k in ipairs(keys) do
local res = redis.call('HKEYS',k)
for j,v in ipairs(res) do
if string.find(v,ARGV[1]) then
redis.call('HDEL',k,v)
end
end
end
You need to call it by providing the following parameters:
EVAL <script> 1 prefix:* cc_..
Please note it blocks the Redis event loop until the script is complete, so it can freeze Redis for a while if you have a large number of keys. Atomicity has a price.
Update:
If you don't need the atomicity, then the following script will avoid blocking Redis for too long (but please note, it will still block if you have a huge global number of keys or if one of your hash object is huge: there is no way to avoid this).
./redis-cli keys 'prefix:*' | awk '
BEGIN {
script = "local res = redis.call('\''HKEYS'\'',KEYS[1]); \
for j,v in ipairs(res) do \
if string.find(v,ARGV[1]) then \
redis.call('\''HDEL'\'',KEYS[1],v); \
end \
end"
}
{
printf "EVAL \"%s\" 1 %s cc_..\n", script, $1
}' | ./redis-cli
(tested with bash)
The way we store hashes in Redis, we suffix hash element id keys by object types within a hash. Sometimes in the data model, we update an object and we need to delete all hash elements of a given object type upon deployment. We wanted to accomplish this without having to delete the entire hash.
For example:
127.0.0.1:6379> keys client*
1) "client"
127.0.0.1:6379> type client
hash
127.0.0.1:6379> hkeys client
1) "123-obj1"
2) "123-obj2"
3) "123-obj3"
4) "123-obj4"
5) "123-obj5"
6) "456-obj1"
7) "456-obj2"
8) "456-obj3"
9) "456-obj4"
10) "456-obj5"
If we add a new field in the application to obj5 and set with a default value, we would need to run the equivalent of "HDEL client *-obj5". However, this does not work in redis-cli.
I have found the following works via BASH:
redis-cli HKEYS 'client' | grep obj5 | awk '{ printf "HDEL client %s\n", $1 }' | redis-cli
If you use different Redis databases in your environment, add the "-n X" switch which is equivalent to "select X" in redis-cli. In this case, select database 4:
redis-cli -n 4 HKEYS 'client' | grep obj5 | awk '{ printf "HDEL client %s\n", $1 }' | redis-cli -n 4