使用sync.map做内存缓存

860 阅读1分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

背景

实际业务场景中有些需求对于数据实时性要求不高,但是对于吞吐量和响应有着极高的要求,这个时候我们可以建立多级缓存来解决此类问题,本篇文章我们讨论的是内存缓存(golang)来实现

实现方案

想到内存缓存,想必大家第一个想到的是golang中的map+mutex、sync.map的方式来实现. 对于读多写少的场景,一般建议大家用sync.map来实现(具体场景的性能测试如下) 在这里插入图片描述

code

package bm

import (
	"hash/crc32"
	"sync"
	"time"
)

const (
	BucketSize = 256
)

type BigSyncMap struct {
    // 分段来存储 数据提供并发访问效率
	cacheBuckets [BucketSize]*sync.Map
	// 存储每个key的过期时间
	expires      *sync.Map
}

var (
	bigMap *BigSyncMap
	once   sync.Once
)
// GetBigSyncMapInstance 获取实例
func GetBigSyncMapInstance() *BigSyncMap {
	once.Do(func() {
		bigMap = &BigSyncMap{
			expires: &sync.Map{},
		}
		for i := 0; i < BucketSize; i++ {
			bigMap.cacheBuckets[i] = &sync.Map{}
		}
		// 开启携程 定时清除过期的data
		go bigMap.flushExpireData()
	})
	return bigMap
}

func (m *BigSyncMap) flushExpireData() {
	for {
		m.expires.Range(func(key, expireTime interface{}) bool {
			if time.Since(expireTime.(time.Time)) > time.Minute*30 {
				m.delete(key.(string))
			}
			return true
		})
		time.Sleep(time.Minute * 2)
	}
}
func (m *BigSyncMap) delete(key string) {
	m.cacheBuckets[checksum(key)].Delete(key)
}
// checksum 根据crc32算法取key的index
func checksum(key string) int {
	return int(crc32.ChecksumIEEE([]byte(key)) % BucketSize)
}
// Load 加载数据
func (m *BigSyncMap) Load(key interface{}) (interface{}, bool) {
	return m.cacheBuckets[checksum(key.(string))].Load(key)
}
//  Store 存储数据,并更新对应key的expire time
func (m *BigSyncMap) Store(key, val interface{}) {
	m.cacheBuckets[checksum(key.(string))].Store(key,val)
	m.expires.Store(key,time.Now())
}