在项目中,Redis不应该被当作传统数据库来使用;储存大量没有过期时间的数据。如果储存大量无过期时间,而且无效的key的话;再加上Redis本身的过期策略没有被正确设置,就会大量占用内存。这样就会导致再多的内存资源也不够用。最近在项目中,就遇到这样的情况。 情况大致是这样,项目中采用Redis二级存储(自己造的一个概念)。大概的意思就是先使用UUID生成一个uuid作为这个uuid作为一个版本存到缓存key为ACLCACHEVERSIONKEY中,即SETACLCACHEVERSIONKEYuuid 然后在不uuid作为另外一个key的一部分。即ACLUSERuuid,比如uuid的值为26a26b84578d40bfab15aeb188a56393,则key为ACLUSER26a26b84578d40bfab15aeb188a56393,缓存到key的数据为HMSETACLUSER26a26b84578d40bfab15aeb1id112345id245678 因为ACLUSER26a26b84578d40bfab15aeb188a56393的是否过期是通过程序中生成新版本的uuid,存储新的用户权限数据后;在程序中删除。因为程序的不健壮,导致有大量过期的版本没有及时删除。经过长年的积累导致Redis存在大量这些无效版本的key。 在测试环境中,可以使用keys命令,模糊查询到需要的key,但这个操作只适合在测试环境中使用,不适合在生产环境中使用,原因是Redis是单线程运行的,当Redis中的数据量很大时,由于此操作会遍历所有数据,并将结果一次性全部返回,执行时间会比较长,从而导致后续操作等待,直接影响系统的正常运行。解决的办法是使用scan命令:scancursor〔MATCHpattern〕〔COUNTcount〕cursor:表示游标,从0开始,此命令执行完后会返回一个新的cursor值。如果cursor!0,则表示还有key未返回,需要再调用scan,并使用此新的cursor值,来获取下一批如果cursor0,则表示遍历结束。pattern:表示模糊匹配的样式count:表示一批最多返回多少条记录,默认为10 集群环境下使用jedis的代码实现importjava。util。HashSimportjava。util。Simportredis。clients。jedis。HostAndPimportredis。clients。jedis。JedisCimportredis。clients。jedis。JedisPoolCimportredis。clients。jedis。ScanPimportredis。clients。jedis。ScanRpublicclassRedisTester{publicstaticJedisClustergetJedisCluster(){JedisPoolConfigconfignewJedisPoolConfig();confignewJedisPoolConfig();config。setMaxTotal(60000);设置最大连接数config。setMaxIdle(1000);设置最大空闲数config。setMaxWaitMillis(3000);设置超时时间config。setTestOnBorrow(true);集群结点SetHostAndPortjedisClusterNodenewHashSetHostAndPort();jedisClusterNode。add(newHostAndPort(yun1。cacheyum。com,6379));jedisClusterNode。add(newHostAndPort(yun2。cacheyum。com,6379));jedisClusterNode。add(newHostAndPort(yun3。cacheyum。com,6379));jedisClusterNode。add(newHostAndPort(yun4。cacheyum。com,6379));jedisClusterNode。add(newHostAndPort(yun5。cacheyum。com,6379));jedisClusterNode。add(newHostAndPort(yun6。cacheyum。com,6379));JedisClusterjcnewJedisCluster(jedisClusterNode,1000,1000,1000,admin321,config);}publicstaticvoidscanCluster(){JedisClusterredisgetJedisCluster();redis。getClusterNodes()。values()。stream()。forEach(pool{Stringcur0;try(JedisjedisNodepool。getResource()){while(!done){ScanParamsscanParamsnewScanParams();scanParams。match(ACLUSER);scanParams。count(10);ScanResultStringrespjedisNode。scan(cur,scanParams);for(Stringresult:resp。getResult()){System。out。println(key:result);}curresp。getStringCursor();System。out。println(cursor:cur);if(cur。equals(0)){}}}});}publicstaticvoidmain(String〔〕args){scanCluster();}} 单机情况下jedis代码实现importjava。util。HashSimportjava。util。Simportredis。clients。jedis。HostAndPimportredis。clients。jedis。Jimportredis。clients。jedis。JedisPoolCimportredis。clients。jedis。ScanPimportredis。clients。jedis。ScanRpublicclassRedisTester{publicstaticJedisgetAloneRedis(){JedisjedisnewJedis(127。0。0。1,6379);System。out。println(connectsuccessfully);jedis。auth(admin321);}publicvoidscanAlone(){JedisjedisgetAloneRedis();Stringcursor0;do{ScanParamsscanParamsnewScanParams();scanParams。match(ACLUSER);scanParams。count(10);ScanResultStringsrjedis。scan(cursor,scanParams);ListStringresultListsr。getResult();for(Stringresult:resultList){System。out。println(key:result);}cursorsr。getStringCursor();System。out。println(cursor:cursor);}while(!cursor。equals(0));}publicstaticvoidmain(String〔〕args){getAloneRedis();}} 使用以下代码就可以找到那些版本的uuid是无效的。找到后,再调用del指令删除;或者为了更加保险,调用expire加个过期时间。让key在某个时间内失效也可以。