iT邦幫忙

2

Golang-sync.Map 高併發的神之好用方法

最近收到了一個需求
需要不斷的在一個data pool隨機找到資料後,給前端顯示新value
剛開始的做法是把資料全部存在DB內
然後再根據flag做where,之後再更新flag

但如此一來
DB雖說沒被吃垮,但假設資料量持續增長,DB鐵定會因為頻繁的update給搞到爆掉
而且從目前評估起來,DB一分鐘內高峰時會做到800-1000次update
聽起來就不太妙XD

於是只把資料Load一次,改用本地cache去做操作
但也出現了一個問題
資料有n筆,我在本地也要做O(n)次搜尋,然後再去改flag
最棒的是這個handler有複數個routing在執行,在加上API也會操作到
從一般的Lock改用RWLock,API還是一樣會hang在那邊到timeout

但直到最後我想到了sync.Map

  1. 沒有race condtion
  2. key-value的資料結構,只要O(1)就能解決問題,還能簡化資料欄位
  3. map本身就是無順序結構,還能來拿當隨機使用
  4. 然後只要用一個sync.Map當作cache pool,資料失效就丟回來pool,資料生效就從pool刪除

一開始的做法

func SelectDataBaseData(){}

func HandleRandomPickData(){
    // Do random pick data from SelectDataBaseData()
    for index:=range afterPickData{
        UpdateDataStatus()
    }
}

func HandleTimeOutData(){
    for index:=range afterPickData{
        UpdateDataStatus()
    }
}

func UpdateDataStatus(){
    // Do data update status to database
}

二次做法

var datas []DataStructure{}
var mux sync.RWMutex

func InitializeCacheData(){
    selectData:=SelectDataBaseData()
    for _,data:=range selectData{
        datas = append(datas,data)
    }
}

func HandleRandomPickData(){
    // Do random pick data from datas
    
    for i:=range afterPickData{
        for j:=range datas{
            if datas[j].key == afterPickData[i].key{
                mux.Lock()
                datas[j].status = 1
                mux.Unlock
            }
        }
    }
}

func HandleTimeOutData(){
    // Do random and pick data
    
    for i:=range afterPickData{
        for j:=range datas{
            if datas[j].key == afterPickData[i].key{
                mux.Lock()
                datas[j].status = 0
                mux.Unlock
            }
        }
    }
}

改良版

var mp sync.Map{}

func InitializeCacheData(){
    datas:=SelectDataBaseData()
    for _,data:=range datas {
        mp.Store(data.key,data.value)
    }
}

func HandleRandomPickData(){
    count:= 0
    mp.Range(func(key interface{}, value interface{}) bool {
        if count == 10 {
            return false
        }
        
        PickKey:=key.(T)
        PickValue:=value.(T)
        
        mp.Delete(key.(T))
        count++
        return true
    })
}

func HandleTimeOutData(){
    data:=GetTimeOutValue()
    for index:=ramge data{
        mp.Store(data[i].Key,data[i].Value)
    }
}

總結

在閒閒沒事的時候總是會靈光一閃
想到一個做pool的辦法直接解決所有問題
還能利用資料結構的特性帶來時間上的優勢
加上package還有改良過對race condition的影響
只有最好用沒有更好用XD


尚未有邦友留言

立即登入留言