Redis 對(duì)象共享

2018-08-02 14:50 更新

除了用于實(shí)現(xiàn)引用計(jì)數(shù)內(nèi)存回收機(jī)制之外, 對(duì)象的引用計(jì)數(shù)屬性還帶有對(duì)象共享的作用。

舉個(gè)例子, 假設(shè)鍵 A 創(chuàng)建了一個(gè)包含整數(shù)值 100 的字符串對(duì)象作為值對(duì)象, 如圖 8-20 所示。

如果這時(shí)鍵 B 也要?jiǎng)?chuàng)建一個(gè)同樣保存了整數(shù)值 100 的字符串對(duì)象作為值對(duì)象, 那么服務(wù)器有以下兩種做法:

  1. 為鍵 B 新創(chuàng)建一個(gè)包含整數(shù)值 100 的字符串對(duì)象;
  2. 讓鍵 A 和鍵 B 共享同一個(gè)字符串對(duì)象;

以上兩種方法很明顯是第二種方法更節(jié)約內(nèi)存。

在 Redis 中, 讓多個(gè)鍵共享同一個(gè)值對(duì)象需要執(zhí)行以下兩個(gè)步驟:

  1. 將數(shù)據(jù)庫(kù)鍵的值指針指向一個(gè)現(xiàn)有的值對(duì)象;
  2. 將被共享的值對(duì)象的引用計(jì)數(shù)增一。

舉個(gè)例子, 圖 8-21 就展示了包含整數(shù)值 100 的字符串對(duì)象同時(shí)被鍵 A 和鍵 B 共享之后的樣子, 可以看到, 除了對(duì)象的引用計(jì)數(shù)從之前的 1 變成了 2 之外, 其他屬性都沒(méi)有變化。

共享對(duì)象機(jī)制對(duì)于節(jié)約內(nèi)存非常有幫助, 數(shù)據(jù)庫(kù)中保存的相同值對(duì)象越多, 對(duì)象共享機(jī)制就能節(jié)約越多的內(nèi)存。

比如說(shuō), 假設(shè)數(shù)據(jù)庫(kù)中保存了整數(shù)值 100 的鍵不只有鍵 A 和鍵 B 兩個(gè), 而是有一百個(gè), 那么服務(wù)器只需要用一個(gè)字符串對(duì)象的內(nèi)存就可以保存原本需要使用一百個(gè)字符串對(duì)象的內(nèi)存才能保存的數(shù)據(jù)。

目前來(lái)說(shuō), Redis 會(huì)在初始化服務(wù)器時(shí), 創(chuàng)建一萬(wàn)個(gè)字符串對(duì)象, 這些對(duì)象包含了從 0 到 9999 的所有整數(shù)值, 當(dāng)服務(wù)器需要用到值為 0到 9999 的字符串對(duì)象時(shí), 服務(wù)器就會(huì)使用這些共享對(duì)象, 而不是新創(chuàng)建對(duì)象。

注意

創(chuàng)建共享字符串對(duì)象的數(shù)量可以通過(guò)修改 redis.h/REDIS_SHARED_INTEGERS 常量來(lái)修改。

舉個(gè)例子, 如果我們創(chuàng)建一個(gè)值為 100 的鍵 A , 并使用 OBJECT REFCOUNT 命令查看鍵 A 的值對(duì)象的引用計(jì)數(shù), 我們會(huì)發(fā)現(xiàn)值對(duì)象的引用計(jì)數(shù)為 2 :

redis> SET A 100
OK

redis> OBJECT REFCOUNT A
(integer) 2

引用這個(gè)值對(duì)象的兩個(gè)程序分別是持有這個(gè)值對(duì)象的服務(wù)器程序, 以及共享這個(gè)值對(duì)象的鍵 A , 如圖 8-22 所示。

如果這時(shí)我們?cè)賱?chuàng)建一個(gè)值為 100 的鍵 B , 那么鍵 B 也會(huì)指向包含整數(shù)值 100 的共享對(duì)象, 使得共享對(duì)象的引用計(jì)數(shù)值變?yōu)?nbsp;3 :

redis> SET B 100
OK

redis> OBJECT REFCOUNT A
(integer) 3

redis> OBJECT REFCOUNT B
(integer) 3

圖 8-23 展示了共享值對(duì)象的三個(gè)程序。

另外, 這些共享對(duì)象不單單只有字符串鍵可以使用, 那些在數(shù)據(jù)結(jié)構(gòu)中嵌套了字符串對(duì)象的對(duì)象(linkedlist 編碼的列表對(duì)象、 hashtable 編碼的哈希對(duì)象、 hashtable 編碼的集合對(duì)象、以及 zset 編碼的有序集合對(duì)象)都可以使用這些共享對(duì)象。

為什么 Redis 不共享包含字符串的對(duì)象?

當(dāng)服務(wù)器考慮將一個(gè)共享對(duì)象設(shè)置為鍵的值對(duì)象時(shí), 程序需要先檢查給定的共享對(duì)象和鍵想創(chuàng)建的目標(biāo)對(duì)象是否完全相同, 只有在共享對(duì)象和目標(biāo)對(duì)象完全相同的情況下, 程序才會(huì)將共享對(duì)象用作鍵的值對(duì)象, 而一個(gè)共享對(duì)象保存的值越復(fù)雜, 驗(yàn)證共享對(duì)象和目標(biāo)對(duì)象是否相同所需的復(fù)雜度就會(huì)越高, 消耗的 CPU 時(shí)間也會(huì)越多:

  • 如果共享對(duì)象是保存整數(shù)值的字符串對(duì)象, 那么驗(yàn)證操作的復(fù)雜度為 O(1) ;
  • 如果共享對(duì)象是保存字符串值的字符串對(duì)象, 那么驗(yàn)證操作的復(fù)雜度為 O(N) ;
  • 如果共享對(duì)象是包含了多個(gè)值(或者對(duì)象的)對(duì)象, 比如列表對(duì)象或者哈希對(duì)象, 那么驗(yàn)證操作的復(fù)雜度將會(huì)是 O(N^2) 。

因此, 盡管共享更復(fù)雜的對(duì)象可以節(jié)約更多的內(nèi)存, 但受到 CPU 時(shí)間的限制, Redis 只對(duì)包含整數(shù)值的字符串對(duì)象進(jìn)行共享。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)