起因

下午接到运维反馈,生产redis有个执行keys的命令请求太慢了,要两三秒才能响应

涉及命令如下:

KEYS ttl_600::findHeadFootData-15349232-*-head

什么是keys命令?

keys官方文档 http://www.redis.cn/commands/keys.html

KEYS pattern

查找所有符合给定模式pattern(正则表达式)的 key 。

时间复杂度为O(N),N为数据库里面key的数量。

例如,Redis在一个有1百万个key的数据库里面执行一次查询需要的时间是40毫秒 。

警告: KEYS 的速度非常快,但在一个大的数据库中使用它仍然可能造成性能问题,如果你需要从一个数据集中查找特定的 KEYS, 你最好还是用 Redis 的集合结构 SETS 来代替。

支持的正则表达模式:

h?llo 匹配 hello, hallo 和 hxllo

h*llo 匹配 hllo 和 heeeello

h[ae]llo 匹配 hello 和 hallo, 但是不匹配 hillo

h[^e]llo 匹配 hallo, hbllo, … 但是不匹配 hello

h[a-b]llo 匹配 hallo 和 hbllo

返回值

所有符合条件的key

Redis提供的所有API操作,相对于服务端方面都是one by one执行的,命令是一个接着一个执行的,不存在并行执行的情况。

虽说REDIS执行KEYS命令很快,但是由于生产环境上有近六百万KEY,以至于KEYS命令需要两三秒,这两三秒就会导致其它命令阻塞着,这在生产中是灾难性的;

redis提供了一个scan的命令

scan命令官方文档 http://www.redis.cn/commands/scan.html

SCAN cursor [MATCH pattern] [COUNT count]

cursor 游标

[MATCH pattern] 需要正则匹配的字符串

count 扫描的key的个数

SCAN 命令及其相关的 SSCAN, HSCAN 和 ZSCAN 命令都用于增量迭代一个集合元素。

  • SCAN 命令用于迭代当前数据库中的key集合。
  • SSCAN 命令用于迭代SET集合中的元素。
  • HSCAN 命令用于迭代Hash类型中的键值对。
  • ZSCAN 命令用于迭代SortSet集合中的元素和元素对应的分值

栗子:

redis 127.0.0.1:6379> scan 0 MATCH *11*
1) "288"
2) 1) "key:911"
redis 127.0.0.1:6379> scan 288 MATCH *11*
1) "224"
2) (empty list or set)
redis 127.0.0.1:6379> scan 224 MATCH *11*
1) "80"
2) (empty list or set)
redis 127.0.0.1:6379> scan 80 MATCH *11*
1) "176"
2) (empty list or set)
redis 127.0.0.1:6379> scan 176 MATCH *11* COUNT 1000
1) "0"
2) 1) "key:611"
2) "key:711"
3) "key:118"
4) "key:117"
5) "key:311"
6) "key:112"
7) "key:111"
8) "key:110"
9) "key:113"
10) "key:211"
11) "key:411"
12) "key:115"
13) "key:116"
14) "key:114"
15) "key:119"
16) "key:811"
17) "key:511"
18) "key:11"
redis 127.0.0.1:6379>

在上面这个例子中, 第一次迭代使用 0 作为游标, 表示开始一次新的迭代。第二次迭代使用的是第一次迭代时返回的游标 17 ,作为新的迭代参数 。

显而易见,SCAN命令的返回值 是一个包含两个元素的数组, 第一个数组元素是用于进行下一次迭代的新游标, 而第二个数组元素则是一个数组, 这个数组中包含了所有被迭代的元素。

在第二次调用 SCAN 命令时, 命令返回了游标 0 , 这表示迭代已经结束, 整个数据集已经被完整遍历过了。

0 作为游标开始一次新的迭代, 一直调用 SCAN 命令, 直到命令返回游标 0 , 我们称这个过程为一次完整遍历

java代码实践一下

上面已经说了keys和scan的命令了,下面用jedis来进行实践一下,在本地编码自测:

<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.3</version>
</dependency>
<!--hutool工具类的jar包不是必须的,我是用习惯这个工具类了,你们有其他工具类直接去掉hutool即可-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.6.5</version>
</dependency>
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.TimeInterval;
import cn.hutool.core.thread.ExecutorBuilder;
import lombok.extern.slf4j.Slf4j;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.ScanParams;
import redis.clients.jedis.ScanResult; import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger; @Slf4j
public class JedisService { public static Jedis getJedis(){
return new Jedis("localhost",6379,1000000000);
} volatile static Boolean testCycleSetExitFlag = Boolean.FALSE;
/**
* 初始化数据到redis
*/
public static void initData() throws InterruptedException {
TimeInterval timeInterval = DateUtil.timer();
log.info("initData start");
getJedis().set("111CCCCCCCCCCCCCC1111","1");//先把查找的key设置进去
//初始化数据个数
AtomicInteger count = new AtomicInteger(10000*2000); //开100个客户端去执行set命令
Integer totalClientNum = 100; ExecutorService executor = ExecutorBuilder.create()
.setCorePoolSize(10)
.setMaxPoolSize(50)
.setWorkQueue(new LinkedBlockingQueue<>(1024))
.build(); CountDownLatch countDownLatch = new CountDownLatch(totalClientNum);
for (int i = 0; i < totalClientNum; i++) {
executor.submit(()->{
Jedis jedis = getJedis();
while (true){
Integer crrentCount = count.decrementAndGet();
if(crrentCount<=0){
break;
}
if(crrentCount%10000==0){
log.info(" 设置key的数量还剩:{} 已耗时:{}毫秒",crrentCount,timeInterval.interval());
}
jedis.set(UUID.randomUUID().toString().replaceAll("-",""),"1");
}
countDownLatch.countDown();
});
}
countDownLatch.await();
log.info("initData end 总耗时:{}毫秒",timeInterval.interval());
executor.shutdown();
} public static void main(String[] args) throws Exception {
Jedis jedis = getJedis();
System.out.println("服务正在运行: "+jedis.ping());
// initData();//初始化数据
log.info("现在redis服务有{}个key",jedis.dbSize());
String pattern = "*CCCCCCCCCCCCCC*"; //第一步:先验证一下keys和scan的执行效率
keys(pattern);
scan(pattern); log.info("----------------我是一条分割线---------------");
//第二步:来验证一下keys和scan命令是否会阻塞其它命令
new Thread(()->{
testCycleSet("1111","11");
}).start(); Thread.sleep(2000);//休眠两秒 keys(pattern);
scan(pattern);
testCycleSetExitFlag = true;
log.info("----------------我还是一条分割线---------------");
//下面测试一下设置不同count的效率
scan(pattern,100);
scan(pattern,200);
scan(pattern,300);
scan(pattern,400);
scan(pattern,500);
scan(pattern,1000);
scan(pattern,2000);
scan(pattern,5000);
scan(pattern,10000);
}
//循环设置一个key值 测试用的
public static void testCycleSet(String key,String value){
Jedis jedis = getJedis();
while (true){
if(testCycleSetExitFlag){
break;
}
Long startTime = System.currentTimeMillis();
log.info("testCycleSet key:{} value:{} start",key,value);
try {
jedis.set(key,value);
}catch (Exception e){
log.error("testCycleSet",e);
} log.info("testCycleSet key:{} value:{} end 耗时:{}毫秒",key,value,(System.currentTimeMillis() - startTime));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static Set<String> keys(String pattern){
Long startTime = System.currentTimeMillis();
log.info("jedis keys start 匹配的key:{}",pattern);
Set<String> keySet = getJedis().keys(pattern);
log.info("jedis keys end 匹配的key:{} 结果集大小:{} 耗时:{}毫秒 ",pattern,keySet.size(),(System.currentTimeMillis() - startTime));
return keySet; }
public static Set<String> scan(String pattern){
return scan(pattern,300);
}
public static Set<String> scan(String pattern,int count){
Long startTime = System.currentTimeMillis();
Jedis jedis = getJedis();
log.info("jedis scan start 匹配的key:{} 每次遍历{}个key",pattern,count);
String index = "0";
Set<String> keySet = new HashSet<String>();//匹配到的结果集
Integer scanNum = 0;
try {
ScanParams params = new ScanParams();
params.match(pattern);
params.count(count);
while (true){
ScanResult<String> scanResult = jedis.scan(index,params);
index = scanResult.getStringCursor();//下标重新赋值
List<String> result = scanResult.getResult();//扫描到的key值
if(CollUtil.isNotEmpty(result)){
keySet.addAll(result);//扫描到的key放到Set
} if("0".equals(index)){
break;
}
scanNum++;
}
} catch (Exception e) {
log.info("redis异常" + e.getMessage());
}
log.info("jedis scan end 匹配的key:{} 每次遍历{}个key scan执行了:{}次 结果集大小:{} 总耗时:{}毫秒",pattern,count,scanNum,keySet.size(),(System.currentTimeMillis() - startTime));
return keySet;
}
}
服务正在运行: PONG
00:34:30.776 [main] INFO com.test.JedisService - 现在redis服务有25000000个key
00:34:30.780 [main] INFO com.test.JedisService - jedis keys start 匹配的key:*CCCCCCCCCCCCCC*
00:34:41.493 [main] INFO com.test.JedisService - jedis keys end 匹配的key:*CCCCCCCCCCCCCC* 结果集大小:1 耗时:10713毫秒
00:34:41.493 [main] INFO com.test.JedisService - jedis scan start 匹配的key:*CCCCCCCCCCCCCC* 每次遍历300个key
00:35:09.307 [main] INFO com.test.JedisService - jedis scan end 匹配的key:*CCCCCCCCCCCCCC* 每次遍历300个key scan执行了:83231次 结果集大小:1 总耗时:27814毫秒
00:35:09.307 [main] INFO com.test.JedisService - ----------------我是一条分割线---------------
00:35:09.352 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 start
00:35:09.355 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 end 耗时:3毫秒
00:35:10.358 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 start
00:35:10.359 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 end 耗时:0毫秒
00:35:11.356 [main] INFO com.test.JedisService - jedis keys start 匹配的key:*CCCCCCCCCCCCCC*
00:35:11.360 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 start
00:35:22.724 [main] INFO com.test.JedisService - jedis keys end 匹配的key:*CCCCCCCCCCCCCC* 结果集大小:1 耗时:11368毫秒
00:35:22.725 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 end 耗时:11365毫秒
00:35:22.725 [main] INFO com.test.JedisService - jedis scan start 匹配的key:*CCCCCCCCCCCCCC* 每次遍历300个key
00:35:23.728 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 start
00:35:23.729 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 end 耗时:1毫秒
00:35:24.734 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 start
00:35:24.735 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 end 耗时:1毫秒
00:35:25.739 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 start
00:35:25.740 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 end 耗时:1毫秒
00:35:26.744 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 start
00:35:26.744 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 end 耗时:0毫秒
00:35:27.747 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 start
00:35:27.747 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 end 耗时:0毫秒
00:35:28.751 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 start
00:35:28.751 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 end 耗时:0毫秒
00:35:29.755 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 start
00:35:29.757 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 end 耗时:1毫秒
00:35:30.761 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 start
00:35:30.761 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 end 耗时:0毫秒
00:35:31.766 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 start
00:35:31.767 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 end 耗时:1毫秒
00:35:32.767 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 start
00:35:32.768 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 end 耗时:1毫秒
00:35:33.772 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 start
00:35:33.773 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 end 耗时:1毫秒
00:35:34.776 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 start
00:35:34.776 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 end 耗时:0毫秒
00:35:35.779 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 start
00:35:35.780 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 end 耗时:1毫秒
00:35:36.784 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 start
00:35:36.785 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 end 耗时:1毫秒
00:35:37.788 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 start
00:35:37.788 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 end 耗时:0毫秒
00:35:38.793 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 start
00:35:38.793 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 end 耗时:0毫秒
00:35:39.796 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 start
00:35:39.796 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 end 耗时:0毫秒
00:35:40.797 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 start
00:35:40.797 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 end 耗时:0毫秒
00:35:41.800 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 start
00:35:41.801 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 end 耗时:1毫秒
00:35:42.806 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 start
00:35:42.806 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 end 耗时:0毫秒
00:35:43.811 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 start
00:35:43.811 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 end 耗时:0毫秒
00:35:44.816 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 start
00:35:44.816 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 end 耗时:0毫秒
00:35:45.821 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 start
00:35:45.821 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 end 耗时:0毫秒
00:35:46.826 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 start
00:35:46.826 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 end 耗时:0毫秒
00:35:47.830 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 start
00:35:47.830 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 end 耗时:0毫秒
00:35:48.835 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 start
00:35:48.835 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 end 耗时:0毫秒
00:35:49.840 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 start
00:35:49.840 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 end 耗时:0毫秒
00:35:50.845 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 start
00:35:50.845 [Thread-0] INFO com.test.JedisService - testCycleSet key:1111 value:11 end 耗时:0毫秒
00:35:51.225 [main] INFO com.test.JedisService - jedis scan end 匹配的key:*CCCCCCCCCCCCCC* 每次遍历300个key scan执行了:83231次 结果集大小:1 总耗时:28500毫秒
00:35:51.225 [main] INFO com.test.JedisService - ----------------我还是一条分割线---------------
00:35:51.226 [main] INFO com.test.JedisService - jedis scan start 匹配的key:*CCCCCCCCCCCCCC* 每次遍历100个key
00:36:24.747 [main] INFO com.test.JedisService - jedis scan end 匹配的key:*CCCCCCCCCCCCCC* 每次遍历100个key scan执行了:249070次 结果集大小:1 总耗时:33522毫秒
00:36:24.747 [main] INFO com.test.JedisService - jedis scan start 匹配的key:*CCCCCCCCCCCCCC* 每次遍历200个key
00:36:54.580 [main] INFO com.test.JedisService - jedis scan end 匹配的key:*CCCCCCCCCCCCCC* 每次遍历200个key scan执行了:124768次 结果集大小:1 总耗时:29833毫秒
00:36:54.580 [main] INFO com.test.JedisService - jedis scan start 匹配的key:*CCCCCCCCCCCCCC* 每次遍历300个key
00:37:23.674 [main] INFO com.test.JedisService - jedis scan end 匹配的key:*CCCCCCCCCCCCCC* 每次遍历300个key scan执行了:83231次 结果集大小:1 总耗时:29094毫秒
00:37:23.674 [main] INFO com.test.JedisService - jedis scan start 匹配的key:*CCCCCCCCCCCCCC* 每次遍历400个key
00:37:56.541 [main] INFO com.test.JedisService - jedis scan end 匹配的key:*CCCCCCCCCCCCCC* 每次遍历400个key scan执行了:62441次 结果集大小:1 总耗时:32867毫秒
00:37:56.542 [main] INFO com.test.JedisService - jedis scan start 匹配的key:*CCCCCCCCCCCCCC* 每次遍历500个key
00:38:28.282 [main] INFO com.test.JedisService - jedis scan end 匹配的key:*CCCCCCCCCCCCCC* 每次遍历500个key scan执行了:49962次 结果集大小:1 总耗时:31740毫秒
00:38:28.282 [main] INFO com.test.JedisService - jedis scan start 匹配的key:*CCCCCCCCCCCCCC* 每次遍历1000个key
00:39:00.211 [main] INFO com.test.JedisService - jedis scan end 匹配的key:*CCCCCCCCCCCCCC* 每次遍历1000个key scan执行了:24990次 结果集大小:1 总耗时:31929毫秒
00:39:00.211 [main] INFO com.test.JedisService - jedis scan start 匹配的key:*CCCCCCCCCCCCCC* 每次遍历2000个key
00:39:31.754 [main] INFO com.test.JedisService - jedis scan end 匹配的key:*CCCCCCCCCCCCCC* 每次遍历2000个key scan执行了:12497次 结果集大小:1 总耗时:31543毫秒
00:39:31.754 [main] INFO com.test.JedisService - jedis scan start 匹配的key:*CCCCCCCCCCCCCC* 每次遍历5000个key
00:40:03.548 [main] INFO com.test.JedisService - jedis scan end 匹配的key:*CCCCCCCCCCCCCC* 每次遍历5000个key scan执行了:4999次 结果集大小:1 总耗时:31794毫秒
00:40:03.548 [main] INFO com.test.JedisService - jedis scan start 匹配的key:*CCCCCCCCCCCCCC* 每次遍历10000个key
00:40:36.329 [main] INFO com.test.JedisService - jedis scan end 匹配的key:*CCCCCCCCCCCCCC* 每次遍历10000个key scan执行了:2499次 结果集大小:1 总耗时:32781毫秒
这是500万key  不同count执行的结果

服务正在运行: PONG
01:23:29.449 [main] INFO com.test.JedisService - 现在redis服务有5000000个key
01:23:29.452 [main] INFO com.test.JedisService - jedis keys start 匹配的key:*CCCCCCCCCCCCCC*
01:23:31.172 [main] INFO com.test.JedisService - jedis keys end 匹配的key:*CCCCCCCCCCCCCC* 结果集大小:1 耗时:1720毫秒
01:23:31.172 [main] INFO com.test.JedisService - jedis scan start 匹配的key:*CCCCCCCCCCCCCC* 每次遍历300个key
01:23:36.587 [main] INFO com.test.JedisService - jedis scan end 匹配的key:*CCCCCCCCCCCCCC* 每次遍历300个key scan执行了:16649次 结果集大小:1 总耗时:5415毫秒
01:23:36.587 [main] INFO com.test.JedisService - ----------------我还是一条分割线---------------
01:23:36.587 [main] INFO com.test.JedisService - jedis scan start 匹配的key:*CCCCCCCCCCCCCC* 每次遍历100个key
01:23:43.030 [main] INFO com.test.JedisService - jedis scan end 匹配的key:*CCCCCCCCCCCCCC* 每次遍历100个key scan执行了:49851次 结果集大小:1 总耗时:6443毫秒
01:23:43.031 [main] INFO com.test.JedisService - jedis scan start 匹配的key:*CCCCCCCCCCCCCC* 每次遍历200个key
01:23:48.801 [main] INFO com.test.JedisService - jedis scan end 匹配的key:*CCCCCCCCCCCCCC* 每次遍历200个key scan执行了:24963次 结果集大小:1 总耗时:5769毫秒
01:23:48.801 [main] INFO com.test.JedisService - jedis scan start 匹配的key:*CCCCCCCCCCCCCC* 每次遍历300个key
01:23:54.355 [main] INFO com.test.JedisService - jedis scan end 匹配的key:*CCCCCCCCCCCCCC* 每次遍历300个key scan执行了:16649次 结果集大小:1 总耗时:5554毫秒
01:23:54.355 [main] INFO com.test.JedisService - jedis scan start 匹配的key:*CCCCCCCCCCCCCC* 每次遍历400个key
01:23:59.711 [main] INFO com.test.JedisService - jedis scan end 匹配的key:*CCCCCCCCCCCCCC* 每次遍历400个key scan执行了:12490次 结果集大小:1 总耗时:5356毫秒
01:23:59.711 [main] INFO com.test.JedisService - jedis scan start 匹配的key:*CCCCCCCCCCCCCC* 每次遍历500个key
01:24:05.016 [main] INFO com.test.JedisService - jedis scan end 匹配的key:*CCCCCCCCCCCCCC* 每次遍历500个key scan执行了:9994次 结果集大小:1 总耗时:5305毫秒
01:24:05.016 [main] INFO com.test.JedisService - jedis scan start 匹配的key:*CCCCCCCCCCCCCC* 每次遍历1000个key
01:24:11.594 [main] INFO com.test.JedisService - jedis scan end 匹配的key:*CCCCCCCCCCCCCC* 每次遍历1000个key scan执行了:4998次 结果集大小:1 总耗时:6578毫秒
01:24:11.594 [main] INFO com.test.JedisService - jedis scan start 匹配的key:*CCCCCCCCCCCCCC* 每次遍历2000个key
01:24:18.137 [main] INFO com.test.JedisService - jedis scan end 匹配的key:*CCCCCCCCCCCCCC* 每次遍历2000个key scan执行了:2499次 结果集大小:1 总耗时:6543毫秒
01:24:18.137 [main] INFO com.test.JedisService - jedis scan start 匹配的key:*CCCCCCCCCCCCCC* 每次遍历5000个key
01:24:24.655 [main] INFO com.test.JedisService - jedis scan end 匹配的key:*CCCCCCCCCCCCCC* 每次遍历5000个key scan执行了:999次 结果集大小:1 总耗时:6518毫秒
01:24:24.655 [main] INFO com.test.JedisService - jedis scan start 匹配的key:*CCCCCCCCCCCCCC* 每次遍历10000个key
01:24:31.257 [main] INFO com.test.JedisService - jedis scan end 匹配的key:*CCCCCCCCCCCCCC* 每次遍历10000个key scan执行了:499次 结果集大小:1 总耗时:6602毫秒

小结

keys和scan是否会阻塞其他命令?

看日志显而易见

keys命令会阻塞其它命令

scan命令则相当于分批次遍历全部key,遍历一部分key后把,所在位置(游标)返回给客户端,下次客户端拿着上次返回的游标,继续执行scan命令往后遍历,直至遍历完成,所以不会长时间阻塞redis。

scan命令count设置多大合适

我们redis服务器上面key的数量通常在五百万到七百万这个范围

经过上面测试:

scan命令 count设置成300

两千五百万的key会执行八万次的scan,耗时30秒就能遍历完,每秒大概执行3000次

五百万的key 会执行16649次的scan,总耗时6秒,每秒大概执行3000次

而生产上redis的的qps很轻松的扛住10w,所以设置300会比较合适。

最终

本地自测通过,发到灰度环境,测试反馈业务无异常,发到生产观察了几天运维也没不良的反馈,问题解决~

redis中keys命令带来的线上性能问题的更多相关文章

  1. Redis的KEYS命令引起宕机事件

    摘要: 使用 Redis 的开发者必看,吸取教训啊! 原文:Redis 的 KEYS 命令引起 RDS 数据库雪崩,RDS 发生两次宕机,造成几百万的资金损失 作者:陈浩翔 Fundebug经授权转载 ...

  2. Redis 的 KEYS 命令不能乱用啊

    KESY 命令 时间复杂度: O(N) , 假设Redis中的键名和给定的模式的长度有限的情况下,N为数据库中key的个数. Redis Keys 命令用于查找所有符合给定模式 pattern 的 k ...

  3. 关于redis的keys命令的性能问题

    KEYS pattern 查找所有符合给定模式 pattern 的 key . KEYS * 匹配数据库中所有 key . KEYS h?llo 匹配 hello , hallo 和 hxllo 等. ...

  4. Redis中常用命令

    连接操作相关的命令 quit:关闭连接(connection) auth:简单密码认证 持久化 save:将数据同步保存到磁盘 bgsave:将数据异步保存到磁盘 lastsave:返回上次成功将数据 ...

  5. redis中set命令的源码分析

    首先在源码中的redis.c文件中有一个结构体:redisCommand redisCommandTable[],这个结构体中定义了每个命令对应的函数,源码中的set命令对应的函数是setComman ...

  6. 8、Redis中sort命令详解

    写在前面的话:读书破万卷,编码如有神 ------------------------------------------------- 1.排序 (1)sort:可以对List.Set.ZSet里面 ...

  7. Allegro中解决鼠标放在走线上网络名、走线长度显示不出来的问题

    一些PCB设计者在使用allegro时,由于一些误操作 导致当鼠标放在走线(cline)和网络(net)上面时,软件没有显示该走线的所属网络,或者相关的长度信息.本人经过help文档发现,以下方法可以 ...

  8. 盘点 Oracle 11g 中新特性带来的10大性能影响

    Oracle的任何一个新版本,总是会带来大量引人瞩目的新特性,但是往往在这些新特性引入之初,首先引起的是一些麻烦,因为对于新技术的不了解.因为对于旧环境的不适应,从Oracle产品到技术服务运维,总是 ...

  9. 谈谈iOS app的线上性能监测

    在移动端开发者中最重要的KPI应该是崩溃率.当崩溃率稳定下来后,工作的重心就应该转移到性能优化上.那么问题来了,如果你的项目也没有接入任何性能监测SDK,没有量化的指标来衡量,那你说你优化了性能领导信 ...

随机推荐

  1. LinkedList类详解

    LinkedList类中的方法与实现原理 目录 一.数据结构 二.类标题 三.字段 四.构造函数 五.方法分析 5.1 共有方法 public boolean add(Object o) public ...

  2. 隐藏页面元素 css

    一.前言 在平常的样式排版中,我们经常遇到将某个模块隐藏的场景 通过css隐藏元素的方法有很多种,它们看起来实现的效果是一致的 但实际上每一种方法都有一丝轻微的不同,这些不同决定了在一些特定场合下使用 ...

  3. 冒泡排序(BubbleSort)

    介绍: 冒泡排序是一种最基础的交换排序(两两比较待排序的关键字,交换不满足次序要求的那对数,直到整个表都满足次序要求为止),工作方式如同碳酸饮料中的二氧化碳气泡最终会上浮到顶端一样,故名"冒 ...

  4. Oracle 19c Data Guard DML Redirection ADG备库上执行DML重定向(未来更好的进行读写分离)

    资料来自官方网站: https://docs.oracle.com/en/database/oracle/oracle-database/19/sbydb/managing-oracle-data-g ...

  5. 【10.5NOIP普及模拟】sum

    [10.5NOIP普及模拟]sum 文章目录 [10.5NOIP普及模拟]sum 题目描述 输入 输出 输入输出样例 样例输入 样例输出 解析 code 题目描述 小x有很多糖果,分成了 N 堆,排成 ...

  6. java面试-生产环境服务器变慢,谈谈你的诊断思路

    1.uptime:查询linux系统负载 11:16:16 系统当前时间 up 64 days, 19:23 从上次启动开始系统运行的时间3 users 连接数量,同一用户多个连接的时候算多个load ...

  7. 如何获取canvas当前的缩放值

    项目中一直有一个问题困扰着我,我们的画布可以缩放平移旋转,支持拖拽生成图形,生成手写笔迹,如果用户选择的线条粗细为5像素,那么即使画布缩放过绘制出的线条粗细也应该是视觉上的5px,所以再绘制时赋值给c ...

  8. spring-cloud-gateway 服务网关

    Spring Cloud Gateway是Spring Cloud官方推出的第二代网关框架,取代Zuul网关.网关作为流量的,在微服务系统中有着非常作用,网关常见的功能有路由转发.权限校验.限流控制等 ...

  9. php连接MySQL数据库的三种方式(mysql/mysqli/pdo)

    引言 PHP与MySQL的连接有三种API接口,分别是:PHP的MySQL扩展 .PHP的mysqli扩展 .PHP数据对象(PDO) ,下面针对以上三种连接方式做下总结,以备在不同场景下选出最优方案 ...

  10. Thinkphp5助手函数和Thinkphp3的单字母函数对应参照表