我需要在我的应用程序中维护每个项目类别(大约2000个类别)的40个最近添加的,最受欢迎/最喜欢的项目的列表。我存储了视图数量&不喜欢每件商品。为此,我希望在app服务器上维护一个内存结构,以便存储&检索这些项目列表。
您对如何实现此内存数据结构有任何想法,& 重要,请记住相关的内存占用量。最小化它)?
使用:
Java 1.6
答案 0 :(得分: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
...
}
您应该将该对象持久化为单个对象(或者如果您的框架使其变得容易,则作为单个对象)。您的服务应该对该对象采取行动,决定其中应包含的内容以及如何移动内容。这将是一个阅读量很大的解决方案,但不像其他静态数据那样重读,因为可能人们会做很多观点。如果你正在使用像Hibernate这样的东西,那么很容易从项目列表跳转到数据库中的实际项目。
请注意,我没有讨论基础算法,因为您没有问过这个问题,而是讨论如何实现数据结构。如果您可以提供有关当前框架的详细信息,我们可以讨论更多细节。
答案 1 :(得分:4)
你签出了Priority Queue吗?一旦您设置了正确的比较器,这似乎可以满足您的订购需求。如果列表大小是动态的,那么内存大小可能是个问题。但是,既然您知道每个列表中有多少项,您只需将该大小指定为初始容量即可。
答案 2 :(得分:3)
我将做一个相当大的假设,那就是当你说“存储视图数量和不喜欢”时,你的意思是它们以一种查询友好的格式存储(即一个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,in你的情况,40)。
然后启动一个可以执行上述操作的线程(或者一个计划任务,或一个cron库任务,无论你用什么来管理应用程序中的定时事件),你的缓存都会定期刷新。如果您的系统意外关闭,那么一旦重新启动线程,它将自动从数据库重建。
这是一个非常简单的缓存的基础。如果您愿意,可以将其设置为更复杂,将其设置为单例,添加“forceRefresh()”方法以更新当前刷新窗口之外的数据,将其设置为保持并刷新单个线程上的多个缓存,或者甚至整个猪都使用第三方缓存库。
缓存是解决此类问题的正常方法,而且从长远来看通常更容易理解和维护。
答案 3 :(得分: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;
}
}