首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

怎么设计一个LRU Cache

2012-11-19 
如何设计一个LRU Cache? 如何设计一个LRU Cache?Google和百度的面试题都出现了设计一个Cache的题目,什么是

如何设计一个LRU Cache?

 如何设计一个LRU Cache?

Google和百度的面试题都出现了设计一个Cache的题目,什么是Cache,如何设计简单的Cache,通过搜集资料,本文给出个总结。

 通常的问题描述可以是这样:

Question:

[1] Design a layer in front of a system which cache the last n requests and the responses to them from the system.

在一个系统之上设计一个Cache,缓存最近的n个请求以及系统的响应。
what data structure would you use to implement the cache in the later to support following operations.

用什么样的数据结构设计这个Cache才能满足下面的操作呢?
[a] When a request comes look it up in the cache and if it hits then return the response from here and do not pass the request to the system
[b] If the request is not found in the cache then pass it on to the system
[c] Since cache can only store the last n requests, Insert the n+1th request in the cache and delete one of the older requests from the cache

因为Cache只缓存最新的n个请求,向Cache插入第n+1个请求时,从Cache中删除最旧的请求。

[d]Design one cache such that all operations can be done in O(1) – lookup, delete and insert.

 Cache简介:

Cache(高速缓存), 一个在计算机中几乎随时接触的概念。CPU中Cache能极大提高存取数据和指令的时间,让整个存储器(Cache+内存)既有Cache的高速度,又能有内存的大容量;操作系统中的内存page中使用的Cache能使得频繁读取的内存磁盘文件较少的被置换出内存,从而提高访问速度;数据库中数据查询也用到Cache来提高效率;即便是Powerbuilder的DataWindow数据处理也用到了Cache的类似设计。Cache的算法设计常见的有FIFO(first in first out)和LRU(least recently used)。根据题目的要求,显然是要设计一个LRU的Cache。

 解题思路:

Cache中的存储空间往往是有限的,当Cache中的存储块被用完,而需要把新的数据Load进Cache的时候,我们就需要设计一种良好的算法来完成数据块的替换。LRU的思想是基于“最近用到的数据被重用的概率比较早用到的大的多”这个设计规则来实现的。

为了能够快速删除最久没有访问的数据项和插入最新的数据项,我们双向链表连接Cache中的数据项,并且保证链表维持数据项从最近访问到最旧访问的顺序。每次数据项被查询到时,都将此数据项移动到链表头部(O(1)的时间复杂度)。这样,在进行过多次查找操作后,最近被使用过的内容就向链表的头移动,而没有被使用的内容就向链表的后面移动。当需要替换时,链表最后的位置就是最近最少被使用的数据项,我们只需要将最新的数据项放在链表头部,当Cache满时,淘汰链表最后的位置就是了。

  注: 对于双向链表的使用,基于两个考虑。首先是Cache中块的命中可能是随机的,和Load进来的顺序无关。其次,双向链表插入、删除很快,可以灵活的调整相互间的次序,时间复杂度为O(1)。

    查找一个链表中元素的时间复杂度是O(n),每次命中的时候,我们就需要花费O(n)的时间来进行查找,如果不添加其他的数据结构,这个就是我们能实现的最高效率了。目前看来,整个算法的瓶颈就是在查找这里了,怎么样才能提高查找的效率呢?Hash表,对,就是它,数据结构中之所以有它,就是因为它的查找时间复杂度是O(1)。

梳理一下思路:对于Cache的每个数据块,我们设计一个数据结构来储存Cache块的内容,并实现一个双向链表,其中属性next和prev时双向链表的两个指针,key用于存储对象的键值,value用户存储要cache块对象本身。

 Cache的接口:

查询:

    根据键值查询hashmap,若命中,则返回节点,否则返回null。从双向链表中删除命中的节点,将其重新插入到表头。所有操作的复杂度均为O(1)。

    插入:

      将新的节点关联到Hashmap 如果Cache满了,删除双向链表的尾节点,同时删除Hashmap对应的记录将新的节点插入到双向链表中头部

      更新:

        和查询相似

        删除:

          从双向链表和Hashmap中同时删除对应的记录。

          LRU Cache的Java 实现:

           public interface Cache<K extends Comparable, V> {

             V get(K obj);  //查询

             void put(K key, V obj); //插入和更新

             void put(K key, V obj, long validTime);

             void remove(K key); //删除

             Pair[] getAll();

             int size();

          }

            public class Pair<K extends Comparable, V> implements Comparable<Pair> {

             public Pair(K key1, V value1) {

                this.key = key1;

                this.value = value1;

             }

             public K key;

             public V value;

             public boolean equals(Object obj) {

                if(obj instanceof Pair) {

                   Pair p = (Pair)obj;

                   return key.equals(p.key)&&value.equals(p.value);

                }

                return false;

             }

             @SuppressWarnings("unchecked")

             public int compareTo(Pair p) {

                int v = key.compareTo(p.key);

                if(v==0) {

                   if(p.value instanceof Comparable) {

                      return ((Comparable)value).compareTo(p.value);

                   }

                }

                return v;

             }

             @Override

             public int hashCode() {

                return key.hashCode()^value.hashCode();

             }

             @Override

             public String toString() {

                return key+": "+value;

             }

          }

           public class LRUCache<K extends Comparable, V> implements Cache<K, V>,

                Serializable {

             private static final long serialVersionUID = 3674312987828041877L;

             Map<K, Item> m_map = Collections.synchronizedMap(new HashMap<K, Item>());

             Item m_start = new Item();      //表头

             Item m_end = new Item();        //表尾

             int m_maxSize;

             Object m_listLock = new Object();        //用于并发的锁

             static class Item {

                public Item(Comparable k, Object v, long e) {

                   key = k;

                   value = v;

                   expires = e;

                }

                public Item() {}

                public Comparable key;        //键值

                public Object value;          //对象

                 public long expires;          //有效期

                public Item previous;

                public Item next;

             }

             void removeItem(Item item) {

                synchronized(m_listLock) {

                   item.previous.next = item.next;

                   item.next.previous = item.previous;

                }

             }

             void insertHead(Item item) {

                synchronized(m_listLock) {

                   item.previous = m_start;

                   item.next = m_start.next;

                   m_start.next.previous = item;

                   m_start.next = item;

                }

             }

             void moveToHead(Item item) {

                synchronized(m_listLock) {

                   item.previous.next = item.next;

                   item.next.previous = item.previous;

                   item.previous = m_start;

                   item.next = m_start.next;

                   m_start.next.previous = item;

                   m_start.next = item;

                }

             }

             public LRUCache(int maxObjects) {

                m_maxSize = maxObjects;

                m_start.next = m_end;

                m_end.previous = m_start;

             }

             @SuppressWarnings("unchecked")

             public Pair[] getAll() {

                Pair p[] = new Pair[m_maxSize];

                int count = 0;

                synchronized(m_listLock) {

                   Item cur = m_start.next;

                   while(cur!=m_end) {

                      p[count] = new Pair(cur.key, cur.value);

                      ++count;

                      cur = cur.next;

                   }

                }

                Pair np[] = new Pair[count];

                System.arraycopy(p, 0, np, 0, count);

                return np;

             }

             @SuppressWarnings("unchecked")

             public V get(K key) {

                Item cur = m_map.get(key);

                if(cur==null) {

                   return null;

                }

               //过期则删除对象

                if(System.currentTimeMillis()>cur.expires) {

                   m_map.remove(cur.key);

                   removeItem(cur);

                   return null;

                }

                if(cur!=m_start.next) {

                   moveToHead(cur);

                }

                return (V)cur.value;

             }

             public void put(K key, V obj) {

                put(key, obj, -1);

             }

             public void put(K key, V value, long validTime) {

                Item cur = m_map.get(key);

                if(cur!=null) {

                   cur.value = value;

                   if(validTime>0) {

                      cur.expires = System.currentTimeMillis()+validTime;

                   }

                   else {

                      cur.expires = Long.MAX_VALUE;

                   }

                   moveToHead(cur);  //成为最新的对象,移动到头部

                   return;

                }

                if(m_map.size()>=m_maxSize) {

                   cur = m_end.previous;

                   m_map.remove(cur.key);

                   removeItem(cur);

                }

                long expires=0;

                if(validTime>0) {

                   expires = System.currentTimeMillis()+validTime;

                }

                else {

                   expires = Long.MAX_VALUE;

                }

                Item item = new Item(key, value, expires);

                insertHead(item);

                m_map.put(key, item);

             }

             public void remove(K key) {

                Item cur = m_map.get(key);

                if(cur==null) {

                   return;

                }

                m_map.remove(key);

                removeItem(cur);

             }

             public int size() {

                return m_map.size();

             }

          }

          LRU Cache的C#实现:

          using System;
          using System.Collections;

          namespace Common.LRUCacheHelper
          {
              /// <summary>
              /// LRUCacheHelper 的摘要说明。
              /// </summary>
              public class LRUCacheHelperbase
              {
                  readonly System.Collections.Hashtable _dict = null;
                  readonly ArrayList _queue = null;        
                  readonly object _syncRoot = new object();
                  private readonly int _max;
                  
                  public LRUCacheHelperbase()
                  {
                      _max = 20;
                      _dict = Hashtable.Synchronized(new Hashtable(_max));
                      _queue = ArrayList.Synchronized(new ArrayList(_max));
                  }
                  public LRUCacheHelperbase(int max)
                  {
                      _max = max < 20?20:max;
                      _dict = Hashtable.Synchronized(new Hashtable(_max));
                      _queue = ArrayList.Synchronized(new ArrayList(_max));
                  }
                  public LRUCacheHelperbase(int capacity, int max)
                  {    
                      _max = max < 20?20:max;
                      _dict = Hashtable.Synchronized(new Hashtable(capacity));
                      _queue = ArrayList.Synchronized(new ArrayList(capacity));
                  }
                  
                  public void add(object Key, object Value)
                  {
                      lock (_syncRoot)
                      {
                          try
                          {
                              if(this._dict.ContainsKey(Key))
                                  _dict.Remove(Key);
                              if(this._queue.Contains(Key))
                                  _queue.Remove(Key);

                              int count = _dict.Count;
                              if (count >= _max)
                              {
                                  int needRemoveCount = count - _max+1;
                                  for (int i = 0; i < needRemoveCount; i++)
                                  {
                                      object ret = _queue[_queue.Count-1];
                                      if(this._dict.ContainsKey(ret))
                                          _dict.Remove(ret);
                                      _queue.Remove(ret);
                                  }
                              }
                              _dict[Key] = Value;
                              _queue.Add(Key);

                          }
                          catch{}
                      }
                  }
                  
                  
                  public void Delete(object Key)
                  {
                      lock (_syncRoot)
                      {
                          try
                          {
                              if(this._dict.ContainsKey(Key))
                                  _dict.Remove(Key);
                              if(this._queue.Contains(Key))
                                  _queue.Remove(Key);
                          }
                          catch{}
                      }
                  }
                  public object get(object Key)
                  {
                      lock (_syncRoot)
                      {
                          object ret = null;
                          try
                          {
                              if(this._dict.ContainsKey(Key))
                              {
                                  ret =_dict[Key];
                              }
                          }
                          catch{}
                          return ret;
                      }
                  }

                  public bool Contains(object Key)
                  {
                      return this._dict.ContainsKey(Key);
                  }
                  
              }
          }



热点排行