再谈缓存

凡是涉及管理数据的系统,都可以用图书馆来考虑,都要面临图书的位置查找和实际摆放两个问题,对应的两大组件就是就是index + store,所有的数据管理系统都包含这两部分。

缓存从过期又什么触发的角度分为容量触发和时间触发,

容量触发,就是缓存满了,需要换出一些空间,按换出策略又分为

LRU

LFU

TTL到期触发,又分为

1)固定TTL的过期,比如httpSession

2)每个项目单独指定TTL的过期,每个数据item单独指定过期时间


最基本的缓存可以只用一个map,index直接指向数据的物理地址(Object 的reference)。更general的情况是存一个locator,这个locator交给store能直接返回数据。因为缓存需要过期策略支持,不同的策略底层存放数据的方式也不同,比如FIFO就是用一个list按创建时间排队就好,缓存满了就过期队头;LRU在此之上,还要支持访问一次就从队列中间移动到队尾。

对于LFU,经典的做法是用一个key为访问次数的最小堆,访问一次freq ++, 然后 调整堆,堆顶永远是访问次数最少的item。get(), put()都是O(lgn)的。还有一种O(1)的方法, 利用freq每次只是++的特点,一个freq一个节点,挂一个访问次数为freq的数据节点的list,一个数据项被访问了,就把它移到freq->next下的list,如果freq->next 不是freq + 1,就创建之。具体方法见http://ju.outofmemory.cn/entry/50447


对于FIFO和LRU,底层的store结构都是list,从OO的角度,ExpireStrategy可以建立在store之上,看以是看作是内部的housekeeping,Index -> Store <-ExpireStrategy。对于FIFO,get(Key)的时候,没有什么额外的housekeeping工作, 对于LRU,则需要把那个被访问的Item移动到队尾。


 对于LFU,需要专门的store结构支持,OO的模型是 Index -> Store ->LFUStore


还有一种定时过期缓存, 在Session实现那篇文章讨论过,如果是都是一样的TTL(time to live),比如httpSession都是30分钟,则也是用list排队,和LRU类似,只是过期不是由缓存满触发,而是时间,要多存个时间戳,可以单独线程check也可以在每次操作时候顺带看看队头是否过期,反正是O(1)的。如果每个Item的可以单独指定TTL,那么就得用stl::set或者最小堆,维护最早due的Item. Java的DelayQueue就是专门干这个的。



你可能感兴趣的:(再谈缓存)