日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

如何修改Series和DataFrame类型中的元素值_Redis的HSCAN命令中COUNT参数的失效场景

發(fā)布時間:2024/10/12 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 如何修改Series和DataFrame类型中的元素值_Redis的HSCAN命令中COUNT参数的失效场景 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

前提

?

這是一篇Redis命令使用不當(dāng)?shù)牟瓤咏?jīng)歷分享

?

筆者最近在做一個項目時候使用Redis存放客戶端展示的訂單列表,列表需要進(jìn)行分頁。由于筆者先前對Redis的各種數(shù)據(jù)類型的使用場景并不是十分熟悉,于是先入為主地看到Hash類型的數(shù)據(jù)結(jié)構(gòu),假定:

USER_ID:1 ORDER_ID:ORDER_XX: {"amount": "100","orderId":"ORDER_XX"} ORDER_ID:ORDER_YY: {"amount": "200","orderId":"ORDER_YY"}

感覺Hash類型完全滿足需求實現(xiàn)的場景。然后想當(dāng)然地考慮使用HSCAN命令進(jìn)行分頁,引發(fā)了后面遇到的問題。

SCAN和HSCAN命令

SCAN命令如下:

SCAN?cursor?[MATCH?pattern]?[COUNT?count]?[TYPE?type]//?返回值如下://?1.?cursor,數(shù)值類型,下一輪的起始游標(biāo)值,0代表遍歷結(jié)束//?2.?遍歷的結(jié)果集合,列表

SCAN命令在Redis2.8.0版本中新增,時間復(fù)雜度計算如下:每一輪遍歷的時間復(fù)雜度為O(1),所有元素遍歷完畢直到游標(biāo)cursor返回0的時間復(fù)雜度為O(N),其中N為集合內(nèi)元素的數(shù)量。SCAN是針對整個Database內(nèi)的所有KEY進(jìn)行漸進(jìn)式的遍歷,它不會一直阻塞Redis,也就是使用SCAN命令遍歷KEY的性能有可能會優(yōu)于KEY *命令。對于Hash類型有一個衍生的命令HSCAN專門用于遍歷Hash類型及其相關(guān)屬性(Field)的字段:

HSCAN?key?cursor?[MATCH?pattern]?[COUNT?count]//?返回值如下://?1.?cursor,數(shù)值類型,下一輪的起始游標(biāo)值,0代表遍歷結(jié)束//?2.?遍歷的結(jié)果集合,是一個映射

筆者當(dāng)時沒有仔細(xì)查閱Redis的官方文檔,想當(dāng)然地認(rèn)為Hash類型的分頁簡單如下(偏激一點假設(shè)每頁數(shù)據(jù)只有1條):

//?第一頁HSCAN?USER_ID:1?0?COUNT?1????<=?這里認(rèn)為返回的游標(biāo)值為1//?第二頁HSCAN?USER_ID:1?1?COUNT?1????<=?這里認(rèn)為返回的游標(biāo)值為0,結(jié)束迭代

實際上,執(zhí)行的結(jié)果如下:

HSCAN?USER_ID:1?0?COUNT?1//?結(jié)果0??ORDER_ID:ORDER_XX?{"amount":?"100","orderId":"ORDER_XX"}?ORDER_ID:ORDER_YY?{"amount":?"200","orderId":"ORDER_YY"}

也就是在第一輪遍歷的時候,KEY對應(yīng)的所有Field-Value已經(jīng)全量返回。筆者嘗試增加哈希集合KEY = USER_ID:1里面的元素,但是數(shù)據(jù)量相對較大的時候,依然沒有達(dá)到預(yù)期的分頁效果;另一個方面,嘗試修改命令中的COUNT值,發(fā)現(xiàn)無論如何修改COUNT值都不會對遍歷的結(jié)果產(chǎn)生任何影響(也就是還是在第一輪迭代返回全部結(jié)果)。百思不得其解的情況下,只能仔細(xì)翻閱官方文檔尋找解決方案。在SCAN命令的COUNT屬性描述中找到了原因:

r-h-p-1

簡單翻譯理解一下:

SCAN命令以及其衍生命令并不保證每一輪迭代返回的元素數(shù)量,但是可以使用COUNT屬性憑經(jīng)驗調(diào)整SCAN命令的行為。COUNT指定每次調(diào)用應(yīng)該完成遍歷的元素的數(shù)量,以便于遍歷集合,「本質(zhì)只是一個提示值」(just a hint,hint意思為暗示)。

  • COUNT默認(rèn)值為10。
  • 當(dāng)遍歷的目標(biāo)Set、Hash、Sorted Set或者Key空間足夠大可以使用一個哈希表表示并且不使用MATCH屬性的前提下,Redis服務(wù)端會返回COUNT或者比COUNT大的遍歷元素結(jié)果集合。
  • 當(dāng)遍歷只包含Integer值的Set集合(也稱為intsets),或者ziplists類型編碼的Hash或者Sorted Set集合(說明這些集合里面的元素占用的空間足夠小),那么SCAN命令會返回集合中的所有元素,直接忽略COUNT屬性。
  • 注意第3點,這個就是在Hash集合中使用HSCAN命令COUNT屬性失效的根本原因。Redis配置中有兩個和Hash類型ziplist編碼的相關(guān)配置值:

    hash-max-ziplist-entries 512hash-max-ziplist-value 64

    在如下兩個條件之一滿足的時候,Hash集合的編碼會由ziplist會轉(zhuǎn)成dict(字典類型編碼是哈希表,即hashtable):

    • 當(dāng)Hash集合中的數(shù)據(jù)項(即Field-Value對)的「數(shù)目超過512」的時候。
    • 當(dāng)Hash集合中插入的任意一個Field-Value對中的「Value長度超過64」的時候。

    當(dāng)Hash集合的編碼會由ziplist會轉(zhuǎn)成dict,Redis為Hash類型的內(nèi)存空間占用優(yōu)化相當(dāng)于失敗了,降級為相對消耗更多內(nèi)存的字典類型編碼,這個時候,HSCAN命令COUNT屬性才會起效。

    案例驗證

    ?

    查詢Redis中Key的編碼類型的命令為:object encoding $KEY

    ?

    簡單驗證一下上一節(jié)得出的結(jié)論,寫入一個測試數(shù)據(jù)如下:

    //?70個XHSET?USER_ID:2?ORDER_ID:ORDER_XXX?XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX???//?70個YHSET?USER_ID:2?ORDER_ID:ORDER_YYY?YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY

    接著開始測試一下HSCAN命令:

    //?查看編碼object?encoding?USER_ID:2//?編碼結(jié)果hashtable//?第一輪迭代HSCAN?USER_ID:2?0?COUNT?1//?第一輪迭代返回結(jié)果2??ORDER_ID:ORDER_YYY?YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY//?第二輪迭代?HSCAN?USER_ID:2?2?COUNT?10??ORDER_ID:ORDER_XXX?XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

    測試案例中故意讓兩個值的長度為70,大于64,也就是讓Hash集合轉(zhuǎn)變?yōu)閐ict(hashtable)類型,使得COUNT屬性生效。但是,這種做法是放棄了Redis為Hash集合的內(nèi)存優(yōu)化。此前驗證的是hash-max-ziplist-value配置項的臨界值,還可以編寫一個例子驗證hash-max-ziplist-entries的臨界值:

    //?下面的代碼需要確保本地安裝了Redis,并且引入Redis的客戶端依賴:io.lettuce:lettuce-core:5.3.3.RELEASEpublic?class?HashScanCountSample?{????static?String?KEY?=?"HS";????static?int?THRESHOLD?=?513;????static?int?COUNT?=?5;????public?static?void?main(String[]?args)?throws?Exception?{????????ScanArgs?scanArgs?=?new?ScanArgs().limit(COUNT);????????RedisURI?redisUri?=?RedisURI.create("127.0.0.1",?6379);????????RedisClient?redisClient?=?RedisClient.create(redisUri);????????RedisCommands?commands?=?redisClient.connect().sync();????????commands.del(KEY);????????int?total?=?10;????????for?(int?i?=?1;?i?<=?total;?i++)?{????????????String?fv?=?String.valueOf(i);????????????commands.hset(KEY,?fv,?fv);????????}????????ScanCursor?scanCursor?=?ScanCursor.INITIAL;????????int?idx?=?1;????????processScan(total,?scanArgs,?commands,?scanCursor,?idx);????????for?(int?i?=?11;?i?<=?THRESHOLD;?i++)?{????????????String?fv?=?String.valueOf(i);????????????commands.hset(KEY,?fv,?fv);????????}????????scanCursor?=?ScanCursor.INITIAL;????????total?=?THRESHOLD;????????idx?=?1;????????processScan(total,?scanArgs,?commands,?scanCursor,?idx);????}????private?static?void?processScan(int?total,?ScanArgs?scanArgs,?RedisCommands?commands,?ScanCursor?scanCursor,?int?idx)?{????????System.out.println(String.format("%d個F-V的HS的編碼:%s",?total,?commands.objectEncoding(KEY)));????????System.out.println(String.format("%d個F-V的HS進(jìn)行HSCAN...",?total));????????MapScanCursor?result;????????while?(!(result?=?commands.hscan(KEY,?scanCursor,?scanArgs)).isFinished())?{????????????System.out.println(String.format("%d個F-V的HS進(jìn)行HSCAN第%d次遍歷,size=%d",?total,?idx,?result.getMap().size()));????????????scanCursor?=?new?ScanCursor(result.getCursor(),?result.isFinished());????????????idx++;????????}????????System.out.println(String.format("%d個F-V的HS進(jìn)行HSCAN第%d次遍歷,size=%d",?total,?idx,?result.getMap().size()));????}}//?某次輸出結(jié)果10個F-V的HS的編碼:ziplist10個F-V的HS進(jìn)行HSCAN...10個F-V的HS進(jìn)行HSCAN第1次遍歷,size=10......513個F-V的HS的編碼:hashtable513個F-V的HS進(jìn)行HSCAN...513個F-V的HS進(jìn)行HSCAN第1次遍歷,size=5......513個F-V的HS進(jìn)行HSCAN第92次遍歷,size=6513個F-V的HS進(jìn)行HSCAN第93次遍歷,size=6513個F-V的HS進(jìn)行HSCAN第94次遍歷,size=5

    這里看到,最終遍歷513個F-V的Hash類型的KEY,最多每次能遍歷出9個F-V對,這里只是其中一次的測試數(shù)據(jù),也就是說COUNT值即使固定為一個常量,但是遍歷出來的數(shù)據(jù)集合中的元素數(shù)量不一定為COUNT,但是大多數(shù)情況下為COUNT。

    ?

    不過可以推斷出一點,如果Hash中的F-V對的數(shù)量小于512,并且所有的V的長度都比較短,HSCAN命令會一次遍歷出該KEY的所有的F-V對

    ?

    顯然,HSCAN命令天然不是為了做數(shù)據(jù)分頁而設(shè)計的,而是為了漸進(jìn)式的迭代(也就是如果需要迭代的集合很大,也不會一直阻塞Redis服務(wù))。所以筆者最后放棄了使用HSCAN命令,尋找更適合做數(shù)據(jù)分頁查詢的其他Redis命令。

    小結(jié)

    通過這簡單的踩坑案例,筆者得到一些經(jīng)驗:

    • 切忌先入為主,使用中間件的時候要結(jié)合實際的場景。
    • 使用工具的之前要仔細(xì)閱讀工具的使用手冊。
    • 要通過一些案例驗證自己的猜想或者推導(dǎo)的結(jié)果。

    HSCAN命令中的COUNT屬性的功能和Redis服務(wù)的配置項hash-max-ziplist-value、hash-max-ziplist-entries以及KEY的編碼類型息息相關(guān)。Redis提供的API十分豐富,這些API的版本兼容性做得十分優(yōu)秀,后面應(yīng)該還會遇到更多的踩坑經(jīng)驗。

    總結(jié)

    以上是生活随笔為你收集整理的如何修改Series和DataFrame类型中的元素值_Redis的HSCAN命令中COUNT参数的失效场景的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。