需求:redis作为一个内存型的数据库,我们需要对过期key保持关注,从info keyspace中可以看出有多少key没有设置过期时间,那么到底是哪些呢?
说明:关于redis ttl 的返回值,请参考http://redisdoc.com/key/ttl.html

测试数据:

  1. 5.5.5.101:6379> get c_100
  2. "100"
  3. 5.5.5.101:6379> ttl c_100
  4. (integer) -1
  5. 5.5.5.101:6379> expire c_100 600
  6. (integer) 1
  7. 5.5.5.101:6379> expire c_1000 600
  8. (integer) 1
  9. 5.5.5.101:6379> expire c_888 600
  10. (integer) 1
  11. 5.5.5.101:6379> dbsize
  12. (integer) 10000
  13. 5.5.5.101:6379> info keyspace
  14. # Keyspace
  15. db0:keys=10000,expires=3,avg_ttl=583699

获取没有设置ttl过期的key名字

  1. db_ip=5.5.5.101
  2. db_port=6379
  3. password=abc123
  4. cursor=0
  5. cnt=100
  6. new_cursor=0
  7. redis-cli -h $db_ip -p $db_port -a $password scan $cursor count $cnt > scan_tmp_result
  8. new_cursor=`sed -n '1p' scan_tmp_result`
  9. sed -n '2,$p' scan_tmp_result > scan_result
  10. cat scan_result |while read line
  11. do
  12. ttl_result=`redis-cli -h $db_ip -p $db_port -a $password ttl $line`
  13. if [[ $ttl_result == -1 ]];then
  14. echo $line >> no_ttl.log
  15. fi
  16. done
  17. while [ $cursor -ne $new_cursor ]
  18. do
  19. redis-cli -h $db_ip -p $db_port -a $password scan $new_cursor count $cnt > scan_tmp_result
  20. new_cursor=`sed -n '1p' scan_tmp_result`
  21. sed -n '2,$p' scan_tmp_result > scan_result
  22. cat scan_result |while read line
  23. do
  24. ttl_result=`redis-cli -h $db_ip -p $db_port -a $password ttl $line`
  25. if [[ $ttl_result == -1 ]];then
  26. echo $line >> no_ttl.log
  27. fi
  28. done
  29. done
  30. rm -rf scan_tmp_result
  31. rm -rf scan_result

查看结果:

  1. [redis@lxd-vm1 ~]$ wc -l no_ttl.log
  2. 9997 no_ttl.log
  3. [redis@lxd-vm1 ~]$

线上redis有一些历史遗留的未设置过期时间的key,导致redis空间占用较多,DBA告警后要我们自己清除,于是我写了一个脚本在不影响线上服务的情况下清除(使用keys命令会导致请求hang住)

  1. import sys
  2. import redis
  3. import os
  4. pool = redis.ConnectionPool(host = "host_name",password = "pass_word",port = 6379)
  5. r = redis.StrictRedis(connection_pool = pool)
  6. match = sys.argv[1]+"*"
  7. print(match)
  8. count = 10000
  9. for key in r.scan_iter(match = match,count = count):
  10. time = r.ttl(key)
  11. if time==-1:
  12. r.expire(key,1000)
  13. print("set expire key:",key)
  14. print(time)

这里host_name和pass_word打个码
sys.argv[1]指传入的第一个参数,执行的时候就用python3 xxx.py hello 就会自动扫描hello开头的key,步进10000个,然后将其过期时间设为1000秒,过一会就自动过期了。
在业务低峰期的话步进可以设大点,这样清的会比较快,我们为了清几十万个key花了半个多小时。

  1. ServiceStack.Redis\tests\ServiceStack.Redis.Tests\RedisClientTestsBase.cs
  2. using NUnit.Framework;
  3. using System.Diagnostics;
  4. using System.Text;
  5. namespace ServiceStack.Redis.Tests
  6. {
  7. public class RedisClientTestsBase
  8. {
  9. protected string CleanMask = null;
  10. protected RedisClient Redis;
  11. protected void Log(string fmt, params object[] args)
  12. {
  13. Debug.WriteLine("{0}", string.Format(fmt, args).Trim());
  14. }
  15. [OneTimeSetUp]
  16. public virtual void OnBeforeTestFixture()
  17. {
  18. RedisClient.NewFactoryFn = () => new RedisClient(TestConfig.SingleHost);
  19. using (var redis = RedisClient.New())
  20. {
  21. redis.FlushAll();
  22. }
  23. }
  24. [OneTimeTearDown]
  25. public virtual void OnAfterTestFixture()
  26. {
  27. }
  28. [SetUp]
  29. public virtual void OnBeforeEachTest()
  30. {
  31. Redis = RedisClient.New();
  32. }
  33. [TearDown]
  34. public virtual void OnAfterEachTest()
  35. {
  36. try
  37. {
  38. if (Redis.NamespacePrefix != null && CleanMask == null) CleanMask = Redis.NamespacePrefix + "*";
  39. if (CleanMask != null) Redis.SearchKeys(CleanMask).ForEach(t => Redis.Del(t));
  40. Redis.Dispose();
  41. }
  42. catch (RedisResponseException e)
  43. {
  44. // if exception has that message then it still proves that BgSave works as expected.
  45. if (e.Message.StartsWith("Can't BGSAVE while AOF log rewriting is in progress"))
  46. return;
  47. throw;
  48. }
  49. }
  50. protected string PrefixedKey(string key)
  51. {
  52. return string.Concat(Redis.NamespacePrefix, key);
  53. }
  54. public RedisClient GetRedisClient()
  55. {
  56. var client = new RedisClient(TestConfig.SingleHost);
  57. return client;
  58. }
  59. public RedisClient CreateRedisClient()
  60. {
  61. var client = new RedisClient(TestConfig.SingleHost);
  62. return client;
  63. }
  64. public string GetString(byte[] stringBytes)
  65. {
  66. return Encoding.UTF8.GetString(stringBytes);
  67. }
  68. public byte[] GetBytes(string stringValue)
  69. {
  70. return Encoding.UTF8.GetBytes(stringValue);
  71. }
  72. }
  73. }
  74. ServiceStack.Redis\tests\ServiceStack.Redis.Tests\RedisScanTests.cs
  75. [Test]
  76. public void Can_scan_100_collection_over_cursor()
  77. {
  78. var allKeys = new HashSet<string>();
  79. Redis.FlushAll();
  80. var keys = 100.Times(x => "KEY" + x);
  81. Redis.SetAll(keys.ToSafeDictionary(x => x));
  82. var i = 0;
  83. var ret = new ScanResult();
  84. while (true)
  85. {
  86. ret = Redis.Scan(ret.Cursor, 10);
  87. i++;
  88. ret.AsStrings().ForEach(x => allKeys.Add(x));
  89. if (ret.Cursor == 0) break;
  90. }
  91. Assert.That(i, Is.GreaterThanOrEqualTo(2));
  92. Assert.That(allKeys.Count, Is.EqualTo(keys.Count));
  93. Assert.That(allKeys, Is.EquivalentTo(keys));
  94. }

https://www.cnblogs.com/imdba/p/10161343.html