简历上写了,面试官问了,我懵了,不会了~
time to live,为key设置失效时间。如果有大量的key,存在着大量的调度器,每一个key使用自己的调度器管理自己的任务时间周期,会很浪费资源,因此不得不提到
时间轮算法
。
核心是存储key->expiretime的映射map,expireTime到达时,自动删除过期的key。
时间轮算法
不止是redis,其他使用定时特点的都使用时间轮算法。它是一个高效的利用线程资源进行批量化调度的调度模型,大量的调度任务使用同一个调度器,高效的管理各种周期性任务。
type TimeWheel struct {
interval time.Duration
ticker *time.Ticker
slots []*list.List
timer map[string]*location
currentPos int
slotNum int
addTaskChannel chan task
removeTaskChannel chan string
stopChannel chan bool
使用一个数组表示整个时间轮,数组的每个元素是一个双向循环链表,存储着当前时间间隔中的任务。每个时间轮的时间间隔为 interval,时间轮的个数为slowNum,currentPos指向当前正在执行的时间轮。
开启一个以interval为时间间隔的时间轮
**tw**.ticker = ***time***.NewTicker(**tw**.interval)
时间轮进行select监听
当时间轮开始运行,定时跳动一格 ,更改当前的调度位置currentpos = (currentPos+1)%slotNum
,同时在列表中从头节点开始遍历,对于已经过期的节点,会执行回调函数,从数据库中删除。对于下次get key时,会根据ttlMap获取过期时间,与当前时间相比看是否过期,如果过期直接从内存中删除。
timewheel.At(expireTime, taskKey, func() {
keys := []string{key}
db.RWLocks(keys, nil)
deferdb.RWUnLocks(keys, nil)
logger.Info("expire " + key)
rawExpireTime, ok :=db.ttlMap.Get(key)
if !ok {
return
expireTime, _ := rawExpireTime.(time.Time)
expired :=time.Now().After(expireTime)
if expired {
db.Remove(key)
判断是否要新增任务case task := <-tw.addTaskChannel:
根据expireTime/interval/slotNum
判断当前任务所处的圈数,根据(currentPos+delay/interval)%slotNum
判断当前所处第几个间隔中
将任务放入该位置的间隔对应的列表中
判断removeTaskChannel
是否有任务
从该时间轴对应的列表中移除该任务
判断stopChannel
是否开启,即是否停止时间轮算法
停止定时器的计时
expire key second: key在seconds秒后过期
向addTaskChannel中放入任务,key是“expire:”+key,time是此次key所对应的过期时间。
TTL key: 查询键的剩余过期时间
raw, exists := db.ttlMap.Get(key)
expireTime, _ := raw.(time.Time)
ttl := expireTime.Sub(time.Now())
persist key : 清除key的过期时间
从ttlmap中移除key和expireTime的映射**db**.ttlMap.Remove(key)
将key放入removeTaskChannel,select中监听到channel的io,会移除时间轮中对应位置的任务**tw**.removeTaskChannel <- key
对于过期时间功能的实现,主要使用时间轮算法,通过一个循环数组实现,由定时任务(默认1s)控制着数组元素的更改,数组的每一个元素指向一个双向链表,存储着对应时间段的任务。当使用expire命令行增加key的过期时间时,根据过期时间计算出该任务所处时间轮的圈数和位置。
使用三个channel,addTaskChannel对应着增加过期时间的任务,包含了所在时间轮的位置和key;stopChannel表示是否要停用该时间轮算法;removeTaskChannel
是为了实现persist指令,清除ttl,将时间轮中对应位置的任务移除。同时会使用map记录key到expireTime的映射,便于ttl命令查询过期时间。使用select监听三个channel,并进行相应的事件。
定时任务运行,定时更改当前要运行的任务列表,同时执行回调删除过期的key。
www.cnblogs.com/luozhiyun/p…
github.com/Zerlina-ysl…