后續補充結論
仔細查看前輩們堆的代碼中發現居然調用了大量key*查詢,導致走的遍歷非常慢!因為這相當與全部數據量遍歷,即這個原因導致了查詢速度與數據量成正比,推測也是CPU占用高的元兇;即使加上key前綴再匹配*也會走全部遍歷的邏輯,因為reids是用的哈希做索引,不給出完整key是無法確定范圍的;后面再寫一篇文章詳細講一下這個吧
在Redis中,獲取一個key的操作復雜度為O(1),即常數時間復雜度。這意味著無論集合的大小如何增加,獲取單個元素的時間都是相同的,非常高效。這是因為Redis使用了哈希表(hash table)這種數據結構。
Redis交互速度很慢,達到幾十到一百毫秒一次
問題描述:
執行top命令發現redis占用達到100%
redis交互速度慢,一次要幾十到一百毫秒一次;原因查看上面補充結論
解決思路
查看redis數據量,比如我這里達到了30萬
經過本地測試,redis交互的速度跟redis交互量成正比,也就是說redis沒數據時交互在10ms以內,數據量大的話一次可能花費更久的時間
集群方案
我在本地虛擬機建立了3個主節點3個從節點的redis集群,經過壓力測試單節點情況cpu占到30%;改為集群模式redis每個節點占用在10%以下
可以得出結論集群方案確實能降低單節點cpu壓力大的問題
**速度測試:**我這測試里單節點速度要比集群方案快,數據量增加后依然是單節點速度比集群快,目前看到集群的優勢僅僅是降低了單節點cpu的負載(當然這是在只考慮速度,不考慮容錯的情況下)
并發問題
我的程序中開了多線程跟redis做壓力測試交互,但是由于redis內部是單線程處理的,所以增加線程數量對速度沒有提升,且太多線程數還可能造成時間片競爭;但是多個線程確實比單線程在單位時間內交互次數多一些,我猜測是利用上了程序內部的執行時間,但也沒快很多,1.5倍左右?
雖然我的環境是多核心處理器,但是關于redis:
Redis本身是一個單線程的數據庫服務器,它使用一個事件循環來處理所有的客戶端請求和操作。這意味著在Redis服務器內部,所有的操作都是按順序處理的,一個接一個地執行。
然而,Redis支持多個并發客戶端連接。這些客戶端連接可以同時發送請求給Redis服務器。雖然Redis在內部是單線程的,但它使用了非阻塞的I/O操作和事件驅動的模型來實現高并發。這意味著Redis可以同時處理多個客戶端請求,而不會阻塞其他客戶端。
當多個客戶端同時連接到Redis時,每個客戶端都有自己的線程或進程來處理Redis通信。這樣,多個客戶端可以并發地發送請求和接收響應,而不會相互干擾。
測試下經常報錯:RedisSystemException: RedisException: Connection closed
org.springframework.data.redis.RedisSystemException: Redis exception; nested exception is io.lettuce.core.RedisException: Connection closedat org.springframework.data.redis.connection.lettuce.LettuceExceptionConverter.convert(LettuceExceptionConverter.java:74)at org.springframework.data.redis.connection.lettuce.LettuceExceptionConverter.convert(LettuceExceptionConverter.java:41)at org.springframework.data.redis.PassThroughExceptionTranslationStrategy.translate(PassThroughExceptionTranslationStrategy.java:44)at org.springframework.data.redis.FallbackExceptionTranslationStrategy.translate(FallbackExceptionTranslationStrategy.java:42)at org.springframework.data.redis.connection.lettuce.LettuceConnection.convertLettuceAccessException(LettuceConnection.java:277)at org.springframework.data.redis.connection.lettuce.LettuceConnection.await(LettuceConnection.java:1085)at org.springframework.data.redis.connection.lettuce.LettuceConnection.lambda$doInvoke$4(LettuceConnection.java:938)at org.springframework.data.redis.connection.lettuce.LettuceInvoker$Synchronizer.invoke(LettuceInvoker.java:673)at org.springframework.data.redis.connection.lettuce.LettuceInvoker$DefaultSingleInvocationSpec.get(LettuceInvoker.java:589)at org.springframework.data.redis.connection.lettuce.LettuceKeyCommands.exists(LettuceKeyCommands.java:79)at org.springframework.data.redis.connection.DefaultedRedisConnection.exists(DefaultedRedisConnection.java:81)at org.springframework.data.redis.core.RedisTemplate.lambda$hasKey$7(RedisTemplate.java:782)at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:224)at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:191)at org.springframework.data.redis.core.RedisTemplate.hasKey(RedisTemplate.java:782)at com.bestdata.em.drs.util.RedisUtil.hasKey(RedisUtil.java:98)at com.bestdata.em.drs.receive.serivce.impl.HasMoreBusinessServiceImpl.insertFormulaValueToMongoDB(HasMoreBusinessServiceImpl.java:180)at com.bestdata.em.drs.receive.serivce.impl.HasMoreBusinessServiceImpl.moreBusinessOperations(HasMoreBusinessServiceImpl.java:113)at com.bestdata.em.drs.receive.serivce.FormulaDataProcessingService.sensorFormulaCalculation(FormulaDataProcessingService.java:264)at com.bestdata.em.drs.receive.serivce.FormulaDataProcessingService.run(FormulaDataProcessingService.java:167)at java.lang.Thread.run(Thread.java:750)
16:18:33.404 [FormulaThreadPoll-9] ERROR c.b.e.d.r.s.i.HasMoreBusinessServiceImpl - Unite Alarm Exception:Redis exception; nested exception is io.lettuce.core.RedisException: Connection closed
問題排查:
服務端和redis是否在同一網段?
是內網,沒問題
Redis資源占用
執行top命令
Redis CPU占用一直高達50%~100%,3到5秒就跳一次(此時生產環境有其他程序和redis交互,但是沒有特別大)
Redis數據量占用很高,且這是單點部署非集群模式
執行壓力測試程序:Redis CPU占用一直高達100%
結論
看到redis的cpu占用100%我本以為是這個導致的RedisSystemException:Connection closed,但是后來反復測試發現報錯原因不是這個,而是我在測試過程中強行kill進程,導致出現了連接關閉的報錯(這是我在idea中停止運行程序后彈了一下這個異常后發現的)
再經過測試redis cpu占用100%確實會引發異常,但卻是超時的問題:org.springframework.dao.QueryTimeoutException: Redis command timed out; nested exception is io.lettuce.core.RedisCommandTimeoutException:
提高Redis服務器資源配置確實可以降低單點壓力;但是對速度的提升不明顯,甚至經過本地壓力測試還慢了