14

我目前正在使用 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?

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

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

4

4 回答 4

20

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

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

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

于 2014-04-07T16:23:50.367 回答
9

我们在带有 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 方法时都会重用准备好的语句。

于 2014-04-17T12:11:01.747 回答
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());

}
于 2015-07-30T13:16:52.867 回答
-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 查询一个。

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

正如丹尼尔所解释的那样,bind()new BoundStmt(param)使得这个线程安全。

于 2017-04-03T09:11:30.943 回答