范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

快速构建Go语言KVCache

  缓存是几乎所有程序或产品的必需品,我们在每个角落都能看到它,比如网页、数据库、应用程序等。
  它的意义在于它所带来的效率。 这就像在你忙于燃烧卡路里的问题时,把你最喜欢的零食放在桌子上,让你快速拿一下。
  熟悉和可快速构建一个本地kv缓存对日常开发很重要,所以我将从头开始实现一个健全且可用的本地 kv 缓存。KV-Cache 的基本元素
  首先是实现键值缓存时应考虑的因素:贮存
  一般最常用的数据结构是map[string]Element{},其中string为key,其中element包含value信息。元素
  最简单的元素至少应该包含值和过期时间。并且值类型通常是interface{},可以根据场景换成string、int或者其他特定类型。并发
  缓存必须考虑并发访问,除非它是特定于线程的映射。和其他高级语言一样,Go 也提供了一个线程安全的映射(sync.map)。
  当然,将 map 与 sync.RWMutex 组合起来可以实现相同的功能,提供更大的灵活性并帮助更好地理解 Go。容量
  如果要在 Kubernetes Pod 中运行,则需要提前设置缓存大小。
  否则,它可能会消耗过多的内存,并导致整个 pod 因内存限制而被淘汰。删除过期的
  及时释放过期的key可以提高缓存利用率,节省开销,提升性能。GC 调优
  它与 Go 相关,减少了 GC 对缓存的影响。而sync.Pool可以用来进一步优化。API 设计
  Cache有Put、Get、Remove和Flush 4个基本功能,并且开放给更多的方法以获得更好的支持。缓存实现
  考虑到这些元素,我们现在要设计缓存。
  首先是两个简单的步骤。1)定义所有类型和功能。type Cache struct {      defaultExpiration time.Duration      elements map[string]Elem      capacity int64      size int64      lock *sync.RWMutex      pool *sync.Pool      cleaner *cleaner }   type Elem struct {     K string // Needed for the sync.Pool     V interface{}     Expiration int64     LastHit int64 }   type cleaner struct {     Interval time.Duration     stop chan bool }   func (c *Cache) Get(k string) (v interface{}, err error) {     return nil, nil }   func (c *Cache) Put(k string, v interface{}) error {     return nil, nil }   func (c *Cache) Remove(k string) (isFound bool, err error) {     return false, nil }   func (c *Cache) Flush() error {    return nil }   // Used in cleaning job func (c *Cache) RemoveExpired() {}   // Run cleaning job func (cl *cleaner) Run(c *Cache) {}定义默认参数
  在 New 函数中使用它们。const (         DEFAULT_EXPIRATION = 10 * time.Minute         DEFAULT_CLEAN_DURATION = 10 * time.Minute         DEFAULT_CAP = 1024 )   func NewCache() (*Cache, error) {       return newCache(DEFAULT_CAP, DEFAULT_EXPIRATION, DEFAULT_CLEAN_DURATION) }   func newCache(cap int64, expiration time.Duration, clean_duration time.Duration) (*Cache, error) {     return &Cache{             defaultExpiration: expiration,             elements: make(map[string]Elem, cap),             capacity: cap,             lock: new(sync.RWMutex),             cleaner: &cleaner{             Interval: clean_duration,             stop: make(chan bool),         },         pool: &sync.Pool{             New: func() interface{} {                 return &Entry{}             },         },     }, nil }
  然后是 put 方法。 在为map设置key和val之前,需要做一些额外的工作。获取并设置密钥的过期时间。获取和设置密钥的最后访问时间。
  在缓存已满,但无法删除过期key为新key腾出空间的场景下,我们需要引入一些额外的淘汰策略。
  LRU,最近最少用户,就是我们在这种情况下应用的:最近没有被访问过的键将被淘汰。 但是,需要注意的是,它对历史数据并不友好。
  还有一些其他的淘汰政策,例如,
  LFU,最不常用的,淘汰了低频率访问的键。 潜在的问题是,如果密钥在短时间内被频繁访问,它们将在缓存中保留很长时间,尽管以后不会访问。
  FIFO,先进先出,最新的保留。 其缺点与 LFU 相同。
  ARC,自适应替换缓存。 作为 LRU 的高级版本,它通过与 LFU 和 LRU 集成,4 个队列,消耗更多内存来实现更高性能的缓存。
  此外,还有其他算法,如双队列、MRU 等。
  最后,我们来看看sync.Pool。 只有在立即使用存储的对象时才需要选择加入功能。 否则,池中的对象将在 Get 中起作用之前被频繁替换。 但这是我们未来需要改进缓存的功能。
  一步一步,Put方法就搞定了。func (c *Cache) Put(k string, v interface{}) error {     expire := time.Now().Add(DEFAULT_EXPIRATION).UnixNano()     lastHit := time.Now().UnixNano()     if c.size+1 > c.capacity {         // LRU kicks in         if err := c.removeLeastVisited(); err != nil {             return err         }     }     c.lock.Lock()     defer c.lock.Unlock()       if ele, ok := c.elements[k]; ok {         ele.V = v         ele.Expiration = expire         ele.LastHit = lastHit         return nil     }       ele := Elem{         V:          v,         Expiration: expire,         LastHit:    lastHit,     }     c.pool.Put(ele)     c.elements[k] = ele     c.size = c.size + 1     return nil }
  然后你会发现Get the function已经在你的口袋里了:只需从pool和map中获取结果,并更新最后访问时间和过期时间。 func (c *Cache) Get(k string) (v interface{}, err error) {     ele := c.pool.Get()     if item, ok := ele.(Elem); ok {         if item.K == k {             return item.V, nil         }     }     expire := time.Now().Add(DEFAULT_EXPIRATION).UnixNano()     lastHit := time.Now().UnixNano()     c.lock.RLock()     defer c.lock.RUnlock()     if ele, ok := c.elements[k]; ok {         ele.Expiration = expire         ele.LastHit = lastHit         return ele.V, nil     }     return nil, nil }
  接下来要做的就是构建一个自动清理器,通过启动一个 goroutine 定期清理过期时间小于当前时间的键。// Used in cleaning job func (c *Cache) RemoveExpired() {     now := time.Now().UnixNano()     c.lock.Lock()     defer c.lock.Unlock()     for k, v := range c.elements {         if v.Expiration > 0 && now > v.Expiration {             _, _ = c.Remove(k)         }     } }   // Run cleaning job func (cl *cleaner) Run(c *Cache) {     ticker := time.NewTicker(cl.Interval)     for {         select {         case <-ticker.C:             c.RemoveExpired()         case <-cl.stop:             ticker.Stop()             return         }     } }   func stopJanitor(c *Cache) {     c.cleaner.stop <- true }
  同时,将以下两行添加到前面的 New 方法中。 go c.cleaner.Run(c) runtime.SetFinalizer(c, stopCleaner)
  至此,缓存功能基本完成,下面我们测试下性能。性能比较
  迫不及待的想把我们自己搭建的缓存和Github上那些完美的缓存做个性能对比,我以go-cache和hashicorp-lrucache为例,写了一个benchmark测试,对比一下访问效率。package golang_localcache   import (     "fmt"     "testing"     "time"       hashicorp "github.com/hashicorp/golang-lru"     gocache "github.com/patrickmn/go-cache" )   func BenchmarkGoCache(b *testing.B) {     c := gocache.New(1*time.Minute, 5*time.Minute)       b.Run("Put", func(b *testing.B) {         for i := 0; i < b.N; i++ {             c.Add(toKey(i), toKey(i), gocache.DefaultExpiration)         }     })       b.Run("Get", func(b *testing.B) {         for i := 0; i < b.N; i++ {             value, found := c.Get(toKey(i))             if found {                 _ = value             }         }     }) }   func BenchmarkCache(b *testing.B) {     c, _ := NewCache()     b.Run("Put", func(b *testing.B) {         for i := 0; i < b.N; i++ {             c.Put(toKey(i), toKey(i))         }     })       b.Run("Get", func(b *testing.B) {         for i := 0; i < b.N; i++ {               value, _ := c.Get(toKey(i))             if value != nil {                 _ = value             }         }     }) }   func BenchmarkHashicorpLRU(b *testing.B) {     // c := cache2go.Cache("test")     c, _ := hashicorp.New(1024)       b.Run("Put", func(b *testing.B) {         for i := 0; i < b.N; i++ {             c.Add(toKey(i), toKey(i))         }     })       b.Run("Get", func(b *testing.B) {         for i := 0; i < b.N; i++ {               value, err := c.Get(toKey(i))             if err == true {                 _ = value             }         }     }) }   func toKey(i int) string {     return fmt.Sprintf("item:%d", i) }
  结果符合我的预期。 我们的缓存比那些成熟的开源缓存慢。 但是,当它只花费我们 20 分钟时,我们还能期待什么。
  结果给了我一个提示,hashicorp-cache 这么快一定是有原因的,以后我们可以在单独讲一下!改进方法
  我们的缓存速度较慢,但 我们可以做一些事情来加快速度。那怎么办?
  最重要的因素是并发和缓存大小,两者相互影响:并发越大,元素越多,内存占用越大,缓存越慢。因此,减少并发是第一要务。
  这里我们需要一种着色方法,将一个缓存映射拆分为多个,以降低并发可能性并缩小每个缓存的大小。毫无疑问,散列最常用于阴影,因为哈希结果具有高离散率,即高随机性。
  Hash避免产生过多的内存分配,缓解垃圾回收带来的压力。哈希算法非常高效。
  可以很容易地得出结论,哈希方法的速度决定了着色算法的效率,因为密钥是通过 hash(key) 分配给不同的缓存的。
  那么问题就在于选择哪种算法。 MD5 和 SHA-256 是最常见的哈希算法,FNV 和 DJB2 各有优势。如果您在这些选项上苦苦挣扎,请进行基准比较。
  此外,添加更多方法,如直接访问字符串和直接存储 int,或优化 sync.Pool 使用也是改进缓存的方法。结束
  互联网世界有很多的开源缓存,这使得我们免于自己编写,而且效率更高。但是,当你更了解其中实现原理的时候,开发起来会更加的高效。

千元买高品质手机?OPPOA系列新机为最优解,六大亮点爱不释手随着各大手机品牌对中端机市场的重视,近几年有不少品牌开始在此价位段发力,在产品注入了许多不俗的亮点以求吸引消费者的目光。例如国产手机品牌OPPO近几年的A系列,正是因为在各方面都拥三星第三代折叠手机明天开售,从耐用性实用性看是否值得入手2019年,三星发布了第一代折叠手机GalaxyFold,开启了手机产品新形态。如今,三星折叠手机已经进化至第三代。一周多前,三星正式发布了GalaxyZFold3和Flip3两款租号玩网游曝光腾讯网易等被约谈来源北京商报8月30日,国家新闻出版署印发通知,要求所有游戏企业仅可在周五周六周日和法定节假日每日的20时至21时向未成年人提供1小时服务。但近期,未成年人通过租号买号等途径绕过监央媒怒批未成年采取租号形式玩腾讯游戏,腾讯的回复有点敷衍只要思想不滑坡,办法总比困难多。世人皆知,腾讯游戏在国内拥有极大的影响力,腾讯游戏在为腾讯带来极大的财力之时,也为腾讯公司带来了一系列负面影响。前段时间,网络游戏的新规出台,仅可在给鸿蒙系统公平竞争环境,要求中国在售手机一半搭载鸿蒙系统可否?不用强求友商安装鸿蒙,只需把安卓,苹果列入危害国家安全的黑名单即可。本人已经使用鸿蒙系统半个月了,非常好用,也很漂亮。作为中国人必须支持什么叫公平,西方国家所谓的民主自由,不都是为针对新闻摄影,尼康与佳能相比,哪个更好?很多年前,在单位做宣传干事的时候,办公室里我和我的对桌,也就是我们两个专门的摄影记者,一个用尼康,一个佳能,我就是那个用尼康的。离开后,我的另一位同事接替了我的工作,今年上半年回去小米的主管都称呼消费者为ds,为什么还有这么多人买小米呢?首先,做为一个小米手机用户我并不认同ds这个称呼,小米主管这个称呼多数情况下也是自嘲吧,如果真的这么骂自己的用户,怕不是得了神经病,那得有多狂。其次,小米有很多自己的优势,创新能力被严重低估的三款旗舰手机,亮点众多颜值高,只怪太多人不了解懂手机的人都知道,选手机最好一步到位,本文介绍的三款旗舰机,实力被严重低估,但自身配置颜值兼备,亮点数不胜数,只怪太多人不了解!一vivoX60Pro拍照实力顶级的高端影像旗舰机优2000元以下的性价比之王手机性价比高的手机的手机确实很多,但2000元以下的手机论性价比只能是这款手机真我GtNeo。这款手机搭载天玑1200处理器,跑分更是达到了70分。在性能方面,2000元以下的手机无人发布在即,小米11Lite5GNE通过NBTC认证ITHouseMessage今年3月9日,Xiaomi11青年版在中国发布。根据MySmartPrice的说法,此模型的国际版本在印度推出。目前,NBTC认证网站拥有Xiaomi5有什么免费视频格式转换软件?格式工程,下载这个软件不仅能免费转视频格式还能转图片和音频的格式,具体怎么操作可以看老李之前录的这个视频,希望能帮到你。httpswww。ixigua。comi6809955101
20152020,中台的那些开山者们关注88号实验室,看中台小百科中台,从提出至今就一直备受瞩目。有人信奉它,认为它能解决关于企业数字化的一切有人质疑它,认为它是一个看上去很美的玄学概念。但不管支持也好,反对也罢,中术业有专攻!论自拍效果如何,还得看这三款机型如今,用手机自拍和记录生活的小伙伴们越来越多,尤其对于女孩子们来说,不管在什么时候都有可能随手自拍一张。也正是因为如此,各大手机厂商在手机的前置相机方面也是纷纷发力,不仅对硬件进行(图文视频)教程两种方法拍摄盗梦空间效果,大疆OM4轻松搞定大疆云台(DJIOM4)华为手机(HUAWEIP30pro),轻松拍摄盗梦空间效果,本人总结出了两种拍摄方法,接下来会详细解说拍摄过程。第一种方法利用大疆云台(DJIOM4)旋转拍图文教程用大疆云台OM4运镜拍摄,后期实现变速转场效果用大疆云台(DJIOM4)简单运镜拍摄,后期在剪辑软件中实现变速转场效果,接下来介绍拍摄和剪辑的详细过程。(一)第1种方法相同运镜方向,后期用曲线变速功能实现变速转场效果。利用大疆小米有品推出性价比之王,仅179元媲美德国大牌,米粉太狠了为什么我每天坚持早晚刷牙,还是有牙菌斑呢,而且牙齿还黄黄的,看着就糟心,为此,我还尝试了在网上比较火的巴氏刷牙法,然而我尝试了一次就放弃了。将牙刷与牙长轴呈45度角指向根尖,使刷毛财税行业如何有效获客,凭实力跑赢同行,不妨试试这几招税务行业发展迅速,其中代理记账公司特别引人注意。此外,伴随着廉价代理记账大范畴发生,税务销售市场受到损伤,也意味着税务领域营销愈来愈难,各家公司可以获取的顾客也将越来越低。以前税务从事电商行业怎样获客,来看看这几点,绝对让你受益如今电子商务行业借助互联网技术的流量而生,但通过20年的流量争夺,互联网技术的流量红利早已见顶。应对传统电商推广费用高总流量成本增加的短板,提升传统式电子商务模式,完成盈利开创性提SoundPEATSsonic质感不输千元的TWS随着手机工艺的进步,如今越来越多的手机厂商都取消了自家手机上的3。5mm耳机接口,取而代之的则是无线蓝牙耳机。也正因为如此,近些年来越来越多的手机厂商或者配件厂商都开始推出了自家的看奇书中台实践详解中台建设方法论2020年,疫情重创各行各业新基建赛道开启国家推动数字化转型和产业升级的浪潮奔涌而来。中台作为数字经济领域的新基建被越来越多企业所重视,并以其颠覆性的技术理念推动着企业数字化转型的云徙荣登2020数字营销百强报告,中台赛道第一在数字营销行业,信息瞬息万变,各类新概念层出不穷,同时也不断有新公司涌现,由于信息不对称等原因,市场营销人员,特别是广告主的高层决策层看到的与行业内真实情况的,往往存在很大差距。为中台赋能,直企拥抱数字化的捷径2020加速了直企进行数字化升级,也让经销商和创业伙伴学会拥抱新的变化。2020新经济风云榜暨知识经济专家委员会上,云徙科技荣获中国直销30年杰出服务奖。云徙科技新直销事业部总经理