日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

redis出现过多command 慢查询slowlog出现command命令

發布時間:2023/12/10 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 redis出现过多command 慢查询slowlog出现command命令 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

大家好,我是烤鴨:

今天分享一個問題,一個關于redis slowlog,執行過多 command命令的問題。

?

問題來源

所有走redis 的接口tp99和平均耗時是原來的兩倍不止,運維說redis 的qps也翻倍了。查了下slowlog,發現command 命令占大多數,除了兩條業務請求,其他都是command。

?

command命令返回的是當前redis所能執行的命令和對應的賬號權限,也就不超過200條數據吧。(但頻繁執行會增加qps,影響集群性能)

這個command命令是確實由業務服務發起的(ip符合),但是代碼里也沒有顯示的調用(第一時間感覺是sdk有問題)。由于問題最開始出現在5天前,讓運維查了一周前的記錄,發現command命令也存在,只是沒那么明顯,比例大概1/20(調20次get命令,會出現一次command)。

?

源碼查看,找蛛絲馬跡

項目框架用的是 springboot的版本2.0.4.RELEASE,對應 spring-boot-starter-data-redis的版本2.0.4.RELEASE

對應 lettuce-core 版本 5.0.4.RELEASE。

分別從這幾個包搜 command 命令,搜不到。

代碼里用的比較多的有 redisTemplate.executePipelined 方法,跟這個代碼進去看一下。

以get方法為例,由于默認調用的是 lettuce,RedisStringCommands.get ——> LettuceStringCommands.getConnection(getAsyncConnection) —>

LettuceConnection.getConnection()—>

而 LettuceConnection中的變量 asyncSharedConn 會是null,所以每次都得嘗試重新連接

?

LettuceConnection.getDedicatedConnection()—> ClusterConnectionProvider.getConnection (配置連接池的話走的是 LettucePoolingConnectionProvider.getConnection,所以每次不需要再次創建連接 )

?

而LettucePoolingConnectionProvider.getConnection會先判斷連接池是否存在,是否不存在,會先去創建。

ClusterConnectionProvider.getConnection 和 LettucePoolingConnectionProvider.getConnection 創建連接池時調用的都是 LettuceConnectionProvider.getConnection

?

LettuceConnectionProvider.getConnection(每次創建連接都會執行 command命令)

?

RedisClusterClient.connect—> RedisClusterClient.connectClusterImpl

這個方法里的調用了 connection.inspectRedisState();

command() 方法進入到了 ClusterFutureSyncInvocationHandler.handleInvocation

?

這里能看到每一次調用的命令,打開debug日志也能看到。

?

原因清楚了,就是每次執行命令都會檢查 LettuceConnection asyncDedicatedConn是否為空, 如果為空,就會再次連接,再次連接就會執行command命令。為啥呢?連接沒有池化。看下配置類。 ?

package com.my.maggie.demo.config; ? ? import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.data.redis.RedisProperties; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.data.redis.connection.RedisClusterConfiguration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.RedisNode; import org.springframework.data.redis.connection.RedisPassword; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.util.StringUtils; ? import java.util.HashSet; import java.util.List; import java.util.Set; ? ? @Configuration public class RedisConfigration { ?@Bean@ConfigurationProperties(prefix="spring.redis-one")@Primarypublic RedisProperties redisPropertiesCache() {return new RedisProperties();} ? ?@Bean@Primarypublic LettuceConnectionFactory redisConnectionFactoryOne() {List<String> clusterNodes = redisPropertiesCache().getCluster().getNodes();Set<RedisNode> nodes = new HashSet<RedisNode>();clusterNodes.forEach(address -> nodes.add(new RedisNode(address.split(":")[0].trim(), Integer.parseInt(address.split(":")[1]))));RedisClusterConfiguration clusterConfiguration = new RedisClusterConfiguration();clusterConfiguration.setClusterNodes(nodes);if (!StringUtils.isEmpty(RedisPassword.of(redisPropertiesCache().getPassword()))) {clusterConfiguration.setPassword(RedisPassword.of(redisPropertiesCache().getPassword()));}return new LettuceConnectionFactory(clusterConfiguration);} ?@Bean(name="redisTemplateOne")public StringRedisTemplate redisTemplateCache(@Qualifier("redisConnectionFactoryOne") RedisConnectionFactory redisConnectionFactory){return new StringRedisTemplate(redisConnectionFactory);} ? }

解決方案

  • 配置類增加池化配置
    ?

    @Bean@Primarypublic LettuceConnectionFactory redisConnectionFactoryOne() {// 連接池配置GenericObjectPoolConfig genericObjectPoolConfig = new GenericObjectPoolConfig();genericObjectPoolConfig.setMaxIdle(100);genericObjectPoolConfig.setMinIdle(10);genericObjectPoolConfig.setMaxTotal(5);genericObjectPoolConfig.setMaxWaitMillis(-1);genericObjectPoolConfig.setTimeBetweenEvictionRunsMillis(100);LettuceClientConfiguration clientConfig = LettucePoolingClientConfiguration.builder().commandTimeout(Duration.ofMillis(1000)).shutdownTimeout(Duration.ofMillis(1000)).poolConfig(genericObjectPoolConfig).build();// redis 配置List<String> clusterNodes = redisPropertiesCache().getCluster().getNodes();Set<RedisNode> nodes = new HashSet<RedisNode>();clusterNodes.forEach(address -> nodes.add(new RedisNode(address.split(":")[0].trim(), Integer.parseInt(address.split(":")[1]))));RedisClusterConfiguration clusterConfiguration = new RedisClusterConfiguration();clusterConfiguration.setClusterNodes(nodes);if (!StringUtils.isEmpty(RedisPassword.of(redisPropertiesCache().getPassword()))) {clusterConfiguration.setPassword(RedisPassword.of(redisPropertiesCache().getPassword()));}return new LettuceConnectionFactory(clusterConfiguration,clientConfig);}

    ?

  • 使用springboot自帶的 StringRedisTemplate (2.0 以上版本默認 lettuce ,會自動創建連接池)

  • 升級springboot 版本(2.1.0.RELEASE 及以上)

  • 總結

    個人覺得這應該是算是 spring-data-redis 的一個bug吧。 升級版本確實可以解決這個問題。看下新版本怎么解決的。

    2.1.0.RELEASE 以前

    LettuceConnectionFactory,構造參數第一個是 asyncSharedConn,直接傳的是 null

    @Overridepublic RedisClusterConnection getClusterConnection() { ?if (!isClusterAware()) {throw new InvalidDataAccessApiUsageException("Cluster is not configured!");} ?return new LettuceClusterConnection(connectionProvider, clusterCommandExecutor,clientConfiguration.getCommandTimeout());}

    LettuceClusterConnection

    public LettuceClusterConnection(LettuceConnectionProvider connectionProvider, ClusterCommandExecutor executor,Duration timeout) { ?super(null, connectionProvider, timeout.toMillis(), 0); ?Assert.notNull(executor, "ClusterCommandExecutor must not be null."); ?this.topologyProvider = new LettuceClusterTopologyProvider(getClient());this.clusterCommandExecutor = executor;this.disposeClusterCommandExecutorOnClose = false;}

    2.1.0.RELEASE 以后

    LettuceConnectionFactory,asyncSharedConn 這個值是傳入的,getOrCreateSharedConnection(),沒有就創建,所以不會有null的情況

    public RedisClusterConnection getClusterConnection() { ?if (!isClusterAware()) {throw new InvalidDataAccessApiUsageException("Cluster is not configured!");} ?RedisClusterClient clusterClient = (RedisClusterClient) client; ?return getShareNativeConnection()? new LettuceClusterConnection((StatefulRedisClusterConnection<byte[], byte[]>) getOrCreateSharedConnection().getConnection(),connectionProvider, clusterClient, clusterCommandExecutor, clientConfiguration.getCommandTimeout()): new LettuceClusterConnection(null, connectionProvider, clusterClient, clusterCommandExecutor,clientConfiguration.getCommandTimeout());}

    ?

    ?

    總結

    以上是生活随笔為你收集整理的redis出现过多command 慢查询slowlog出现command命令的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。