cache preparedstatement?

2g32fytz  于 2021-06-10  发布在  Cassandra
关注(0)|答案(1)|浏览(518)

我正在缓存准备好的语句,以便在使用DataStaxJava驱动程序(cassandra)时不必再次准备它。。下面是我的代码,它可以工作:

private static final ConcurrentHashMap<String, PreparedStatement> cache = new ConcurrentHashMap<>();

  public ResultSetFuture send(final String cql, final Object... values) {
    return executeWithSession(new SessionCallable<ResultSetFuture>() {
      @Override
      public ResultSetFuture executeWithSession(Session session) {
        BoundStatement bs = getStatement(cql, values);
        bs.setConsistencyLevel(consistencyLevel);
        return session.executeAsync(bs);
      }
    });
  }

  private BoundStatement getStatement(final String cql, final Object... values) {
    Session session = getSession();
    PreparedStatement ps = cache.get(cql);
    // no statement cached, create one and cache it now.
    // below line is causing thread safety issue..
    if (ps == null) {
      ps = session.prepare(cql);
      PreparedStatement old = cache.putIfAbsent(cql, ps);
      if (old != null)
        ps = old;
    }
    return ps.bind(values);
  }

但问题是 send 方法将由多个线程调用,因此我怀疑 getStatement 方法不是线程安全的,因为 if (ps == null) 检查。。我怎样才能保证线程安全?
我想避免使用 synchronize 所以我想看看有没有更好的办法。我现在正在使用Java7。

33qvvth1

33qvvth11#

你可以用 computeIfAbsent 相反。根据文件:
如果指定的键尚未与值关联,则尝试使用给定的Map函数计算其值,并将其输入此Map,除非为null。整个方法调用是以原子方式执行的,因此每个键最多应用一次函数。在计算过程中,其他线程在此Map上尝试的某些更新操作可能会被阻止,因此计算应该简短,并且不能尝试更新此Map的任何其他Map。
代码如下所示:

private BoundStatement getStatement(final String cql, final Object... values) {
   PreparedStatement pr = cache.computeIfAbsent(
        query,  key -> session.prepare(key));
   return pr.bind(values);
}

虽然,因为准备可能需要更长的时间,不建议。。。我会使用显式锁。。。
p、 请注意,java驱动程序4.x有内置的缓存,所以可以多次准备相同的语句。

相关问题