欧美阿v视频在线大全_亚洲欧美中文日韩V在线观看_www性欧美日韩欧美91_亚洲欧美日韩久久精品

主頁 > 知識庫 > 為啥懶 Redis 是更好的 Redis

為啥懶 Redis 是更好的 Redis

熱門標簽:太原營銷外呼系統 地圖標注費用 玄武湖地圖標注 竹間科技AI電銷機器人 百度商家地圖標注怎么做 西藏教育智能外呼系統價格 小紅書怎么地圖標注店 最簡單的百度地圖標注 地圖標注如何即時生效

英文原文:Lazy Redis is better Redis

前言

大家都知道 Redis 是單線程的。真正的內行會告訴你,實際上 Redis 并不是完全單線程,因為在執行磁盤上的特定慢操作時會有多線程。目前為止多線程操作絕大部分集中在 I/O 上以至于在不同線程執行異步任務的小型庫被稱為 bio.c: 也就是 Background I/O。

然而前陣子我提交了一個問題,在問題里我承諾提供一個很多人(包括我自己)都想要的功能,叫做“免費懶加載”。原始的問題在這

問題的根本在于,Redis 的 DEL 操作通常是阻塞的。因此如果你發送 Redis “DEL mykey” 命令,碰巧你的 key 有 5000萬個對象,那么服務器將會阻塞幾秒鐘,在此期間服務器不會處理其他請求。歷史上這被當做 Redis 設計的副作用而被接受,但是在特定的用例下這是一個局限。DEL 不是唯一的阻塞式命令,卻是特殊的一個命令,因為我們認為:Redis 非常快,只要你用復雜度為 O(1) 和 O(log_N) 的命令。你可以自由使用 O(N) 的命令,但是要知道這不是我們優化的用例,你需要做好延遲的準備。

這聽起來很合理,但是同時即便用快速操作創建的對象也需要被刪除。在這種情況下,Redis 會阻塞。

第一次嘗試

對于單線程服務器,為了讓操作不阻塞,最簡單的方式就是用增量的方式一點點來,而不是一下子把整個世界都搞定。例如,如果要釋放一個百萬級的對象,可以每一個毫秒釋放1000個元素,而不是在一個 for() 循環里一次性全做完。CPU 的耗時是差不多的,也許會稍微多一些,因為邏輯更多一些,但是從用戶來看延時更少一些。當然也許實際上并沒有每毫秒刪除1000個元素,這只是個例子。重點是如何避免秒級的阻塞。在 Redis 內部做了很多事情:最顯然易見的是 LRU 淘汰機制和 key 的過期,還有其他方面的,例如增量式的對 hash 表進行重排。

剛開始我們是這樣嘗試的:創建一個新的定時器函數,在里面實現淘汰機制。對象只是被添加到一個鏈表里,每次定時器調用的時候,會逐步的、增量式的去釋放。這需要一些小技巧,例如,那些用哈希表實現的對象,會使用 Redis 的 SCAN 命令里相同的機制去增量式的釋放:在字典里設置一個游標來遍歷和釋放元素。通過這種方式,在每次定時器調用的時候我們不需要釋放整個哈希表。在重新進入定時器函數時,游標可以告訴我們上次釋放到哪里了。

適配是困難的

你知道這里最困難的部分是哪里嗎?這次我們是在增量式的做一件很特別的事情:釋放內存。如果內存的釋放是增量式的,服務器的內容增長將會非常快,最后為了得到更少的延時,會消耗調無限的內存。這很糟,想象一下,有下面的操作:

WHILE 1
    SADD myset element1 element2 … many many many elements
    DEL myset
END

如果慢慢的在后臺去刪除myset,同時SADD調用又在不斷的添加大量的元素,內存使用量將會一直增長。

好在經過一段嘗試之后,我找到一種可以工作的很好的方式。定時器函數里使用了兩個想法來適應內存的壓力:

1.檢測內存趨勢:增加還是減少?以決定釋放的力度。

2.同時適配定時器的頻率,避免在只有很少需要釋放的時候去浪費CPU,不用頻繁的去中斷事件循環。當確實需要的時候,定時器也可以達到大約300HZ的頻率。
這里有一小段代碼,不過這個想法現在已經不再實現了:

/計算內存趨勢,只要是上次和這次內存都在增加,就傾向于認為內存趨勢
 是增加的 */

if (prev_mem lt; mem) mem_trend = 1;
   
mem_trend *= 0.9; /* 逐漸衰減 */
   
int mem_is_raising = mem_trend gt; .1;

   
/* 釋放一些元素 */
   
size_t workdone = lazyfreeStep(LAZYFREE_STEP_SLOW);

   
/* 根據現有狀態調整定時器頻率 */
   
if (workdone) {
       
    if (timer_period == 1000) timer_period = 20;

    if (mem_is_raising timer_period gt; 3)
           
        timer_period--; /* Raise call frequency. */
       
    else if (!mem_is_raising timer_period lt; 20)

        timer_period++; /* Lower call frequency. */
   
} else {
       
  timer_period = 1000;    /* 1 HZ */
   
}

這是一個小技巧,工作的也很好。不過郁悶的是我們還是不得不在單線程里執行。要做好需要有很多的邏輯,而且當延遲釋放(lazy free)周期很繁忙的時候,每秒能完成的操作會降到平時的65%左右。
如果是在另一個線程去釋放對象,那就簡單多了:如果有一個線程只做釋放操作的話,釋放總是要比在數據集里添加數據來的要快。

當然,主線程和延遲釋放線程直接對內存分配器的使用肯定會有競爭,不過 Redis 在內存分配上只用到一小部分時間,更多的時間用在I/O、命令分發、緩存失敗等等。
不過,要實現線程化的延遲釋放有一個大問題,那就是 Redis 自身。內部實現完全是追求對象的共享,最終都是些引用計數。干嘛不盡可能的共享呢?這樣可以節省內存和時間。例如:SUNIONSTORE 命令最后得到的是目標集合的共享對象。類似的,客戶端的輸出緩存包含了作為返回結果發送給socket的對象的列表,于是在類似 SMEMBERS 這樣的命令調用之后,集合的所有成員都有可能最終在輸出緩存里被共享。看上去對象共享是那么有效、漂亮、精彩,還特別酷。

但是,嘿,還需要再多說一句的是,如果在 SUNIONSTORE 命令之后重新加載了數據庫,對象都取消了共享,內存也會突然回復到最初的狀態。這可不太妙。接下來我們發送應答請求給客戶端,會怎么樣?當對象比較小時,我們實際上是把它們拼接成線性的緩存,要不然進行多次 write() 調用效率是不高的!(友情提示,writev() 并沒有幫助)。于是我們大部分情況下是已經復制了數據。對于編程來說,沒有用的東西卻存在,通常意味著是有問題的。
事實上,訪問一個包含聚合類型數據的key,需要經過下面這些遍歷過程:

key -gt; value_obj -gt; hash table -gt; robj -gt; sds_string

如果去掉整個 tobj 結構體,把聚合類型轉換成 SDS 字符串類型的哈希表(或者跳表)會怎么樣?(SDS是Redis內部使用的字符串類型)。

這樣做有個問題,假設有個命令:SADD myset myvalue,舉個例子來說,我們做不到通過client->argv[2] 來引用用來實現集合的哈希表的某個元素。我們不得不很多次的把值復制出來,即使數據已經在客戶端命令解析后創建的參數 vector 里,也沒辦法去復用。Redis的性能受控于緩存失效,我們也許可以用稍微間接一些的辦法來彌補一下。
于是我在這個 lazyfree 的分支上開始了一項工作,并且在 Twitter 上聊了一下,但是沒有公布上下文的細節,結果所有的人都覺得我像是絕望或者瘋狂了(甚至有人喊道 lazyfree 到底是什么玩意)。那么,我到底做了什么呢?

把客戶端的輸出緩存由 robj 結構體改成動態字符串。在創建 reply 的時候總是復制值的內容。
把所有的 Redis 數據類型轉換成 SDS 字符串,而不是使用共享對象結構。聽上去很簡單?實際上這花費了數周的時間,涉及到大約800行高風險的代碼修改。不過現在全都測試通過了。
把 lazyfree 重寫成線程化的。
結果是 Redis 現在在內存使用上更加高效,因為在數據結構的實現上不再使用 robj 結構體(不過由于某些代碼還涉及到大量的共享,所以 robj 依然存在,例如在命令分發和復制部分)。線程化的延遲釋放工作的很好,比增量的方式更能減少內存的使用,雖然增量方式在實現上與線程化的方式相似,并且也沒那么糟糕。現在,你可以刪除一個巨大的 key,性能損失可以忽略不計,這非常有用。不過,最有趣的事情是,在我測過的一些操作上,Redis 現在都要更快一些。消除間接引用(Less indirection)最后勝出,即使在不相關的一些測試上也更快一些,還是因為客戶端的輸出緩存現在更加簡單和高效。

最后我把增量式的延遲釋放實現從分支里刪除,只保留了線程化的實現。

關于 API 的一點備注

不過 API 又怎么樣了呢?DEL 命令仍然是阻塞的,默認還跟以前一樣,因為在 Redis 中 DEL 命令就意味著釋放內存,我并不打算改變這一點。所以現在你可以用新的命令 UNLINK,這個命令更清晰的表明了數據的狀態。

UNLINK 是一個聰明的命令:它會計算釋放對象的開銷,如果開銷很小,就會直接按 DEL 做的那樣立即釋放對象,否則對象會被放到后臺隊列里進行處理。除此之外,這兩個命令在語義上是相同的。

我們也實現了 FLUSHALL/FLUSHDB 的非阻塞版本,不過沒有新增的 API,而是增加了一個 LAZY 選項,說明是否更改命令的行為。

不只是延遲釋放

現在聚合數據類型的值都不再共享了,客戶端的輸出緩存也不再包含共享對象了,這一點有很多文章可做。例如,現在終于可以在 Redis 里實現線程化的 I/O,從而不同的客戶端可以由不同的線程去服務。也就是說,只有訪問數據庫才需要全局的鎖,客戶端的讀寫系統調用,甚至是客戶端發送的命令的解析,都可以在線程中去處理。這跟 memcached 的設計理念類似,我比較期待能夠被實現和測試。

還有,現在也可以在其他線程實現針對聚合數據類型的特定的慢操作,可以讓某些 key 被“阻塞”,但是所有其他的客戶端不會被阻塞。這個可以用很類似現在的阻塞操作的方式去完成(參考blocking.c),只是增加一個哈希表保存那些正在處理的 key 和對應的客戶端。于是一個客戶端請求類似 SMEMBERS 這樣的命令,可能只是僅僅阻塞住這一個 key,然后會創建輸出緩存處理數據,之后在釋放這個 key。只有那些嘗試訪問相同的 key 的客戶端,才會在這個 key 被阻塞的時候被阻塞住。
所有這些需求起了更激烈的內部變化,但這里的底線我們已很少顧忌。我們可以補償對象復制時間來減少高速緩存的缺失,以更小的內存占用聚合數據類型,所以我們現在可依照線程化的 Redis 來進行無共享化設計,這一設計,可以很容易超越我們的單線程。在過去,一個線程化的 Redis 看起來總像是一個壞主意,因為為了實現并發訪問數據結構和對象其必定是一組互斥鎖,但幸運的是還有別的選擇獲得這兩個環境的優勢。如果我們想要,我們依然可以選擇快速操作服務,就像我們過去在主線程所做的那樣。這包含在復雜的代價之上,獲取執行智能(performance-wise)。

計劃表

我在內部增加了很多東西,明天就上線看上去是不現實的。我的計劃是先讓3.2版(已經是unstable狀態)成為候選版本(RC)狀態,然后把我們的分支合并到進入unstable的3.4版本。

不過在合并之前,需要對速度做細致的回歸測試,這有不少工作要做。

如果你現在就想嘗試的話,可以從Github上下載lazyfree分支。不過要注意的是,當前我并不是很頻繁的更新這個分支,所以有些地方可能會不能工作。

您可能感興趣的文章:
  • 超強、超詳細Redis數據庫入門教程
  • redis常用命令、常見錯誤、配置技巧等分享
  • Redis操作命令總結
  • Redis中5種數據結構的使用場景介紹
  • 30個php操作redis常用方法代碼例子
  • 64位Windows下安裝Redis教程
  • Linux下Redis的安裝和部署

標簽:廣東 澳門 唐山 贛州 香港 景德鎮 林芝 揚州

巨人網絡通訊聲明:本文標題《為啥懶 Redis 是更好的 Redis》,本文關鍵詞  為啥,懶,Redis,是,更,好的,;如發現本文內容存在版權問題,煩請提供相關信息告之我們,我們將及時溝通與處理。本站內容系統采集于網絡,涉及言論、版權與本站無關。
  • 相關文章
  • 下面列出與本文章《為啥懶 Redis 是更好的 Redis》相關的同類信息!
  • 本頁收集關于為啥懶 Redis 是更好的 Redis的相關信息資訊供網民參考!
  • 推薦文章
    欧美阿v视频在线大全_亚洲欧美中文日韩V在线观看_www性欧美日韩欧美91_亚洲欧美日韩久久精品
  • <rt id="w000q"><acronym id="w000q"></acronym></rt>
  • <abbr id="w000q"></abbr>
    <rt id="w000q"></rt>
    国产ts丝袜人妖系列视频| 欧美久久久久中文字幕| 久久一区二区三区四区| 午夜视频在线观看一区| 午夜视频在线免费看| 色系网站成人免费| 中文字幕在线不卡视频| 福利电影一区二区| 午夜精品福利在线视频| 中文欧美字幕免费| 成人午夜在线视频| 美女的奶胸大爽爽大片| 最新成人av在线| 99久久er热在这里只有精品15| 91精品少妇一区二区三区蜜桃臀| 国产视频视频一区| 国产精品一区专区| 国产免费美女视频| 国产精品素人一区二区| 国产99精品视频| 一本大道久久精品懂色aⅴ| 综合久久综合久久| 91香蕉视频在线| 欧美日韩三级一区| 日韩av一级电影| 国产真实乱人偷精品人妻| 久久美女高清视频 | 欧美日韩一级视频| 亚洲国产成人porn| 国产熟妇搡bbbb搡bbbb| www欧美成人18+| 国产精品一区在线观看你懂的| 战狼4完整免费观看在线播放版| 欧美激情中文字幕| 不卡的av中国片| 欧美日韩久久久久久| 视频一区在线播放| 69精品无码成人久久久久久| 国产精品色一区二区三区| 不卡电影一区二区三区| 欧美日韩综合不卡| 日本亚洲天堂网| 992在线观看| 亚洲精品自拍动漫在线| 国产真实乱人偷精品| 欧美大片免费久久精品三p| 国产在线日韩欧美| 色婷婷综合久色| 日本人妖一区二区| 黑人狂躁日本娇小| 一二三四社区欧美黄| 欧洲一级黄色片| 欧美精彩视频一区二区三区| 日本女人黄色片| 日韩欧美亚洲一区二区| 国产成人亚洲综合色影视| 欧美性三三影院| 久久99在线观看| 色婷婷av一区| 免费欧美日韩国产三级电影| 99国产精品久久久久久久久久| 欧美精品aⅴ在线视频| 激情文学综合插| 欧美午夜精品理论片a级按摩| 日韩二区在线观看| 四虎影院中文字幕| 午夜精品久久久久久不卡8050| 久久视频精品在线观看| 亚洲久本草在线中文字幕| 国产sm调教视频| 亚洲综合在线观看视频| 摸摸摸bbb毛毛毛片| 一区二区三区国产| 日本va欧美va欧美va精品| 精品一区二区在线观看视频| 亚洲一区在线播放| 永久av免费网站| 日韩av中文字幕一区二区| 国产盗摄x88av| 久久精品国产99久久6| 欧美亚洲一区三区| 国产伦精品一区二区三区视频青涩| 欧美日韩一区在线| 国产盗摄一区二区| 欧美变态tickling挠脚心| 99免费精品视频| 久久精品夜夜夜夜久久| 五月天丁香社区| 中文字幕中文字幕一区二区| 在线观看福利片| 午夜日韩在线电影| 日本精品视频一区二区三区| 免费日本视频一区| 538在线一区二区精品国产| 成人精品国产福利| 久久久亚洲精华液精华液精华液| 性猛交╳xxx乱大交| 国产精品久线在线观看| 欧美亚洲色综久久精品国产| 石原莉奈一区二区三区在线观看| 色综合久久天天| 国产乱码精品一品二品| 日韩免费看的电影| 伦理片一区二区| 亚洲精品免费在线播放| www.97视频| 国产美女娇喘av呻吟久久| 日韩午夜激情电影| 稀缺呦国内精品呦| 亚洲综合激情网| 色综合久久精品| 成人综合激情网| 国产丝袜欧美中文另类| av网站免费在线看| 免费成人在线视频观看| 91精品国产美女浴室洗澡无遮挡| 91浏览器打开| 亚洲免费观看高清在线观看| 2025国产精品自拍| 国产成人精品一区二区三区四区| 亚洲精品一区二区三区精华液| 国产美女视频免费观看下载软件| 亚洲国产精品久久艾草纯爱| 欧美日本在线播放| 91蝌蚪porny| 亚洲综合小说图片| 欧美日韩aaa| 日本人妻一区二区三区| 一区二区三区电影在线播| 一本久道久久综合中文字幕| 风间由美中文字幕在线看视频国产欧美| 久久一夜天堂av一区二区三区| 亚洲黄色免费视频| 黄色资源网久久资源365| 久久久久久久久久久电影| 摸摸摸bbb毛毛毛片| 国产乱码一区二区三区| 久久久久久久久久久电影| 成人午夜免费影院| 国产成a人亚洲精| 综合婷婷亚洲小说| 欧美亚洲另类激情小说| 美女黄色一级视频| 日本大胆欧美人术艺术动态 | av在线播放成人| 亚洲精品高清在线观看| 欧美熟乱第一页| 人妻av一区二区| 奇米综合一区二区三区精品视频| 日韩欧美你懂的| 日韩免费成人av| 国产91高潮流白浆在线麻豆| 91黄色免费视频| 蜜臀久久99精品久久久久宅男 | 久久99精品视频| 国产亚洲精品aa午夜观看| 欧美肥妇bbwbbw| 99久久久久久| 亚洲成人av福利| 精品国产乱码久久久久久蜜臀| 日韩av片在线| 成人不卡免费av| 亚洲一区二区三区四区五区中文 | 国产精品毛片一区二区| 国产乱码字幕精品高清av | 国产一区二区三区四区在线| 国产91精品久久久久久久网曝门| 一区二区三区欧美在线观看| 欧美肥妇毛茸茸| 国产在线免费av| 99v久久综合狠狠综合久久| 午夜不卡在线视频| 久久婷婷一区二区三区| www青青草原| 亚洲少妇18p| 国产成人欧美日韩在线电影| 亚洲综合在线视频| www国产精品av| 色吧成人激情小说| 51调教丨国产调教视频| 福利一区福利二区| 丝袜亚洲另类欧美| 国产日产亚洲精品系列| 欧美日韩一区三区四区| av黄色在线免费观看| 91麻豆免费观看| 久久91精品国产91久久小草| 一区精品在线播放| 日韩欧美久久一区| 一本色道a无线码一区v| 中日韩精品一区二区三区| 成人av先锋影音| 日产精品久久久久久久性色| 国产精品久久99| 日韩欧美一二三区| 欧美影视一区二区三区| 亚洲av毛片基地| www.日本高清| 不卡免费追剧大全电视剧网站| 久久se这里有精品|