Batalla de dos yakozuna, o Cassandra contra HBase. Experiència de l'equip de Sberbank

Это даже не шутка, похоже, что именно эта картинка наиболее точно отражает суть этих БД, и в конце будет понятно почему:

Batalla de dos yakozuna, o Cassandra contra HBase. Experiència de l'equip de Sberbank

Согласно DB-Engines Ranking, две самых популярных NoSQL колоночных базы — это Cassandra (далее CS) и HBase (HB).

Batalla de dos yakozuna, o Cassandra contra HBase. Experiència de l'equip de Sberbank

Per voluntat del destí, el nostre equip de gestió de càrrega de dades a Sberbank ja ho ha fet fa molt de temps i treballa estretament amb HB. Durant aquest temps, vam estudiar força bé els seus punts forts i febles i vam aprendre a cuinar-lo. Tanmateix, la presència d'una alternativa en forma de CS sempre ens va obligar a turmentar-nos una mica amb dubtes: hem fet la decisió correcta? A més, els resultats comparacions, realitzat per DataStax, van dir que CS supera fàcilment HB amb gairebé una puntuació aclaparadora. D'altra banda, DataStax és una part interessada i no hauríeu de creure la seva paraula. També ens va confondre la poca informació sobre les condicions de la prova, així que vam decidir esbrinar per nosaltres mateixos qui és el rei de BigData NoSql, i els resultats obtinguts van resultar molt interessants.

Tanmateix, abans de passar als resultats de les proves realitzades, cal descriure els aspectes significatius de les configuracions de l'entorn. El fet és que CS es pot utilitzar en un mode que permet la pèrdua de dades. Aquells. és quan només un servidor (node) és responsable de les dades d'una determinada clau, i si per algun motiu falla, es perdrà el valor d'aquesta clau. Per a moltes tasques això no és crític, però per al sector bancari aquesta és l'excepció més que la regla. En el nostre cas, és important tenir diverses còpies de dades per a un emmagatzematge fiable.

Поэтому рассматривался исключительно режим работы CS в режиме тройной репликации, т.е. создание кейспейса выполнялось с такими параметрами:

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

A continuació, hi ha dues maneres d'assegurar el nivell de coherència requerit. Regla general:
NW + NR > RF

Això vol dir que el nombre de confirmacions dels nodes en escriure (NW) més el nombre de confirmacions dels nodes en llegir (NR) ha de ser superior al factor de replicació. En el nostre cas, RF = 3, el que significa que les opcions següents són adequades:
2 + 2 > 3
3 + 1 > 3

Так как нам принципиально важно максимально надежно сохранить данные, была выбрана схема 3+1. К тому же HB работает по аналогичному принципу, т.е. такое сравнение будет более честным.

Cal tenir en compte que DataStax va fer el contrari en el seu estudi, van establir RF = 1 tant per a CS com per a HB (per a aquest últim canviant la configuració HDFS). Aquest és un aspecte realment important perquè l'impacte en el rendiment de CS en aquest cas és enorme. Per exemple, la imatge següent mostra l'augment del temps necessari per carregar dades a CS:

Batalla de dos yakozuna, o Cassandra contra HBase. Experiència de l'equip de Sberbank

Aquí veiem el següent: com més fils competidors escriuen dades, més temps triguen. Això és natural, però és important que la degradació del rendiment de RF = 3 sigui significativament més gran. En altres paraules, si escrivim 4 fils en 5 taules cadascuna (20 en total), aleshores RF=3 perd aproximadament 2 vegades (150 segons per RF=3 enfront de 75 per RF=1). Però si augmentem la càrrega carregant dades en 8 taules amb 5 fils cadascuna (40 en total), la pèrdua de RF=3 ja és de 2,7 vegades (375 segons enfront de 138).

Potser aquest és en part el secret de les proves de càrrega reeixides realitzades per DataStax per a CS, perquè per a HB al nostre estand canviar el factor de replicació de 2 a 3 no va tenir cap efecte. Aquells. els discs no són el coll d'ampolla HB per a la nostra configuració. No obstant això, aquí hi ha moltes altres trampes, perquè cal tenir en compte que la nostra versió de HB va ser lleugerament pegada i ajustada, els entorns són completament diferents, etc. També val la pena assenyalar que potser no sé com preparar CS correctament i hi ha algunes maneres més efectives de treballar-hi, i espero que ho descobrirem als comentaris. Però primer és el primer.

Все тесты производились на железном кластере состоящем из 4 серверов, каждый в конфигурации:

CPU: Xeon E5-2680 v4 @ 2.40 GHz 64 fils.
Discs: HDD SATA de 12 peces
versió de java: 1.8.0_111

Versió CS: 3.11.5

paràmetres cassandra.ymlnombre de fitxes: 256
hinted_handoff_enabled: cert
hinted_handoff_throttle_in_kb: 1024
max_hints_delivery_threads: 2
directori_hints: /data10/cassandra/hints
hints_flush_period_in_ms: 10000
mida_max_de_fitxers_de_indicacions_en_mb: 128
batchlog_replay_throttle_in_kb: 1024
autenticador: AllowAllAuthenticator
autoritzador: AllowAllAuthorizer
role_manager: CassandraRoleManager
validesa_de_rols_en_ms: 2000
permissions_validity_in_ms: 2000
credencials_validity_in_ms: 2000
particionador: org.apache.cassandra.dht.Murmur3Partitioner
directoris_fitxers_dades:
- /data1/cassandra/data # cada directori dataN és un disc separat
- /data2/cassandra/data
- /data3/cassandra/data
- /data4/cassandra/data
- /data5/cassandra/data
- /data6/cassandra/data
- /data7/cassandra/data
- /data8/cassandra/data
directori_commitlog: /data9/cassandra/commitlog
cdc_enabled: fals
disk_failure_policy: aturar
commit_failure_policy: aturar
prepared_statements_cache_size_mb:
thrift_prepared_statements_cache_size_mb:
key_cache_size_in_mb:
clau_cache_període_desat: 14400
row_cache_size_in_mb: 0
row_cache_save_period: 0
counter_cache_size_in_mb:
counter_cache_save_period: 7200
directori_caches_desats: /data10/cassandra/saved_caches
commitlog_sync: periòdic
commitlog_sync_period_in_ms: 10000
commitlog_segment_size_in_mb: 32
proveïdor_llavor:
- nom_classe: org.apache.cassandra.locator.SimpleSeedProvider
Paràmetres:
— llavors: "*,*"
concurrent_reads: 256 # provat 64 - no s'ha observat cap diferència
concurrent_writes: 256 # provat 64 - no s'ha observat cap diferència
concurrent_counter_writes: 256 # provat 64 - no s'ha observat cap diferència
concurrent_materialized_view_writes: 32
memtable_heap_space_in_mb: 2048 # provat 16 GB - va ser més lent
memtable_allocation_type: heap_buffers
index_summary_capacity_in_mb:
index_summary_resize_interval_in_minutes: 60
trickle_fsync: fals
trickle_fsync_interval_in_kb: 10240
port_emmagatzematge: 7000
port_emmagatzematge_ssl: 7001
adreça_escolta: *
adreça_difusió: *
listen_on_broadcast_address: cert
internode_authenticator: org.apache.cassandra.auth.AllowAllInternodeAuthenticator
start_native_transport: cert
port_de_transport_natiu: 9042
start_rpc: cert
adreça_rpc: *
port_rpc: 9160
rpc_keepalive: cert
rpc_server_type: sincronització
mida_de_transport_framed_d'estalvi_en_mb: 15
incremental_backups: fals
snapshot_before_compaction: fals
auto_snapshot: cert
mida_índex_columna_en_kb: 64
column_index_cache_size_in_kb: 2
compactadors_concurrents: 4
rendiment_compactat_mb_per_s: 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: fals
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:
internade_encryption: cap
client_encryption_options:
habilitat: fals
compressió_entrenus: dc
inter_dc_tcp_nodelay: fals
tracetype_query_ttl: 86400
tracetype_repair_ttl: 604800
enable_user_defined_functions: fals
enable_scripted_user_defined_functions: fals
windows_timer_interval: 1
transparent_data_encryption_options:
habilitat: fals
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
compactation_large_partition_warning_threshold_mb: 100
gc_warn_threshold_in_ms: 1000
back_pressure_enabled: fals
enable_materialized_views: cert
enable_sasi_indexes: cert

Configuració del GC:

### Configuració del CMS-XX:+UtilitzaParNewGC
-XX:+UtilitzaConcMarkSweepGC
-XX:+CMSParallelRemarkEnabled
-XX:SurvivorRatio=8
-XX:MaxTenuringThreshold=1
-XX:CMSInitiatingOccupancyFraction=75
-XX:+Utilitza CMSI només per a l'ocupació
-XX:CMSWaitDuration=10000
-XX:+CMSParallelInitialMarkEnabled
-XX:+CMSEdenChunksRecordAlways
-XX:+CMSClassUnloadingEnabled

Памяти jvm.options выделялось 16Gb (еще пробовали 32 Gb, разницы не замечено).

Les taules es van crear amb l'ordre:

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

Версия HB: 1.2.0-cdh5.14.2 (в классе org.apache.hadoop.hbase.regionserver.HRegion нами был исключен MetricsRegion который приводил к GC при кол-ве регионов более 1000 на RegionServer)

Paràmetres HBase no predeterminatsZookeeper.session.timeout: 120000
hbase.rpc.timeout: 2 minut(s)
hbase.client.scanner.timeout.period: 2 minut(s)
hbase.master.handler.count: 10
hbase.regionserver.lease.period, hbase.client.scanner.timeout.period: 2 minut(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 dia(s)
Fragment de configuració avançada del servei HBase (vàlvula de seguretat) per a 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
Opcions de configuració de Java per a HBase RegionServer:
-XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=70 -XX:+CMSParallelRemarkEnabled -XX:ReservedCodeCacheSize=256m
hbase.snapshot.master.timeoutMillis: 2 minut(s)
hbase.snapshot.region.timeout: 2 minut(s)
hbase.snapshot.master.timeout.millis: 2 minut(s)
Mida màxima del registre del servidor REST d'HBase: 100 MiB
Còpia de seguretat màxima de fitxers de registre del servidor REST d'HBase: 5
Mida màxima del registre del servidor HBase Thrift: 100 MiB
Còpia de seguretat màxima de fitxers de registre del servidor HBase Thrift: 5
Mida màxima del registre mestre: 100 MiB
Còpies de seguretat màximes de fitxers de registre mestres: 5
Mida màxima del registre de RegionServer: 100 MiB
Còpia de seguretat màxima de fitxers de registre de RegionServer: 5
Finestra de detecció del mestre actiu HBase: 4 minut(s)
dfs.client.hedged.read.threadpool.mida: 40
dfs.client.hedged.read.threshold.millis: 10 mil·lisegons
hbase.rest.threads.min: 8
hbase.rest.threads.max: 150
Descriptors màxims de fitxers de procés: 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.mida: 20
Fils de moviment de regió: 6
Mida del munt Java del client en bytes: 1 GiB
Grup predeterminat del servidor REST HBase: 3 GiB
Grup predeterminat del servidor HBase Thrift: 3 GiB
Mida del munt de Java de l'HBase Master en bytes: 16 GiB
Mida de l'emmagatzematge de Java de l'HBase RegionServer en bytes: 32 GiB

+ ZooKeeper
maxClientCnxns: 601
maxSessionTimeout: 120000
Creació de taules:
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'}

Hi ha un punt important aquí: la descripció de DataStax no diu quantes regions es van utilitzar per crear les taules HB, tot i que això és fonamental per a grans volums. Per tant, per a les proves es va triar quantitat = 64, que permet emmagatzemar fins a 640 GB, és a dir. taula de mida mitjana.

В HBase на момент проведения теста было 22 тысячи таблиц и 67 тысяч регионов (это было бы убийственно для версии 1.2.0, если бы не патч о котором сказано выше).

Ara pel codi. Com que no estava clar quines configuracions eren més avantatjoses per a una base de dades concreta, es van realitzar proves en diverses combinacions. Aquells. en algunes proves, es van carregar 4 taules simultàniament (els 4 nodes es van utilitzar per a la connexió). En altres proves hem treballat amb 8 taules diferents. En alguns casos, la mida del lot era de 100, en altres de 200 (paràmetre del lot - vegeu el codi a continuació). La mida de les dades per al valor és de 10 bytes o 100 bytes (dataSize). En total, es van escriure i llegir 5 milions de registres a cada taula cada vegada. Al mateix temps, es van escriure/llegir 5 fils a cada taula (número de fil - thNum), cadascun dels quals utilitzava el seu propi rang de claus (recompte = 1 milió):

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 conseqüència, es va proporcionar una funcionalitat similar per a 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);
    }
}

Так как в HB о равномерном распределении данных должен заботиться клиент, то функция соления ключа выглядела так:

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

Ara la part més interessant: els resultats:

Batalla de dos yakozuna, o Cassandra contra HBase. Experiència de l'equip de Sberbank

El mateix en forma de gràfic:

Batalla de dos yakozuna, o Cassandra contra HBase. Experiència de l'equip de Sberbank

L'avantatge d'HB és tan sorprenent que hi ha la sospita que hi ha algun tipus de coll d'ampolla a la configuració de CS. Tanmateix, buscar a Google i cercar els paràmetres més evidents (com concurrent_writes o memtable_heap_space_in_mb) no va accelerar les coses. Al mateix temps, els troncs estan nets i no juren res.

Данные легли по нодам равномерно, статистика со всех нод примерно одинаковая.

Així es veuen les estadístiques de la taula d'un dels nodesEspai clau: ks
Recompte de lectura: 9383707
Latència de lectura: 0.04287025042448576 ms
Recompte d'escriptura: 15462012
Latència d'escriptura: 0.1350068438699957 ms
Fluxes pendents: 0
Taula: t1
Recompte SSTable: 16
Espai utilitzat (en directe): 148.59 MiB
Espai utilitzat (total): 148.59 MiB
Espai utilitzat per les instantànies (total): 0 bytes
Memòria fora del munt utilitzada (total): 5.17 MiB
Relació de compressió SSTable: 0.5720989576459437
Nombre de particions (estimació): 3970323
Recompte de cèl·lules memorables: 0
Mida de les dades memoritzables: 0 bytes
Memòria memorable fora de l'heap utilitzada: 0 bytes
Recompte d'interruptors memorables: 5
Recompte de lectura local: 2346045
Latència de lectura local: NaN ms
Nombre d'escriptures locals: 3865503
Latència d'escriptura local: NaN ms
Fluxes pendents: 0
Percentatge reparat: 0.0
Filtre de floració falsos positius: 25
Relació falsa del filtre de floració: 0.00000
Espai de filtre Bloom utilitzat: 4.57 MiB
Filtre de floració fora de la memòria del munt utilitzada: 4.57 MiB
Resum de l'índex fora de la memòria dinàmica utilitzada: 590.02 KiB
Metadades de compressió fora de la memòria de pila utilitzada: 19.45 KiB
Bytes mínims de partició compactada: 36
Bytes màxim de partició compactada: 42
Bytes mitjans de la partició compactada: 42
Mitjana de cèl·lules vives per porció (darrers cinc minuts): NaN
Cel·les vives màximes per porció (darrers cinc minuts): 0
Mitjana de làpides per llesca (darrers cinc minuts): NaN
Màxim de làpides per porció (últims cinc minuts): 0
Mutacions abandonades: 0 bytes

Un intent de reduir la mida del lot (fins i tot enviant-lo individualment) no va tenir cap efecte, només va empitjorar. És possible que, de fet, aquest sigui realment el màxim rendiment per a CS, ja que els resultats obtinguts per a CS són similars als obtinguts per a DataStax: uns centenars de milers d'operacions per segon. A més, si ens fixem en la utilització dels recursos, veurem que CS utilitza molta més CPU i discs:

Batalla de dos yakozuna, o Cassandra contra HBase. Experiència de l'equip de Sberbank
На рисунке показана утилизация во время прогона всех тестов подряд для обоих БД.

Pel que fa al poderós avantatge lector d'HB. Aquí podeu veure que per a ambdues bases de dades, la utilització del disc durant la lectura és extremadament baixa (les proves de lectura són la part final del cicle de proves de cada base de dades, per exemple, per a CS, és de 15:20 a 15:40). En el cas d'HB, el motiu és clar: la majoria de les dades es pengen a la memòria, al memstore, i algunes s'emmagatzemen a la memòria cau a blockcache. Pel que fa a CS, no està molt clar com funciona, però el reciclatge del disc tampoc és visible, però per si de cas, s'ha intentat habilitar la memòria cau row_cache_size_in_mb = 2048 i establir la memòria cau = {'keys': 'ALL', 'rows_per_partition': '2000000'}, però això ho va fer encara una mica pitjor.

També val la pena esmentar una vegada més un punt important sobre el nombre de regions a HB. En el nostre cas, el valor es va especificar com a 64. Si el reduïu i el feu igual, per exemple, a 4, aleshores, en llegir, la velocitat baixa 2 vegades. El motiu és que memstore s'omplirà més ràpidament i els fitxers s'esborraran més sovint i, en llegir-los, caldrà processar més fitxers, cosa que és una operació força complicada per a HB. En condicions reals, això es pot tractar pensant a través d'una estratègia de divisió prèvia i compactació; en particular, utilitzem una utilitat autoescrita que recull les escombraries i comprimeix els fitxers HF constantment en segon pla. És molt possible que per a les proves de DataStax hagin assignat només 1 regió per taula (que no és correcta) i això aclariria una mica per què HB era tan inferior en les seves proves de lectura.

D'això s'extreuen les següents conclusions preliminars. Suposant que no es van cometre errors importants durant les proves, Cassandra sembla un colós amb peus d'argila. Més precisament, mentre s'equilibra sobre una cama, com a la imatge de l'inici de l'article, mostra resultats relativament bons, però en una baralla en les mateixes condicions perd totalment. Al mateix temps, tenint en compte la baixa utilització de la CPU al nostre maquinari, vam aprendre a plantar dos RegionServer HB per host i, per tant, vam duplicar el rendiment. Aquells. Tenint en compte la utilització dels recursos, la situació de CS és encara més deplorable.

Per descomptat, aquestes proves són força sintètiques i la quantitat de dades que es va utilitzar aquí és relativament modesta. És possible que si canviéssim a terabytes, la situació seria diferent, però mentre que per a HB podem carregar terabytes, per a CS això va resultar problemàtic. Sovint va llançar una OperationTimedOutException fins i tot amb aquests volums, tot i que els paràmetres d'espera d'una resposta ja s'han augmentat diverses vegades en comparació amb els predeterminats.

Надеюсь, что совместными усилиями мы найдем узкие места CS и если получится её ускорить, то в конце поста обязательно добавлю информацию об итоговых результатах.

UPD: Gràcies als consells dels companys, vaig aconseguir accelerar la lectura. Era:
159 operacions (644 taules, 4 fluxos, lot 5).
Afegit per:
.withLoadBalancingPolicy(nou TokenAwarePolicy(DCAwareRoundRobinPolicy.builder().build()))
I vaig jugar amb el nombre de fils. El resultat és el següent:
4 taules, 100 fils, lot = 1 (peça per peça): 301 operacions
4 taules, 100 fils, lot = 10: 447 operacions
4 taules, 100 fils, lot = 100: 625 operacions

Позже применю другие советы по тюнингу, прогоню полный цикл тестирования и добавлю результаты в конце поста.

Font: www.habr.com

Afegeix comentari