La batalla de los dos Yakozun, o Cassandra vs HBase. Experiencia del equipo de Sberbank

Esto ni siquiera es una broma, parece que esta imagen en particular refleja con mayor precisi贸n la esencia de estas bases de datos, y al final quedar谩 claro por qu茅:



Seg煤n el ranking DB-Engines, las dos bases de columnas NoSQL m谩s populares son Cassandra (en adelante CS) y HBase (HB).



Por voluntad del destino, nuestro equipo de gesti贸n de carga de datos en Sberbank ha estado trabajando estrechamente con HB durante mucho tiempo . Durante este tiempo, estudiamos sus fortalezas y debilidades bastante bien y aprendimos a cocinarlo. Sin embargo, la presencia de una alternativa en forma de CS todo el tiempo me hizo atormentarme con dudas: 驴tomamos la decisi贸n correcta? Adem谩s, los resultados de la comparaci贸n llevada a cabo por DataStax dijeron que CS f谩cilmente derrota a HB con una puntuaci贸n casi aplastante. Por otro lado, DataStax es una persona interesada, y no debe decir nada aqu铆. Adem谩s, una cantidad bastante peque帽a de informaci贸n sobre las condiciones de prueba era vergonzosa, por lo que decidimos averiguar por nuestra cuenta qui茅n es el rey de BigData NoSql, y los resultados fueron muy interesantes.

Sin embargo, antes de pasar a los resultados de las pruebas realizadas, es necesario describir los aspectos esenciales de las configuraciones del entorno. El hecho es que CS se puede usar en modo de tolerancia de p茅rdida de datos. Es decir Esto es cuando solo un servidor (nodo) es responsable de los datos de una determinada clave, y si se cae por alguna raz贸n, el valor de esta clave se perder谩. Para muchas tareas esto no es cr铆tico, pero para el sector bancario esta es la excepci贸n m谩s que la regla. En nuestro caso, es importante tener varias copias de datos para un almacenamiento confiable.

Por lo tanto, solo se consider贸 el modo CS de triple replicaci贸n, es decir, La creaci贸n de casos se realiz贸 con los siguientes par谩metros:

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

Adem谩s, hay dos formas de garantizar el nivel de consistencia requerido. Regla general:
NW + NR> RF

Esto significa que el n煤mero de confirmaciones de los nodos al escribir (NW) m谩s el n煤mero de confirmaciones de los nodos al leer (NR) debe ser mayor que el factor de replicaci贸n. En nuestro caso, RF = 3, por lo que las siguientes opciones son adecuadas:
2 + 2> 3
3 + 1> 3

Dado que es fundamental para nosotros mantener los datos lo m谩s confiables posible, se eligi贸 un esquema 3 + 1. Adem谩s, HB funciona de manera similar, es decir Tal comparaci贸n ser铆a m谩s honesta.

Cabe se帽alar que DataStax hizo lo contrario en su investigaci贸n, establecieron RF = 1 para CS y HB (para este 煤ltimo al cambiar la configuraci贸n de HDFS). Este es un aspecto realmente importante, porque el impacto en el rendimiento de CS en este caso es enorme. Por ejemplo, la siguiente imagen muestra el aumento de tiempo requerido para cargar datos en CS:



Aqu铆 vemos lo siguiente: mientras m谩s hilos de la competencia escriben datos, m谩s tiempo demora. Esto es natural, pero es importante que la degradaci贸n del rendimiento para RF = 3 sea significativamente mayor. En otras palabras, si escribimos en 4 tablas en cada una de las 5 transmisiones (un total de 20), entonces RF = 3 pierde aproximadamente 2 veces (150 segundos RF = 3 frente a 75 para RF = 1). Pero si aumentamos la carga al cargar datos en 8 tablas en cada una de las 5 transmisiones (un total de 40), perder RF = 3 ya es 2,7 veces (375 segundos frente a 138).

Quiz谩s en parte este es el secreto de la prueba exitosa de DataStax para la prueba de carga CS, porque para HB en nuestro stand, cambiar el factor de replicaci贸n de 2 a 3 no tuvo ning煤n efecto. Es decir Los discos no son el cuello de botella para HB para nuestra configuraci贸n. Sin embargo, hay muchas otras trampas, porque debe tenerse en cuenta que nuestra versi贸n de HB fue ligeramente parcheada y oscurecida, los entornos son completamente diferentes, etc. Tambi茅n vale la pena se帽alar que tal vez simplemente no s茅 c贸mo preparar CS correctamente y hay algunas formas m谩s efectivas de trabajar con 茅l y espero en los comentarios que descubramos. Pero lo primero es lo primero.

Todas las pruebas se realizaron en un cl煤ster de hierro que consta de 4 servidores, cada uno en una configuraci贸n:

CPU: Xeon E5-2680 v4 @ 2.40GHz 64 hilos.
Discos: 12 unidades de disco duro SATA
versi贸n de Java: 1.8.0_111


Versi贸n CS: 3.11.5

Par谩metros cassandra.yml
num_tokens: 256
hinted_handoff_enabled: true
hinted_handoff_throttle_in_kb: 1024
max_hints_delivery_threads: 2
directorio_consejos: / data10 / cassandra / consejos
hints_flush_period_in_ms: 10000
max_hints_file_size_in_mb: 128
batchlog_replay_throttle_in_kb: 1024
autenticador: AllowAllAuthenticator
autorizador: AllowAllAuthorizer
role_manager: CassandraRoleManager
roles_validity_in_ms: 2000
permisos_validez_en_ms: 2000
credentials_validity_in_ms: 2000
particionador: org.apache.cassandra.dht.Murmur3Partitioner
data_file_directories:
- / data1 / cassandra / data # cada directorio dataN es una unidad separada
- / data2 / cassandra / data
- / data3 / cassandra / data
- / data4 / cassandra / data
- / data5 / cassandra / data
- / data6 / cassandra / data
- / data7 / cassandra / data
- / data8 / cassandra / data
commit_directory: / data9 / cassandra / commitlog
cdc_enabled: false
disk_failure_policy: stop
commit_failure_policy: detener
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
directorio_cach茅s_ guardados: / data10 / cassandra / salva_cach茅s
commitlog_sync: peri贸dico
commitlog_sync_period_in_ms: 10000
commitlog_segment_size_in_mb: 32
seed_provider:
- class_name: org.apache.cassandra.locator.SimpleSeedProvider
par谩metros:
- semillas: "*, *"
concurrent_reads: 256 # intent贸 64 - no se not贸 diferencia
concurrent_writes: 256 # intent贸 64 - no se not贸 ninguna diferencia
concurrent_counter_writes: 256 # intent贸 64 - no se not贸 ninguna diferencia
concurrent_materialized_view_writes: 32
memtable_heap_space_in_mb: 2048 # intent贸 16 GB - fue m谩s 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
puerto_almacenamiento: 7000
ssl_storage_port: 7001
listen_address: *
broadcast_address: *
listen_on_broadcast_address: verdadero
internode_authenticator: org.apache.cassandra.auth.AllowAllInternodeAuthenticator
start_native_transport: true
native_transport_port: 9042
start_rpc: verdadero
rpc_address: *
rpc_port: 9160
rpc_keepalive: verdadero
rpc_server_type: sincronizaci贸n
thrift_framed_transport_size_in_mb: 15
incremental_backups: falso
snapshot_before_compaction: false
auto_snapshot: true
column_index_size_in_kb: 64
column_index_cache_size_in_kb: 2
concurrent_compactors: 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
opciones_encriptaci贸n_servidor:
cifrado internode: ninguno
opciones_cifrado_cliente:
habilitado: falso
internode_compression: dc
inter_dc_tcp_nodelay: falso
tracetype_query_ttl: 86400
tracetype_repair_ttl: 604800
enable_user_defined_functions: false
enable_scripted_user_defined_functions: false
windows_timer_interval: 1
opciones_encriptaci贸n_de_datos_transparentes:
habilitado: 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_pressure_enabled: false
enable_materialized_views: true
enable_sasi_indexes: true

Configuraci贸n de GC:

### Configuraci贸n de CMS
-XX: + UseParNewGC
-XX: + UseConcMarkSweepGC
-XX: + CMSParallelRemarkEnabled
-XX: SurvivorRatio = 8
-XX: MaxTenuringThreshold = 1
-XX: CMSInitiatingOccupancyFraction = 75
-XX: + UseCMSInitiatingOccupancyOnly
-XX: CMSWaitDuration = 10000
-XX: + CMSParallelInitialMarkEnabled
-XX: + CMSEdenChunksRecordAlways
-XX: + CMSClassUnloadingEnabled


La memoria jvm.options se asign贸 a 16 Gb (a煤n se intent贸 32 Gb, no se not贸 ninguna diferencia).

La creaci贸n de tablas fue realizada por el comando:

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

Versi贸n HB: 1.2.0-cdh5.14.2 (en la clase org.apache.hadoop.hbase.regionserver.HRegion excluimos MetricsRegion que condujo a GC con m谩s de 1000 regiones en RegionServer)

Opciones de HBase no predeterminadas
zookeeper.session.timeout: 120000
hbase.rpc.timeout: 2 minuto (s)
hbase.client.scanner.timeout.period: 2 minuto (s)
hbase.master.handler.count: 10
hbase.regionserver.lease.period, hbase.client.scanner.timeout.period: 2 minuto (s)
hbase.regionserver.handler.count: 160
hbase.regionserver.metahandler.count: 30
hbase.regionserver.logroll.period: 4 hora (s)
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 d铆a (s)
Fragmento de configuraci贸n avanzada del servicio HBase (v谩lvula de seguridad) para 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
Opciones de configuraci贸n de Java para HBase RegionServer:
-XX: + UseParNewGC -XX: + UseConcMarkSweepGC -XX: CMSInitiatingOccupancyFraction = 70 -XX: + CMSParallelRemarkEnabled -XX: ReservedCodeCacheSize = 256m
hbase.snapshot.master.timeoutMillis: 2 minuto (s)
hbase.snapshot.region.timeout: 2 minuto (s)
hbase.snapshot.master.timeout.millis: 2 minuto (s)
Tama帽o m谩ximo de registro del servidor REST de HBase: 100 MiB
HBase REST Server M谩ximo de copias de seguridad de archivos de registro: 5
Tama帽o m谩ximo de registro del servidor HBase Thrift: 100 MiB
HBase Thrift Server Respaldos m谩ximos de archivos de registro: 5
Tama帽o de registro maestro m谩ximo: 100 MiB
Master M谩ximo de copias de seguridad de archivos de registro: 5
Tama帽o de registro m谩ximo de RegionServer: 100 MiB
Registros de archivos de registro m谩ximos de RegionServer: 5
Ventana de detecci贸n de maestro activo de HBase: 4 minuto (s)
dfs.client.hedged.read.threadpool.size: 40
dfs.client.hedged.read.threshold.millis: 10 milisegundos (s)
hbase.rest.threads.min: 8
hbase.rest.threads.max: 150
Descriptores m谩ximos de archivos de proceso: 180,000
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
Hilos Mover Regi贸n: 6
Tama帽o de almacenamiento din谩mico Java del cliente en bytes: 1 GiB
Grupo predeterminado del servidor REST de HBase: 3 GiB
Grupo predeterminado del servidor HBase Thrift: 3 GiB
Tama帽o de almacenamiento din谩mico de Java de HBase Master en bytes: 16 GiB
Tama帽o de almacenamiento din谩mico de Java de HBase RegionServer en bytes: 32 GiB

+ ZooKeeper
maxClientCnxns: 601
maxSessionTimeout: 120000

Crear tablas:
hbase org.apache.hadoop.hbase.util.RegionSplitter ns: t1 UniformSplit -c 64 -f cf
alter 'ns: t1', {NAME => 'cf', DATA_BLOCK_ENCODING => 'FAST_DIFF', COMPRESSION => 'GZ'}

Hay un punto importante: la descripci贸n de DataStax no dice cu谩ntas regiones se usaron para crear las tablas HB, aunque esto es cr铆tico para grandes vol煤menes. Por lo tanto, para las pruebas, se eligi贸 el n煤mero = 64, que permite almacenar hasta 640 GB, es decir, Mesa de tama帽o mediano.

En el momento de la prueba, HBase ten铆a 22 mil tablas y 67 mil regiones (esto ser铆a mortal para la versi贸n 1.2.0, si no fuera por el parche mencionado anteriormente).

Ahora para el c贸digo. Como no estaba claro qu茅 configuraciones son m谩s ventajosas para una base de datos particular, las pruebas se llevaron a cabo en varias combinaciones. Es decir En algunas pruebas, la carga fue simult谩neamente a 4 tablas (se utilizaron los 4 nodos para la conexi贸n). En otras pruebas, trabajaron con 8 tablas diferentes. En algunos casos, el tama帽o del lote era 100, en otros 200 (par谩metro del lote - ver el c贸digo a continuaci贸n). El tama帽o de los datos para el valor es de 10 bytes o 100 bytes (tama帽o de datos). En total, se escribieron y restaron 5 millones de registros cada vez en cada tabla. Al mismo tiempo, se escribieron / leyeron 5 secuencias en cada tabla (el n煤mero de secuencia es thNum), cada una de las cuales utiliz贸 su propio rango de claves (cuenta = 1 mill贸n):

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

En consecuencia, se proporcion贸 una funcionalidad similar para 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); } } 

Dado que el cliente debe ocuparse de la distribuci贸n uniforme de los datos en HB, la funci贸n de salaz贸n clave se ve铆a as铆:

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

Ahora los m谩s interesantes son los resultados:



Lo mismo que un gr谩fico:



La ventaja de HB es tan sorprendente que existe la sospecha de alg煤n tipo de cuello de botella en la configuraci贸n de CS. Sin embargo, la b煤squeda en Google y la torsi贸n de los par谩metros m谩s obvios (como concurrent_writes o memtable_heap_space_in_mb) no dieron aceleraci贸n. Al mismo tiempo, los registros est谩n limpios, no juren por nada.

Los datos se distribuyen de manera uniforme en los nodos, las estad铆sticas de todos los nodos son aproximadamente las mismas.

Aqu铆 est谩n las estad铆sticas en la tabla con uno de los nodos
Keyspace: ks
Leer cuenta: 9383707
Latencia de lectura: 0.04287025042448576 ms
Cuenta de escritura: 15462012
Latencia de escritura: 0.1350068438699957 ms
Lavados pendientes: 0
Tabla: t1
Cuenta de tabla: 16
Espacio utilizado (en vivo): 148.59 MiB
Espacio utilizado (total): 148.59 MiB
Espacio utilizado por las instant谩neas (total): 0 bytes
Memoria de almacenamiento din谩mico utilizada (total): 5,17 MiB
Relaci贸n de compresi贸n SSTable: 0.5720989576459437
N煤mero de particiones (estimado): 3970323
Recuento celular memorable: 0
Tama帽o de datos memorables: 0 bytes
Memoria de almacenamiento din谩mico no utilizable: 0 bytes
Recuento de conmutadores memorables: 5
Cuenta de lectura local: 2346045
Latencia de lectura local: NaN ms
Recuento de escritura local: 3865503
Latencia de escritura local: NaN ms
Rubores pendientes: 0
Porcentaje reparado: 0.0
Filtro de Bloom falsos positivos: 25
Relaci贸n falsa del filtro Bloom: 0.00000
Espacio de filtro de Bloom utilizado: 4.57 MiB
Bloom filtro de memoria de mont贸n utilizada: 4.57 MiB
Resumen del 铆ndice de la memoria del mont贸n utilizada: 590.02 KiB
Metadatos de compresi贸n de la memoria del mont贸n utilizada: 19.45 KiB
Bytes m铆nimos de partici贸n compactada: 36
Bytes m谩ximos de partici贸n compactada: 42
Bytes medios de partici贸n compactada: 42
Promedio de c茅lulas vivas por corte (煤ltimos cinco minutos): NaN
M谩ximo de c茅lulas vivas por segmento (煤ltimos cinco minutos): 0
Promedio de l谩pidas por rebanada (煤ltimos cinco minutos): NaN
L谩pidas m谩ximas por rebanada (煤ltimos cinco minutos): 0
Mutaciones descartadas: 0 bytes

Un intento de reducir el tama帽o del lote (hasta el env铆o uno por uno) no tuvo efecto, solo empeor贸. De hecho, es posible que este sea realmente el m谩ximo rendimiento para CS, ya que los resultados obtenidos en CS son similares a los obtenidos para DataStax: alrededor de cientos de miles de operaciones por segundo. Adem谩s, si observa la utilizaci贸n de los recursos, ver谩 que CS usa mucha m谩s CPU y discos:


La figura muestra la utilizaci贸n durante la ejecuci贸n de todas las pruebas seguidas para ambas bases de datos.

En cuanto a los poderosos beneficios de lectura de HB. Se puede ver que para ambas bases de datos, la utilizaci贸n del disco durante la lectura es extremadamente baja (las pruebas de lectura son la parte final del ciclo de prueba para cada base de datos, por ejemplo, para CS de 15:20 a 15:40). En el caso de HB, la raz贸n es clara: la mayor铆a de los datos se cuelgan en la memoria, en el memstore, y algunos se almacenaron en cach茅 en la cach茅 de bloques. En cuanto a CS, no est谩 muy claro c贸mo funciona, sin embargo, la utilizaci贸n del disco tampoco es visible, pero por si acaso, se hizo un intento de activar el cach茅 row_cache_size_in_mb = 2048 y establecer el almacenamiento en cach茅 = {'keys': 'ALL', 'rows_per_partition': ' 2,000,000 '}, pero eso lo empeor贸 a煤n m谩s.

Tambi茅n vale una vez m谩s decir un punto significativo sobre el n煤mero de regiones en HB. En nuestro caso, se indic贸 el valor 64. Si lo reduce y lo iguala a, por ejemplo, 4, entonces, al leer, la velocidad se reduce 2 veces. La raz贸n es que memstore se atascar谩 m谩s r谩pido y los archivos se enjuagar谩n con m谩s frecuencia y, al leerlo, necesitar谩 procesar m谩s archivos, lo cual es una operaci贸n bastante complicada para HB. En condiciones reales, esto puede tratarse pensando en la estrategia de preplit y compactificaci贸n, en particular, utilizamos una utilidad de fabricaci贸n propia que recolecta basura y comprime HFiles constantemente en segundo plano. Es posible que para las pruebas DataStax, generalmente se asignara 1 regi贸n por tabla (lo cual no es correcto) y esto aclarar铆a un poco por qu茅 HB perdi贸 tanto en sus pruebas de lectura.

Las conclusiones preliminares de esto son las siguientes. Asumiendo que no se cometieron errores graves durante las pruebas, Cassandra es como un coloso con pies de arcilla. M谩s precisamente, mientras se balancea sobre una pierna, como en la imagen al comienzo del art铆culo, muestra resultados relativamente buenos, pero cuando pelea en las mismas condiciones, pierde directamente. Al mismo tiempo, teniendo en cuenta la baja utilizaci贸n de la CPU en nuestro hardware, aprendimos a plantar dos HB de RegionServer por host y, por lo tanto, duplicamos la productividad. Es decir Teniendo en cuenta la utilizaci贸n de recursos, la situaci贸n de CS es a煤n m谩s deplorable.

Por supuesto, estas pruebas son bastante sint茅ticas y la cantidad de datos que se utiliz贸 aqu铆 es relativamente modesta. Es posible que al cambiar a terabytes, la situaci贸n sea diferente, pero si para HB podemos cargar terabytes, entonces para CS esto result贸 ser problem谩tico. A menudo arroj贸 una OperationTimedOutException incluso con estos vol煤menes, aunque los par谩metros de expectativa de respuesta ya aumentaron varias veces en comparaci贸n con los predeterminados.

Espero que mediante esfuerzos conjuntos encontremos los cuellos de botella de CS y si logramos acelerarlo, entonces definitivamente agregar茅 informaci贸n sobre los resultados finales al final de la publicaci贸n.

UPD: Las siguientes pautas se aplicaron en la configuraci贸n de CS:

disk_optimization_strategy: spinning
MAX_HEAP_SIZE = "32G"
HEAP_NEWSIZE = "3200M"
-Xms32G
-Xmx32G
-XX: + UseG1GC
-XX: G1RSetUpdatingPauseTimePercent = 5
-XX: MaxGCPauseMillis = 500
-XX: InitiatingHeapOccupancyPercent = 70
-XX: ParallelGCThreads = 32
-XX: ConcGCThreads = 8

En cuanto a la configuraci贸n del sistema operativo, este es un procedimiento bastante largo y complicado (obtener root, reiniciar servidores, etc.), por lo que estas recomendaciones no se aplicaron. Por otro lado, ambas bases de datos est谩n en igualdad de condiciones, por lo que todo es justo.

En la parte del c贸digo, se crea un conector para todos los hilos que escriben en la tabla:
 connector = new CassandraConnector(); connector.connect(node, null, CL); session = connector.getSession(); session.getCluster().getConfiguration().getSocketOptions().setConnectTimeoutMillis(120000); KeyspaceRepository sr = new KeyspaceRepository(session); sr.useKeyspace(keyspace); prepared = session.prepare("insert into " + tableName + " (id, title) values (?, ?)"); 


Los datos se enviaron por enlace:
 for (Long key = count * thNum; key < count * (thNum + 1); key++) { String value = RandomStringUtils.random(dataSize, true, true); session.execute(prepared.bind(key, value)); } 


Esto no tuvo un impacto significativo en el rendimiento de grabaci贸n. Para mayor fiabilidad, lanc茅 la carga con la herramienta YCSB, absolutamente el mismo resultado. A continuaci贸n se muestran las estad铆sticas para un hilo (de 4):

2020-01-18 14: 41: 53: 180 315 segundos: 10,000,000 operaciones; 21589.1 operaciones actuales / seg; [LIMPIEZA: Cuenta = 100, M谩x = 2236415, M铆n = 1, Promedio = 22356.39, 90 = 4, 99 = 24, 99.9 = 2236415, 99.99 = 2236415] [INSERTAR: Cuenta = 119551, M谩x = 174463, M铆n = 273, Promedio = 2582.71, 90 = 3491, 99 = 16767, 99.9 = 99711, 99.99 = 171263]
[GENERAL], RunTime (ms), 315539
[GENERAL], rendimiento (ops / seg), 31691.803548848162
[TOTAL_GCS_PS_Scavenge], Cuenta, 161
[TOTAL_GC_TIME_PS_Scavenge], Tiempo (ms), 2433
[TOTAL_GC_TIME _% _ PS_Scavenge], Tiempo (%), 0.7710615803434757
[TOTAL_GCS_PS_MarkSweep], recuento, 0
[TOTAL_GC_TIME_PS_MarkSweep], Tiempo (ms), 0
[TOTAL_GC_TIME _% _ PS_MarkSweep], Tiempo (%), 0.0
[TOTAL_GCs], recuento, 161
[TOTAL_GC_TIME], tiempo (ms), 2433
[TOTAL_GC_TIME_%], Tiempo (%), 0.7710615803434757
[INSERTAR], Operaciones, 10,000,000
[INSERTAR], Promedio de latencia (us), 3114.2427012
[INSERTAR], MinLatency (nosotros), 269
[INSERTAR], MaxLatency (nosotros), 609279
[INSERTAR], 95thPercentileLatency (us), 5007
[INSERTAR], 99thPercentileLatency (nosotros), 33439
[INSERTAR], Retorno = OK, 10000000


Aqu铆 puede ver que la velocidad de una transmisi贸n es de aproximadamente 32 mil registros por segundo, 4 transmisiones funcionaron, resulta 128 mil. Parece que no hay nada m谩s que exprimir en la configuraci贸n actual del subsistema de disco.

Sobre leer m谩s interesante. Gracias al consejo de camaradas, pudo acelerar radicalmente. La lectura se realiz贸 no en 5 secuencias, sino en 100. Un aumento a 200 no produjo un efecto. Tambi茅n agregado al constructor:
.withLoadBalancingPolicy (nuevo TokenAwarePolicy (DCAwareRoundRobinPolicy.builder (). build ()))

Como resultado, si antes la prueba mostraba 159 644 operaciones (5 transmisiones, 4 tablas, 100 lotes), ahora:
100 hilos, 4 tablas, lote = 1 (individualmente): 301969 operaciones
100 hilos, 4 tablas, lote = 10: 447 608 operaciones
100 hilos, 4 tablas, lote = 100: 625 655 operaciones

Como los resultados son mejores con lotes, realic茅 pruebas similares * con HB:

* Dado que cuando se trabajaba en 400 subprocesos, la funci贸n RandomStringUtils, que se usaba anteriormente, cargaba la CPU en un 100%, fue reemplazada por un generador m谩s r谩pido.

Por lo tanto, un aumento en el n煤mero de subprocesos al cargar datos da un peque帽o aumento en el rendimiento de HB.

En cuanto a la lectura, aqu铆 est谩n los resultados de varias opciones. A pedido de 0x62ash , el comando flush se ejecut贸 antes de leer, y tambi茅n se ofrecen otras opciones para comparar:
Memstore: lectura de memoria, es decir antes de enjuagar al disco.
HFile + zip: lectura de archivos comprimidos por el algoritmo GZ.
HFile + upzip: lee archivos sin compresi贸n.

Una caracter铆stica interesante es notable: los archivos peque帽os (consulte el campo "Datos", donde se escriben 10 bytes) se procesan m谩s lentamente, especialmente si est谩n comprimidos. Obviamente, esto solo es posible hasta cierto tama帽o, obviamente, un archivo de 5 GB no se procesar谩 m谩s r谩pido que 10 MB, pero indica claramente que en todas estas pruebas todav铆a no hay un campo arado para investigar varias configuraciones.

Por inter茅s, correg铆 el c贸digo YCSB para trabajar con lotes HB de 100 piezas para medir la latencia y m谩s. A continuaci贸n se muestra el resultado del trabajo de 4 copias que escribieron en sus tablas, cada una con 100 hilos. Result贸 lo siguiente:
Una operaci贸n = 100 registros
[GENERAL], RunTime (ms), 1165415
[GENERAL], Rendimiento (ops / seg), 858.06343662987
[TOTAL_GCS_PS_Scavenge], Cuenta, 798
[TOTAL_GC_TIME_PS_Scavenge], Tiempo (ms), 7346
[TOTAL_GC_TIME _% _ PS_Scavenge], Tiempo (%), 0.6303334005483026
[TOTAL_GCS_PS_MarkSweep], cuenta, 1
[TOTAL_GC_TIME_PS_MarkSweep], Tiempo (ms), 74
[TOTAL_GC_TIME _% _ PS_MarkSweep], Tiempo (%), 0.006349669431061038
[TOTAL_GCs], recuento, 799
[TOTAL_GC_TIME], tiempo (ms), 7420
[TOTAL_GC_TIME_%], Tiempo (%), 0.6366830699793635
[INSERTAR], Operaciones, 1,000,000
[INSERTAR], Latencia promedio (us), 115893.891644
[INSERTAR], MinLatency (nosotros), 14528
[INSERTAR], MaxLatency (nosotros), 1470463
[INSERTAR], 95thPercentileLatency (us), 248319
[INSERTAR], 99thPercentileLatency (nosotros), 445951
[INSERTAR], Retorno = OK, 1,000,000

20/01/19 13:19:16 INFO client.ConnectionManager $ HConnectionImplementation: Closing zookeeper sessionid = 0x36f98ad0a4ad8cc
20/01/19 13:19:16 INFO zookeeper.ZooKeeper: Sesi贸n: 0x36f98ad0a4ad8cc cerrado
20/01/19 13:19:16 INFO zookeeper.ClientCnxn: EventThread cerrado
[GENERAL], RunTime (ms), 1165806
[GENERAL], Rendimiento (ops / seg), 857.7756504941646
[TOTAL_GCS_PS_Scavenge], Cuenta, 776
[TOTAL_GC_TIME_PS_Scavenge], Tiempo (ms), 7517
[TOTAL_GC_TIME _% _ PS_Scavenge], Tiempo (%), 0.6447899564764635
[TOTAL_GCS_PS_MarkSweep], cuenta, 1
[TOTAL_GC_TIME_PS_MarkSweep], Tiempo (ms), 63
[TOTAL_GC_TIME _% _ PS_MarkSweep], Tiempo (%), 0.005403986598113236
[TOTAL_GCs], cuenta, 777
[TOTAL_GC_TIME], tiempo (ms), 7580
[TOTAL_GC_TIME_%], Tiempo (%), 0.6501939430745767
[INSERTAR], Operaciones, 1,000,000
[INSERTAR], Latencia promedio (EE. UU.), 116042.207936
[INSERTAR], MinLatency (nosotros), 14056
[INSERTAR], MaxLatency (nosotros), 1462271
[INSERTAR], 95thPercentileLatency (us), 250239
[INSERTAR], 99thPercentileLatency (nosotros), 446719
[INSERTAR], Retorno = OK, 1,000,000

20/01/19 13:19:16 INFO client.ConnectionManager $ HConnectionImplementation: Closing zookeeper sessionid = 0x26f98ad07b6d67e
20/01/19 13:19:16 INFO zookeeper.ZooKeeper: Sesi贸n: 0x26f98ad07b6d67e cerrado
20/01/19 13:19:16 INFO zookeeper.ClientCnxn: EventThread cerrado
[GENERAL], RunTime (ms), 1165999
[GENERAL], rendimiento (ops / seg), 857.63366863951
[TOTAL_GCS_PS_Scavenge], recuento, 818
[TOTAL_GC_TIME_PS_Scavenge], Time(ms), 7557
[TOTAL_GC_TIME_%_PS_Scavenge], Time(%), 0.6481137633908777
[TOTAL_GCS_PS_MarkSweep], Count, 1
[TOTAL_GC_TIME_PS_MarkSweep], Time(ms), 79
[TOTAL_GC_TIME_%_PS_MarkSweep], Time(%), 0.006775305982252128
[TOTAL_GCs], Count, 819
[TOTAL_GC_TIME], Time(ms), 7636
[TOTAL_GC_TIME_%], Time(%), 0.6548890693731299
[INSERT], Operations, 1000000
[INSERT], AverageLatency(us), 116172.212864
[INSERT], MinLatency(us), 7952
[INSERT], MaxLatency(us), 1458175
[INSERT], 95thPercentileLatency(us), 250879
[INSERT], 99thPercentileLatency(us), 446463
[INSERT], Return=OK, 1000000

20/01/19 13:19:17 INFO client.ConnectionManager$HConnectionImplementation: Closing zookeeper sessionid=0x36f98ad0a4ad8cd
20/01/19 13:19:17 INFO zookeeper.ZooKeeper: Session: 0x36f98ad0a4ad8cd closed
20/01/19 13:19:17 INFO zookeeper.ClientCnxn: EventThread shut down
[OVERALL], RunTime(ms), 1166860
[OVERALL], Throughput(ops/sec), 857.000839860823
[TOTAL_GCS_PS_Scavenge], Count, 707
[TOTAL_GC_TIME_PS_Scavenge], Time(ms), 7239
[TOTAL_GC_TIME_%_PS_Scavenge], Time(%), 0.6203829079752499
[TOTAL_GCS_PS_MarkSweep], Count, 1
[TOTAL_GC_TIME_PS_MarkSweep], Time(ms), 67
[TOTAL_GC_TIME_%_PS_MarkSweep], Time(%), 0.0057419056270675145
[TOTAL_GCs], Count, 708
[TOTAL_GC_TIME], Time(ms), 7306
[TOTAL_GC_TIME_%], Time(%), 0.6261248136023173
[INSERT], Operations, 1000000
[INSERT], AverageLatency(us), 116230.849308
[INSERT], MinLatency(us), 7352
[INSERT], MaxLatency(us), 1443839
[INSERT], 95thPercentileLatency(us), 250623
[INSERT], 99thPercentileLatency(us), 447487
[INSERT], Return=OK, 1000000


Resulta que si CS AverageLatency (us) tiene un registro de 3114, entonces HB AverageLatency (us) = 1162 (recuerde que 1 operaci贸n = 100 registros y, por lo tanto, debe dividirse).

En general, se obtiene esta conclusi贸n: bajo condiciones dadas, hay una ventaja significativa de HBase. Sin embargo, no se puede descartar que el SSD y el ajuste cuidadoso del sistema operativo cambien radicalmente la imagen. Tambi茅n debe comprender que mucho depende de los escenarios de uso, puede resultar f谩cilmente que si no toma 4 tablas, sino 400 y trabaja con terabytes, el equilibrio de fuerzas se desarrollar谩 de una manera completamente diferente. Como dec铆an los cl谩sicos: la pr谩ctica es el criterio de la verdad. Tienes que intentarlo. Por un lado, ScyllaDB ahora tiene sentido verificar, por lo que debe continuar ...

Source: https://habr.com/ru/post/484096/


All Articles