Questu ùn hè ancu un scherzu, pare chì sta stampa particulare riflette più precisamente l'essenza di queste basa di dati, è à a fine serà chjaru perchè:
Sicondu DB-Engines Ranking, i dui basati di dati di colonna NoSQL più populari sò Cassandra (in seguitu CS) è HBase (HB).
Per a vuluntà di u destinu, a nostra squadra di gestione di carica di dati in Sberbank hà digià
In ogni casu, prima di passà à i risultati di e teste realizate, hè necessariu di discrìviri l'aspettu significativu di e cunfigurazioni di l'ambiente. U fattu hè chì CS pò esse usatu in un modu chì permette a perdita di dati. Quelli. questu hè quandu solu un servitore (node) hè rispunsevuli di e dati di una certa chjave, è se per una certa ragiuni falla, u valore di sta chjave serà persu. Per parechje attività ùn hè micca criticu, ma per u settore bancariu hè l'eccezzioni piuttostu chè a regula. In u nostru casu, hè impurtante avè parechje copie di dati per un almacenamentu affidabile.
Dunque, solu u modu operativu CS in u modu di replicazione triple hè statu cunsideratu, i.e. A creazione di u casu hè stata fatta cù i seguenti parametri:
CREATE KEYSPACE ks WITH REPLICATION = {'class' : 'NetworkTopologyStrategy', 'datacenter1' : 3};
In seguitu, ci sò dui modi per assicurà u livellu necessariu di coherenza. Regula generale:
NW + NR > RF
Chì significa chì u numeru di cunferma da i nodi quandu scrive (NW) più u numeru di cunferma da i nodi quandu leghje (NR) deve esse più grande di u fattore di replicazione. In u nostru casu, RF = 3, chì significa chì e seguenti opzioni sò adattati:
2 + 2 > 3
3 + 1 > 3
Siccomu hè fundamentalmente impurtante per noi di almacenà e dati in modu affidabile quantu pussibule, u schema 3 + 1 hè statu sceltu. Inoltre, HB travaglia nantu à un principiu simili, i.e. un tali paraguni sarà più ghjustu.
Hè da nutà chì DataStax hà fattu u cuntrariu in u so studiu, anu stabilitu RF = 1 per CS è HB (per l'ultimi cambiendu i paràmetri HDFS). Questu hè un aspettu veramente impurtante perchè l'impattu nantu à u rendiment CS in questu casu hè enormu. Per esempiu, a stampa quì sottu mostra l'aumentu di u tempu necessariu per carica dati in CS:
Quì vedemu i seguenti: i fili più cuncurrenti scrivenu dati, u più longu hè necessariu. Questu hè naturali, ma hè impurtante chì a degradazione di u rendiment per RF = 3 hè significativamente più altu. In altri palori, se scrivemu 4 fili in 5 tavule ognunu (20 in totale), allora RF = 3 perde circa 2 volte (150 seconde per RF = 3 versus 75 per RF = 1). Ma s'è no aumentemu a carica caricandu dati in 8 tavule cù 5 fili ognunu (40 in totale), allora a perdita di RF = 3 hè digià 2,7 volte (375 seconde versus 138).
Forse questu hè in parte u sicretu di a prova di carica di successu realizata da DataStax per CS, perchè per HB in u nostru stand cambià u fattore di replicazione da 2 à 3 ùn hà micca avutu alcun effettu. Quelli. i dischi ùn sò micca u collu di bottiglia HB per a nostra cunfigurazione. In ogni casu, ci sò parechje altre trappule quì, perchè deve esse nutatu chì a nostra versione di HB hè stata ligeramente patched è tweaked, l'ambienti sò completamente diffirenti, etc. Hè vale a nutà ancu chì forse ùn sò micca sapè cumu preparà CS currettamente è ci sò parechje modi più efficaci di travaglià cun ellu, è spergu chì avemu da sapè in i cumenti. Ma prima cosa prima.
Tutte e teste sò state eseguite nantu à un cluster hardware custituitu di 4 servitori, ognunu cù a seguente cunfigurazione:
CPU: Xeon E5-2680 v4 @ 2.40GHz 64 threads.
Dischi: 12 pezzi SATA HDD
versione java: 1.8.0_111
Versione CS: 3.11.5
paràmetri cassandra.ymlnum_tokens: 256
hinted_handoff_enabled: veru
hinted_handoff_throttle_in_kb: 1024
max_hints_delivery_threads: 2
directory_hints: /data10/cassandra/hints
hints_flush_period_in_ms: 10000
max_hints_file_size_in_mb: 128
batchlog_replay_throttle_in_kb: 1024
authenticator: AllowAllAuthenticator
autorisatore: AllowAllAuthorizer
role_manager: CassandraRoleManager
roles_validity_in_ms: 2000
permissions_validity_in_ms: 2000
credentials_validity_in_ms: 2000
partitioner: org.apache.cassandra.dht.Murmur3Partitioner
data_file_directories:
- /data1/cassandra/data # ogni repertoriu dataN hè un discu separatu
- /data2/cassandra/data
- /data3/cassandra/data
- /data4/cassandra/data
- /data5/cassandra/data
- /data6/cassandra/data
- /data7/cassandra/data
- /data8/cassandra/data
commitlog_directory: /data9/cassandra/commitlog
cdc_enabled: false
disk_failure_policy: ferma
commit_failure_policy: piantà
prepared_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
salvate_caches_directory: /data10/cassandra/saved_caches
commitlog_sync: periodic
commitlog_sync_period_in_ms: 10000
commitlog_segment_size_in_mb: 32
seed_provider:
- class_name: org.apache.cassandra.locator.SimpleSeedProvider
paràmetri:
- sementi: "*,*"
concurrent_reads: 256 # pruvatu 64 - nisuna differenza rimarcata
concurrent_writes: 256 # pruvatu 64 - nisuna differenza osservata
concurrent_counter_writes: 256 # pruvatu 64 - nisuna differenza rimarcata
concurrent_materialized_view_writes: 32
memtable_heap_space_in_mb: 2048 # pruvatu 16 GB - era più lento
memtable_allocation_type: heap_buffers
index_summary_capacity_in_mb:
index_summary_resize_interval_in_minutes: 60
trickle_fsync: false
trickle_fsync_interval_in_kb: 10240
Storage_port: 7000
ssl_storage_port: 7001
indirizzu_ascolta: *
indirizzu_diffusione: *
listen_on_broadcast_address: veru
internode_authenticator: org.apache.cassandra.auth.AllowAllInternodeAuthenticator
start_native_transport: veru
portu_nativu_trasportu: 9042
start_rpc: veru
indirizzu_rpc: *
rpc_port: 9160
rpc_keepalive: veru
rpc_server_type: sync
thrift_framed_transport_size_in_mb: 15
incremental_backups: false
snapshot_before_compaction: false
auto_snapshot: vera
column_index_size_in_kb: 64
column_index_cache_size_in_kb: 2
compattatori_concurrenti: 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: false
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
server_encryption_options:
internode_encryption: nimu
client_encryption_options:
attivatu : false
internode_compression: dc
inter_dc_tcp_nodelay: false
tracetype_query_ttl: 86400
tracetype_repair_ttl: 604800
enable_user_defined_functions: false
enable_scripted_user_defined_functions: false
windows_timer_interval: 1
transparent_data_encryption_options:
attivatu : false
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_pressure_enabled: false
enable_materialized_views: veru
enable_sasi_indexes: true
Impostazioni GC:
### Impostazioni CMS-XX:+UseParNewGC
-XX:+UseConcMarkSweepGC
-XX:+CMSParallelRemarkEnabled
-XX: SurvivorRatio = 8
-XX:MaxTenuringThreshold=1
-XX:CMSIinitiatingOccupancyFraction=75
-XX:+Use CMSInitiatingOccupancyOnly
-XX:CMSWaitDuration=10000
-XX:+CMSParallelInitialMarkEnabled
-XX:+CMSEdenChunksRecordAlways
-XX:+CMSClassUnloadingEnabled
A memoria di jvm.options hè stata attribuita 16Gb (avemu ancu pruvatu 32 Gb, ùn ci hè nudda differenza).
I tavulini sò stati creati cù u cumandimu:
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 (in a classa org.apache.hadoop.hbase.regionserver.HRegion avemu esclusu MetricsRegion chì hà purtatu à GC quandu u numeru di regioni era più di 1000 in RegionServer)
Parametri HBase non predeterminatizookeeper.session.timeout: 120000
hbase.rpc.timeout: 2 minuti (s)
hbase.client.scanner.timeout.period: 2 minuti (s)
hbase.master.handler.count: 10
hbase.regionserver.lease.period, hbase.client.scanner.timeout.period: 2 minutu(s)
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.compactionThreshold: 5
hbase.hstore.blockingStoreFiles: 200
hbase.hregion.majorcompaction: 1 ghjornu(i)
HBase Service Advanced Configuration Snippet (Valve di Sicurezza) per hbase-site.xml:
hbase.regionserver.wal.codecorg.apache.hadoop.hbase.regionserver.wal.IndexedWALEditCodec
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 minutu(s)
hbase.snapshot.region.timeout: 2 minutu(s)
hbase.snapshot.master.timeout.millis: 2 minutu(s)
HBase REST Server Max Log Size: 100 MiB
HBase REST Server Massima copia di salvezza di i file di log: 5
HBase Thrift Server Max Log Size: 100 MiB
HBase Thrift Server Backup Massimu di File Log: 5
Dimensione massima di u logu maestru: 100 MiB
Backup Massimu di File Log Master: 5
RegionServer Max Log Size: 100 MiB
RegionServer Backup Massimu di File Log: 5
HBase Active Master Detection Finestra: 4 minuti (s)
dfs.client.hedged.read.threadpool.size: 40
dfs.client.hedged.read.threshold.millis: 10 millisecondi (s)
hbase.rest.threads.min: 8
hbase.rest.threads.max: 150
Descriptori Massimu di Prucessu File: 180000
hbase.thrift.minWorkerThreads: 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.size: 20
Threads di Mover di Regione: 6
Client Java Heap Size in Byte: 1 GiB
HBase REST Server Default Group: 3 GiB
HBase Thrift Server Gruppu Default: 3 GiB
Java Heap Size di HBase Master in Byte: 16 GiB
Dimensione Java Heap di HBase RegionServer in Byte: 32 GiB
+ ZooKeeper
maxClientCnxns: 601
maxSessionTimeout: 120000
Creazione di tavule:
hbase org.apache.hadoop.hbase.util.RegionSplitter ns:t1 UniformSplit -c 64 -f cf
altera 'ns:t1', {NAME => 'cf', DATA_BLOCK_ENCODING => 'FAST_DIFF', COMPRESSION => 'GZ'}
Ci hè un puntu impurtante quì - a descrizzione di DataStax ùn dice micca quantu regioni sò stati utilizati per creà e tavule HB, ancu s'ellu hè criticu per grandi volumi. Dunque, per i testi, a quantità = 64 hè stata scelta, chì permette di almacenà finu à 640 GB, i.e. tavola di taglia media.
À u mumentu di a prova, HBase avia 22 mila tavule è 67 mila regioni (questu saria statu letale per a versione 1.2.0 se ùn era micca per u patch citatu sopra).
Avà per u codice. Siccomu ùn era micca chjaru chì cunfigurazioni eranu più vantaghji per una basa di dati particulari, i testi sò stati realizati in diverse cumminazzioni. Quelli. in certi testi, 4 tavule sò stati caricati simultaneamente (tutti i nodi 4 sò stati utilizati per a cunnessione). In altri testi avemu travagliatu cù 8 diverse tavule. In certi casi, a dimensione di batch era 100, in altri 200 (parametru batch - vede u codice sottu). A dimensione di dati per u valore hè 10 bytes o 100 bytes (dataSize). In totale, 5 milioni di registri sò stati scritti è leghje in ogni tavula ogni volta. À u listessu tempu, 5 fili sò stati scritti / leghje à ogni tavula (numeru di filu - thNum), ognuna di quale hà utilizatu a so propria gamma di chjave (count = 1 million):
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);
}
}
In cunseguenza, una funziunalità simili hè stata furnita per HB:
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);
}
}
Siccomu in HB u cliente deve piglià cura di a distribuzione uniforme di dati, a funzione chjave di salatura pareva cusì:
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);
}
Avà a parte più interessante - i risultati:
A stessa cosa in forma di graficu:
U vantaghju di HB hè cusì surprisante chì ci hè un suspettu chì ci hè un tipu di bottleneck in a cunfigurazione CS. Tuttavia, Google è a ricerca di i paràmetri più evidenti (cum'è concurrent_writes o memtable_heap_space_in_mb) ùn anu micca acceleratu e cose. À u listessu tempu, i ghjurnali sò puliti è ùn ghjurà nunda.
I dati sò stati distribuiti uniformemente in i nodi, e statistiche di tutti i nodi eranu apprussimatamente listessi.
Questu hè ciò chì e statistiche di a tavula pareanu da unu di i nodiKeyspace: ks
Conte di lettura: 9383707
Latenza di lettura: 0.04287025042448576 ms
Scrive Count: 15462012
Latenza di scrittura: 0.1350068438699957 ms
Flushes pendenti: 0
Tabella: t1
SSTable count: 16
Spaziu utilizatu (live): 148.59 MiB
Spaziu utilizatu (totale): 148.59 MiB
Spaziu utilizatu da i snapshots (totale): 0 bytes
Memoria off heap utilizata (totale): 5.17 MiB
Rapportu di Cumpressione SSTable: 0.5720989576459437
Numaru di partizioni (stima): 3970323
Conte di cellule memorabile: 0
Dimensione di dati memorabile: 0 byte
Memtable off memoria heap utilizata: 0 byte
Conte di interruttore memorabile: 5
Conte di lettura locale: 2346045
Latenza di lettura locale: NaN ms
Conte di scrittura lucale: 3865503
Latenza di scrittura locale: NaN ms
Flush in attesa: 0
Percentuale riparatu: 0.0
Filtru Bloom falsi pusitivi: 25
Filtru di Bloom false ratio: 0.00000
Spaziu di filtru Bloom utilizatu: 4.57 MiB
Filtru Bloom off memoria di mucchio utilizata: 4.57 MiB
Riassuntu di l'indici fora di a memoria heap utilizata: 590.02 KiB
Metadati di cumpressione fora di a memoria heap utilizata: 19.45 KiB
Byte minimi di partizioni compattate: 36
Byte massimi di partizioni compattate: 42
Byte mediu di partizioni compattate: 42
Medie di cellule vive per fetta (ultimi cinque minuti): NaN
Massimu di cellule vive per fetta (ultimi cinque minuti): 0
Medie tombstones per fetta (ultimi cinque minuti): NaN
Lapidi massimi per fetta (ultimi cinque minuti): 0
Mutazioni eliminate: 0 byte
Un tentativu di riduce a dimensione di u batch (ancu l'inviendu individualmente) ùn hà micca effettu, hè solu peghju. Hè pussibule chì in fattu questu hè veramente u rendiment massimu per CS, postu chì i risultati ottenuti per CS sò simili à quelli ottenuti per DataStax - circa centinaie di millaie di operazioni per seconda. Inoltre, se guardemu l'utilizazione di e risorse, vedemu chì CS usa assai più CPU è dischi:
A figura mostra l'utilizazione durante a corsa di tutti i testi in una fila per e duie basa di dati.
In quantu à u putente vantaghju di lettura di HB. Quì pudete vede chì per e duie basa di dati, l'utilizazione di u discu durante a lettura hè estremamente bassu (i testi di lettura sò a parte finale di u ciculu di teste per ogni basa di dati, per esempiu per CS questu hè da 15:20 à 15:40). In u casu di HB, u mutivu hè chjaru - a maiò parte di e dati pende in memoria, in u memstore, è alcuni sò cache in blockcache. In quantu à CS, ùn hè micca assai chjaru cumu si travaglia, ma u riciclamentu di u discu ùn hè micca visibile, ma solu in casu, un tentativu hè statu fattu per attivà a cache row_cache_size_in_mb = 2048 è stabilisce caching = {'keys': 'ALL', 'rows_per_partition': '2000000'}, ma questu hà fattu ancu un pocu peghju.
Hè dinù vale à dì una volta di più un puntu impurtante circa u numeru di rigioni in HB. In u nostru casu, u valore hè statu specificatu cum'è 64. Se u reducià è fate uguali à, per esempiu, 4, dopu à leghje, a vitezza scende da 2 volte. U mutivu hè chì u memstore si riempia più veloce è i fugliali seranu sbulicati più spessu è quandu leghje, più fugliali anu da esse processati, chì hè una operazione abbastanza cumplicata per HB. In cundizioni reali, questu pò esse trattatu pensendu à una strategia di presplitting è di compactificazione; in particulare, usemu una utilità autoscritta chì raccoglie a basura è cumpressa HFiles constantemente in u sfondate. Hè abbastanza pussibule chì per e teste DataStax anu attribuitu solu 1 regione per tavula (chì ùn hè micca currettu) è questu un pocu chjarificà perchè HB era cusì inferjuri in i so testi di lettura.
I seguenti cunclusioni preliminari sò tratti da questu. Assumindu chì ùn sò micca sbagliati maiò durante a prova, allora Cassandra s'assumiglia à un colossu cù pedi d'argilla. Più precisamente, mentre ch'ella equilibra nantu à una gamba, cum'è in a stampa à u principiu di l'articulu, mostra risultati relativamente boni, ma in una lotta sottu à e stesse cundizioni, perde ghjustu. À u listessu tempu, tenendu in contu a bassa utilizazione di CPU nantu à u nostru hardware, avemu amparatu à plantà dui RegionServer HBs per host è cusì duppià u rendiment. Quelli. In cunsiderà l'utilizazione di risorse, a situazione per CS hè ancu più deplorable.
Di sicuru, sti testi sò abbastanza sintetici è a quantità di dati chì hè stata utilizata quì hè relativamente modesta. Hè pussibule chì, se avemu cambiatu à terabytes, a situazione seria diversa, ma mentre chì per HB pudemu carricà terabytes, per CS questu hè statu problematicu. Spessu hà lanciatu una OperationTimedOutException ancu cù questi volumi, ancu s'è i paràmetri per aspittà una risposta sò digià aumentati parechje volte cumparatu cù quelli predeterminati.
Spergu chì per mezu di i sforzi cumuni truvemu i buttiglii di CS è se pudemu accelerà, allora à a fine di u post aghjustà definitivamente infurmazioni nantu à i risultati finali.
UPD : Grazie à i cunsiglii di i camaradi, aghju riesciutu à accelerà a lettura. Era:
159 ops (644 tavule, 4 flussi, batch 5).
Aghjuntu:
.withLoadBalancingPolicy(nova TokenAwarePolicy(DCAwareRoundRobinPolicy.builder().build ()))
E aghju ghjucatu cù u numeru di fili. U risultatu hè u seguente:
4 tavule, 100 fili, batch = 1 (pezzu per pezzu): 301 ops
4 tavule, 100 fili, batch = 10: 447 ops
4 tavule, 100 fili, batch = 100: 625 ops
In seguitu appiccà altri cunsiglii di tuning, eseguite un ciclu di prova cumpletu è aghjunghje i risultati à a fine di u post.
Source: www.habr.com