Battaglia di due yakozuna, o Cassandra vs HBase. Esperienza del team Sberbank

Questo non è nemmeno uno scherzo, sembra che questa particolare immagine rifletta più accuratamente l'essenza di questi database, e alla fine sarà chiaro il motivo:

Battaglia di due yakozuna, o Cassandra vs HBase. Esperienza del team Sberbank

Secondo DB-Engines Ranking, i due database a colonne NoSQL più popolari sono Cassandra (di seguito CS) e HBase (HB).

Battaglia di due yakozuna, o Cassandra vs HBase. Esperienza del team Sberbank

Per volontà del destino, il nostro team di gestione del caricamento dei dati presso Sberbank lo ha già fatto lungo e lavora a stretto contatto con HB. Durante questo periodo, abbiamo studiato abbastanza bene i suoi punti di forza e di debolezza e abbiamo imparato a cucinarlo. Tuttavia, la presenza di un'alternativa sotto forma di CS ci ha sempre costretto a tormentarci un po' con i dubbi: abbiamo fatto la scelta giusta? Inoltre, i risultati confronto, eseguito da DataStax, hanno affermato che CS batte facilmente HB con un punteggio quasi schiacciante. D’altra parte, DataStax è una parte interessata e non dovresti credergli sulla parola. Eravamo anche confusi dalla quantità piuttosto piccola di informazioni sulle condizioni di test, quindi abbiamo deciso di scoprire da soli chi è il re di BigData NoSql, e i risultati ottenuti si sono rivelati molto interessanti.

Tuttavia, prima di passare ai risultati dei test eseguiti, è necessario descrivere gli aspetti significativi delle configurazioni ambientali. Il fatto è che CS può essere utilizzato in una modalità che consente la perdita di dati. Quelli. questo è quando solo un server (nodo) è responsabile dei dati di una determinata chiave e, se per qualche motivo fallisce, il valore di questa chiave andrà perso. Per molti compiti ciò non è fondamentale, ma per il settore bancario questa è l’eccezione piuttosto che la regola. Nel nostro caso, è importante disporre di diverse copie dei dati per un'archiviazione affidabile.

Pertanto è stata considerata solo la modalità operativa CS in modalità tripla replica, ovvero La realizzazione del casespace è stata effettuata con i seguenti parametri:

CREATE KEYSPACE ks WITH REPLICATION = {'class' : 'NetworkTopologyStrategy', 'datacenter1' : 3};

Successivamente, ci sono due modi per garantire il livello di coerenza richiesto. Regola generale:
NO + NR > RF

Ciò significa che il numero di conferme dai nodi in scrittura (NW) più il numero di conferme dai nodi in lettura (NR) devono essere maggiori del fattore di replica. Nel nostro caso RF = 3, il che significa che sono adatte le seguenti opzioni:
2 + 2 > 3
3 + 1 > 3

Poiché per noi è di fondamentale importanza archiviare i dati nel modo più affidabile possibile, è stato scelto lo schema 3+1. Inoltre, HB funziona secondo un principio simile, vale a dire un tale confronto sarà più giusto.

Va notato che DataStax ha fatto il contrario nel loro studio, impostando RF = 1 sia per CS che per HB (per quest'ultimo modificando le impostazioni HDFS). Questo è un aspetto davvero importante perché l'impatto sulle prestazioni CS in questo caso è enorme. Ad esempio, l'immagine seguente mostra l'aumento del tempo necessario per caricare i dati in CS:

Battaglia di due yakozuna, o Cassandra vs HBase. Esperienza del team Sberbank

Qui vediamo quanto segue: più thread concorrenti scrivono dati, più tempo impiega. Ciò è naturale, ma è importante che il degrado delle prestazioni per RF=3 sia significativamente più elevato. In altre parole, se scriviamo 4 thread in 5 tabelle ciascuno (20 in totale), allora RF=3 perde circa 2 volte (150 secondi per RF=3 contro 75 per RF=1). Ma se aumentiamo il carico caricando i dati in 8 tabelle con 5 thread ciascuna (40 in totale), la perdita di RF=3 è già 2,7 volte (375 secondi contro 138).

Forse questo è in parte il segreto del successo del test di carico eseguito da DataStax per CS, perché per HB al nostro stand la modifica del fattore di replica da 2 a 3 non ha avuto alcun effetto. Quelli. i dischi non sono il collo di bottiglia dell'HB per la nostra configurazione. Tuttavia, ci sono molte altre insidie ​​​​qui, perché va notato che la nostra versione di HB è stata leggermente modificata e modificata, gli ambienti sono completamente diversi, ecc. Vale anche la pena notare che forse semplicemente non so come preparare correttamente CS e ci sono alcuni modi più efficaci per lavorarci, e spero che lo scopriremo nei commenti. Ma prima le cose principali.

Tutti i test sono stati eseguiti su un cluster hardware composto da 4 server, ciascuno con la seguente configurazione:

CPU: Xeon E5-2680 v4 a 2.40 GHz 64 thread.
Dischi: HDD SATA da 12 pezzi
versione Java: 1.8.0_111

Versione CS: 3.11.5

parametri cassandra.ymlnum_token: 256
hinted_handoff_enabled: vero
hinted_handoff_throttle_in_kb: 1024
max_hints_delivery_threads: 2
directory_hints: /data10/cassandra/hints
suggest_flush_period_in_ms: 10000
max_hints_file_size_in_mb: 128
batchlog_replay_throttle_in_kb: 1024
autenticatore: EnableAllAuthenticator
autorizzazione: EnableAllAuthorizer
role_manager: CassandraRoleManager
roles_validity_in_ms: 2000
permessi_validità_in_ms: 2000
credenziali_validità_in_ms: 2000
partizionatore: org.apache.cassandra.dht.Murmur3Partitioner
directory_file_dati:
- /data1/cassandra/data # ogni directory dataN è un disco separato
- /data2/cassandra/data
- /data3/cassandra/data
- /data4/cassandra/data
- /data5/cassandra/data
- /data6/cassandra/data
- /data7/cassandra/data
- /data8/cassandra/data
directory_commitlog: /data9/cassandra/commitlog
cdc_enabled: falso
disk_failure_policy: ferma
commit_failure_policy: fermati
ready_statements_cache_size_mb:
thrift_prepared_statements_cache_size_mb:
key_cache_size_in_mb:
key_cache_save_period: 14400
row_cache_size_in_mb: 0
row_cache_save_period: 0
counter_cache_size_in_mb:
counter_cache_save_period: 7200
directory_salvata_caches: /data10/cassandra/saved_caches
commitlog_sync: periodico
commitlog_sync_period_in_ms: 10000
commitlog_segment_size_in_mb: 32
fornitore_seme:
- nome_classe: org.apache.cassandra.locator.SimpleSeedProvider
parametri:
- semi: "*,*"
concurrent_reads: 256 # provato 64 - nessuna differenza notata
concurrent_writes: 256 # provato 64 - nessuna differenza notata
concurrent_counter_writes: 256 # provato 64 - nessuna differenza notata
concurrent_materialized_view_writes: 32
memtable_heap_space_in_mb: 2048 # ho provato 16 GB - era più lento
memtable_allocation_type: heap_buffers
index_summary_capacity_in_mb:
index_summary_resize_interval_in_minutes: 60
trickle_fsync: falso
trickle_fsync_interval_in_kb: 10240
porta_archiviazione: 7000
porta_storage_ssl: 7001
indirizzo_ascolto: *
indirizzo_trasmissione: *
listen_on_broadcast_address: vero
internode_authenticator: org.apache.cassandra.auth.AllowAllInternodeAuthenticator
start_native_transport: vero
porta_trasporto_nativa: 9042
start_rpc: vero
indirizzo_rpc: *
porta_rpc: 9160
rpc_keepalive: vero
rpc_server_type: sincronizzazione
thrift_framed_transport_size_in_mb: 15
backup_incrementali: falso
snapshot_before_compaction: falso
auto_snapshot: vero
colonna_indice_dimensione_in_kb: 64
column_index_cache_size_in_kb: 2
concurrent_compattatori: 4
compaction_throughput_mb_per_sec: 1600
sstable_preemptive_open_interval_in_mb: 50
read_request_timeout_in_ms: 100000
range_request_timeout_in_ms: 200000
write_request_timeout_in_ms: 40000
counter_write_request_timeout_in_ms: 100000
cas_contention_timeout_in_ms: 20000
truncate_request_timeout_in_ms: 60000
request_timeout_in_ms: 200000
slow_query_log_timeout_in_ms: 500
cross_node_timeout: falso
endpoint_snitch: GossipingPropertyFileSnitch
Dynamic_snitch_update_interval_in_ms: 100
Dynamic_snitch_reset_interval_in_ms: 600000
Dynamic_snitch_badness_threshold: 0.1
request_scheduler: org.apache.cassandra.scheduler.NoScheduler
opzioni_crittografia_server:
internode_encryption: nessuno
client_encryption_options:
abilitato: falso
internodo_compressione: dc
inter_dc_tcp_nodelay: falso
tracetype_query_ttl: 86400
tracetype_repair_ttl: 604800
abilita_utente_definito_funzioni: falso
abilita_scripted_user_define_functions: falso
intervallo_timer_finestre: 1
trasparente_data_encryption_options:
abilitato: falso
tombstone_warn_threshold: 1000
tombstone_failure_threshold: 100000
batch_size_warn_threshold_in_kb: 200
batch_size_fail_threshold_in_kb: 250
unlogged_batch_across_partitions_warn_threshold: 10
compaction_large_partition_warning_threshold_mb: 100
gc_warn_threshold_in_ms: 1000
back_pression_enabled: falso
Enable_materialized_views: vero
abilita_sasi_indexes: vero

Impostazioni GC:

### Impostazioni CMS-XX:+UsaParNuovoGC
-XX: + UseConcMarkSweepGC
-XX:+CMSParallelOsservazioneAbilitato
-XX:Rapporto Sopravvivenza=8
-XX: Soglia massima di mantenimento = 1
-XX:CMSIinitiatingOccupancyFraction=75
-XX:+UsaCMSInitiatingOccupancyOnly
-XX:CMSWaitDurata=10000
-XX:+CMSParallelInitialMarkAbilitato
-XX:+CMSEdenChunksRegistraSempre
-XX:+CMSClassUnloadingAbilitato

Alla memoria jvm.options sono stati allocati 16Gb (abbiamo provato anche 32 Gb, non è stata notata alcuna differenza).

Le tabelle sono state create con il comando:

CREATE TABLE ks.t1 (id bigint PRIMARY KEY, title text) WITH compression = {'sstable_compression': 'LZ4Compressor', 'chunk_length_kb': 64};

Versione HB: 1.2.0-cdh5.14.2 (nella classe org.apache.hadoop.hbase.regionserver.HRegion abbiamo escluso MetricsRegion che portava a GC quando il numero di regioni era superiore a 1000 su RegionServer)

Parametri HBase non predefinitizookeeper.session.timeout: 120000
hbase.rpc.timeout: 2 minuti
hbase.client.scanner.timeout.period: 2 minuti
hbase.master.handler.count: 10
hbase.regionserver.lease.period, hbase.client.scanner.timeout.period: 2 minuti
hbase.regionserver.handler.count: 160
hbase.regionserver.metahandler.count: 30
hbase.regionserver.logroll.period: 4 ora/e
hbase.regionserver.maxlogs: 200
hbase.hregion.memstore.flush.size: 1 GiB
hbase.hregion.memstore.block.multiplier: 6
hbase.hstore.compactionSoglia: 5
hbase.hstore.blockingStoreFiles: 200
hbase.hregion.majorcompaction: 1 giorno/i
Snippet di configurazione avanzata del servizio HBase (valvola di sicurezza) per hbase-site.xml:
hbase.regionserver.wal.codecorg.apache.hadoop.hbase.regionserver.wal.IndexedWALEEditCodec
hbase.master.namespace.init.timeout3600000
hbase.regionserver.optionalcacheflushinterval18000000
hbase.regionserver.thread.compaction.large12
hbase.regionserver.wal.enablecompressiontrue
hbase.hstore.compaction.max.size1073741824
hbase.server.compactchecker.interval.multiplier200
Opzioni di configurazione Java per HBase RegionServer:
-XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=70 -XX:+CMSParallelRemarkEnabled -XX:ReservedCodeCacheSize=256m
hbase.snapshot.master.timeoutMillis: 2 minuti
hbase.snapshot.region.timeout: 2 minuti
hbase.snapshot.master.timeout.millis: 2 minuti
Dimensione massima registro server REST HBase: 100 MiB
Numero massimo di backup dei file di registro del server REST HBase: 5
Dimensione massima registro server HBase Thrift: 100 MiB
Numero massimo di backup dei file di registro del server HBase Thrift: 5
Dimensione massima registro master: 100 MiB
Numero massimo di backup principali dei file di registro: 5
Dimensione massima registro RegionServer: 100 MiB
Numero massimo di backup dei file di registro di RegionServer: 5
Finestra di rilevamento del master attivo HBase: 4 minuti
dfs.client.hedged.read.threadpool.size: 40
dfs.client.hedged.read.threshold.millis: 10 millisecondi
hbase.rest.threads.min: 8
hbase.rest.threads.max: 150
Numero massimo di descrittori di file di processo: 180000
hbase.thrift.minWorkerThread: 200
hbase.master.executor.openregion.threads: 30
hbase.master.executor.closeregion.threads: 30
hbase.master.executor.serverops.threads: 60
hbase.regionserver.thread.compaction.small: 6
hbase.ipc.server.read.threadpool.dimensione: 20
Discussioni del motore di regione: 6
Dimensioni heap Java del client in byte: 1 GiB
Gruppo predefinito del server REST HBase: 3 GiB
Gruppo predefinito del server HBase Thrift: 3 GiB
Dimensioni heap Java del master HBase in byte: 16 GiB
Dimensione heap Java del RegionServer HBase in byte: 32 GiB

+ZooKeeper
maxClientCnxns: 601
maxSessionTimeout: 120000
Creazione di tabelle:
hbase org.apache.hadoop.hbase.util.RegionSplitter ns:t1 UniformSplit -c 64 -f cf
altera 'ns:t1', {NAME => 'cf', DATA_BLOCK_ENCODING => 'FAST_DIFF', COMPRESSIONE => 'GZ'}

C'è un punto importante qui: la descrizione di DataStax non dice quante regioni sono state utilizzate per creare le tabelle HB, sebbene questo sia fondamentale per grandi volumi. Pertanto per i test è stata scelta la quantità = 64, che consente di archiviare fino a 640 GB, ovvero tavolo di medie dimensioni.

Al momento del test HBase contava 22mila tabelle e 67mila regioni (questo sarebbe stato letale per la versione 1.2.0 se non fosse stato per la patch sopra menzionata).

Ora per il codice. Poiché non era chiaro quali configurazioni fossero più vantaggiose per un determinato database, i test sono stati eseguiti in diverse combinazioni. Quelli. in alcuni test sono state caricate 4 tabelle contemporaneamente (per la connessione sono stati utilizzati tutti e 4 i nodi). In altri test abbiamo lavorato con 8 tabelle diverse. In alcuni casi, la dimensione del batch era 100, in altri 200 (parametro batch - vedere il codice di seguito). La dimensione dei dati per valore è 10 byte o 100 byte (dataSize). In totale, ogni volta sono stati scritti e letti 5 milioni di record in ciascuna tabella. Allo stesso tempo, su ciascuna tabella sono stati scritti/letti 5 thread (numero di thread - thNum), ognuno dei quali utilizzava il proprio intervallo di chiavi (count = 1 milione):

if (opType.equals("insert")) {
    for (Long key = count * thNum; key < count * (thNum + 1); key += 0) {
        StringBuilder sb = new StringBuilder("BEGIN BATCH ");
        for (int i = 0; i < batch; i++) {
            String value = RandomStringUtils.random(dataSize, true, true);
            sb.append("INSERT INTO ")
                    .append(tableName)
                    .append("(id, title) ")
                    .append("VALUES (")
                    .append(key)
                    .append(", '")
                    .append(value)
                    .append("');");
            key++;
        }
        sb.append("APPLY BATCH;");
        final String query = sb.toString();
        session.execute(query);
    }
} else {
    for (Long key = count * thNum; key < count * (thNum + 1); key += 0) {
        StringBuilder sb = new StringBuilder("SELECT * FROM ").append(tableName).append(" WHERE id IN (");
        for (int i = 0; i < batch; i++) {
            sb = sb.append(key);
            if (i+1 < batch)
                sb.append(",");
            key++;
        }
        sb = sb.append(");");
        final String query = sb.toString();
        ResultSet rs = session.execute(query);
    }
}

Di conseguenza, per HB è stata fornita una funzionalità simile:

Configuration conf = getConf();
HTable table = new HTable(conf, keyspace + ":" + tableName);
table.setAutoFlush(false, false);
List<Get> lGet = new ArrayList<>();
List<Put> lPut = new ArrayList<>();
byte[] cf = Bytes.toBytes("cf");
byte[] qf = Bytes.toBytes("value");
if (opType.equals("insert")) {
    for (Long key = count * thNum; key < count * (thNum + 1); key += 0) {
        lPut.clear();
        for (int i = 0; i < batch; i++) {
            Put p = new Put(makeHbaseRowKey(key));
            String value = RandomStringUtils.random(dataSize, true, true);
            p.addColumn(cf, qf, value.getBytes());
            lPut.add(p);
            key++;
        }
        table.put(lPut);
        table.flushCommits();
    }
} else {
    for (Long key = count * thNum; key < count * (thNum + 1); key += 0) {
        lGet.clear();
        for (int i = 0; i < batch; i++) {
            Get g = new Get(makeHbaseRowKey(key));
            lGet.add(g);
            key++;
        }
        Result[] rs = table.get(lGet);
    }
}

Poiché in HB il cliente deve occuparsi della distribuzione uniforme dei dati, la funzione di key salting si presentava così:

public static byte[] makeHbaseRowKey(long key) {
    byte[] nonSaltedRowKey = Bytes.toBytes(key);
    CRC32 crc32 = new CRC32();
    crc32.update(nonSaltedRowKey);
    long crc32Value = crc32.getValue();
    byte[] salt = Arrays.copyOfRange(Bytes.toBytes(crc32Value), 5, 7);
    return ArrayUtils.addAll(salt, nonSaltedRowKey);
}

Ora la parte più interessante: i risultati:

Battaglia di due yakozuna, o Cassandra vs HBase. Esperienza del team Sberbank

La stessa cosa in forma grafica:

Battaglia di due yakozuna, o Cassandra vs HBase. Esperienza del team Sberbank

Il vantaggio dell'HB è così sorprendente che c'è il sospetto che ci sia una sorta di collo di bottiglia nella configurazione CS. Tuttavia, cercare su Google e cercare i parametri più ovvi (come concurrent_writes o memtable_heap_space_in_mb) non ha accelerato le cose. Allo stesso tempo, i tronchi sono puliti e non giurano nulla.

I dati erano distribuiti uniformemente tra i nodi, le statistiche di tutti i nodi erano più o meno le stesse.

Ecco come appaiono le statistiche della tabella da uno dei nodiSpazio chiave: ks
Conteggio letture: 9383707
Latenza di lettura: 0.04287025042448576 ms
Conteggio scritture: 15462012
Latenza di scrittura: 0.1350068438699957 ms
Scarichi in sospeso: 0
Tabella: t1
Conteggio SSTable: 16
Spazio utilizzato (live): 148.59 MiB
Spazio utilizzato (totale): 148.59 MiB
Spazio utilizzato dagli snapshot (totale): 0 byte
Memoria off heap utilizzata (totale): 5.17 MiB
Rapporto di compressione SSTable: 0.5720989576459437
Numero di partizioni (stima): 3970323
Conteggio delle cellule memorizzabili: 0
Dimensione dei dati memorizzabili: 0 byte
Memoria off heap memorizzabile utilizzata: 0 byte
Conteggio interruttori memorizzabili: 5
Conteggio letture locali: 2346045
Latenza di lettura locale: NaN ms
Conteggio scritture locali: 3865503
Latenza di scrittura locale: NaN ms
Scarichi in sospeso: 0
Percentuale riparata: 0.0
Falsi positivi del filtro Bloom: 25
Rapporto falsi del filtro Bloom: 0.00000
Spazio utilizzato nel filtro Bloom: 4.57 MiB
Filtro Bloom dalla memoria heap utilizzata: 4.57 MiB
Riepilogo dell'indice della memoria off heap utilizzata: 590.02 KiB
Metadati di compressione fuori memoria heap utilizzata: 19.45 KiB
Byte minimi della partizione compattata: 36
Byte massimi della partizione compattata: 42
Byte medi della partizione compattata: 42
Cellule vive medie per fetta (ultimi cinque minuti): NaN
Numero massimo di cellule vive per fetta (ultimi cinque minuti): 0
Tombstone medi per sezione (ultimi cinque minuti): NaN
Numero massimo di lapidi per sezione (ultimi cinque minuti): 0
Mutazioni rilasciate: 0 byte

Un tentativo di ridurre la dimensione del lotto (anche inviandolo individualmente) non ha avuto alcun effetto, è solo peggiorato. È possibile che in realtà questa sia davvero la prestazione massima per CS, poiché i risultati ottenuti per CS sono simili a quelli ottenuti per DataStax: circa centinaia di migliaia di operazioni al secondo. Inoltre, se osserviamo l'utilizzo delle risorse, vedremo che CS utilizza molta più CPU e dischi:

Battaglia di due yakozuna, o Cassandra vs HBase. Esperienza del team Sberbank
La figura mostra l'utilizzo durante l'esecuzione di tutti i test di seguito per entrambi i database.

Per quanto riguarda il potente vantaggio di lettura di HB. Qui puoi vedere che per entrambi i database, l'utilizzo del disco durante la lettura è estremamente basso (i test di lettura sono la parte finale del ciclo di test per ciascun database, ad esempio per CS questo è dalle 15:20 alle 15:40). Nel caso di HB, il motivo è chiaro: la maggior parte dei dati è bloccata in memoria, nel memstore, e alcuni sono memorizzati nella cache nella blockcache. Per quanto riguarda CS, non è molto chiaro come funzioni, ma anche il riciclo del disco non è visibile, ma per ogni evenienza è stato fatto un tentativo di abilitare la cache row_cache_size_in_mb = 2048 e impostare caching = {'keys': 'ALL', 'rows_per_partition': ' 2000000'}, ma questo ha peggiorato ulteriormente le cose.

Vale anche la pena menzionare ancora una volta un punto importante relativo al numero di regioni in HB. Nel nostro caso, il valore è stato specificato come 64. Se lo riduci e lo rendi uguale, ad esempio, a 4, durante la lettura la velocità diminuisce di 2 volte. Il motivo è che il memstore si riempirà più velocemente e i file verranno scaricati più spesso e durante la lettura sarà necessario elaborare più file, il che è un'operazione piuttosto complicata per HB. In condizioni reali, questo può essere risolto pensando attraverso una strategia di presplitting e compattazione; in particolare, utilizziamo un'utilità autoprodotta che raccoglie spazzatura e comprime HFiles costantemente in background. È del tutto possibile che per i test DataStax abbiano assegnato solo 1 regione per tabella (il che non è corretto) e questo chiarirebbe in qualche modo perché HB era così inferiore nei loro test di lettura.

Da ciò si traggono le seguenti conclusioni preliminari. Supponendo che durante i test non siano stati commessi errori importanti, Cassandra sembra un colosso con i piedi d'argilla. Più precisamente, mentre è in equilibrio su una gamba, come nella foto all'inizio dell'articolo, mostra risultati relativamente buoni, ma in un combattimento nelle stesse condizioni perde completamente. Allo stesso tempo, tenendo conto del basso utilizzo della CPU sul nostro hardware, abbiamo imparato a installare due RegionServer HB per host e quindi a raddoppiare le prestazioni. Quelli. Se si tiene conto dell'utilizzo delle risorse, la situazione del CS è ancora più deplorevole.

Naturalmente, questi test sono piuttosto sintetici e la quantità di dati utilizzati in questo caso è relativamente modesta. È possibile che se passassimo ai terabyte la situazione sarebbe diversa, ma mentre per HB possiamo caricare terabyte, per CS questo si è rivelato problematico. Spesso lanciava una OperationTimedOutException anche con questi volumi, sebbene i parametri per l'attesa di una risposta fossero già aumentati più volte rispetto a quelli predefiniti.

Spero che attraverso sforzi congiunti troveremo i colli di bottiglia del CS e se riusciremo ad accelerarlo, alla fine del post aggiungerò sicuramente informazioni sui risultati finali.

UPD: Grazie ai consigli dei compagni sono riuscito ad accelerare la lettura. Era:
159 operazioni (644 tabelle, 4 stream, batch 5).
Inserito il:
.withLoadBalancingPolicy(new TokenAwarePolicy(DCAwareRoundRobinPolicy.builder().build()))
E ho giocato con il numero di thread. Il risultato è il seguente:
4 tavole, 100 thread, lotto = 1 (pezzo per pezzo): 301 op
4 tabelle, 100 thread, batch = 10: 447 op
4 tabelle, 100 thread, batch = 100: 625 op

Successivamente applicherò altri suggerimenti per l'ottimizzazione, eseguirò un ciclo di test completo e aggiungerò i risultati alla fine del post.

Fonte: habr.com

Aggiungi un commento