起因

下午接到运维反馈,生产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集合中的元素和元素对应的分值

栗子:

  1. redis 127.0.0.1:6379> scan 0 MATCH *11*
  2. 1) "288"
  3. 2) 1) "key:911"
  4. redis 127.0.0.1:6379> scan 288 MATCH *11*
  5. 1) "224"
  6. 2) (empty list or set)
  7. redis 127.0.0.1:6379> scan 224 MATCH *11*
  8. 1) "80"
  9. 2) (empty list or set)
  10. redis 127.0.0.1:6379> scan 80 MATCH *11*
  11. 1) "176"
  12. 2) (empty list or set)
  13. redis 127.0.0.1:6379> scan 176 MATCH *11* COUNT 1000
  14. 1) "0"
  15. 2) 1) "key:611"
  16. 2) "key:711"
  17. 3) "key:118"
  18. 4) "key:117"
  19. 5) "key:311"
  20. 6) "key:112"
  21. 7) "key:111"
  22. 8) "key:110"
  23. 9) "key:113"
  24. 10) "key:211"
  25. 11) "key:411"
  26. 12) "key:115"
  27. 13) "key:116"
  28. 14) "key:114"
  29. 15) "key:119"
  30. 16) "key:811"
  31. 17) "key:511"
  32. 18) "key:11"
  33. redis 127.0.0.1:6379>

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

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

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

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

java代码实践一下

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

  1. <dependency>
  2. <groupId>redis.clients</groupId>
  3. <artifactId>jedis</artifactId>
  4. <version>2.9.3</version>
  5. </dependency>
  6. <!--hutool工具类的jar包不是必须的,我是用习惯这个工具类了,你们有其他工具类直接去掉hutool即可-->
  7. <dependency>
  8. <groupId>cn.hutool</groupId>
  9. <artifactId>hutool-all</artifactId>
  10. <version>5.6.5</version>
  11. </dependency>
  1. import cn.hutool.core.collection.CollUtil;
  2. import cn.hutool.core.date.DateUtil;
  3. import cn.hutool.core.date.TimeInterval;
  4. import cn.hutool.core.thread.ExecutorBuilder;
  5. import lombok.extern.slf4j.Slf4j;
  6. import redis.clients.jedis.Jedis;
  7. import redis.clients.jedis.ScanParams;
  8. import redis.clients.jedis.ScanResult;
  9. import java.util.HashSet;
  10. import java.util.List;
  11. import java.util.Set;
  12. import java.util.UUID;
  13. import java.util.concurrent.CountDownLatch;
  14. import java.util.concurrent.ExecutorService;
  15. import java.util.concurrent.LinkedBlockingQueue;
  16. import java.util.concurrent.atomic.AtomicInteger;
  17. @Slf4j
  18. public class JedisService {
  19. public static Jedis getJedis(){
  20. return new Jedis("localhost",6379,1000000000);
  21. }
  22. volatile static Boolean testCycleSetExitFlag = Boolean.FALSE;
  23. /**
  24. * 初始化数据到redis
  25. */
  26. public static void initData() throws InterruptedException {
  27. TimeInterval timeInterval = DateUtil.timer();
  28. log.info("initData start");
  29. getJedis().set("111CCCCCCCCCCCCCC1111","1");//先把查找的key设置进去
  30. //初始化数据个数
  31. AtomicInteger count = new AtomicInteger(10000*2000);
  32. //开100个客户端去执行set命令
  33. Integer totalClientNum = 100;
  34. ExecutorService executor = ExecutorBuilder.create()
  35. .setCorePoolSize(10)
  36. .setMaxPoolSize(50)
  37. .setWorkQueue(new LinkedBlockingQueue<>(1024))
  38. .build();
  39. CountDownLatch countDownLatch = new CountDownLatch(totalClientNum);
  40. for (int i = 0; i < totalClientNum; i++) {
  41. executor.submit(()->{
  42. Jedis jedis = getJedis();
  43. while (true){
  44. Integer crrentCount = count.decrementAndGet();
  45. if(crrentCount<=0){
  46. break;
  47. }
  48. if(crrentCount%10000==0){
  49. log.info(" 设置key的数量还剩:{} 已耗时:{}毫秒",crrentCount,timeInterval.interval());
  50. }
  51. jedis.set(UUID.randomUUID().toString().replaceAll("-",""),"1");
  52. }
  53. countDownLatch.countDown();
  54. });
  55. }
  56. countDownLatch.await();
  57. log.info("initData end 总耗时:{}毫秒",timeInterval.interval());
  58. executor.shutdown();
  59. }
  60. public static void main(String[] args) throws Exception {
  61. Jedis jedis = getJedis();
  62. System.out.println("服务正在运行: "+jedis.ping());
  63. // initData();//初始化数据
  64. log.info("现在redis服务有{}个key",jedis.dbSize());
  65. String pattern = "*CCCCCCCCCCCCCC*";
  66. //第一步:先验证一下keys和scan的执行效率
  67. keys(pattern);
  68. scan(pattern);
  69. log.info("----------------我是一条分割线---------------");
  70. //第二步:来验证一下keys和scan命令是否会阻塞其它命令
  71. new Thread(()->{
  72. testCycleSet("1111","11");
  73. }).start();
  74. Thread.sleep(2000);//休眠两秒
  75. keys(pattern);
  76. scan(pattern);
  77. testCycleSetExitFlag = true;
  78. log.info("----------------我还是一条分割线---------------");
  79. //下面测试一下设置不同count的效率
  80. scan(pattern,100);
  81. scan(pattern,200);
  82. scan(pattern,300);
  83. scan(pattern,400);
  84. scan(pattern,500);
  85. scan(pattern,1000);
  86. scan(pattern,2000);
  87. scan(pattern,5000);
  88. scan(pattern,10000);
  89. }
  90. //循环设置一个key值 测试用的
  91. public static void testCycleSet(String key,String value){
  92. Jedis jedis = getJedis();
  93. while (true){
  94. if(testCycleSetExitFlag){
  95. break;
  96. }
  97. Long startTime = System.currentTimeMillis();
  98. log.info("testCycleSet key:{} value:{} start",key,value);
  99. try {
  100. jedis.set(key,value);
  101. }catch (Exception e){
  102. log.error("testCycleSet",e);
  103. }
  104. log.info("testCycleSet key:{} value:{} end 耗时:{}毫秒",key,value,(System.currentTimeMillis() - startTime));
  105. try {
  106. Thread.sleep(1000);
  107. } catch (InterruptedException e) {
  108. e.printStackTrace();
  109. }
  110. }
  111. }
  112. public static Set<String> keys(String pattern){
  113. Long startTime = System.currentTimeMillis();
  114. log.info("jedis keys start 匹配的key:{}",pattern);
  115. Set<String> keySet = getJedis().keys(pattern);
  116. log.info("jedis keys end 匹配的key:{} 结果集大小:{} 耗时:{}毫秒 ",pattern,keySet.size(),(System.currentTimeMillis() - startTime));
  117. return keySet;
  118. }
  119. public static Set<String> scan(String pattern){
  120. return scan(pattern,300);
  121. }
  122. public static Set<String> scan(String pattern,int count){
  123. Long startTime = System.currentTimeMillis();
  124. Jedis jedis = getJedis();
  125. log.info("jedis scan start 匹配的key:{} 每次遍历{}个key",pattern,count);
  126. String index = "0";
  127. Set<String> keySet = new HashSet<String>();//匹配到的结果集
  128. Integer scanNum = 0;
  129. try {
  130. ScanParams params = new ScanParams();
  131. params.match(pattern);
  132. params.count(count);
  133. while (true){
  134. ScanResult<String> scanResult = jedis.scan(index,params);
  135. index = scanResult.getStringCursor();//下标重新赋值
  136. List<String> result = scanResult.getResult();//扫描到的key值
  137. if(CollUtil.isNotEmpty(result)){
  138. keySet.addAll(result);//扫描到的key放到Set
  139. }
  140. if("0".equals(index)){
  141. break;
  142. }
  143. scanNum++;
  144. }
  145. } catch (Exception e) {
  146. log.info("redis异常" + e.getMessage());
  147. }
  148. log.info("jedis scan end 匹配的key:{} 每次遍历{}个key scan执行了:{}次 结果集大小:{} 总耗时:{}毫秒",pattern,count,scanNum,keySet.size(),(System.currentTimeMillis() - startTime));
  149. return keySet;
  150. }
  151. }
  1. 服务正在运行: PONG
  2. 00:34:30.776 [main] INFO com.test.JedisService - 现在redis服务有25000000key
  3. 00:34:30.780 [main] INFO com.test.JedisService - jedis keys start 匹配的key:*CCCCCCCCCCCCCC*
  4. 00:34:41.493 [main] INFO com.test.JedisService - jedis keys end 匹配的key:*CCCCCCCCCCCCCC* 结果集大小:1 耗时:10713毫秒
  5. 00:34:41.493 [main] INFO com.test.JedisService - jedis scan start 匹配的key:*CCCCCCCCCCCCCC* 每次遍历300key
  6. 00:35:09.307 [main] INFO com.test.JedisService - jedis scan end 匹配的key:*CCCCCCCCCCCCCC* 每次遍历300key scan执行了:83231 结果集大小:1 总耗时:27814毫秒
  7. 00:35:09.307 [main] INFO com.test.JedisService - ----------------我是一条分割线---------------
  8. 00:35:09.352 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 start
  9. 00:35:09.355 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 end 耗时:3毫秒
  10. 00:35:10.358 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 start
  11. 00:35:10.359 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 end 耗时:0毫秒
  12. 00:35:11.356 [main] INFO com.test.JedisService - jedis keys start 匹配的key:*CCCCCCCCCCCCCC*
  13. 00:35:11.360 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 start
  14. 00:35:22.724 [main] INFO com.test.JedisService - jedis keys end 匹配的key:*CCCCCCCCCCCCCC* 结果集大小:1 耗时:11368毫秒
  15. 00:35:22.725 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 end 耗时:11365毫秒
  16. 00:35:22.725 [main] INFO com.test.JedisService - jedis scan start 匹配的key:*CCCCCCCCCCCCCC* 每次遍历300key
  17. 00:35:23.728 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 start
  18. 00:35:23.729 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 end 耗时:1毫秒
  19. 00:35:24.734 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 start
  20. 00:35:24.735 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 end 耗时:1毫秒
  21. 00:35:25.739 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 start
  22. 00:35:25.740 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 end 耗时:1毫秒
  23. 00:35:26.744 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 start
  24. 00:35:26.744 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 end 耗时:0毫秒
  25. 00:35:27.747 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 start
  26. 00:35:27.747 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 end 耗时:0毫秒
  27. 00:35:28.751 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 start
  28. 00:35:28.751 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 end 耗时:0毫秒
  29. 00:35:29.755 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 start
  30. 00:35:29.757 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 end 耗时:1毫秒
  31. 00:35:30.761 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 start
  32. 00:35:30.761 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 end 耗时:0毫秒
  33. 00:35:31.766 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 start
  34. 00:35:31.767 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 end 耗时:1毫秒
  35. 00:35:32.767 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 start
  36. 00:35:32.768 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 end 耗时:1毫秒
  37. 00:35:33.772 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 start
  38. 00:35:33.773 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 end 耗时:1毫秒
  39. 00:35:34.776 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 start
  40. 00:35:34.776 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 end 耗时:0毫秒
  41. 00:35:35.779 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 start
  42. 00:35:35.780 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 end 耗时:1毫秒
  43. 00:35:36.784 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 start
  44. 00:35:36.785 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 end 耗时:1毫秒
  45. 00:35:37.788 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 start
  46. 00:35:37.788 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 end 耗时:0毫秒
  47. 00:35:38.793 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 start
  48. 00:35:38.793 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 end 耗时:0毫秒
  49. 00:35:39.796 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 start
  50. 00:35:39.796 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 end 耗时:0毫秒
  51. 00:35:40.797 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 start
  52. 00:35:40.797 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 end 耗时:0毫秒
  53. 00:35:41.800 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 start
  54. 00:35:41.801 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 end 耗时:1毫秒
  55. 00:35:42.806 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 start
  56. 00:35:42.806 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 end 耗时:0毫秒
  57. 00:35:43.811 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 start
  58. 00:35:43.811 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 end 耗时:0毫秒
  59. 00:35:44.816 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 start
  60. 00:35:44.816 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 end 耗时:0毫秒
  61. 00:35:45.821 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 start
  62. 00:35:45.821 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 end 耗时:0毫秒
  63. 00:35:46.826 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 start
  64. 00:35:46.826 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 end 耗时:0毫秒
  65. 00:35:47.830 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 start
  66. 00:35:47.830 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 end 耗时:0毫秒
  67. 00:35:48.835 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 start
  68. 00:35:48.835 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 end 耗时:0毫秒
  69. 00:35:49.840 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 start
  70. 00:35:49.840 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 end 耗时:0毫秒
  71. 00:35:50.845 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 start
  72. 00:35:50.845 [Thread-0] INFO com.test.JedisService - testCycleSet key1111 value:11 end 耗时:0毫秒
  73. 00:35:51.225 [main] INFO com.test.JedisService - jedis scan end 匹配的key:*CCCCCCCCCCCCCC* 每次遍历300key scan执行了:83231 结果集大小:1 总耗时:28500毫秒
  74. 00:35:51.225 [main] INFO com.test.JedisService - ----------------我还是一条分割线---------------
  75. 00:35:51.226 [main] INFO com.test.JedisService - jedis scan start 匹配的key:*CCCCCCCCCCCCCC* 每次遍历100key
  76. 00:36:24.747 [main] INFO com.test.JedisService - jedis scan end 匹配的key:*CCCCCCCCCCCCCC* 每次遍历100key scan执行了:249070 结果集大小:1 总耗时:33522毫秒
  77. 00:36:24.747 [main] INFO com.test.JedisService - jedis scan start 匹配的key:*CCCCCCCCCCCCCC* 每次遍历200key
  78. 00:36:54.580 [main] INFO com.test.JedisService - jedis scan end 匹配的key:*CCCCCCCCCCCCCC* 每次遍历200key scan执行了:124768 结果集大小:1 总耗时:29833毫秒
  79. 00:36:54.580 [main] INFO com.test.JedisService - jedis scan start 匹配的key:*CCCCCCCCCCCCCC* 每次遍历300key
  80. 00:37:23.674 [main] INFO com.test.JedisService - jedis scan end 匹配的key:*CCCCCCCCCCCCCC* 每次遍历300key scan执行了:83231 结果集大小:1 总耗时:29094毫秒
  81. 00:37:23.674 [main] INFO com.test.JedisService - jedis scan start 匹配的key:*CCCCCCCCCCCCCC* 每次遍历400key
  82. 00:37:56.541 [main] INFO com.test.JedisService - jedis scan end 匹配的key:*CCCCCCCCCCCCCC* 每次遍历400key scan执行了:62441 结果集大小:1 总耗时:32867毫秒
  83. 00:37:56.542 [main] INFO com.test.JedisService - jedis scan start 匹配的key:*CCCCCCCCCCCCCC* 每次遍历500key
  84. 00:38:28.282 [main] INFO com.test.JedisService - jedis scan end 匹配的key:*CCCCCCCCCCCCCC* 每次遍历500key scan执行了:49962 结果集大小:1 总耗时:31740毫秒
  85. 00:38:28.282 [main] INFO com.test.JedisService - jedis scan start 匹配的key:*CCCCCCCCCCCCCC* 每次遍历1000key
  86. 00:39:00.211 [main] INFO com.test.JedisService - jedis scan end 匹配的key:*CCCCCCCCCCCCCC* 每次遍历1000key scan执行了:24990 结果集大小:1 总耗时:31929毫秒
  87. 00:39:00.211 [main] INFO com.test.JedisService - jedis scan start 匹配的key:*CCCCCCCCCCCCCC* 每次遍历2000key
  88. 00:39:31.754 [main] INFO com.test.JedisService - jedis scan end 匹配的key:*CCCCCCCCCCCCCC* 每次遍历2000key scan执行了:12497 结果集大小:1 总耗时:31543毫秒
  89. 00:39:31.754 [main] INFO com.test.JedisService - jedis scan start 匹配的key:*CCCCCCCCCCCCCC* 每次遍历5000key
  90. 00:40:03.548 [main] INFO com.test.JedisService - jedis scan end 匹配的key:*CCCCCCCCCCCCCC* 每次遍历5000key scan执行了:4999 结果集大小:1 总耗时:31794毫秒
  91. 00:40:03.548 [main] INFO com.test.JedisService - jedis scan start 匹配的key:*CCCCCCCCCCCCCC* 每次遍历10000key
  92. 00:40:36.329 [main] INFO com.test.JedisService - jedis scan end 匹配的key:*CCCCCCCCCCCCCC* 每次遍历10000key scan执行了:2499 结果集大小:1 总耗时:32781毫秒
  1. 这是500key 不同count执行的结果
  2. 服务正在运行: PONG
  3. 01:23:29.449 [main] INFO com.test.JedisService - 现在redis服务有5000000key
  4. 01:23:29.452 [main] INFO com.test.JedisService - jedis keys start 匹配的key:*CCCCCCCCCCCCCC*
  5. 01:23:31.172 [main] INFO com.test.JedisService - jedis keys end 匹配的key:*CCCCCCCCCCCCCC* 结果集大小:1 耗时:1720毫秒
  6. 01:23:31.172 [main] INFO com.test.JedisService - jedis scan start 匹配的key:*CCCCCCCCCCCCCC* 每次遍历300key
  7. 01:23:36.587 [main] INFO com.test.JedisService - jedis scan end 匹配的key:*CCCCCCCCCCCCCC* 每次遍历300key scan执行了:16649 结果集大小:1 总耗时:5415毫秒
  8. 01:23:36.587 [main] INFO com.test.JedisService - ----------------我还是一条分割线---------------
  9. 01:23:36.587 [main] INFO com.test.JedisService - jedis scan start 匹配的key:*CCCCCCCCCCCCCC* 每次遍历100key
  10. 01:23:43.030 [main] INFO com.test.JedisService - jedis scan end 匹配的key:*CCCCCCCCCCCCCC* 每次遍历100key scan执行了:49851 结果集大小:1 总耗时:6443毫秒
  11. 01:23:43.031 [main] INFO com.test.JedisService - jedis scan start 匹配的key:*CCCCCCCCCCCCCC* 每次遍历200key
  12. 01:23:48.801 [main] INFO com.test.JedisService - jedis scan end 匹配的key:*CCCCCCCCCCCCCC* 每次遍历200key scan执行了:24963 结果集大小:1 总耗时:5769毫秒
  13. 01:23:48.801 [main] INFO com.test.JedisService - jedis scan start 匹配的key:*CCCCCCCCCCCCCC* 每次遍历300key
  14. 01:23:54.355 [main] INFO com.test.JedisService - jedis scan end 匹配的key:*CCCCCCCCCCCCCC* 每次遍历300key scan执行了:16649 结果集大小:1 总耗时:5554毫秒
  15. 01:23:54.355 [main] INFO com.test.JedisService - jedis scan start 匹配的key:*CCCCCCCCCCCCCC* 每次遍历400key
  16. 01:23:59.711 [main] INFO com.test.JedisService - jedis scan end 匹配的key:*CCCCCCCCCCCCCC* 每次遍历400key scan执行了:12490 结果集大小:1 总耗时:5356毫秒
  17. 01:23:59.711 [main] INFO com.test.JedisService - jedis scan start 匹配的key:*CCCCCCCCCCCCCC* 每次遍历500key
  18. 01:24:05.016 [main] INFO com.test.JedisService - jedis scan end 匹配的key:*CCCCCCCCCCCCCC* 每次遍历500key scan执行了:9994 结果集大小:1 总耗时:5305毫秒
  19. 01:24:05.016 [main] INFO com.test.JedisService - jedis scan start 匹配的key:*CCCCCCCCCCCCCC* 每次遍历1000key
  20. 01:24:11.594 [main] INFO com.test.JedisService - jedis scan end 匹配的key:*CCCCCCCCCCCCCC* 每次遍历1000key scan执行了:4998 结果集大小:1 总耗时:6578毫秒
  21. 01:24:11.594 [main] INFO com.test.JedisService - jedis scan start 匹配的key:*CCCCCCCCCCCCCC* 每次遍历2000key
  22. 01:24:18.137 [main] INFO com.test.JedisService - jedis scan end 匹配的key:*CCCCCCCCCCCCCC* 每次遍历2000key scan执行了:2499 结果集大小:1 总耗时:6543毫秒
  23. 01:24:18.137 [main] INFO com.test.JedisService - jedis scan start 匹配的key:*CCCCCCCCCCCCCC* 每次遍历5000key
  24. 01:24:24.655 [main] INFO com.test.JedisService - jedis scan end 匹配的key:*CCCCCCCCCCCCCC* 每次遍历5000key scan执行了:999 结果集大小:1 总耗时:6518毫秒
  25. 01:24:24.655 [main] INFO com.test.JedisService - jedis scan start 匹配的key:*CCCCCCCCCCCCCC* 每次遍历10000key
  26. 01:24:31.257 [main] INFO com.test.JedisService - jedis scan end 匹配的key:*CCCCCCCCCCCCCC* 每次遍历10000key 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. 【java框架】SpringBoot(5)--SpringBoot整合分布式Dubbo+Zookeeper

    1.理论概述 1.1.分布式 分布式系统是若干独立计算机的集合,这些计算机对于用户来讲就像单个系统. 由多个系统集成成一个整体,提供多个功能,组合成一个板块,用户在使用上看起来是一个服务.(比如淘宝网 ...

  2. KubeEdge边缘自治设计原理

    这一篇内容主要是KubeEdge中边缘节点组件EdgeCore的原理介绍. KubeEdge架构-EdgeCore 上图中深蓝色的都是kubeedg自己实现的组件,亮蓝色是k8s社区原生组件.这篇主要 ...

  3. shell分支与循环结构

    1. 条件选择 1.1 条件判断分支介绍 格式 if COMMANDS; then COMMANDS; [ elif COMMANDS; then COMMANDS; ]... [ else COMM ...

  4. JS基础学习第四天

    对象(Object) 对象是JS中的引用数据类型对象是一种复合数据类型,在对象中可以保存多个不同数据类型的属性使用typeof检查一个对象时,会返回object 对象的分类: 1.内建对象- 由ES标 ...

  5. SparkStreaming使用mapWithState时,设置timeout()无法生效问题解决方案

    前言 当我在测试SparkStreaming的状态操作mapWithState算子时,当我们设置timeout(3s)的时候,3s过后数据还是不会过期,不对此key进行操作,等到30s左右才会清除过期 ...

  6. BUAA_2021_SE_READING_#2

    项目 内容 这个作业属于哪个课程 2021春季软件工程(罗杰 任健) 这个作业的要求在哪里 个人阅读作业#2 我在这个课程的目标是 通过课程学习,完成第一个可以称之为"软件"的项目 ...

  7. DDD实战让中台和微服务的落地如虎添翼

    微服务到底怎么拆分和设计才算合理,拆多小才叫微服务?有没有好的方法来指导微服务和中台的设计呢? 深入DDD的核心知识体系与设计思想,带你掌握一套完整而系统的基于DDD的微服务拆分与设计方法,助力落地边 ...

  8. JavaScript深入理解-Set、Map、WeakSet和WeakMap

    Set Set 对象允许储存任何类型的唯一值,无论是原始值或者是对象引用 本质:构造函数,用来生成 Set 数据结构 描述 Set 对象是值的集合,你可以按照插入的顺序迭代它的元素.Set 中的元素只 ...

  9. MySQL数据库高级四:工具拾遗(视图)

    视图

  10. hbuilderX打包苹果证书的申请方法

    现在uniapp越来越火,hbuilderX和apicloud这些工具使用html+js语言就可以开发强大的app,大大降低了app开发的技术门槛. hbuilderX或apicloud在打包ios应 ...