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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

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

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

大家好,我是烤鴨:

今天分享一個(gè)問題,一個(gè)關(guān)于redis slowlog,執(zhí)行過多 command命令的問題。

?

問題來源

所有走redis 的接口tp99和平均耗時(shí)是原來的兩倍不止,運(yùn)維說redis 的qps也翻倍了。查了下slowlog,發(fā)現(xiàn)command 命令占大多數(shù),除了兩條業(yè)務(wù)請(qǐng)求,其他都是command。

?

command命令返回的是當(dāng)前redis所能執(zhí)行的命令和對(duì)應(yīng)的賬號(hào)權(quán)限,也就不超過200條數(shù)據(jù)吧。(但頻繁執(zhí)行會(huì)增加qps,影響集群性能)

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

?

源碼查看,找蛛絲馬跡

項(xiàng)目框架用的是 springboot的版本2.0.4.RELEASE,對(duì)應(yīng) spring-boot-starter-data-redis的版本2.0.4.RELEASE

對(duì)應(yīng) lettuce-core 版本 5.0.4.RELEASE。

分別從這幾個(gè)包搜 command 命令,搜不到。

代碼里用的比較多的有 redisTemplate.executePipelined 方法,跟這個(gè)代碼進(jìn)去看一下。

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

LettuceConnection.getConnection()—>

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

?

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

?

而LettucePoolingConnectionProvider.getConnection會(huì)先判斷連接池是否存在,是否不存在,會(huì)先去創(chuàng)建。

ClusterConnectionProvider.getConnection 和 LettucePoolingConnectionProvider.getConnection 創(chuàng)建連接池時(shí)調(diào)用的都是 LettuceConnectionProvider.getConnection

?

LettuceConnectionProvider.getConnection(每次創(chuàng)建連接都會(huì)執(zhí)行 command命令)

?

RedisClusterClient.connect—> RedisClusterClient.connectClusterImpl

這個(gè)方法里的調(diào)用了 connection.inspectRedisState();

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

?

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

?

原因清楚了,就是每次執(zhí)行命令都會(huì)檢查 LettuceConnection asyncDedicatedConn是否為空, 如果為空,就會(huì)再次連接,再次連接就會(huì)執(zhí)行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 以上版本默認(rèn) lettuce ,會(huì)自動(dòng)創(chuàng)建連接池)

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

  • 總結(jié)

    個(gè)人覺得這應(yīng)該是算是 spring-data-redis 的一個(gè)bug吧。 升級(jí)版本確實(shí)可以解決這個(gè)問題??聪滦掳姹驹趺唇鉀Q的。

    2.1.0.RELEASE 以前

    LettuceConnectionFactory,構(gòu)造參數(shù)第一個(gè)是 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 這個(gè)值是傳入的,getOrCreateSharedConnection(),沒有就創(chuàng)建,所以不會(huì)有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());}

    ?

    ?

    總結(jié)

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

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。