2012-04-03 44 views
5

我需要在我的应用程序中为每个项目类别(2000年左右的总类别)维护最近添加的40个最受欢迎/最受欢迎的项目列表。我确实存储了意见数&没有喜欢每个项目。为此,我正在寻找可能在应用程序服务器上维护内存结构,以便存储&检索这些项目列表。如何在Web应用程序中维护每个项目类别的“当前最受欢迎”项目列表?

你有关于如何实现这在内存中的数据结构,& 重要任何想法,同时要注意相关的内存占用&它减少到最远的程度)?


使用:

的Java 1.6

+7

这里有很多问题需要解决。我认为你需要一个衰减功能,以便在6个月前的喜欢和访问与上周的活动相比打折。另外,你关于占用最小空间的数据结构的问题......这是你真正想问的问题吗?在担心空间之前,您不应该专注于让您的功能先行吗? – ControlAltDel 2012-04-03 19:10:33

+0

http://stackoverflow.com/questions/81346/most-efficient-way-to-increment-a-map-value-in-java – sfk 2012-04-12 21:49:03

+1

内存中的排名对象?拧数据。 'select *,(views * .10 + likes)作为vw_categories_with_decayed_views_and_likes的排名按排名desc的限制40' – Louis 2012-04-12 22:24:19

回答

7

在您解决内存中的结构问题之前,请考虑您的服务器必须重新引导时会发生什么情况。这种内存结构将会消失。如果这没有问题,那么你应该使用内存结构。如果不是,您可能需要考虑简单地使用单独的域对象来处理这种类型的元数据。

在内存

Apache的处理器线程不共享内存,因此要做到这一点,最好的办法可能是安装喜欢的事memcached。每次你想获得当前的项目,你都会打一个特定的键(“topforty”)。 Memcached保持不变,任何线程都可以同时调用它。这是一个高度可扩展的解决方案。

然而,为了它的工作,你必须做额外的工作。某些程序需要评估当前的喜好和意见,并更新topforty键。这可以通过您的管理Web应用程序来完成,也可以通过每小时或每天的cron作业完成。下面定义的服务也可以做到这一点,只需要使用memcached而不是其持久化的对象。

域对象

如果坚持是更关键的,并且你愿意同意移交给你的web应用框架,那么你要创建处理这样的服务:

public interface PopularityService { 
    public List<Item> getTopItems(int count);//gets n top items 

    //lets the service know someone liked a thing 
    public void registerLike(Item item, Person liker); 

    //lets the service know someone viewed a 
    public void registerView(Item item, Person viewer);thing 
} 

这将需要一些支持对象:

public class PopularStuff { 
    public List<Item> popularItems 
    ... 
} 

你应该坚持的是作为单个OB ject(或者如果你的框架变得简单,就像一个单身人士)。你的服务应该对这个对象采取行动,决定它应该在什么位置以及如何移动。这将是一个阅读繁重的解决方案,但不像其他静态数据那样重读,因为大概人们会做很多观点。如果您使用的是Hibernate,那么从项目列表跳转到数据库中的实际项目将非常简单。

请注意,我没有讨论底层算法,因为您没有问过这个问题,而是关于如何实现数据结构。如果您可以提供有关您当前框架的详细信息,则可以讨论更多细节。

+0

感谢您的回答纳撒尼尔。我正在使用JSF,没有像Hibernate等任何东西。 – 2012-04-10 20:58:54

+0

关于服务器重新启动,我将定期将数据保存到数据库,并且当应用程序被扼杀时,它将保存当前状态 – 2012-04-10 21:00:29

+0

我一直计划在应用程序范围内的托管bean中保存此数据结构 – 2012-04-10 21:02:20

4

您检查了Priority Queue?看起来这样可以满足您的订购需求,一旦您设置了正确的比较器。如果您的列表大小是动态的,那么内存大小可能是一个问题。但是由于您知道每个列表中有多少项目,因此您可以将该大小指定为初始容量。

3

我打算做一个很大的假设,那就是当你说你“存储视图数& no的喜欢”时,你的意思是它们以一种查询友好的格式存储(即,一个SQL数据库或同等学历)。因此,您希望将信息存储在内存中的主要原因是缓存数据,从而减少生成页面所需的数据库调用次数。这个假设是否正确?

如果是这样,那么我认为你是过度复杂的问题。而不是使用复杂的数据结构来维护您的信息,请将其视为简单的缓存结构。下面是它是如何工作的一个高度简化的,伪例如:

class TopXCache extends Runnable 
{ 
    Object[] cachedData; 

    int secondsToTimeOut; 
    String sqlQueryToRefreshCache; 

    boolean killSwitch = false; 

    constructor(int itemsToKeepInCache, int secondsToTimeOut, String sqlQueryToRefreshCache) 
    { 
    this.secondsToTimeOut = secondsToTimeOut; 
    this.sqlQueryToRefreshCache = sqlQueryToRefreshCache; 

    this.cachedData = new Object[itemsToKeepInCache]; 
    } 

    void run() // The method the thread will execute 
    { 
    while(!killSwitch) // Allows for "poison pill" shutdown 
    { 
     cachedData = executeQuery(sqlQueryToRefreshCache); 
     wait(secondsToTimeOut); 
    } 
    } 

    void kill() 
    { 
    killSwitch = true; 
    } 
} 

要创建列表,与投票时间(secondsToTimeOut),SQL查询来运行,这将返回数据的最新副本进行实例化( sqlQueryToRefresh)以及列表中所需的项目数(itemsToKeepInCache,在您的案例中为40)。

然后启动一个可执行上述任务的线程(或计划任务或cron库任务,无论您用于管理应用程序中的定时事件),并定期缓存将自行刷新。如果系统意外关闭,那么一旦线程重新启动,它将自动从数据库重建自身。

这是一个令人难以置信的简单缓存的基础。如果你愿意,可以将它设置得更加复杂,将它设置为单例,添加一个“forceRefresh()”方法来更新当前刷新窗口之外的数据,将它设置为在单个线程上保存并刷新多个缓存,或者甚至可以全部使用第三方缓存库。

尽管缓存是解决这类问题的常规解决方案,并且长期而言通常更易于理解和维护。

0

我做@Erica的相同假设,但提供了不同的解决方案:

还假设项目类关系是多到很多。

import java.util.List; 
import java.util.Map; 
import java.util.TreeSet; 
import javax.ejb.EJB; 

@ManagedBean 
@RequestScoped 
public class ItemBean 
{ 
    @EJB 
    private DbService dbService; 

    @ManagedProperty("#{categoryCache}") 
    private CategoryCache cache; 

    public void incrementViewCounter(Item item) 
    { 
     item.setViewCount(item.getViewCount() + 1); 
     dbService.update(item); 
     cache.update(item); 
    } 

    public void incrementLikeCounter(Item item) 
    { 
     item.setLikeCount(item.getViewCount() + 1); 
     dbService.update(item); 
     cache.update(item); 
    } 
} 


@ManagedBean 
@ApplicationScoped 
class CategoryCache 
{ 
    private Map<Integer, ItemSet> categoryMap; 

    public void update(Item item) 
    { 
     ItemReference ref = new ItemReference(item); 

     for(Category c : item.getCategoryList()) 
     { 
      ItemSet set = categoryMap.get(c.getId()); 
      if(set == null) 
      { 
       set = new ItemSet(); 
       categoryMap.put(c.getId(), set); 
      } 

      set.add(ref); 
     } 
    } 
} 

class ItemSet extends TreeSet<ItemReference> 
{ 
    private static final int MAX_ENTRIES = 40; 

    @Override 
    public boolean add(ItemReference ref) 
    { 
     if(contains(ref)) remove(ref); 

     super.add(ref); 

     if(size() > MAX_ENTRIES) 
     { 
      remove(last()); 
     } 

     return true; 
    } 
} 

class ItemReference implements Comparable<ItemReference> 
{ 
    private final Integer id; 
    private final Double rank; 

    public ItemReference(Item item) 
    { 
     this.id = item.getId(); 
     this.rank = item.getViewCount().doubleValue() * 0.1 + item.getLikeCount().doubleValue(); 
    } 

    @Override 
    public int compareTo(ItemReference that) 
    { 
     return -this.getRank().compareTo(that.getRank()); 
    } 

    @Override 
    public int hashCode() 
    { 
     return id.hashCode(); 
    } 

    @Override 
    public boolean equals(Object that) 
    { 
     if(that instanceof ItemReference) 
     { 
      return this.getId().equals(((ItemReference)that).getId()); 
     } 

     return false; 
    } 

    public Integer getId() 
    { 
     return id; 
    } 

    public Double getRank() 
    { 
     return rank; 
    } 
} 
相关问题