日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

java lambda 画蛇添足_技术史上的画蛇添足: Redis HGETALL 排序问题

發布時間:2023/12/14 数据库 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java lambda 画蛇添足_技术史上的画蛇添足: Redis HGETALL 排序问题 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一個系列

在技術史上,有很多彼時的 bug 歷經歲月錘煉最后化身 feature 的事兒發生。

在閱讀一些熱門開源框架代碼時,經常能發現狗血的、難以置信的、童真的、幽默的、這也能寫的,代碼。

以上這些,都值得吐槽。吐槽是為了解構“權威”,此處的權威是應用廣泛,少有人提出異議的框架工具、架構設計、代碼組織等。通過解構,進一步了解軟件工程的本質。

今天要說的,是 Redis …… 里面一個很小的特性。

如果時間允許的話,我想寫成一個系列。

一個例子

我們利用 Redis Hash 實現一個簡單的需求——

存儲一些 tweets,然后讀取出來,順序地展示在頁面上。

技術選型我們采用 Redis 和 Python。

第一步,順序存儲 tweets

import redis

tweets = [{

"id": 250075927172759552,

"geo": null,

"retweeted": false,

"in_reply_to_user_id": null,

"place": null,

...

},

...

]

rc = redis.Redis(host='localhost', port='6379', db=0)

for tweet in tweets:

rc.hsetnx('tweets', tweet['id'], tweet)

登入 redis,查看:

127.0.0.1:6379> HGETALL 'tweets'

1) "250075927172759552"

2) "{...}"

3) "250075927172759553"

4) "{...}"

5) "250075927172759554"

6) "{...}"

7) "250075927172759555"

8) "{...}"

看上去是順序輸出,有點譜,繼續下邊的邏輯實現。

第二步,順序展示 tweets

import redis

rc = redis.Redis(host='localhost', port='6379', db=0)

tweets = rc.hgetall('tweets')

...

這時候我們會發現,tweets 變成了這樣:

tweets = {

'250075927172759552': {...},

'250075927172759555': {...},

...

}

一個無序的 dict。

顯然,這完成不了我們的順序輸出的需求。

難道,是 redis-py 這個框架實現錯了嗎?

Redis HGETALL 和它的 Python 實現

redis-py 的 hgetall

'HGETALL': lambda r: r and pairs_to_dict(r) or {},

def pairs_to_dict(response):

"Create a dict given a list of key/value pairs"

it = iter(response)

return dict(izip(it, it))

一個明顯的字典轉換。

HGETALL 的文檔描述及源碼

再來看 HGETALL 的文檔——

Returns all fields and values of the hash stored at key. In the returned value, every field name is followed by its value, so the length of the reply is twice the size of the hash.

沒有提到任何和排序有關的描述,也即,不保證返回結果的排序。

那,上邊看到的順序輸出是什么意思?Redis 官方客戶端畫蛇添足了?再者說,一個嚴肅的 Hash,key 怎么可能是順序存儲的?

我們看下 HGETALL 的源碼:

https://github.com/antirez/redis/blob/4.0/src/t_hash.c#L37

/* Check the length of a number of objects to see if we need to convert a

* ziplist to a real hash. Note that we only check string encoded objects

* as their string length can be queried in constant time. */

void hashTypeTryConversion(robj *o, robj **argv, int start, int end) {

int i;

if (o->encoding != OBJ_ENCODING_ZIPLIST) return;

...

從源碼可以看到,默認情況下,Redis 的 Hash 是使用 ziplist 進行存儲的,當超出一定限制后,再改為一個正統的 hash 進行存儲。ziplist 是一個雙向鏈表,所以是順序的,這就是上邊我們看到的。下面我們看看在什么情況下會進行轉換。

redis Hash 的 ziplist 和 hash 的轉換條件

# 哈希對象只包含一個鍵和值都不超過 64 個字節的鍵值對

127.0.0.1:6379> HSET test_hash test_key "value's length less than 64 bytes"

(integer) 1

127.0.0.1:6379> OBJECT ENCODING test_hash

"ziplist"

# 向哈希對象添加一個新的鍵值對,鍵的長度為 66 字節

127.0.0.1:6379> HSET test_hash long_long_long_long_long_long_long_long_long_long_long_description "content"

(integer) 1

# 編碼改變

127.0.0.1:6379> OBJECT ENCODING test_hash

"hashtable"

# 創建一個包含 512 個鍵值對的哈希對象

127.0.0.1:6379> EVAL "for i=1, 512 do redis.call('HSET', KEYS[1], i, i) end" 1 "numbers"

(nil)

127.0.0.1:6379> HLEN numbers

(integer) 512

127.0.0.1:6379> OBJECT ENCODING numbers

"ziplist"

# 再向哈希對象添加一個新的鍵值對,使得鍵值對的數量變成 513 個

127.0.0.1:6379> HMSET numbers "key" "value"

OK

127.0.0.1:6379> HLEN numbers

(integer) 513

# 編碼改變

127.0.0.1:6379> OBJECT ENCODING numbers

"hashtable"

在兩種情況下,Hash 會使用 ziplist 進行存儲:

哈希對象保存的所有鍵值對的鍵和值的字符串長度都小于 64 字節;

哈希對象保存的鍵值對數量小于 512 個;

其中,64 字節和 512 分別可以通過配置文件中的 server.hash_max_ziplist_value、server.hash_max_ziplist_entries 進行配置。

結語

通過閱讀文檔和源碼,我們可以知道,乍看 Redis Hash 是順序存儲這個假象可以很好地被解釋。redis 的文檔寫的也沒什么毛病。Redis 默認使用 ziplist 來存儲 Hash、List、ZSet 這些數據結構,目的只有一個:節約內存。

而在本例中,Redis 的這種做法引發了一個錯覺,這是完全合理的,一個 Cache,當然要追求性能。

感謝 Redis 作者及開源貢獻者!

References

總結

以上是生活随笔為你收集整理的java lambda 画蛇添足_技术史上的画蛇添足: Redis HGETALL 排序问题的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。