ARTS #64

Algorithm

146. LRU 緩存

使用雙向鏈表存儲先后順序

type LinkNode struct {
    key, value int
    Pre, Next  *LinkNode
}

type LRUCache struct {
    cacheMap    map[int]*LinkNode
    head, tail  *LinkNode
    maxSize     int
    currentSize int
}

func Constructor(capacity int) (lruCache LRUCache) {
    cacheMap := make(map[int]*LinkNode)
    head := LinkNode{}
    tail := LinkNode{}
    head.Next = &tail
    tail.Pre = &head
    lruCache = LRUCache{
        cacheMap:    cacheMap,
        head:        &head,
        tail:        &tail,
        maxSize:     capacity,
        currentSize: 0,
    }
    return
}

func (this *LRUCache) Get(key int) (result int) {
    linkNode, ok := this.cacheMap[key]
    if ok {
        result = linkNode.value
        // 刪除節(jié)點(diǎn)
        linkNode.Pre.Next = linkNode.Next
        linkNode.Next.Pre = linkNode.Pre
        // 挪到最前面
        linkNode.Next = this.head.Next
        this.head.Next.Pre = linkNode
        this.head.Next = linkNode
        linkNode.Pre = this.head
    }
    if !ok {
        result = -1
    }
    return
}

func (this *LRUCache) Put(key int, value int) {
    linkNode, ok := this.cacheMap[key]
    if ok {
        //  更新值
        linkNode.value = value
        // 刪除節(jié)點(diǎn)
        linkNode.Pre.Next = linkNode.Next
        linkNode.Next.Pre = linkNode.Pre
        // 挪到最前面
        linkNode.Next = this.head.Next
        this.head.Next.Pre = linkNode
        this.head.Next = linkNode
        linkNode.Pre = this.head
    }
    if !ok {
        linkNode = &LinkNode{
            value: value,
            key:   key,
        }
        if this.currentSize < this.maxSize {
            this.cacheMap[key] = linkNode
            // 挪到最前面
            linkNode.Next = this.head.Next
            this.head.Next.Pre = linkNode
            this.head.Next = linkNode
            linkNode.Pre = this.head
            this.currentSize += 1
        } else {
            lastNode := this.tail.Pre
            // 刪除map
            delete(this.cacheMap, lastNode.key)
            // 刪除節(jié)點(diǎn)
            lastNode.Pre.Next = lastNode.Next
            lastNode.Next.Pre = lastNode.Pre
            // 增加map
            this.cacheMap[key] = linkNode
            // 挪到最前面
            linkNode.Next = this.head.Next
            this.head.Next.Pre = linkNode
            this.head.Next = linkNode
            linkNode.Pre = this.head
        }
    }
}

Review

Understanding the Go Scheduler and discovering how it works
本篇文章作者帶著我們從頭實(shí)現(xiàn)了一個(gè)簡易版本的gmp調(diào)度器斜棚,淺顯易懂的向讀者解釋了為什么GMP這樣子設(shè)計(jì)。
首先實(shí)現(xiàn)一個(gè)協(xié)程調(diào)度器,有幾個(gè)比較重要的點(diǎn)或者需求:

  1. 協(xié)程需要時(shí)輕量級的,要比操作系統(tǒng)提供的線程更加輕量級。
  2. 協(xié)程要能夠處理system call和IO操作。
  3. 要能夠?qū)崿F(xiàn)真正的并行
  4. 協(xié)程的調(diào)度要足夠公平,避免同一個(gè)function一直搶占CPU時(shí)間片
  5. 最后是控制創(chuàng)建的協(xié)程數(shù)量印蔬,避免過多搶占操作系統(tǒng)資源。

為了實(shí)現(xiàn)上訴需求脱衙,我們可以一步一步迭代一個(gè)協(xié)程調(diào)度器侥猬。

  1. 首先最簡單的版本,我們實(shí)現(xiàn)的協(xié)程和線程之間為1:1的映射關(guān)系捐韩,也就是我們?yōu)槊總€(gè)協(xié)程創(chuàng)建一個(gè)線程退唠。這里顯而易見的,是不夠輕量的荤胁。需要進(jìn)一步迭代瞧预。
  2. 我們做一點(diǎn)改進(jìn),我們讓協(xié)程和線程之間改成M:N的映射關(guān)系。也就是我們前置性的創(chuàng)建M個(gè)線程垢油,然后用戶需要創(chuàng)建的N個(gè)協(xié)程任務(wù)每次都分配給這M個(gè)線程進(jìn)行處理盆驹。為了實(shí)現(xiàn)這個(gè)機(jī)制,我們引入global task queue秸苗,每次用戶創(chuàng)建的協(xié)程任務(wù)都push到queue里面召娜,并且每個(gè)線程都統(tǒng)一從這個(gè)queue中取任務(wù)去執(zhí)行运褪。因?yàn)槭嵌鄠€(gè)線程從一個(gè)global queue里面取任務(wù)惊楼,為了保證并發(fā)安全,我們需要對每次的取任務(wù)加上mutex鎖秸讹。
  3. 在上訴步驟里面檀咙,為了保障并發(fā)安全我們給global queue加上了鎖,隨著我們的push和pop queue的操作越來越頻繁璃诀,global queue的鎖成為我們的并發(fā)性能瓶頸弧可。為了解決這個(gè)問題,這時(shí)候我們引入了local task queue劣欢,也就是每個(gè)線程維護(hù)自己的local queue棕诵。每次線程都從自己的local queue里面取任務(wù)執(zhí)行,同時(shí)因?yàn)槊總€(gè)線程操作自己的queue也就不存在并發(fā)安全了凿将,不需要加鎖校套。
  4. 為了保障每個(gè)線程不會空轉(zhuǎn),我們還可以引入了Work Stealing的機(jī)制牧抵。也就是每個(gè)線程自己的queue如果空了笛匙,這時(shí)候就可以嘗試去其他線程的queue里面取任務(wù)進(jìn)行執(zhí)行。
  5. 上面實(shí)現(xiàn)的調(diào)度器有個(gè)比較大的問題就是無法處理system call或者IO function犀变,如果某個(gè)協(xié)程的任務(wù)是需要進(jìn)行IO讀寫的話妹孙,這時(shí)候?qū)?yīng)的線程就會處于sleep狀態(tài)(需要等內(nèi)存中斷處理結(jié)束)。為了解決這個(gè)問題获枝,引入了Processors的概念蠢正。具體來說就是我們現(xiàn)在每個(gè)local task queue對應(yīng)的核心處理單元不再是線程而是processor,每個(gè)processor可以對應(yīng)多個(gè)線程省店。這樣子我們調(diào)度器在遇到IO 協(xié)程任務(wù)的時(shí)候嚣崭,要做的就是在正式處理任務(wù)之前new 一個(gè)新的線程,然后老線程去處理IO操作并sleep萨西,而processor可以用新的線程繼續(xù)從local task queue中取任務(wù)并處理有鹿。

TIP

這周在工作過程使用到了bosun,并初步接觸了比較基礎(chǔ)的bosun語法谎脯。bosun queckstart

Share

學(xué)習(xí)mysql 45講

33 | 我查這么多數(shù)據(jù)葱跋, 會不會把數(shù)據(jù)庫內(nèi)存打爆?

全表掃描對server層的影響

在執(zhí)行select語句的時(shí)候,服務(wù)端并不需要保存一個(gè)完整的結(jié)果集娱俺,也就是說稍味, MySQL是“邊讀邊發(fā)的”。 取數(shù)據(jù)和發(fā)數(shù)據(jù)的流程是這樣的:

  1. 獲取一行荠卷, 寫到net_buffer中模庐。 這塊內(nèi)存的大小是由參數(shù)net_buffer_length定義的, 默認(rèn)是16k油宜。
  2. 重復(fù)獲取行掂碱, 直到net_buffer寫滿, 調(diào)用網(wǎng)絡(luò)接口發(fā)出去慎冤。
  3. 如果發(fā)送成功疼燥, 就清空net_buffer, 然后繼續(xù)取下一行蚁堤, 并寫入net_buffer醉者。
  4. 如果發(fā)送函數(shù)返回EAGAIN或WSAEWOULDBLOCK, 就表示本地網(wǎng)絡(luò)棧(socket send buffer) 寫滿了披诗, 進(jìn)入等待撬即。 直到網(wǎng)絡(luò)棧重新可寫, 再繼續(xù)發(fā)送

全表掃描對InnoDB的影響

WAL機(jī)制呈队, 當(dāng)事務(wù)提交的時(shí)候剥槐, 磁盤上的數(shù)據(jù)頁是舊的, 如果這時(shí)候馬上有一個(gè)查詢要來讀這個(gè)數(shù)據(jù)頁掂咒, 不需要馬上把redo log應(yīng)用到磁盤數(shù)據(jù)頁才沧,而是直接讀取Buffer Pool數(shù)據(jù)的內(nèi)容即可,因此Buffer Pool的緩存命中率越高對mysql查詢效率提升越大绍刮。
InnoDB管理Buffer Pool的LRU算法温圆, 是用雙向鏈表來實(shí)現(xiàn)的。并且InnoDB對LRU算法做了改進(jìn)孩革,具體改進(jìn)點(diǎn)為在InnoDB實(shí)現(xiàn)上岁歉, 按照5:3的比例把整個(gè)LRU鏈表分成了young區(qū)域和old區(qū)域。 LRU_old指向的就是old區(qū)域的第一個(gè)位置膝蜈, 是整個(gè)鏈表的5/8處锅移。 也就是說, 靠近鏈表頭部的5/8是young區(qū)
域饱搏, 靠近鏈表尾部的3/8是old區(qū)域非剃。
每次有新的數(shù)據(jù)都會先置到LRU_old節(jié)點(diǎn),同時(shí)old區(qū)域的數(shù)據(jù)如果存在超過1S沒有被淘汰的話推沸,就會移動到y(tǒng)oung區(qū)域的head節(jié)點(diǎn)备绽。
這個(gè)策略最大的收益券坞, 就是在掃描這個(gè)大表的過程中, 雖然也用到了Buffer Pool肺素, 但是對young區(qū)域完全沒有影響恨锚, 從而保證了Buffer Pool響應(yīng)正常業(yè)務(wù)的查詢命中率。


image.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末倍靡,一起剝皮案震驚了整個(gè)濱河市猴伶,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌塌西,老刑警劉巖他挎,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異雨让,居然都是意外死亡雇盖,警方通過查閱死者的電腦和手機(jī)忿等,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進(jìn)店門栖忠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人贸街,你說我怎么就攤上這事庵寞。” “怎么了薛匪?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵捐川,是天一觀的道長。 經(jīng)常有香客問我逸尖,道長古沥,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任娇跟,我火速辦了婚禮岩齿,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘苞俘。我一直安慰自己盹沈,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布吃谣。 她就那樣靜靜地躺著乞封,像睡著了一般。 火紅的嫁衣襯著肌膚如雪岗憋。 梳的紋絲不亂的頭發(fā)上肃晚,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天,我揣著相機(jī)與錄音仔戈,去河邊找鬼关串。 笑死惋鸥,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的悍缠。 我是一名探鬼主播卦绣,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼飞蚓!你這毒婦竟也來了滤港?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤趴拧,失蹤者是張志新(化名)和其女友劉穎溅漾,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體著榴,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡添履,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了脑又。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片暮胧。...
    茶點(diǎn)故事閱讀 39,834評論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖问麸,靈堂內(nèi)的尸體忽然破棺而出往衷,到底是詐尸還是另有隱情,我是刑警寧澤严卖,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布席舍,位于F島的核電站,受9級特大地震影響哮笆,放射性物質(zhì)發(fā)生泄漏来颤。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一稠肘、第九天 我趴在偏房一處隱蔽的房頂上張望福铅。 院中可真熱鬧,春花似錦启具、人聲如沸本讥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽拷沸。三九已至,卻和暖如春薯演,著一層夾襖步出監(jiān)牢的瞬間撞芍,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工跨扮, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留序无,地道東北人验毡。 一個(gè)月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像帝嗡,于是被迫代替她去往敵國和親晶通。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 從三月份找實(shí)習(xí)到現(xiàn)在哟玷,面了一些公司狮辽,掛了不少,但最終還是拿到小米巢寡、百度喉脖、阿里、京東抑月、新浪树叽、CVTE、樂視家的研發(fā)崗...
    時(shí)芥藍(lán)閱讀 42,246評論 11 349
  • go并發(fā)編程入門到放棄 并發(fā)和并行 并發(fā):一個(gè)處理器同時(shí)處理多個(gè)任務(wù)谦絮。 并行:多個(gè)處理器或者是多核的處理器同時(shí)處理...
    yangyunfeng閱讀 565評論 0 2
  • 定位項(xiàng)目中题诵,如何選取定位方案,如何平衡耗電與實(shí)時(shí)位置的精度挨稿? 重要 開始定位仇轻,Application 持有一個(gè)全局...
    賢瑜閱讀 452評論 0 0
  • *面試心聲:其實(shí)這些題本人都沒怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個(gè)offer,總結(jié)起來就是把...
    Dove_iOS閱讀 27,140評論 30 470
  • 1,java堆祭椰,分新生代老年代臭家,新生代有Eden,from surviver方淤,to surviver三個(gè)空間钉赁,堆被...