我目前正在使用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()
来绑定对象),这是否顺利呢?
答案 0 :(得分:17)
您可以只初始化PreparedStatement并在应用程序运行时缓存它。只要Cassandra集群启动,就应该可以使用它。
使用来自多个线程的语句很好(只要不通过setXXX()
方法修改它)。当你调用bind()时,下面的代码只读取PreparedStatement,然后创建一个BoundStatement()的新实例,然后调用者线程可以自由变异。
Here is the source code,如果您感到好奇(搜索bind()
)。
答案 1 :(得分:8)
我们在使用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时都会重用预准备语句。
答案 2 :(得分:0)
以上解决方案适用于密钥空间固定的情况。在多租户情况下,此解决方案是不够的。我只是按照以下方式执行,其中键空间作为参数传递。
检查准备好的语句中的键空间,如果它与传递的参数相同,则不要准备语句,因为在这种情况下它已经准备好了。
private BatchStatement eventBatch(List<SomeEvent> 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());
}
答案 3 :(得分:-2)
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)
使这个线程安全。