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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

redis集群scan_Redis scan命令的一次坑

發布時間:2023/12/2 数据库 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 redis集群scan_Redis scan命令的一次坑 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Redis作為當前服務架構不可或缺的Cache,其支持豐富多樣的數據結構,Redis在使用中其實也有很多坑,本次博主遇到的坑或許說是Java程序員會遇到的多一點,下面就聽博主詳細道來。

線上服務堵塞

String key = keyOf(appid);

int retryCount = 3;

int socketRetryCount = 3;

Exception ex = null;

while(retryCount > 0 && socketRetryCount > 0) {

try {

return redisDao.getMap(key);

}catch (Exception e) {

}

}

12月2日被告知服務出現異常,查看日志發現其運行到上述代碼getMap方法處后日志就沒有內容了。

問題分析

"pool-13-thread-6" prio=10 tid=0x00007f754800e800 nid=0x71b5 waiting on condition [0x00007f758f0ee000]

java.lang.Thread.State: WAITING (parking)

at sun.misc.Unsafe.park(Native Method)

- parking to wait for <0x0000000779b75f40> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)

at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)

at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2043)

at org.apache.commons.pool2.impl.LinkedBlockingDeque.takeFirst(LinkedBlockingDeque.java:583)

at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:442)

at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:363)

at redis.clients.util.Pool.getResource(Pool.java:49)

at redis.clients.jedis.JedisPool.getResource(JedisPool.java:99)

at org.reborndb.reborn.RoundRobinJedisPool.getResource(RoundRobinJedisPool.java:300)

at com.le.smartconnect.adapter.spring.RebornConnectionFactory.getConnection(RebornConnectionFactory.java:43)

at org.springframework.data.redis.core.RedisConnectionUtils.doGetConnection(RedisConnectionUtils.java:128)

at org.springframework.data.redis.core.RedisConnectionUtils.getConnection(RedisConnectionUtils.java:91)

at org.springframework.data.redis.core.RedisConnectionUtils.getConnection(RedisConnectionUtils.java:78)

at xxx.run(xxx.java:80)

at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)

at java.util.concurrent.FutureTask.run(FutureTask.java:262)

at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)

at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)

at java.lang.Thread.run(Thread.java:745)

Locked ownable synchronizers:

- <0x000000074f529b08> (a java.util.concurrent.ThreadPoolExecutor$Worker)

從線程日志可以看出服務堵塞在獲取redis連接處.

分析:

代碼配置中redis最大連接為3000

redis配置中session_max_timeout為0,即永不斷開連接

一次修改分析

從以上兩點分析得出,redis連接被耗盡,于是查找代碼得知由于重寫spring-data-redis中的hscan方面導致,代碼如下:

RedisConnection rc = redisTemplate.getConnectionFactory().getConnection();

if (rc instanceof JedisConnection) {

JedisConnection JedisConnection = (JedisConnection) rc;

return new ConvertingCursor, Map.Entry>(

JedisConnection.hScan(rawValue(key), cursor, scanOptions),

new Converter, Map.Entry>() {

@Override

public Entry convert(final Entry source) {

return new Map.Entry() {

@Override

public String getKey() {

return hashKeySerializer.deserialize(source.getKey());

}

@Override

public String getValue() {

return hashValueSerializer.deserialize(source.getValue());

}

@Override

public String setValue(String value) {

throw new UnsupportedOperationException(

"Values cannot be set when scanning through entries.");

}

};

}

});

} else {

return hashOps.scan(key, scanOptions);

}

上述代碼返回ConvertingCursor后未釋放連接,導出連接被占滿。

二次修改分析

于是修改代碼為正常釋放連接

try {

...

}finally {

RedisConnectionUtils.releaseConnection(rc, factory);

}

代碼經過上線,再次跑程序查看線上日志發現報了大量的Connection time out.

于是博主就思考是不是由于重寫代碼不對,嘗試使用spring-data-redis的原生代碼,即直接調用hashOps.scan(key, scanOptions)方法,再次上線。

上線后觀察日志:發現這次不是報Connection time out,日志中大量報Unknown reply:錯誤。

分析如下:

由于代碼是在多線程環境下運行,有幾百個線程去調用hscan操作,spring-data-redis原生的代碼執行完一次hscan操作后就會關閉連接并返回一個迭代器Cursor,但是遍歷Cursor時在本次count后會再次根據游標重新使用該連接進行查詢,可是連接卻已經被關閉,這時會使用新的連接是可以正常迭代的,但是一旦復用到其他線程使用的連接則會導致報錯Unknown reply.

三次修改分析

經過思考后得出結論,redis在執行scan操作時一旦連接被釋放,那么scan操作將不會進行下去,則報Connection time out.

查閱官方文檔得出結論,redis的scan操作需要full iteration,即最優方式是一個連接將以此scan任務執行完全后釋放該連接。

redis-scan-doc

修改代碼如下:

RedisConnectionFactory factory = redisTemplate.getConnectionFactory();

RedisConnection rc = factory.getConnection();

if (rc instanceof JedisConnection) {

JedisConnection JedisConnection = (JedisConnection) rc;

Cursor> cursorResult = new ConvertingCursor, Map.Entry>(

JedisConnection.hScan(rawValue(key), cursor, scanOptions),

new Converter, Map.Entry>() {

...

});

return new ScanResult>(cursorResult, factory, rc);}

public void releaseConnection() throws IOException{

IOException ex = null;

if(cursor != null) {

try {

cursor.close();

} catch (IOException e) {

ex = e;

}

}

try {

RedisConnectionUtils.releaseConnection(rc, factory);

} catch (Exception e) {

}

if(ex != null) {

throw ex;

}

}

將連接返回給業務代碼,并在業務代碼執行完畢后將連接釋放,問題解決。

總結

連接一旦開啟就必須釋放,否則造成內存泄漏或服務堵塞不可用

重寫代碼時需要謹記仔細查閱官方文檔給出的方案并實施

多線程下使用redis的scan操作需要使用一個連接遍歷完Cursor,而不能復用連接,否則導致報錯Unknown reply.

總結

以上是生活随笔為你收集整理的redis集群scan_Redis scan命令的一次坑的全部內容,希望文章能夠幫你解決所遇到的問題。

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