0

I have a site with millions of users (well, actually it doesn't have any yet, but let's imagine), and I want to calculate some stats like "log-ins in the past hour".

The problem is similar to the one described here: http://highscalability.com/blog/2008/4/19/how-to-build-a-real-time-analytics-system.html

The simplest approach would be to do a select like this:

select count(distinct user_id) 
from logs
where date>='20120601 1200' and date <='20120601 1300' 

(of course other conditions could apply for the stats, like log-ins per country) Of course this would be really slow, mainly if it has millions (or even thousands) of rows, and I want to query this every time a page is displayed.

How would you summarize the data? What should go to the (mem)cache?

EDIT: I'm looking for a way to de-normalize the data, or to keep the cache up-to-date. For example I could increment an in-memory variable every time someone logs in, but that would help to know the total amount of logins, not the "logins in the last hour". Hope it's more clear now.

4

4 回答 4

2

IMO 这里更正确的方法是实现一个连续计算,将相关计数器保存在内存中。每次将用户添加到您的系统时,您都可以启动一个事件,该事件可以通过多种方式处理并更新最后一小时、最后一天甚至总用户计数器。有一些很棒的框架可以进行这种处理。Twitter Storm就是其中之一,另一个是GigaSpaces XAP(免责声明 - 我为 GigaSpaces 工作),特别是本教程,还有Apache S4GridGain

于 2012-06-03T14:33:50.217 回答
1

如果您没有数据库,那没关系。我没有数以百万计的用户,但我有一个可以登录数年的表,其中包含一百万行和亚秒级的简单统计信息。一百万行对于数据库来说并不算多。您不能将日期作为 PK,因为您可以有重复项。为了最小化碎片和插入速度,使日期成为聚集的非唯一索引 asc,这就是数据的来源。不确定您是否有数据库,但在 MSSQL 中可以。索引 user_id 是要测试的。这样做会减慢插入速度,因为这是一个会碎片化的索引。如果您正在寻找相当紧凑的时间跨度,则表扫描可能没问题。

为什么不同的 user_id 而不是登录名是登录名。

具有仅每 x 秒运行一次查询的属性。即使每秒并报告缓存的答案。如果或 200 个页面在一秒钟内点击该属性,那么您肯定不想要 200 个查询。如果统计数据在过去一小时内的信息陈旧一秒钟,那么这仍然是有效的统计数据。

于 2012-06-01T23:14:32.897 回答
0

我最终使用了Esper/NEsper。Uri 的建议也很有用。

Esper 允许我在获取数据时计算其实时统计数据。

于 2012-06-07T03:07:43.563 回答
0

如果你只是用完日志,你可能想看看 Splunk 之类的东西。

通常,如果您想要这种内存和快速(实时),您将创建登录数据的分布式缓存,并在例如 24 小时后驱逐,然后您可以在该缓存中查询例如过去一小时内的登录。

假设登录记录如下所示:

public class Login implements Serializable {
    public Login(String userId, long loginTime) {..}
    public String getUserId() {..}
    public long getLoginTime() {..}
    public long getLastSeenTime() {..}
    public void setLastSeenTime(long logoutTime) {..}
    public long getLogoutTime() {..}
    public void setLogoutTime(long logoutTime) {..}
    String userId;
    long loginTime;
    long lastSeenTime;
    long logoutTime;
}

要支持 24 小时后的驱逐,只需在缓存上配置一个过期时间 (TTL)

<expiry-delay>24h</expiry-delay>

查询当前登录的所有用户:

long oneHourAgo = System.currentTimeMillis() - 60*60*1000;
Filter query = QueryHelper.createFilter("loginTime > " + oneHourAgo
                                        + " and logoutTime = 0");
Set idsLoggedIn = cache.keySet(query);

查询过去一小时内的登录数和/或活跃用户数:

long oneHourAgo = System.currentTimeMillis() - 60*60*1000;
Filter query = QueryHelper.createFilter("loginTime > " + oneHourAgo
                                        + " or lastSeenTime > " + oneHourAgo);
int numActive = cache.keySet(query).size();

(有关查询的更多信息,请参阅http://docs.oracle.com/cd/E15357_01/coh.360/e15723/api_cq.htm。所有这些示例均来自 Oracle Coherence。)

为了全面披露,我在 Oracle 工作。这篇文章中表达的观点和观点是我自己的,并不一定反映我雇主的观点或观点。

于 2014-02-14T19:20:49.227 回答