2017-4-25/设计缓存(LFU)

1. 恒定缓存性能有哪些因素?
命中率、缓存更新策略、缓存最大数据量。
  • 命中率:指请求缓存次数和缓存返回正确结果次数的比例。比例越高,缓存的使用率越高,用来衡量缓存机智的好坏和效率。如果数据频繁更新,命中率就会降低,这个时候需要考虑缓存的合理性。命中率=命中次数/请求次数。
  • 缓存更新策略主要有三个:
    FIFO先进先出,如果一个数据最先进入缓存中,当缓存满了以后,就应该最早淘汰掉。
    LFU最近最少使用,如果一个数据在最近一段时间内使用次数很少,那么在将来一段时间内被使用的可能性也很小。即:当缓存满了以后,应当把最少被访问的数据淘汰。(LFU是淘汰一段时间内使用次数最少的数据。)
    LRU最近最久未使用,如果一个数据在最近一段时间没有被访问到,那么在将来它被访问的可能性也很小。即:当缓存满了以后,应当把最久没有被访问到的数据淘汰。(LRU是淘汰最长时间没有被使用的数据。)
  • 缓存最大数据量是在缓存中能处理元素的最大个数或所能使用的最大存储空间。
2. 如何根据这三者,去设计一个缓存?
问题补充:有时候,如果我们不想用Redis,就会自己设计一个缓存。比如首先是规定缓存的最大数据量,当缓存的最大容量定下来后我们就考虑更新策略,是用LRU还是LFU?如果用LFU,又该怎么实现这个LFU的功能?
设计LFU的思路:
LFU是最近最少使用,也就是一段时间内使用次数最少的数据最先淘汰。可以把这个数据当成key,value代表这个数据被访问的次数,初始可以设为0,每访问这个数据一次,value就+1,一段时间后,哪个value小,就淘汰掉哪个数据。
如何设计LFU的数据结构:
实现LFU在PHP里面可以通过数组实现,从以下几方面考虑:
首先,怎么取值?
其次,怎么清除老旧的数据,也就是怎么设计LFU?
最后,怎么存储?
LFU的实现过程:
  • 定义两个关联数组,key都一样,一个数组叫kv数组,存值,形如key=>value,一个数组叫kc数组,存值被访问的次数,形如key=>count。
  • 定义请求数requestNum和命中数hitNum,来获得命中率。
  • 定义缓存当前数据个数size和缓存总大小totalSize。
  • 定义方法包括:get、add、getHitRate、cleanup等。
    get:通过传入key,从kv数组中返回其值,同时,kc数组中该key对应的count值+1。
    add:通过传入key和value,将数据添加到存值的关联数组中。这时候要考虑LFU,如果缓存没满,直接加进去就行,但如果缓存已经满了,添加新的数据时,最近最少未被使用的数据就要踢掉(cleanup)。
    cleanup:当有数据需要更新的时候,先在kv数组中插入一个key=>value,在kc数组中根据value的访问次数count值,找出最少被访问的几个数据,通过它们的key,把kv数组中的值删掉。
    getHitRate:计算命中率。每当用户请求(get)数据一次,请求数requestNum就+1,如果请求的值在缓存里面找到了,命中数hitNum就+1,则命中率hitRate=hitNum/totalRequestNum。
设计LFU的总结
1)定义两个数组,一个数组存值,一个数组存该值的访问次数;
2)定义三个方法,get取值,add添加,cleanup清除缓存;
3)当用户访问某个数据时,如果查到,就返回;
4)当用户添加数据时,先判断缓存的容量,如果缓存没满,直接添加数据到缓存,如果缓存的容量满了,先将存访问次数的数组从大到小(或从小到大)进行排序,最小的那个即访问最少的数,将该数据删除后,再添加新数据;
5)清除缓存时,是将存访问次数那个数组排序,访问次数最小的就是要删除的数据;
6)缓存用来存储经常查询、更新的数据,其中取值和添加的方法没有先后之分,互不相关。
 
简单代码:
<?php
// 设计一个简单的缓存
class strCache {
// 缓存总大小
var $totalSize = 5;
// 访问数
var $requestNum = 0;
// 命中数
var $hitNum = 0; // 存值key=>value
var $arr1 = array();
// 存访问次数key=>count
var $arr2 = array(); public function get($key) {
$result = "";
$this->requestNum += 1;
if ($this->arr1[$key]) {
$this->hitNum += 1;
$count = 1;
if ($this->arr2[$key]) {
$count += $this->arr2[$key];
}
$arr2[$key] = $count;
$result = $this->arr1[$key];
}
return $result;
} public function add($key, $value) {
$this->cleanup();
$this->arr1[$key] = $value;
$this->arr2[$key] += 1;
return true;
} public function cleanup() {
if (count($this->arr1) == $this->totalSize) {
asort($this->arr2);
$keys = array_keys($this->arr2);
$k = $keys[0];
unset($this->arr1[$k]);
unset($this->arr2[$k]);
}
return true;
} public function getHitRate() {
$hitRate = 0;
if ($this->requestNum != 0) {
$hitRate = $this->hitNum / $this->requestNum;
}
return $hitRate;
}
} // 测试
$strObj = new strCache();
$strObj->add('a', 'wuhan');
$strObj->add('b', 'heilongjiang');
$strObj->add('c', 'shanghai');
$strObj->add('d', 'beijing'); print_r($strObj->arr1);
echo "<pre>";
echo $strObj->get('a');
echo "<pre>";
echo $strObj->get('aa');
echo "<pre>";
echo $strObj->hitNum;
echo "<pre>";
echo $strObj->getHitRate();
上一篇:Hibernate3回顾-1-部署


下一篇:vue-i18n和ElementUI国际化使用