2014-04-07 65 views
11

我目前使用Cassandra 2的Datastax Cassandra驅動程序來執行cql3。這工作正常。我開始使用PreparedStatement's使用Datastax Cassandra驅動程序時需要重複使用PreparedStatement嗎?

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()綁定對象)

回答

15

你可能只是初始化的PreparedStatement一次和應用程序運行時對其進行緩存。只要Cassandra集羣啓動,它應該可用。

從多個線程使用語句是好的(只要您不通過setXXX()方法修改它)。當您調用bind()時,下面的代碼只會讀取PreparedStatement,然後創建一個BoundStatement()的新實例,然後調用者線程可以隨意進行變異。

Here is the source code,如果您好奇(搜索bind())。

+0

感謝Daniel的回覆。當綁定被調用時創建和返回BoundStatement解釋了所有事情,現在我可以正確使用準備語句而不必擔心。 – TinusSky

7

我們在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時間準備的語句重複使用。

+0

歡迎使用準備好的語句以最佳實踐爲基礎展開一個漂亮的基於Spring的示例。 – TinusSky

0

上述解決方案將在密鑰空間固定的情況下工作。如果是多租戶方案,這種解決方案是不夠的。我只是按照以下方式做了,其中keyspace作爲參數傳遞。

檢查預處理語句中的鍵空間,如果它與傳遞參數相同,則不要準備語句,因爲它已經在這種情況下準備好了。

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()); 

} 
-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對象並提供值。

丹尼爾解釋說,bind()確實new BoundStmt(param)這使得這個線程安全。

相關問題