KESY 命令
時間復雜度: O(N) , 假設Redis中的鍵名和給定的模式的長度有限的情況下,N為數(shù)據(jù)庫中key的個數(shù)。
Redis Keys 命令用于查找所有符合給定模式 pattern 的 key
盡管這個操作的時間復雜度是 O(N), 但是常量時間相當?shù)汀@?,在一個普通筆記本上跑Redis,掃描100萬個key只要40毫秒。
命令格式 KEYS pattern
Warning: 生產(chǎn)環(huán)境使用 KEYS 命令需要非常小心。在大的數(shù)據(jù)庫上執(zhí)行命令會影響性能。這個命令適合用來調試和特殊操作,像改變鍵空間keyspace布局。不要在你的代碼中使用 KEYS 。如果你需要一個尋找鍵空間中的key子集,考慮使用SCAN 或 集合結構sets。
支持的匹配模式 patterns:
- h?llo 匹配 hello, hallo 和 hxllo
- h*llo 匹配 hllo 和 heeeello
- h[ae]llo 匹配 hello 和 hallo, 不匹配 hillo
- h[^e]llo 匹配 hallo, hbllo, … 不匹配 hello
- h[a-b]llo 匹配 hallo 和 hbllo
使用 \ 轉義你想匹配的特殊字符。
背景
1、Redis是單線程的,其所有操作都是原子的,不會因并發(fā)產(chǎn)生數(shù)據(jù)異常
2、使用高耗時的Redis命令是很危險的,會占用唯一的一個線程的大量處理時間,導致所有的請求都被拖慢
場景
在生產(chǎn)環(huán)境中執(zhí)行 KEYS 命令的時,因為Redis是單線程的,KEYS 命令的性能隨著數(shù)據(jù)庫數(shù)據(jù)的增多而越來越慢,使用 KEYS 命令時會占用唯一的一個線程的大量處理時間,引發(fā)Redis阻塞并且增加Redis的CPU占用,導致所有的請求都被拖慢,可能造成Redis所在的服務器宕機,情況是很惡劣的,在實際生產(chǎn)運用的過程中請忽略掉。試想如果Redis阻塞超過10秒,如果有集群的場景,可能導致集群判斷Redis已經(jīng)故障,從而進行故障切換。
如果所有的線程在Redis那取不到數(shù)據(jù),情況嚴重時可能會導致應用程序出現(xiàn)雪崩的情況,一瞬間全去數(shù)據(jù)庫取數(shù)據(jù),數(shù)據(jù)庫就宕機了。
其他危險命令
但凡發(fā)現(xiàn)時間復雜度為O(N)的命令,都要慎重,不要在生產(chǎn)上隨便使用。例如hgetall、lrange、smembers、zrange、sinter等命令,它們并非不能使用,但這些命令的時間復雜度都為O(N),使用這些命令需要明確N的值,否則也會出現(xiàn)緩存宕機。
1、flushdb 命令用于清空當前數(shù)據(jù)庫中的所有 key
2、flushall 命令用于清空整個 Redis 服務器的數(shù)據(jù)(刪除所有數(shù)據(jù)庫的所有 key )
3、config 客戶端連接后可配置服務器
如何禁用危險命令
在redis.conf中,在SECURITY這一項中,新增以下配置禁用指定命令:
rename-command FLUSHALL ""
rename-command FLUSHDB ""
rename-command CONFIG ""
rename-command KEYS ""
另外,對于flushall命令,需要設置配置文件中appendonly no,否則服務器是無法啟動。
如果想要保留命令,但是不輕易使用,可以重命名命令來設定:
rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52
TIP:更改記錄到AOF文件或傳輸?shù)綇膶俜掌鞯拿畹拿Q可能會導致問題。
改良建議
一、如果有這種需求的話可以自己對鍵值做索引,比如把各種鍵值存到不同的set里面,分類建立索引,這樣就可以很快的得到數(shù)據(jù),但是這樣也存在一個明顯的缺點,就是浪費寶貴的空間,所以還是要合理考慮,當然也可以想辦法,比如對于有規(guī)律的鍵值,可以存儲他們的始末值等等。
二、針對改良keys和smembers命令也可以使用scan命令
SCAN 命令
Redis從2.8版本開始支持scan命令,可以用來分批次掃描Redis記錄,這樣肯定會導致整個查詢消耗的總時間變大,影響服務使用,但不會影響redis服務卡頓,SCAN命令的基本用法如下:
命令格式 SCAN cursor [MATCH pattern] [COUNT count]
SCAN 命令提供三個參數(shù),第一個是cursor(游標),第二個是要匹配的正則,第三個是單次遍歷的槽位
SCAN 命令及其相關的 SSCAN 命令、 HSCAN 命令和 ZSCAN 命令都用于增量地迭代(incrementally iterate)一集元素(a collection of elements):
- SCAN 命令用于迭代當前數(shù)據(jù)庫中的數(shù)據(jù)庫鍵。
- SSCAN 命令用于迭代集合鍵中的元素。
- HSCAN 命令用于迭代哈希鍵中的鍵值對。
- ZSCAN 命令用于迭代有序集合中的元素(包括元素成員和元素分值)。
以上列出的四個命令都支持增量式迭代,它們每次執(zhí)行都只會返回少量元素,所以這些命令可以用于生產(chǎn)環(huán)境,而不會出現(xiàn)像 KEYS 命令、SMEMBERS 命令帶來的問題 —— 當 KEYS 命令被用于處理一個大的數(shù)據(jù)庫時,又或者 SMEMBERS 命令被用于處理一個大的集合鍵時,它們可能會阻塞服務器達數(shù)秒之久。
不過,增量式迭代命令也不是沒有缺點的:舉個例子,使用 SMEMBERS 命令可以返回集合鍵當前包含的所有元素,但是對于 SCAN 這類增量式迭代命令來說,因為在對鍵進行增量式迭代的過程中,鍵可能會被修改,所以增量式迭代命令只能對被返回的元素提供有限的保證(offer limited guarantees about the returned elements)。
因為 SCAN 、 SSCAN 、 HSCAN 和 ZSCAN 四個命令的工作方式都非常相似, 所以接下來會一并介紹這四個命令,但是要記?。?/p>
- SSCAN 命令、 HSCAN 命令和 ZSCAN 命令的第一個參數(shù)總是一個數(shù)據(jù)庫鍵。
- 而 SCAN 命令則不需要在第一個參數(shù)提供任何數(shù)據(jù)庫鍵 —— 因為它迭代的是當前數(shù)據(jù)庫中的所有數(shù)據(jù)庫鍵。
SCAN 命令的基本用法
SCAN 命令是一個基于游標的迭代器(cursor based iterator):SCAN 命令每次被調用之后, 都會向用戶返回一個新的游標, 用戶在下次迭代時需要使用這個新游標作為 SCAN 命令的游標參數(shù), 以此來延續(xù)之前的迭代過程。
當 SCAN 命令的游標參數(shù)被設置為 0 時, 服務器將開始一次新的迭代, 而當服務器向用戶返回值為 0 的游標時, 表示迭代已結束。
以下是一個 SCAN 命令的迭代過程示例:
redis 127.0.0.1:6379> scan 0
1) "17"
2) 1) "key:12"
2) "key:8"
3) "key:4"
4) "key:14"
5) "key:16"
6) "key:17"
7) "key:15"
8) "key:10"
9) "key:3"
10) "key:7"
11) "key:1"
redis 127.0.0.1:6379> scan 17
1) "0"
2) 1) "key:5"
2) "key:18"
3) "key:0"
4) "key:2"
5) "key:19"
6) "key:13"
7) "key:6"
8) "key:9"
9) "key:11"
在上面這個例子中, 第一次迭代使用 0 作為游標, 表示開始一次新的迭代。
第二次迭代使用的是第一次迭代時返回的游標, 也即是命令回復第一個元素的值 —— 17 。
從上面的示例可以看到, SCAN 命令的回復是一個包含兩個元素的數(shù)組, 第一個數(shù)組元素是用于進行下一次迭代的新游標, 而第二個數(shù)組元素則是一個數(shù)組, 這個數(shù)組中包含了所有被迭代的元素。
在第二次調用 SCAN 命令時, 命令返回了游標 0 , 這表示迭代已經(jīng)結束, 整個數(shù)據(jù)集(collection)已經(jīng)被完整遍歷過了。
以 0 作為游標開始一次新的迭代, 一直調用 SCAN 命令, 直到命令返回游標 0 , 我們稱這個過程為一次完整遍歷(full iteration)。
SCAN 命令的保證(guarantees)
SCAN 命令, 以及其他增量式迭代命令, 在進行完整遍歷的情況下可以為用戶帶來以下保證:從完整遍歷開始直到完整遍歷結束期間, 一直存在于數(shù)據(jù)集內(nèi)的所有元素都會被完整遍歷返回;這意味著, 如果有一個元素, 它從遍歷開始直到遍歷結束期間都存在于被遍歷的數(shù)據(jù)集當中, 那么 SCAN 命令總會在某次迭代中將這個元素返回給用戶。
然而因為增量式命令僅僅使用游標來記錄迭代狀態(tài), 所以這些命令帶有以下缺點:
- 同一個元素可能會被返回多次。處理重復元素的工作交由應用程序負責, 比如說, 可以考慮將迭代返回的元素僅僅用于可以安全地重復執(zhí)行多次的操作上。
- 如果一個元素是在迭代過程中被添加到數(shù)據(jù)集的, 又或者是在迭代過程中從數(shù)據(jù)集中被刪除的, 那么這個元素可能會被返回, 也可能不會, 這是未定義的(undefined)。
SCAN 命令每次執(zhí)行返回的元素數(shù)量
增量式迭代命令并不保證每次執(zhí)行都返回某個給定數(shù)量的元素。
增量式命令甚至可能會返回零個元素, 但只要命令返回的游標不是 0 , 應用程序就不應該將迭代視作結束。
不過命令返回的元素數(shù)量總是符合一定規(guī)則的, 在實際中:
- 對于一個大數(shù)據(jù)集來說, 增量式迭代命令每次最多可能會返回數(shù)十個元素
- 而對于一個足夠小的數(shù)據(jù)集來說, 如果這個數(shù)據(jù)集的底層表示為編碼數(shù)據(jù)結構(encoded data structure,適用于是小集合鍵、小哈希鍵和小有序集合鍵), 那么增量迭代命令將在一次調用中返回數(shù)據(jù)集中的所有元素。
最后, 用戶可以通過增量式迭代命令提供的 COUNT 選項來指定每次迭代返回元素的最大值。
COUNT 選項
雖然增量式迭代命令不保證每次迭代所返回的元素數(shù)量, 但我們可以使用 COUNT 選項, 對命令的行為進行一定程度上的調整。
基本上, COUNT 選項的作用就是讓用戶告知迭代命令, 在每次迭代中應該從數(shù)據(jù)集里返回多少元素。
雖然 COUNT 選項只是對增量式迭代命令的一種提示(hint), 但是在大多數(shù)情況下, 這種提示都是有效的。
- COUNT 參數(shù)的默認值為 10 。
- 在迭代一個足夠大的、由哈希表實現(xiàn)的數(shù)據(jù)庫、集合鍵、哈希鍵或者有序集合鍵時, 如果用戶沒有使用 MATCH 選項, 那么命令返回的元素數(shù)量通常和 COUNT 選項指定的一樣, 或者比 COUNT 選項指定的數(shù)量稍多一些。
- 在迭代一個編碼為整數(shù)集合(intset,一個只由整數(shù)值構成的小集合)、 或者編碼為壓縮列表(ziplist,由不同值構成的一個小哈?;蛘咭粋€小有序集合)時, 增量式迭代命令通常會無視 COUNT 選項指定的值, 在第一次迭代就將數(shù)據(jù)集包含的所有元素都返回給用戶。
并非每次迭代都要使用相同的 COUNT 值
用戶可以在每次迭代中按自己的需要隨意改變 COUNT 值, 只要記得將上次迭代返回的游標用到下次迭代里面就可以了
MATCH 選項
和 KEYS 命令一樣, 增量式迭代命令也可以通過提供一個 glob 風格的模式參數(shù), 讓命令只返回和給定模式相匹配的元素, 這一點可以通過在執(zhí)行增量式迭代命令時, 通過給定 MATCH參數(shù)來實現(xiàn)。
以下是一個使用 MATCH 選項進行迭代的示例:
redis 127.0.0.1:6379> sadd myset 1 2 3 foo foobar feelsgood
(integer) 6
redis 127.0.0.1:6379> sscan myset 0 match f*
1) "0"
2) 1) "foo"
2) "feelsgood"
3) "foobar"
需要注意的是, 對元素的模式匹配工作是在命令從數(shù)據(jù)集中取出元素之后, 向客戶端返回元素之前的這段時間內(nèi)進行的, 所以如果被迭代的數(shù)據(jù)集中只有少量元素和模式相匹配, 那么迭代命令或許會在多次執(zhí)行中都不返回任何元素。
以下是這種情況的一個例子:
redis 127.0.0.1:6379> scan 0 MATCH *11*
1) "288"
2) 1) "key:911"
redis 127.0.0.1:6379> scan 288 MATCH *11*
1) "224"
2) (empty list or set)
redis 127.0.0.1:6379> scan 224 MATCH *11*
1) "80"
2) (empty list or set)
redis 127.0.0.1:6379> scan 80 MATCH *11*
1) "176"
2) (empty list or set)
redis 127.0.0.1:6379> scan 176 MATCH *11* COUNT 1000
1) "0"
2) 1) "key:611"
2) "key:711"
3) "key:118"
4) "key:117"
5) "key:311"
6) "key:112"
7) "key:111"
8) "key:110"
9) "key:113"
10) "key:211"
11) "key:411"
12) "key:115"
13) "key:116"
14) "key:114"
15) "key:119"
16) "key:811"
17) "key:511"
18) "key:11"
如你所見, 以上的大部分迭代都不返回任何元素。
在最后一次迭代, 我們通過將 COUNT 選項的參數(shù)設置為 1000 , 強制命令為本次迭代掃描更多元素, 從而使得命令返回的元素也變多了。
并發(fā)執(zhí)行多個迭代
在同一時間, 可以有任意多個客戶端對同一數(shù)據(jù)集進行迭代, 客戶端每次執(zhí)行迭代都需要傳入一個游標, 并在迭代執(zhí)行之后獲得一個新的游標, 而這個游標就包含了迭代的所有狀態(tài), 因此, 服務器無須為迭代記錄任何狀態(tài)。
中途停止迭代
因為迭代的所有狀態(tài)都保存在游標里面, 而服務器無須為迭代保存任何狀態(tài), 所以客戶端可以在中途停止一個迭代, 而無須對服務器進行任何通知。
即使有任意數(shù)量的迭代在中途停止, 也不會產(chǎn)生任何問題。
使用錯誤的游標進行增量式迭代
使用間斷的(broken)、負數(shù)、超出范圍或者其他非正常的游標來執(zhí)行增量式迭代并不會造成服務器崩潰, 但可能會讓命令產(chǎn)生未定義的行為。
未定義行為指的是, 增量式命令對返回值所做的保證可能會不再為真。
只有兩種游標是合法的:
1、在開始一個新的迭代時, 游標必須為 0 。
2、增量式迭代命令在執(zhí)行之后返回的, 用于延續(xù)(continue)迭代過程的游標。
迭代終結的保證
增量式迭代命令所使用的算法只保證在數(shù)據(jù)集的大小有界(bounded)的情況下, 迭代才會停止, 換句話說, 如果被迭代數(shù)據(jù)集的大小不斷地增長的話, 增量式迭代命令可能永遠也無法完成一次完整迭代。
從直覺上可以看出, 當一個數(shù)據(jù)集不斷地變大時, 想要訪問這個數(shù)據(jù)集中的所有元素就需要做越來越多的工作, 能否結束一個迭代取決于用戶執(zhí)行迭代的速度是否比數(shù)據(jù)集增長的速度更快。
時間復雜度:
增量式迭代命令每次執(zhí)行的復雜度為 O(1) , 對數(shù)據(jù)集進行一次完整迭代的復雜度為 O(N) , 其中 N 為數(shù)據(jù)集中的元素數(shù)量。
返回值:
SCAN 命令、 SSCAN 命令、 HSCAN 命令和 ZSCAN 命令都返回一個包含兩個元素的 multi-bulk 回復:回復的第一個元素是字符串表示的無符號 64 位整數(shù)(游標), 回復的第二個元素是另一個 multi-bulk 回復, 這個 multi-bulk 回復包含了本次被迭代的元素。
SCAN 命令返回的每個元素都是一個數(shù)據(jù)庫鍵。
SSCAN 命令返回的每個元素都是一個集合成員。
HSCAN 命令返回的每個元素都是一個鍵值對,一個鍵值對由一個鍵和一個值組成。
ZSCAN 命令返回的每個元素都是一個有序集合元素,一個有序集合元素由一個成員(member)和一個分值(score)組成。
更多用法可查看Redis SCAN命令:
http://doc.redisfans.com/key/scan.html
參考:
https://redis.io
https://www.dazhuanlan.com/2019/12/17/5df832fd189f6
總結
到此這篇關于Redis的KEYS 命令千萬不能亂用的文章就介紹到這了,更多相關Redis的KEYS 命令內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
您可能感興趣的文章:- Redis遍歷所有key的兩個命令(KEYS 和 SCAN)
- redis keys與scan命令的區(qū)別說明
- 解決spring中redistemplate不能用通配符keys查出相應Key的問題
- 在RedisTemplate中使用scan代替keys指令操作
- Redis命令使用技巧之Keys的相關操作
- Redis不使用 keys 命令獲取鍵值信息的方法
- redis 用scan指令 代替keys指令(詳解)
- 淺談Redis的keys命令到底有多慢