數(shù)據(jù)類型精講:set/zset 核心命令 + 場(chǎng)景 + 其他類型與漸進(jìn)式遍歷----《Hello Redis!》(3))
文章目錄前言set(集合)有關(guān)set的命令set在Redis中的應(yīng)用場(chǎng)景zset類型(有序集合)有關(guān)zset的命令zset在Redis中的應(yīng)用場(chǎng)景其他類型漸進(jìn)式遍歷前言在上一篇文章中我們深入學(xué)習(xí)了 Redisstring、hash、list三大基礎(chǔ)數(shù)據(jù)類型掌握了緩存、對(duì)象存儲(chǔ)、簡(jiǎn)單隊(duì)列等核心場(chǎng)景的實(shí)現(xiàn)方式。而在實(shí)際開(kāi)發(fā)中去重統(tǒng)計(jì)、社交關(guān)系、實(shí)時(shí)排行榜、精準(zhǔn)推薦等高頻業(yè)務(wù)需要更專業(yè)的數(shù)據(jù)結(jié)構(gòu)支撐 ——set無(wú)序集合與zset有序集合正是解決這類問(wèn)題的利器。set 以無(wú)序、去重為核心特性搭配交集、并集、差集等集合運(yùn)算可輕松實(shí)現(xiàn)用戶標(biāo)簽、共同好友、UV 統(tǒng)計(jì)等功能zset 在 set 的基礎(chǔ)上增加score 分?jǐn)?shù)維度實(shí)現(xiàn)元素自動(dòng)排序是排行榜、優(yōu)先級(jí)隊(duì)列、限時(shí)任務(wù)等場(chǎng)景的最優(yōu)解。除此之外Redis 還提供了 stream、geo、HyperLogLog、Bitmap 等專用類型針對(duì)消息隊(duì)列、地理位置、海量統(tǒng)計(jì)、位存儲(chǔ)做了極致優(yōu)化。同時(shí)生產(chǎn)環(huán)境中禁止使用keys、smembers等全量遍歷命令* 漸進(jìn)式遍歷scan** 成為安全遍歷數(shù)據(jù)的唯一方案。本文將完整拆解set 與 zset 的特性、底層編碼、高頻命令、業(yè)務(wù)場(chǎng)景簡(jiǎn)要介紹 Redis 其他專用類型并詳解 scan 漸進(jìn)式遍歷的使用規(guī)則與注意事項(xiàng)幫你掌握 Redis 高級(jí)數(shù)據(jù)能力寫出安全、高效的生產(chǎn)級(jí)代碼。set(集合)集合就是一個(gè)key里面可以有多個(gè)memberset的特性:1.集合中的元素式無(wú)序的 2.集合中的元素是不能重復(fù)的 3.集合里面的元素都是string類型的(跟List一樣)關(guān)于set的內(nèi)部編碼:當(dāng)元素均為整數(shù)并且元素個(gè)數(shù)不是很多的時(shí)候用intset;不然的話就用hashtable有關(guān)set的命令sadd:將一個(gè)或多個(gè)元素添加到集合中自動(dòng)去重重復(fù)元素?zé)o法添加 語(yǔ)法:sadd key member1 [member2 ...]返回值:本次添加成功的元素個(gè)數(shù) 時(shí)間復(fù)雜度:O(1)smembers:獲取集合中的所有元素元素順序無(wú)序 語(yǔ)法:smembers key返回值:集合中的所有元素列表 時(shí)間復(fù)雜度:O(N)sismember:判斷元素是否存在于集合中 語(yǔ)法:sismember key member返回值:1表示元素存在0表示元素不存在或key不存在 時(shí)間復(fù)雜度:O(1)scard:獲取集合的元素個(gè)數(shù) 語(yǔ)法:scard key返回值:集合內(nèi)的元素個(gè)數(shù) 時(shí)間復(fù)雜度:O(1)spop:從集合中刪除并返回一個(gè)或多個(gè)隨機(jī)元素元素取出后會(huì)從集合中移除 語(yǔ)法:spop key [count]返回值:取出的元素列表 時(shí)間復(fù)雜度:O(N), N為count指定的數(shù)量–不指定count時(shí)默認(rèn)是取出一個(gè)元素–注意:這個(gè)取出的元素是隨機(jī)的smove:將元素從源集合移動(dòng)到目標(biāo)集合中 語(yǔ)法:smove 源集合 目標(biāo)集合 member返回值:1表示移動(dòng)成功0表示移動(dòng)失敗 時(shí)間復(fù)雜度:O(1)srem:從集合中刪除指定的一個(gè)或多個(gè)元素 語(yǔ)法:srem key member1 [member2 ...]返回值:本次操作成功刪除的元素個(gè)數(shù) 時(shí)間復(fù)雜度:O(N), N為要?jiǎng)h除的元素個(gè)數(shù)有關(guān)集合間的操作:(交并差補(bǔ))sinter:獲取多個(gè)集合的交集元素返回同時(shí)存在于所有集合中的元素 語(yǔ)法:sinter key1 [key2 ...]返回值:交集的元素列表 時(shí)間復(fù)雜度:O(N*M), N為最小集合元素個(gè)數(shù)M為最大集合元素個(gè)數(shù)sinterstore:獲取多個(gè)集合的交集元素并將結(jié)果保存到目標(biāo)集合中 語(yǔ)法:sinterstore 目標(biāo)集合 key1 [key2 ...]返回值:交集的元素個(gè)數(shù) 時(shí)間復(fù)雜度:O(N*M), N為最小集合元素個(gè)數(shù)M為最大集合元素個(gè)數(shù)sunion:獲取多個(gè)集合的并集元素返回所有集合中不重復(fù)的全部元素 語(yǔ)法:sunion key1 [key2...]返回值:并集的元素列表 時(shí)間復(fù)雜度:O(N), N為所有集合的總元素個(gè)數(shù)sunionstore:獲取多個(gè)集合的并集元素并將結(jié)果保存到目標(biāo)集合中 語(yǔ)法:sunionstore 目標(biāo)集合 key [key ...]返回值:并集的元素個(gè)數(shù) 時(shí)間復(fù)雜度:O(N), N為所有給定集合的總元素個(gè)數(shù)sdiff:獲取多個(gè)集合的差集元素返回僅存在于第一個(gè)集合且不存在于其他所有集合中的元素 語(yǔ)法:sdiff key1 [key2 ...]返回值:差集的元素列表 時(shí)間復(fù)雜度:O(N), N為所有給定集合的總元素個(gè)數(shù)sdiffstore:獲取第一個(gè)集合且不存在于其他所有集合中的元素并將結(jié)果保存到目標(biāo)集合中語(yǔ)法:sdiffstore destination key1 [key2...]返回值:差集的元素個(gè)數(shù) 時(shí)間復(fù)雜度:O(N), N為所有給定集合的總元素個(gè)數(shù)–這上面的時(shí)間復(fù)雜度是怎么得出的跟內(nèi)部的源碼設(shè)計(jì)有關(guān)了set在Redis中的應(yīng)用場(chǎng)景1.在Redis中用set存用戶標(biāo)簽(也就是做用戶畫像)2.在Redis中用set算共同好友3.用set統(tǒng)計(jì)去重訪問(wèn)量zset類型(有序集合)有序集合就是一個(gè)key里面有多個(gè)member然后每個(gè)member還有一個(gè)score這個(gè)有序集合的順序就是依據(jù)這個(gè)score去排的score跟member的關(guān)系有點(diǎn)像pair而不是鍵值對(duì)zset的特性1.集合中的元素是有序的2.member不允許重復(fù)但是score可以重復(fù)3.有序集合里的member都是string類型的score是浮點(diǎn)型的zset底層數(shù)據(jù)結(jié)構(gòu)的默認(rèn)排序規(guī)則:(內(nèi)部是按升序排序的)分?jǐn)?shù)不同按照分?jǐn)?shù)來(lái)排序分?jǐn)?shù)相同按照元素自身字符串的字典序來(lái)排序如果修改的分?jǐn)?shù)影響到了之前的順序就會(huì)自動(dòng)移動(dòng)元素位置保持原有的升序順序不變關(guān)于zset的內(nèi)部編碼:如果有序集合中的元素個(gè)數(shù)較少并且單個(gè)元素體積較小用ziplist來(lái)存;其他情況用skiplist來(lái)存儲(chǔ)–具體的邊界是可以去配置文件里面自行修改的關(guān)于跳表:跳表本質(zhì)上就是一個(gè)復(fù)雜鏈表,但是查詢?cè)氐臅r(shí)間復(fù)雜度是O(logN)有關(guān)zset的命令zadd:添加或更新有序集合中的元素及關(guān)聯(lián)分?jǐn)?shù)語(yǔ)法:zadd key [nx | xx] [lt | gt] [ch] [incr] score member [score1 member1 ...][nx|xx]:不選選項(xiàng)時(shí):如果當(dāng)前member不存在就添加member;如果當(dāng)前member已經(jīng)存在此時(shí)就會(huì)更新分?jǐn)?shù) nx:當(dāng)前member不存在才進(jìn)行操作 xx:當(dāng)前member存在才進(jìn)行操作[lt|gt]:lt:對(duì)于已存在的元素新分?jǐn)?shù)元素當(dāng)前分?jǐn)?shù)時(shí)才更新分?jǐn)?shù);對(duì)于不存在的元素如果無(wú)[nx|xx]添加元素 gt:對(duì)于已存在的元素新分?jǐn)?shù)元素當(dāng)前分?jǐn)?shù)時(shí)才更新分?jǐn)?shù);對(duì)于不存在的元素如果無(wú)[nx|xx]添加元素 incr:給元素的分?jǐn)?shù)加上指定的分?jǐn)?shù)--跟[lt|gt]一起用時(shí):lt或gt看的是算出來(lái)的新分?jǐn)?shù)和原來(lái)的分?jǐn)?shù)進(jìn)行比較返回值:默認(rèn)返回本次新增的元素個(gè)數(shù)使用ch選項(xiàng)后返回新增更新的元素總數(shù)時(shí)間復(fù)雜度:O(logN)N為集合元素?cái)?shù)量時(shí)間復(fù)雜度不是O(1)的原因:因?yàn)槭侨钟行虻膠card:獲取有序集合的元素總數(shù) 語(yǔ)法:zcard key返回值:集合中所有元素的總個(gè)數(shù) 時(shí)間復(fù)雜度:O(1)zcount:統(tǒng)計(jì)有序集合中分?jǐn)?shù)在指定區(qū)間內(nèi)的元素個(gè)數(shù) 語(yǔ)法:zcount key min max返回值:分?jǐn)?shù)滿足[min, max]條件的元素個(gè)數(shù) 時(shí)間復(fù)雜度:O(logN)N為集合元素?cái)?shù)量–支持使用-inf和inf來(lái)作為min和max–也支持使用(改成開(kāi)區(qū)間zcount能做到時(shí)間復(fù)雜度是O(logN)的原因:zset內(nèi)部會(huì)記錄每個(gè)元素當(dāng)前的排行/次序所以查找到元素就指定的元素所在的次序(下標(biāo))所以找min和max對(duì)應(yīng)的元素然后算出它的次序最后做減法就行了引申:負(fù)無(wú)窮大和無(wú)窮小不是一個(gè)概念 --無(wú)窮小是無(wú)限趨近于0zrange:按下標(biāo)區(qū)間獲取有序集合的元素(按分?jǐn)?shù)升序返回) 語(yǔ)法:zrange key start stop [withscores]返回值:[start,stop]內(nèi)的元素列表,加上withscores的話分?jǐn)?shù)也會(huì)連帶返回(支持負(fù)數(shù)下標(biāo))時(shí)間復(fù)雜度:O(logN M)N為集合元素總數(shù)M為返回的元素?cái)?shù)量返回值雖然是閉區(qū)間但是可以通過(guò)加括號(hào)給它搞成開(kāi)區(qū)間 eg:(start這樣 注意:stop)不行只能寫成(stop!!!zrevrange:按下標(biāo)區(qū)間獲取有序集合的元素(按分?jǐn)?shù)降序返回) 語(yǔ)法:zrevrange key start stop [withscores]返回值:[start,stop]內(nèi)的元素列表,加上withscores的話分?jǐn)?shù)也會(huì)連帶返回(支持負(fù)數(shù)下標(biāo))時(shí)間復(fù)雜度:O(logN M)N為集合元素總數(shù)M為返回的元素?cái)?shù)量–注意:Redis6.2.0之后就把zrevrange的降序查詢功能合并給zrange里了zrangebyscore:按分?jǐn)?shù)區(qū)間獲取有序集合的元素(按分?jǐn)?shù)升序返回) 語(yǔ)法:zrangebyscore key min max [withscores]返回值:范圍在[min,max]內(nèi)的元素列表,加上withscores的話分?jǐn)?shù)也會(huì)連帶返回–如果想開(kāi)區(qū)間的話也能用(時(shí)間復(fù)雜度:O(logN M)N為集合元素總數(shù)M為返回的元素?cái)?shù)量zpopmax:刪除并返回有序集合中分?jǐn)?shù)最高的count個(gè)元素語(yǔ)法:zpopmax key [count]如果不寫count的話默認(rèn)count是1返回值:元素與分?jǐn)?shù)成對(duì)列表如 [“three”, “3”]時(shí)間復(fù)雜度:O(log(N) * M)N為集合元素總數(shù)M為刪除的元素?cái)?shù)量–zpopmin是刪除并返回有序集合中分?jǐn)?shù)最低的count個(gè)元素 其他的跟zpopmax一樣–bzpopmin和bzpopmax是他們的阻塞版本–如果是同分?jǐn)?shù)的情況的話zpopmax和bzpopmax會(huì)優(yōu)先刪除字典序大的zpopmin和bzpopmax會(huì)優(yōu)先刪除字典序小的bzpopmax:zpopmax的阻塞版本刪除并返回有序集合中分?jǐn)?shù)最高的1個(gè)元素?zé)o元素時(shí)阻塞等待語(yǔ)法:bzpopmax key1 [key2 ...] timeout這里的timeout單位是s支持小數(shù)形式返回值:「key名, 元素, 分?jǐn)?shù)」三元組無(wú)元素時(shí)返回nil時(shí)間復(fù)雜度:O(logN)N為集合元素總數(shù)–bzpopmin:zpopmin的阻塞版本刪除并返回有序集合中分?jǐn)?shù)最低的1個(gè)元素?zé)o元素時(shí)阻塞等待(其他的跟bzpopmax一樣)關(guān)于Redis這里為啥不把最后一個(gè)或者最前那一個(gè)元素記錄下來(lái)讓尾刪和頭刪成O(1)1.雖然redis源碼中針對(duì)有序集合記錄了頭部和尾部這樣的特殊位置但是在實(shí)際刪除的時(shí)候調(diào)用的是通用的刪除函數(shù)所以沒(méi)優(yōu)化成O(1)2.還有就是因?yàn)镺(logN)其實(shí)不算很慢 3.而且因?yàn)閦set底層是跳表所以不行模擬阻塞隊(duì)列的話可以用list模擬阻塞的優(yōu)先級(jí)隊(duì)列的話可以用zsetzrank:返回zset中指定元素在升序排列下的下標(biāo)(分?jǐn)?shù)越小排名越靠前) 語(yǔ)法:zrank key member返回值:元素的排名下標(biāo)0開(kāi)始元素不存在時(shí)返回 nil 時(shí)間復(fù)雜度:O(logN)N為集合元素總數(shù)zrevrank:返回zset中指定元素在降序排列下的下標(biāo)(分?jǐn)?shù)越高排名越靠前) 語(yǔ)法:zrevrank key member返回值:元素的排名下標(biāo)0開(kāi)始元素不存在時(shí)返回 nil 時(shí)間復(fù)雜度:O(logN)N為集合元素總數(shù)zscore:獲取zset中指定元素的分?jǐn)?shù) 語(yǔ)法:zscore key member返回值:元素的分?jǐn)?shù)元素不存在時(shí)返回 nil 時(shí)間復(fù)雜度:O(1)為啥zscore是O(1)zrank是O(logN):zscore是直接去查哈希表;zrank是因?yàn)楣1砝镏淮媪薽ember和score不知道排名所以需要去跳表里面找zrem:刪除Zset中一個(gè)或多個(gè)指定元素按member精確匹配 語(yǔ)法:zrem key member1 [member2 ...]返回值:成功刪除的元素個(gè)數(shù)不存在的元素不計(jì)入結(jié)果時(shí)間復(fù)雜度:O(M*logN)M為刪除的元素?cái)?shù)N為集合總元素?cái)?shù)zremrangebyrank:按升序下標(biāo)區(qū)間刪除有序集合元素下標(biāo)從0開(kāi)始區(qū)間為閉區(qū)間[start, stop]語(yǔ)法:zremrangebyrank key start stop返回值:成功刪除的元素個(gè)數(shù)時(shí)間復(fù)雜度:O(logN M)N為集合總元素?cái)?shù)M為區(qū)間包含的元素個(gè)數(shù)zremrangebyscore:按分?jǐn)?shù)區(qū)間([min,max])刪除有序集合元素支持(語(yǔ)法:zremrangebyscore key min max返回值:成功刪除的元素個(gè)數(shù)時(shí)間復(fù)雜度:O(logN M)N為集合總元素?cái)?shù)M為區(qū)間包含的元素個(gè)數(shù)zincrby:為指定的元素對(duì)應(yīng)的分?jǐn)?shù)添加指定的分?jǐn)?shù)(increment可以是負(fù)數(shù))語(yǔ)法:zincrby key increment member返回值:操作后元素的新分?jǐn)?shù) 時(shí)間復(fù)雜度:O(logN)N為集合總元素?cái)?shù)–如果member不存在的話會(huì)自動(dòng)創(chuàng)建(key也一樣)–這個(gè)操作不光會(huì)修改分?jǐn)?shù)同時(shí)還會(huì)移動(dòng)元素位置讓整個(gè)有序集合仍然是升序的注意:zset里按分?jǐn)?shù)操作的命令才支持(按下標(biāo)操作的命令不支持(zset集合間操作的命令:zinterstore:求多個(gè)有序集合的交集并存入目標(biāo)集合中語(yǔ)法:zinterstore 目標(biāo)集合 numkeys key1 [key2...] [weights 權(quán)重1 ...] [aggregate sum|min|max]關(guān)于其中選項(xiàng)的介紹:numkeys:說(shuō)明有幾個(gè)key--需要這個(gè)選項(xiàng)的原因:避免Redis把選項(xiàng)和keys混淆[weights 權(quán)重1...]:設(shè)置各個(gè)輸入集合的權(quán)重不設(shè)置的話默認(rèn)是1[aggregatesum|min|max]:如果不選的話默認(rèn)是sum--這個(gè)就是在有元素member相同但是score不同時(shí)對(duì)score的取舍--sum的話就是根據(jù)權(quán)重來(lái)求summin和max就是根據(jù)權(quán)重后的結(jié)果來(lái)取舍--eg:zset1有個(gè)元素score是10zset2有個(gè)元素score是20如果他們的menber相同-他倆默認(rèn)情況下的交集的score就是30返回值:目標(biāo)集合的元素個(gè)數(shù)時(shí)間復(fù)雜度:O(N*K M*logM)N為輸入集合中最小的元素個(gè)數(shù)K為輸入集合的個(gè)數(shù)M為結(jié)果集合的元素個(gè)數(shù)-這個(gè)時(shí)間復(fù)雜度可以近似成O(M*logM)–這些時(shí)間復(fù)雜度一般是不用記得面試也不會(huì)問(wèn)這些–注意:zinter,zunion和zdiff是Redis6.2.0版本才開(kāi)始支持的有序集合命令zunionstore:求多個(gè)有序集合的并集并存入目標(biāo)集合中語(yǔ)法:zunionstore 目標(biāo)集合 numkeys key1 [key2...] [weights 權(quán)重1 ...] [aggregate sum|min|max]–(其他的跟zinterstore差不多)ZUNIONSTORE:計(jì)算多個(gè)有序集合的并集并保存到目標(biāo)集合支持分?jǐn)?shù)權(quán)重與聚合方式默認(rèn)求和 語(yǔ)法: ZUNIONSTORE destination numkeys key [key …] [WEIGHTS weight …] [AGGREGATE SUM|MIN|MAX] 返回值:目標(biāo)集合的元素個(gè)數(shù) 時(shí)間復(fù)雜度:O(N M*logM)N為所有輸入集合的總元素?cái)?shù)M為結(jié)果集合的元素?cái)?shù)zset集合間在比較元素相同時(shí)只要member相同就行(eg:求交集時(shí))zset在Redis中的應(yīng)用場(chǎng)景1.制作需要實(shí)時(shí)更新的排行榜系統(tǒng)時(shí)用zset存數(shù)據(jù)–只要把玩家信息和對(duì)應(yīng)的分?jǐn)?shù)放到有序集合中的話就自然形成了一個(gè)排行榜–如果是那種需要加權(quán)處理的數(shù)據(jù)處理的方法:搞多個(gè)key存各個(gè)數(shù)據(jù)最后比如用zinterstore進(jìn)行加權(quán)引申:相關(guān)的估算1KB 約等于 一千字節(jié)1MB 約等于 一百萬(wàn)字節(jié)1GB 約等于 十億字節(jié)其他類型相較于上面那幾個(gè)類型這里的類型會(huì)不常用些但是特定場(chǎng)景用時(shí)會(huì)特別好用這里介紹:streamgeospatialhyperloglogbitmapsbitfieldsstream類型:本質(zhì)是一個(gè)阻塞的消息隊(duì)列(比list當(dāng)阻塞隊(duì)列好)–生產(chǎn)者發(fā)信息然后消費(fèi)者處理geospatial類型:是Redis專門為地理位置數(shù)據(jù)設(shè)計(jì)的有序集合–用來(lái)存儲(chǔ)經(jīng)緯度然后就可以讓用戶給定一個(gè)坐標(biāo)和需要的范圍限制然后就可以去剛才存儲(chǔ)的點(diǎn)里去找hyperloglog類型:估算集合中不重復(fù)元素的個(gè)數(shù)–應(yīng)用場(chǎng)景:比如統(tǒng)計(jì)網(wǎng)站的獨(dú)立訪客數(shù)–這個(gè)類型不會(huì)存集合中元素本身只會(huì)存元素的特征然后用概率算法估算數(shù)量(Redis中實(shí)現(xiàn)這個(gè)的誤差差不多0.81%)–這樣的話每次都只會(huì)占用12KB的內(nèi)存bitmap類型:用比特位去存儲(chǔ)元素(相當(dāng)于是set類型針對(duì)整數(shù)的特別優(yōu)化版本比set更加節(jié)省空間)–跟hyperloglog的對(duì)比:雖然hyperloglog更加節(jié)省空間但是hyperloglog是沒(méi)有存儲(chǔ)元素內(nèi)容的只存了特征bitfield類型:就是一串二進(jìn)制序列然后可以自行定義從哪個(gè)位置開(kāi)始用多少個(gè)比特位是存的是…還能對(duì)這個(gè)存儲(chǔ)的數(shù)據(jù)直接進(jìn)行查改–相較于string或者h(yuǎn)ash的話會(huì)更加的節(jié)省空間–bitmap是單比特位的bitfield是多比特位的漸進(jìn)式遍歷這個(gè)的話就是每執(zhí)行一次命令就只獲取其中的一小部分key–來(lái)保證當(dāng)前這一次操作不會(huì)特別卡–這個(gè)遍歷是能隨時(shí)終止的不會(huì)對(duì)服務(wù)器產(chǎn)生任何的副作用如果想要得到所有的key就需要多次執(zhí)行scan命令–但是要注意:用scan遍歷也是有風(fēng)險(xiǎn)的盡量不要做遍歷所有key這個(gè)操作scan:語(yǔ)法:SCAN cursor [match pattern] [count 單次遍歷的數(shù)量] [type 類型]關(guān)于這里的選項(xiàng):[match pattern]:跟前面介紹的key的那個(gè)pattern一樣[count 單次遍歷的數(shù)量]:限制這一次遍歷最多能獲取到多少個(gè)元素,默認(rèn)是10--注意:這里的count只是給redis的一個(gè)建議實(shí)際返回的key的個(gè)數(shù)不一定是完全相同的(在中間遍歷時(shí)也會(huì)這樣)[type 類型]:只返回value類型和指定的類型一致的key--如果不寫這個(gè)選項(xiàng)的話就是表示不做任何類型的過(guò)濾遍歷Redis中所有類型的key關(guān)于cursor:這個(gè)是從哪個(gè)位置開(kāi)始遍歷的標(biāo)記(傳0的話表示從頭開(kāi)始遍歷)返回值:本地得到的key和下一次cursor應(yīng)該填啥(如果cursor返回值是0說(shuō)明已經(jīng)遍歷完畢了)–注意:返回回來(lái)的cursor不一定是遞增的因?yàn)榉祷鼗貋?lái)的cursor不具有可讀性是哈希表內(nèi)部的槽位索引時(shí)間復(fù)雜度:O(1)注意:不要在邊遍歷時(shí)邊修改內(nèi)容!!!–不僅僅是Redis任何場(chǎng)景下都忌諱這么搞的!比如C用線程安全去解決這個(gè)問(wèn)題