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


<<:  Azure Database for MySQL 手把手基础教学

>>:  伸缩自如的Flask [day 26] Flask with ML

爬虫怎麽爬 从零开始的爬虫自学 DAY25 python网路爬虫开爬6-资料储存

前言 各位早安,书接上回我们将程序码改得更方便阅读,还加上抓取连结的功能,今天我们要来把这些抓到的资...

Day 07 line bot sdk python范例程序在做什麽

知道了line bot sdk python上的程序的功能是回复你和你传送相同的讯息。这边会看成是在...

Day 30 「无心之心,道之所存」结语

Christopher Alexander 在「建筑的永恒之道」中,开头第一句就说了:「无心之心,道...

#13 Automation

自动化 (Automation) 指的是写出一个程序,让原本需要人工手动执行的多个步骤变成全部都由程...

电子书阅读器上的浏览器 [Day18] 支援夜间模式

夜间模式在电子书阅读器上通常效果不会很好,黑色的底色会造成很多残影。不过,因为我也会在一般手机上使用...