成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

想在生產(chǎn)搞事情?那試試這些 Redis 命令

存儲 存儲軟件 Redis
事情是這樣的,前一段時(shí)間阿粉公司生產(chǎn)交易偶發(fā)報(bào)錯(cuò),一番排查下來最終原因是因?yàn)?Redis 命令執(zhí)行超時(shí)。

[[341874]]

本文轉(zhuǎn)載自微信公眾號「Java極客技術(shù)  」,作者鴨血粉絲。轉(zhuǎn)載本文請聯(lián)系Java極客技術(shù)  公眾號。   

哎,最近阿粉又雙叒叕犯事了。

事情是這樣的,前一段時(shí)間阿粉公司生產(chǎn)交易偶發(fā)報(bào)錯(cuò),一番排查下來最終原因是因?yàn)?Redis 命令執(zhí)行超時(shí)。

可是令人不解的是,生產(chǎn)交易僅僅使用 Redis set 這個(gè)簡單命令,這個(gè)命令講道理是不可能會執(zhí)行這么慢。

那到底是什么導(dǎo)致這個(gè)問題那?

為了找出這個(gè)問題,我們查看分析了一下 Redis 最近的慢日志,最終發(fā)現(xiàn)耗時(shí)比較多命令為 keys XX*

看到這個(gè)命令操作的鍵的前綴,阿粉才發(fā)現(xiàn)這是自己負(fù)責(zé)的應(yīng)用。可是阿粉排查一下,雖然自己的代碼并沒有主動(dòng)去使用 keys命令,但是底層使用框架卻在間接使用,于是就有了今天這個(gè)問題。

問題原因

阿粉負(fù)責(zé)的應(yīng)用是一個(gè)管理后臺應(yīng)用,權(quán)限管理使用 Shiro 框架,由于存在多個(gè)節(jié)點(diǎn),需要使用分布式 Session,于是這里使用 Redis 存儲 Session 信息。

由于 Shiro 并沒有直接提供 Redis 存儲 Session 組件,阿粉不得不使用 Github 一個(gè)開源組件 shiro-redis。

由于 Shiro 框架需要定期驗(yàn)證 Session 是否有效,于是 Shiro 底層將會調(diào)用 SessionDAO#getActiveSessions 獲取所有的 Session 信息。

而 shiro-redis 正好繼承 SessionDAO 這個(gè)接口,底層使用用keys 命令查找 Redis 所有存儲的 Session key。

  1. public Set<byte[]> keys(byte[] pattern){ 
  2.     checkAndInit(); 
  3.     Set<byte[]> keys = null
  4.     Jedis jedis = jedisPool.getResource(); 
  5.     try{ 
  6.         keys = jedis.keys(pattern); 
  7.     }finally{ 
  8.         jedis.close(); 
  9.     } 
  10.     return keys; 

找到問題原因,解決辦法就比較簡單了,github 上查找到解決方案,升級一下 shiro-redis 到最新版本。

在這個(gè)版本,shiro-redis 采用 scan命令代替 keys,從而修復(fù)這個(gè)問題。

  1. public Set<byte[]> keys(byte[] pattern) { 
  2.     Set<byte[]> keys = null
  3.     Jedis jedis = jedisPool.getResource(); 
  4.  
  5.     try{ 
  6.         keys = new HashSet<byte[]>(); 
  7.         ScanParams params = new ScanParams(); 
  8.         params.count(count); 
  9.         params.match(pattern); 
  10.         byte[] cursor = ScanParams.SCAN_POINTER_START_BINARY; 
  11.         ScanResult<byte[]> scanResult; 
  12.         do{ 
  13.             scanResult = jedis.scan(cursor,params); 
  14.             keys.addAll(scanResult.getResult()); 
  15.             cursor = scanResult.getCursorAsBytes(); 
  16.         }while(scanResult.getStringCursor().compareTo(ScanParams.SCAN_POINTER_START) > 0); 
  17.     }finally{ 
  18.         jedis.close(); 
  19.     } 
  20.     return keys; 
  21.  

雖然問題成功解決了,但是阿粉心里還是有點(diǎn)不解。

為什么keys 指令會導(dǎo)致其他命令執(zhí)行變慢?

為什么Keys 指令查詢會這么慢?

為什么Scan 指令就沒有問題?

Redis 執(zhí)行命令的原理

首先我們來看第一個(gè)問題,為什么keys 指令會導(dǎo)致其他命令執(zhí)行變慢?

回答這個(gè)問題,我們首先看下 Redis 客戶端執(zhí)行一條命令的情況:

站在客戶端的視角,執(zhí)行一條命令分為三步:

  • 發(fā)送命令
  • 執(zhí)行命令
  • 返回結(jié)果

但是這僅僅客戶端自己以為的過程,但是實(shí)際上同一時(shí)刻,可能存在很多客戶端發(fā)送命令給 Redis,而 Redis 我們都知道它采用的是單線程模型。

為了處理同一時(shí)刻所有的客戶端的請求命令,Redis 內(nèi)部采用了隊(duì)列的方式,排隊(duì)執(zhí)行。

于是客戶端執(zhí)行一條命令實(shí)際需要四步:

  1. 發(fā)送命令
  2. 命令排隊(duì)
  3. 執(zhí)行命令
  4. 返回結(jié)果

由于 Redis 單線程執(zhí)行命令,只能順序從隊(duì)列取出任務(wù)開始執(zhí)行。

只要 3 這個(gè)過程執(zhí)行命令速度過慢,隊(duì)列其他任務(wù)不得不進(jìn)行等待,這對外部客戶端看來,Redis 好像就被阻塞一樣,一直得不到響應(yīng)。

所以使用 Redis 過程切勿執(zhí)行需要長時(shí)間運(yùn)行的指令,這樣可能導(dǎo)致 Redis 阻塞,影響執(zhí)行其他指令。

KEYS 原理

接下來開始回答第二個(gè)問題,為什么Keys 指令查詢會這么慢?

回答這個(gè)問題之前,請大家回想一下 Redis 底層存儲結(jié)構(gòu)。

這里阿粉復(fù)制之前文章內(nèi)容,Redis 底層使用字典這種結(jié)構(gòu),這個(gè)結(jié)構(gòu)與 Java HashMap 底層比較類似。

keys命令需要返回所有的符合給定模式 pattern 的 Redis 中鍵,為了實(shí)現(xiàn)這個(gè)目的,Redis 不得不遍歷字典中 ht[0]哈希表底層數(shù)組,這個(gè)時(shí)間復(fù)雜度為 「O(N)」(N 為 Redis 中 key 所有的數(shù)量)。

如果 Redis 中 key 的數(shù)量很少,那么這個(gè)執(zhí)行速度還是也會很快。等到 Redis key 的數(shù)量慢慢更加,上升到百萬、千萬、甚至上億級別,那這個(gè)執(zhí)行速度就會很慢很慢。

下面是阿粉本地做的一次實(shí)驗(yàn),使用 lua 腳本往 Redis 中增加 10W 個(gè) key,然后使用 keys 查詢所有鍵,這個(gè)查詢大概會阻塞十幾秒的時(shí)間。

  1. eval "for i=1,100000  do redis.call('set',i,i+1) end" 0 

這里阿粉使用 Docker 部署 Redis,性能可能會稍差。

SCAN 原理

最后我們來看下第三個(gè)問題,為什么scan 指令就沒有問題?

這是因?yàn)?scan命令采用一種黑科技-「基于游標(biāo)的迭代器」。

每次調(diào)用 scan 命令,Redis 都會向用戶返回一個(gè)新的游標(biāo)以及一定數(shù)量的 key。下次再想繼續(xù)獲取剩余的 key,需要將這個(gè)游標(biāo)傳入 scan 命令, 以此來延續(xù)之前的迭代過程。

簡單來講,scan 命令使用分頁查詢 redis 。

下面是一個(gè) scan 命令的迭代過程示例:

scan 命令使用游標(biāo)這種方式,巧妙將一次全量查詢拆分成多次,降低查詢復(fù)雜度。

雖然 scan 命令時(shí)間復(fù)雜度與 keys一樣,都是 「O(N)」,但是由于 scan 命令只需要返回少量的 key,所以執(zhí)行速度會很快。

最后,雖然scan 命令解決 keys不足,但是同時(shí)也引入其他一些缺陷:

  • 同一個(gè)元素可能會被返回多次,這就需要我們應(yīng)用程序增加處理重復(fù)元素功能。
  • 如果一個(gè)元素在迭代過程增加到 redis,或者說在迭代過程被刪除,那個(gè)這個(gè)元素會被返回,也可能不會。

以上這些缺陷,在我們開發(fā)中需要考慮這種情況。

除了 scan以外,redis 還有其他幾個(gè)用于增量迭代命令:

  • sscan:用于迭代當(dāng)前數(shù)據(jù)庫中的數(shù)據(jù)庫鍵,用于解決 smembers 可能產(chǎn)生阻塞問題
  • hscan命令用于迭代哈希鍵中的鍵值對,用于解決 hgetall 可能產(chǎn)生阻塞問題。
  • zscan:命令用于迭代有序集合中的元素(包括元素成員和元素分值),用于產(chǎn)生 zrange 可能產(chǎn)生阻塞問題。

總結(jié)

Redis 使用單線程執(zhí)行操作命令,所有客戶端發(fā)送過來命令,Redis 都會現(xiàn)放入隊(duì)列,然后從隊(duì)列中順序取出執(zhí)行相應(yīng)的命令。

如果任一任務(wù)執(zhí)行過慢,就會影響隊(duì)列中其他任務(wù)的,這樣在外部客戶端看來,遲遲拿不到 Redis 的響應(yīng),看起來就很阻塞了一樣。

所以不要在生產(chǎn)執(zhí)行 keys、smembers、hgetall、zrange這類可能造成阻塞的指令,如果真需要執(zhí)行,可以使用相應(yīng)的scan 命令漸進(jìn)式遍歷,可以有效防止阻塞問題。

 

責(zé)任編輯:武曉燕 來源: Java極客技術(shù)
相關(guān)推薦

2022-09-04 21:17:03

高可用Linkerd

2011-09-19 10:43:19

Nuget

2020-02-25 15:47:05

ElasticsearLucene地方

2018-11-20 10:10:54

Redis數(shù)據(jù)庫模糊查詢

2021-12-03 07:27:29

EFCore生產(chǎn)環(huán)境

2021-10-29 15:27:42

智能眼鏡汽車

2023-11-14 17:40:32

2019-01-08 06:14:21

邊緣計(jì)算物聯(lián)網(wǎng)IOT

2017-07-21 16:09:11

互聯(lián)網(wǎng)

2015-11-04 15:04:18

流行詞IT互聯(lián)網(wǎng)

2020-05-09 09:53:36

黑客游戲公司網(wǎng)絡(luò)攻擊

2017-10-12 13:33:04

華為

2023-12-26 18:54:22

2015-08-03 09:08:29

2013-01-25 09:08:58

云計(jì)算移動(dòng)化IaaS

2021-05-07 11:25:29

項(xiàng)目網(wǎng)關(guān)流量

2017-06-19 14:54:47

2022-05-26 09:00:00

網(wǎng)站抓取Lightrun開發(fā)

2021-03-10 07:20:42

Redis命令數(shù)據(jù)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 97精品超碰一区二区三区 | 亚洲精品一区二区网址 | 久草视频在线看 | 日韩免费视频一区二区 | 992人人草 | 黄色精品 | 国产不卡一区 | 欧美a级网站 | 成人精品一区二区三区 | 成人免费视频网站在线看 | 国产一级在线观看 | 特级做a爰片毛片免费看108 | 日韩看片| 超碰激情| 亚洲欧美激情国产综合久久久 | 一区二区免费在线视频 | 99久久99久久精品国产片果冰 | 精品视频免费 | av网址在线播放 | 91成人 | 一区二区三区精品在线 | 在线看一区二区 | 欧美精品一区二区三区在线播放 | 欧洲视频一区二区 | 免费一区二区三区在线视频 | 国产精品毛片久久久久久久 | 精品久久影院 | 久久99视频 | 亚洲 欧美 日韩 在线 | 一区二区三区中文字幕 | 亚洲三区在线观看 | 久久99深爱久久99精品 | 久久久国产一区二区三区四区小说 | www.youjizz.com日韩 | 91精品久久久久久久久久入口 | 亚洲人人 | 亚洲一区二区日韩 | 亚洲国产一区二区三区在线观看 | 一级黄色片在线免费观看 | 精品在线一区 | 日韩在线视频观看 |