使用Datastax Cassandra驱动程序时重用PreparedStatement?

我目前正在使用Cassandra 2的Datastax Cassandra驱动程序来执行cql3。 这工作正常。 我开始使用PreparedStatement's

 Session session = sessionProvider.getSession(); try { PreparedStatement ps = session.prepare(cql); ResultSet rs = session.execute(ps.bind(objects)); if (irsr != null) { irsr.read(rs); } } 

有时我会在日志中收到驱动程序的警告:

 Re-preparing already prepared query . Please note that preparing the same query more than once is generally an anti-pattern and will likely affect performance. Consider preparing the statement only once. 

这个警告是有道理的,但我不确定我应该如何重用PreparedStatement

我应该在构造函数/ init方法中创建所有的PreparedStatement而不是简单地使用它们吗?

但是当多个线程同时使用相同的PreparedStatement时(特别是调用PreparedStatement.bind()来绑定对象),这是否顺利?

您可以只初始化PreparedStatement并在应用程序运行时对其进行缓存。 只要Cassandra集群启动,它就应该可以使用。

使用来自多个线程的语句很好(只要你不通过setXXX()方法修改它)。 当你调用bind()时,下面的代码只读取PreparedStatement,然后创建一个BoundStatement()的新实例,然后调用者线程可以自由变异。

这是源代码 ,如果你很好奇(搜索bind() )。

我们在使用Spring的web应用程序中使用cassandra。 在我们的例子中,当封装针对cf(我们的存储库)的操作的bean被实例化时,我们创建PreparedStatements。

在这里,您有我们正在使用的代码片段:

 @Repository public class StatsRepositoryImpl implements StatsRepository { @SuppressWarnings("unused") @PostConstruct private void initStatements(){ if (cassandraSession == null){ LOG.error("Cassandra 2.0 not available"); } else { GETSTATS_BY_PROJECT = cassandraSession.prepare(SELECTSTATS+" WHERE projectid = ?"); } } @Override public Stats findByProject(Project project) { Stats stats = null; BoundStatement boundStatement = new BoundStatement(GETSTATS_BY_PROJECT); ResultSet rs = cassandraSession.execute(boundStatement.bind(project.getId())); for (Row row : rs){ stats = mapRowToStats(row); } return stats; } 

通过这种方式,每次执行方法findByProject时都会重用预准备语句。

如果密钥空间是固定的,上述解决方案将起作用。 在多租户情况下,此解决方案是不够的。 我只是按照以下方式执行,其中键空间作为参数传递。

检查准备好的语句中的键空间,如果它与传递的参数相同,则不要准备语句,因为在这种情况下它已经准备好了。

  private BatchStatement eventBatch(List events, String keySpace) { BatchStatement batch = new BatchStatement(); String preparedStmtKeySpace = propEventPer == null? "" : propEventPer.getQueryKeyspace(); if(!keySpace.equals("\"" + preparedStmtKeySpace + "\"")) { eventStmt = cassandraOperations.getSession().prepare(colFamilyInsert(keySpace + "." + "table_name")); } .... private RegularStatement colFamilyInsert(String colFamilyName) { return insertInto(colFamilyName) .value(PERSON_ID, bindMarker()) .value(DAY, bindMarker()); } 
 03-Apr-2017 10:02:24,120 WARN [com.datastax.driver.core.Cluster] (cluster1-worker-2851) Re-preparing already prepared query is generally an anti-pattern and will likely affect performance. Consider preparing the statement only once. Query='select * from xxxx where cjpid=? and cjpsnapshotid =?' 

创建一个PreparedStatement对象池,每个CQL查询一个对象。

然后,当客户端请求这些查询时,通过调用bind()获取相应的缓存PS对象并提供值。

正如Daniel所解释的那样, bind()执行new BoundStmt(param) ,使这个线程安全。