java Riutilizzare PreparedStatement quando si utilizza Datastax Cassandra Driver?




datastax cassandra driver maven (4)

Attualmente sto usando il driver Cassasta di Datastax per Cassandra 2 per eseguire cql3. Funziona correttamente Ho iniziato a utilizzare 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);
    }
}

A volte ricevo un avviso dal conducente nel mio log:

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.

Questo avvertimento ha senso, ma non sono sicuro di come dovrei riutilizzare il PreparedStatement ?

Dovrei semplicemente creare tutto il mio PreparedStatement in un metodo costruttore / init e semplicemente usarli?

Ma va bene quando più thread utilizzano lo stesso PreparedStatement allo stesso tempo (in particolare chiamando PreparedStatement.bind() per associare oggetti)


Potresti semplicemente inizializzare PreparedStatement una volta e metterlo in cache mentre l'app è in esecuzione. Dovrebbe essere disponibile per l'uso finché il cluster Cassandra è attivo.

L'uso dell'istruzione da più thread va bene (a condizione che non lo si modifichi attraverso i metodi setXXX() ). Quando si chiama bind (), il codice sottostante legge solo PreparedStatement e quindi crea una nuova istanza di BoundStatement () che il thread del chiamante è quindi libero di modificare.

Ecco il codice sorgente , se sei curioso (cerca bind() ).


Stiamo usando cassandra in un'applicazione web con Spring. Nel nostro caso creiamo i PreparedStatements quando il bean che incapsula l'operazione su cf (il nostro repository) è istatato.

Qui hai uno snippet del codice che stiamo usando:

@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;
    } 

In questo modo le istruzioni preparate vengono riutilizzate ogni volta che eseguiamo il metodo findByProject.


La soluzione sopra funzionerà nel caso in cui lo spazio della chiave sia stato risolto. In caso di scenario multi-tenant, questa soluzione non è sufficiente. Ho semplicemente fatto nel modo seguente, dove lo spazio delle chiavi è passato come argomento.

Controllare lo spazio delle chiavi dall'istruzione preparata, se è uguale all'argomento passato, quindi non preparare l'istruzione poiché è già stata preparata in questo caso.

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

}

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 =?'

Creare un pool di oggetti PreparedStatement, uno per ogni query CQL.

Quindi, quando queste query vengono richieste dal client, recuperare il rispettivo oggetto PS memorizzato nella cache e fornire i valori chiamando bind() .

come spiegato da Daniel, bind() esegue un new BoundStmt(param) che rende questo thread sicuro.







datastax-java-driver