使用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)
,使这个线程安全。
- 获得Cassandra Writes背压的最佳方法是什么?
- 主线程java.lang.NoClassDefFoundError中的exception
- 如何在CQL 3预处理语句中绑定IN子句值?
- Apache Spark需要5到6分钟才能从Cassandra中简单计算1亿行
- 与Apache Cassandra的SoapUI JDBC连接
- Apache-Cassandra 0.8.2中的UnavailableException()
- 为何我无法连接到cassandra
- 如何使用datastax java驱动程序有效地使用批量写入cassandra?
- 使用Datastax Cassandra本机Java客户端管理不同一致性级别的最佳实践