【问题标题】:More Efficient Way of Doing This SQL Query? A time comparison query?执行此 SQL 查询的更有效方式?时间比较查询?
【发布时间】:2013-08-31 14:47:35
【问题描述】:

我有这个 SQL 查询,它每 5 秒查询一次数据库,以确定当前谁在积极使用该软件。活动用户在过去 10 秒内对服务器进行了 ping 操作。 (表格会根据用户活动正确更新,并且我有一个线程在会话超时时逐出条目,一切正常)。

我正在寻找一种更有效/更快的方法来执行此操作,因为它被频繁调用,大约每 5 秒一次。此外,数据库中最多可能有 500 个用户。语言是 Java,但问题确实适用于任何语言。

List<String> r = new ArrayList<String>();
Calendar c =  Calendar.getInstance();
long threshold = c.get(Calendar.SECOND) + c.get(Calendar.MINUTE)*60 + c.get(Calendar.HOUR_OF_DAY)*60*60 - 10;

String tmpSql = "SELECT user_name, EXTRACT(HOUR FROM last_access_ts) as hour, EXTRACT(MINUTE FROM last_access_ts) as minute, EXTRACT(SECOND FROM last_access_ts) as second FROM user_sessions";
DBResult rs = DB.select(tmpSql);
for (int i=0; i<rs.size(); i++)
{
   Map<String, Object> result = rs.get(i);
   long hour = (Long)result.get("hour");
   long minute = (Long)result.get("minute");
   long second = (Long)result.get("second");
   if (hour*60*60 + minute*60 + second > threshold)
      r.add(result.get("user_name").toString());
}

return r;

【问题讨论】:

    标签: java mysql sql


    【解决方案1】:

    如果你想让它运行得更快,那么在user_sessions(last_access_ts, user_name)上创建一个索引,并在查询中做日期逻辑:

    select user_name
    from user_sessions
    where last_access_ts >= now() - 5/(24*60*60);
    

    这确实有一个缺点。您大概会经常更新last_access_ts 字段。该字段的索引也必须更新。从好的方面来说,这是一个覆盖索引,因此索引本身可以满足查询,而不需要求助于原始数据页。

    【讨论】:

      【解决方案2】:

      我会将逻辑从 Java 转移到 DB。这意味着您将if 翻译成where,然后选择有效结果的名称。

      SELECT user_name FROM user_sessions WHERE last_access_ts &gt; ?

      在您的示例中,c 代表当前时间。结果很可能为空。

      所以你的问题应该更多关于数据库上的日期时间操作。

      【讨论】:

        【解决方案3】:

        只需让数据库使用此查询为您进行比较:

            SELECT 
              user_name
            FROM user_sessions
            where TIMESTAMPDIFF(SECOND, last_access_ts, current_timestamp) > 10
        

        完整示例:

        List<String> r = new ArrayList<String>();
        Calendar c =  Calendar.getInstance();
        long threshold = c.get(Calendar.SECOND) + c.get(Calendar.MINUTE)*60 + c.get(Calendar.HOUR_OF_DAY)*60*60 - 10;
        // this will return all users that were inactive for longer than 10 seconds
        String tmpSql = "SELECT 
              user_name
            FROM user_sessions
            where TIMESTAMPDIFF(SECOND, last_access_ts, current_timestamp) > 10";
        DBResult rs = DB.select(tmpSql);
        for (int i=0; i<rs.size(); i++)
        {
           Map<String, Object> result = rs.get(i);
           r.add(result.get("user_name").toString());
        }
        
        return r;
        

        SQLFiddle

        【讨论】:

          【解决方案4】:

          解决方案是将代码中的逻辑删除到 sql 查询中,以便仅使用 where 子句从该选择中获取活动用户。

          使用 sql 内置函数可以更快地获取更少的记录并减少代码中的迭代。

          将此添加到您的 sql 查询中以仅获取活动用户:

          Where TIMESTAMPDIFF(SECOND, last_access_ts, current_timestamp) > 10
          

          这将为您获取日期在 10 秒之前或更早的所有记录。

          【讨论】:

            【解决方案5】:

            在您的选择中尝试 MySQL TimeDiff 函数。这样您就可以只选择处于活动状态的结果,而无需进行任何其他计算。

            链接:MySQL: how to get the difference between two timestamps in seconds

            【讨论】:

            【解决方案6】:

            如果我猜对了,那么您的 user_sessions 表中只有 500 个条目。在这种情况下,我什至不会关心索引。把它们丢掉。对于如此低的记录数,数据库引擎可能无论如何都不会使用它们。由于没有在每次记录更新时更新索引而获得的性能提升可能高于查询开销。

            如果您关心数据库压力,请将查询/更新间隔延长至 1 分钟或更长(如果您的应用程序允许)。不过,Gordon Linoff 的回答应该会给您最好的查询性能。

            作为旁注(因为它以前曾咬过我):如果您不为所有用户回调使用相同的同步时间,那么您的“活跃用户逻辑”在设计上存在缺陷。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2015-05-08
              • 1970-01-01
              • 1970-01-01
              • 2020-02-20
              • 1970-01-01
              • 2016-05-10
              相关资源
              最近更新 更多