Reputation: 1681
In my setup, the info
command shows me the following:
[keys] => 1128
[expires] => 1125
I'd like to find those 3 keys without an expiration date. I've already checked the docs to no avail. Any ideas?
Upvotes: 64
Views: 49521
Reputation: 2552
I wanted a fast Lua script, but with the recommended SCAN
command and a nice output. It contains some example conditions, so you can easily adapt it to your needs.
local maxcount = 10000
local result, cursor, count, total_memory = {}, '0', 0, 0
repeat
local scan_result = redis.call('SCAN', cursor, 'COUNT', 100)
cursor = scan_result[1]
for _, key in ipairs(scan_result[2]) do
if string.match(key, '^prefix') then
local ttl = redis.call('TTL', key) / 3600
local idle_time = redis.call('OBJECT', 'IDLETIME', key) / 3600
if (ttl < 0 or ttl > 24) and idle_time > 24 then
local memory_usage = redis.call('MEMORY', 'USAGE', key)
--[[ comment the following line to only get the summary ]]
table.insert(result, string.format('%.1fh %.1fh %.1fkb %s', ttl, idle_time, memory_usage / 1024, key))
total_memory = total_memory + memory_usage
count = count + 1
--[[ redis.call('DEL', key) ]]
end
if count >= maxcount then
break
end
end
end
until cursor == '0' or count >= maxcount
table.insert(result, string.format('Count: %d, Size: %.1fmb', count, total_memory / (1024 * 1024)))
return result
You can run it like that:
$ redis-cli --eval filename.lua
I only had RedisInsight installed on Windows, it has a built-in CLI, but I had to convert it to a one-liner (just replace line breaks by spaces) and run EVAL "..." 0
.
It took (without the delete) about 20s to search 4M keys and return 10K of them in my case.
Upvotes: 0
Reputation: 601
I was trying to accomplish the same at work, where I need to find keys without TTL set and then delete them.
This script is written in Java, I ran it using my IDE.
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.params.ScanParams;
class RemoveRedisKeysWithoutTtl {
public static void main(String[] args) {
JedisPool jedisPool = new JedisPool(new JedisPoolConfig(), "localhost", 6379);
try (Jedis jedis = jedisPool.getResource()) {
// call the method to remove keys with no TTL set
removeKeysWithNoTTL(jedis);
System.out.println("Keys with no TTL removed successfully.");
}
// close the connection
jedisPool.close();
}
public static void removeKeysWithNoTTL(Jedis jedis) {
// update the pattern to match on your key
ScanParams scanParams = new ScanParams().match("*");
// start at the beginning of the cursor, so value is 0
String cursor = ScanParams.SCAN_POINTER_START;
do {
var scanResult = jedis.scan(cursor, scanParams);
var keys = scanResult.getResult();
// iterate through keys
for (String key : keys) {
// check if TTL is -1 (no expiration) or -2 (key does not exist)
if (jedis.ttl(key) == -1 || jedis.ttl(key) == -2) {
// Delete the key
System.out.println("Deleting key " + key);
jedis.del(key);
}
}
// get the next cursor
cursor = scanResult.getCursor();
} while (!cursor.equals(ScanParams.SCAN_POINTER_START));
}
}
Upvotes: 0
Reputation: 323
I needed to extract non-expiring keys from bigger (40GB) dataset, so using keys
command was not suitable for me. So in case someone is looking for offline/non-blocking solution, you can use https://github.com/sripathikrishnan/redis-rdb-tools for extraction of non-expiring keys from redis rdb dump.
You can just install libraries via pip:
pip install rdbtools python-lzf
Then create simple parser which extracts keys and values which has expiration set to None:
from rdbtools import RdbParser, RdbCallback
from rdbtools.encodehelpers import bytes_to_unicode
class ParserCallback(RdbCallback):
def __init__(self):
super(ParserCallback, self).__init__(string_escape=None)
def encode_key(self, key):
return bytes_to_unicode(key, self._escape, skip_printable=True)
def encode_value(self, val):
return bytes_to_unicode(val, self._escape)
def set(self, key, value, expiry, info):
if expiry is None:
print('%s = %s' % (self.encode_key(key), self.encode_value(value)))
callback = ParserCallback()
parser = RdbParser(callback)
parser.parse('/path/to/dump.rdb')
Upvotes: 3
Reputation: 3091
To me the accepted answer appears unusable for a medium-sized dataset, as it will run a redis-cli
command for each and every key.
Instead I used this lua script to filter the keys inside the redis server:
local show_persistent = ARGV[1] ~= "expiring"
local keys = {}
for i, name in ipairs(redis.call("keys", "*")) do
local persistent = redis.call("pttl", name) < 0
if persistent == show_persistent then
table.insert(keys, name)
end
end
return keys
This can be called as
$ redis-cli --eval show-persistent-keys.lua
to get all keys without an expiration time. It also can be called as
$ redis-cli --eval show-persistent-keys.lua , expiring
to find the opposite key set of all keys with an expiration time set.
On the downside this may block for too long (appears fine for 1 M keys). I'd use scan instead but I happen to have to run this against a legacy Redis at version 2.6, which does not have scan
available.
Upvotes: 5
Reputation: 3488
@Waynn Lue's answer runs but uses the Redis KEYS command which Redis warns about:
Warning: consider KEYS as a command that should only be used in production environments with extreme care. It may ruin performance when it is executed against large databases.
Redis documentation recommends using SCAN.
redis-cli --scan | while read LINE ; do TTL=`redis-cli ttl "$LINE"`; if [ $TTL -eq -1 ]; then echo "$LINE"; fi; done;
If you want to scan for a specific key pattern, use:
redis-cli --scan --pattern "something*"
Upvotes: 67
Reputation: 171
#!/usr/bin/env python
import argparse
import redis
p = argparse.ArgumentParser()
p.add_argument("-i", '--host', type=str, default="127.0.0.1", help="redis host", required=False)
p.add_argument("-p", '--port', type=int, default=6379, help="redis port", required=False)
p.add_argument("-n", '--db', type=int, default=0, help="redis database", required=False)
args = p.parse_args()
r = redis.Redis(host=args.host, port=args.port, db=args.db)
try:
keys = r.keys()
for key in keys:
if r.ttl(key) < 0:
print(key)
except KeyboardInterrupt:
pass
Upvotes: -1
Reputation: 11375
Modified from a site that I can't find now.
redis-cli keys "*" | while read LINE ; do TTL=`redis-cli ttl "$LINE"`; if [ $TTL -eq -1 ]; then echo "$LINE"; fi; done;
edit: Note, this is a blocking call.
Upvotes: 71
Reputation: 1900
In case somebody is getting bad arguments or wrong number of arguments error, put double quotes around $LINE.
So,it would be
redis-cli keys "*" | while read LINE ; do TTL=`redis-cli ttl "$LINE"`; if [ $TTL -eq -1 ]; then echo "$LINE"; fi; done;
This happens when there are spaces in the key.
Upvotes: 4