Kampf zweier Yakozuna oder Cassandra gegen HBase. Erfahrung des Sberbank-Teams

Das ist nicht einmal ein Scherz, es scheint, dass dieses spezielle Bild das Wesen dieser Datenbanken am genauesten widerspiegelt, und am Ende wird klar sein, warum:

Kampf zweier Yakozuna oder Cassandra gegen HBase. Erfahrung des Sberbank-Teams

Laut DB-Engines-Ranking sind Cassandra (im Folgenden CS) und HBase (HB) die beiden beliebtesten NoSQL-Säulendatenbanken.

Kampf zweier Yakozuna oder Cassandra gegen HBase. Erfahrung des Sberbank-Teams

Durch den Willen des Schicksals hat unser Datenlademanagementteam bei der Sberbank dies bereits getan lange und arbeitet eng mit HB zusammen. In dieser Zeit haben wir seine Stärken und Schwächen recht gut studiert und gelernt, wie man es zubereitet. Das Vorhandensein einer Alternative in Form von CS zwang uns jedoch immer dazu, uns ein wenig mit Zweifeln zu quälen: Haben wir die richtige Wahl getroffen? Darüber hinaus die Ergebnisse Vergleiche, durchgeführt von DataStax, sagten sie, dass CS HB mit einem fast überwältigenden Ergebnis leicht schlägt. Andererseits ist DataStax eine interessierte Partei, und Sie sollten sich nicht auf ihr Wort verlassen. Wir waren auch verwirrt über die eher geringe Menge an Informationen über die Testbedingungen und beschlossen daher, selbst herauszufinden, wer der König von BigData NoSql ist, und die erzielten Ergebnisse erwiesen sich als sehr interessant.

Bevor jedoch zu den Ergebnissen der durchgeführten Tests übergegangen wird, müssen die wesentlichen Aspekte der Umgebungskonfigurationen beschrieben werden. Tatsache ist, dass CS in einem Modus verwendet werden kann, der Datenverlust zulässt. Diese. Dies ist der Fall, wenn nur ein Server (Knoten) für die Daten eines bestimmten Schlüssels verantwortlich ist und dieser aus irgendeinem Grund ausfällt, geht der Wert dieses Schlüssels verloren. Für viele Aufgaben ist dies nicht kritisch, für den Bankensektor ist dies jedoch eher die Ausnahme als die Regel. In unserem Fall ist es für eine zuverlässige Speicherung wichtig, mehrere Kopien der Daten zu haben.

Daher wurde nur der CS-Betriebsmodus im Triple-Replication-Modus betrachtet, d. h. Die Erstellung des Casespace erfolgte mit folgenden Parametern:

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

Als nächstes gibt es zwei Möglichkeiten, das erforderliche Maß an Konsistenz sicherzustellen. Allgemeine Regel:
NW + NR > RF

Das bedeutet, dass die Anzahl der Bestätigungen von Knoten beim Schreiben (NW) plus die Anzahl der Bestätigungen von Knoten beim Lesen (NR) größer sein müssen als der Replikationsfaktor. In unserem Fall ist RF = 3, was bedeutet, dass folgende Optionen geeignet sind:
2 + 2> 3
3 + 1> 3

Da es für uns grundsätzlich wichtig ist, die Daten möglichst zuverlässig zu speichern, wurde das 3+1-Schema gewählt. Darüber hinaus funktioniert HB nach einem ähnlichen Prinzip, d. h. Ein solcher Vergleich wäre fairer.

Es ist zu beachten, dass DataStax in ihrer Studie das Gegenteil tat: Sie setzten RF = 1 sowohl für CS als auch für HB (für Letzteres durch Änderung der HDFS-Einstellungen). Dies ist ein wirklich wichtiger Aspekt, da die Auswirkungen auf die CS-Leistung in diesem Fall enorm sind. Das Bild unten zeigt beispielsweise den Anstieg der Zeit, die zum Laden von Daten in CS benötigt wird:

Kampf zweier Yakozuna oder Cassandra gegen HBase. Erfahrung des Sberbank-Teams

Hier sehen wir Folgendes: Je mehr konkurrierende Threads Daten schreiben, desto länger dauert es. Das ist natürlich, aber es ist wichtig, dass der Leistungsabfall für RF=3 deutlich höher ist. Mit anderen Worten: Wenn wir 4 Threads in jeweils 5 Tabellen schreiben (insgesamt 20), dann verliert RF=3 etwa das Zweifache (2 Sekunden für RF=150 gegenüber 3 Sekunden für RF=75). Wenn wir jedoch die Last erhöhen, indem wir Daten in 1 Tabellen mit jeweils 8 Threads (insgesamt 5) laden, beträgt der Verlust von RF=40 bereits das 3-fache (2,7 Sekunden gegenüber 375).

Vielleicht ist dies teilweise das Geheimnis des erfolgreichen Lasttests, den DataStax für CS durchgeführt hat, denn für HB an unserem Stand hatte die Änderung des Replikationsfaktors von 2 auf 3 keine Auswirkung. Diese. Festplatten sind nicht der HB-Engpass für unsere Konfiguration. Allerdings gibt es hier noch viele andere Fallstricke, denn zu beachten ist, dass unsere HB-Version leicht gepatcht und optimiert wurde, die Umgebungen völlig anders sind usw. Es ist auch erwähnenswert, dass ich vielleicht einfach nicht weiß, wie man CS richtig vorbereitet, und dass es einige effektivere Möglichkeiten gibt, damit zu arbeiten, und ich hoffe, dass wir es in den Kommentaren herausfinden. Aber das Wichtigste zuerst.

Alle Tests wurden auf einem Hardware-Cluster bestehend aus 4 Servern mit jeweils folgender Konfiguration durchgeführt:

CPU: Xeon E5-2680 v4 bei 2.40 GHz 64 Threads.
Festplatten: 12 Stück SATA HDD
Java-Version: 1.8.0_111

CS-Version: 3.11.5

cassandra.yml-Parameternum_tokens: 256
hinted_handoff_enabled: wahr
hinted_handoff_throttle_in_kb: 1024
max_hints_delivery_threads: 2
hints_directory: /data10/cassandra/hints
hints_flush_period_in_ms: 10000
max_hints_file_size_in_mb: 128
batchlog_replay_throttle_in_kb: 1024
Authentifikator: AllowAllAuthenticator
Autorisierer: AllowAllAuthorizer
Rollenmanager: CassandraRoleManager
Roles_validity_in_ms: 2000
Permissions_validity_in_ms: 2000
credentials_validity_in_ms: 2000
Partitionierer: org.apache.cassandra.dht.Murmur3Partitioner
data_file_directories:
- /data1/cassandra/data # jedes dataN-Verzeichnis ist eine separate Festplatte
- /data2/cassandra/data
- /data3/cassandra/data
- /data4/cassandra/data
- /data5/cassandra/data
- /data6/cassandra/data
- /data7/cassandra/data
- /data8/cassandra/data
Commitlog-Verzeichnis: /data9/cassandra/commitlog
cdc_enabled: falsch
disk_failure_policy: Stopp
commit_failure_policy: Stopp
vorbereitete_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
gespeichertes_caches_verzeichnis: /data10/cassandra/saved_caches
commitlog_sync: periodisch
commitlog_sync_period_in_ms: 10000
commitlog_segment_size_in_mb: 32
Seed_Provider:
- Klassenname: org.apache.cassandra.locator.SimpleSeedProvider
Parameter:
- Samen: "*,*"
concurrent_reads: 256 # versucht 64 – kein Unterschied festgestellt
concurrent_writes: 256 # versucht 64 - kein Unterschied festgestellt
concurrent_counter_writes: 256 # versucht 64 - kein Unterschied festgestellt
concurrent_materialized_view_writes: 32
memtable_heap_space_in_mb: 2048 # 16 GB ausprobiert – es war langsamer
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
Speicherport: 7000
ssl_storage_port: 7001
listen_address: *
Broadcastadresse: *
listen_on_broadcast_address: true
internode_authenticator: org.apache.cassandra.auth.AllowAllInternodeAuthenticator
start_native_transport: wahr
nativer_transport_port: 9042
start_rpc: wahr
rpc_address: *
rpc_port: 9160
rpc_keepalive: wahr
rpc_server_type: sync
thrift_framed_transport_size_in_mb: 15
inkrementelle_Backups: falsch
snapshot_before_compaction: false
auto_snapshot: wahr
Column_index_size_in_kb: 64
Column_index_cache_size_in_kb: 2
concurrent_compactors: 4
compression_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: keine
client_encryption_options:
aktiviert: falsch
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
transparente_data_encryption_options:
aktiviert: falsch
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
compression_large_partition_warning_threshold_mb: 100
gc_warn_threshold_in_ms: 1000
back_pression_enabled: false
enable_materialized_views: wahr
enable_sasi_indexes: wahr

GC-Einstellungen:

### CMS-Einstellungen-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

Dem jvm.options-Speicher wurden 16 GB zugewiesen (wir haben es auch mit 32 GB versucht, es wurde kein Unterschied festgestellt).

Die Tabellen wurden mit dem Befehl erstellt:

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

HB-Version: 1.2.0-cdh5.14.2 (in der Klasse org.apache.hadoop.hbase.regionserver.HRegion haben wir MetricsRegion ausgeschlossen, was zu GC führte, wenn die Anzahl der Regionen auf RegionServer mehr als 1000 betrug)

Nicht standardmäßige HBase-Parameterzookeeper.session.timeout: 120000
hbase.rpc.timeout: 2 Minute(n)
hbase.client.scanner.timeout.period: 2 Minute(n)
hbase.master.handler.count: 10
hbase.regionserver.lease.period, hbase.client.scanner.timeout.period: 2 Minute(n)
hbase.regionserver.handler.count: 160
hbase.regionserver.metahandler.count: 30
hbase.regionserver.logroll.period: 4 Stunde(n)
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 Tag(e)
HBase Service Advanced Configuration Snippet (Sicherheitsventil) für 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
Java-Konfigurationsoptionen für HBase RegionServer:
-XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=70 -XX:+CMSParallelRemarkEnabled -XX:ReservedCodeCacheSize=256m
hbase.snapshot.master.timeoutMillis: 2 Minute(n)
hbase.snapshot.region.timeout: 2 Minute(n)
hbase.snapshot.master.timeout.millis: 2 Minute(n)
Maximale Protokollgröße des HBase-REST-Servers: 100 MiB
Maximale Protokolldateisicherungen für den HBase-REST-Server: 5
Maximale Protokollgröße des HBase Thrift-Servers: 100 MiB
Maximale Protokolldateisicherungen für HBase Thrift Server: 5
Maximale Master-Protokollgröße: 100 MiB
Maximale Master-Protokolldateisicherungen: 5
Maximale Protokollgröße des RegionServers: 100 MiB
Maximale RegionServer-Protokolldateisicherungen: 5
HBase Active Master-Erkennungsfenster: 4 Minute(n)
dfs.client.hedged.read.threadpool.size: 40
dfs.client.hedged.read.threshold.millis: 10 Millisekunde(n)
hbase.rest.threads.min: 8
hbase.rest.threads.max: 150
Maximale Prozessdateideskriptoren: 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
Region-Mover-Threads: 6
Client-Java-Heap-Größe in Bytes: 1 GiB
Standardgruppe des HBase-REST-Servers: 3 GiB
HBase Thrift Server-Standardgruppe: 3 GiB
Java-Heap-Größe des HBase-Masters in Bytes: 16 GiB
Java-Heap-Größe von HBase RegionServer in Bytes: 32 GiB

+ZooKeeper
maxClientCnxns: 601
maxSessionTimeout: 120000
Tabellen erstellen:
hbase org.apache.hadoop.hbase.util.RegionSplitter ns:t1 UniformSplit -c 64 -f vgl
alter 'ns:t1', {NAME => 'cf', DATA_BLOCK_ENCODING => 'FAST_DIFF', COMPRESSION => 'GZ'}

Hier gibt es einen wichtigen Punkt: In der DataStax-Beschreibung wird nicht angegeben, wie viele Regionen zum Erstellen der HB-Tabellen verwendet wurden, obwohl dies für große Volumina von entscheidender Bedeutung ist. Daher wurde für die Tests die Menge = 64 gewählt, was die Speicherung von bis zu 640 GB ermöglicht, also mittelgroßer Tisch.

Zum Zeitpunkt des Tests verfügte HBase über 22 Tabellen und 67 Regionen (ohne den oben genannten Patch wäre dies für Version 1.2.0 tödlich gewesen).

Nun zum Code. Da nicht klar war, welche Konfigurationen für eine bestimmte Datenbank vorteilhafter waren, wurden Tests in verschiedenen Kombinationen durchgeführt. Diese. In einigen Tests wurden 4 Tabellen gleichzeitig geladen (alle 4 Knoten wurden für die Verbindung verwendet). In anderen Tests haben wir mit 8 verschiedenen Tabellen gearbeitet. In einigen Fällen betrug die Batch-Größe 100, in anderen 200 (Batch-Parameter – siehe Code unten). Die Datengröße für value beträgt 10 Byte oder 100 Byte (dataSize). Insgesamt wurden jedes Mal 5 Millionen Datensätze in jede Tabelle geschrieben und eingelesen. Gleichzeitig wurden 5 Threads in jede Tabelle geschrieben/gelesen (Thread-Nummer – thNum), von denen jeder seinen eigenen Schlüsselbereich verwendete (Anzahl = 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);
    }
}

Dementsprechend wurde für HB eine ähnliche Funktionalität bereitgestellt:

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

Da sich in HB der Client um die gleichmäßige Verteilung der Daten kümmern muss, sah die Key-Salting-Funktion so aus:

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

Nun der interessanteste Teil – die Ergebnisse:

Kampf zweier Yakozuna oder Cassandra gegen HBase. Erfahrung des Sberbank-Teams

Das Gleiche in Diagrammform:

Kampf zweier Yakozuna oder Cassandra gegen HBase. Erfahrung des Sberbank-Teams

Der Vorteil von HB ist so überraschend, dass der Verdacht besteht, dass es im CS-Setup eine Art Engpass gibt. Googeln und Suchen nach den offensichtlichsten Parametern (wie concurrent_writes oder memtable_heap_space_in_mb) hat die Sache jedoch nicht beschleunigt. Gleichzeitig sind die Protokolle sauber und schwören auf nichts.

Die Daten waren gleichmäßig auf die Knoten verteilt, die Statistiken aller Knoten waren ungefähr gleich.

So sehen die Tabellenstatistiken von einem der Knoten ausSchlüsselraum: ks
Leseanzahl: 9383707
Leselatenz: 0.04287025042448576 ms
Schreibanzahl: 15462012
Schreiblatenz: 0.1350068438699957 ms
Ausstehende Flushes: 0
Tabelle: t1
SSTable-Anzahl: 16
Benutzter Platz (live): 148.59 MiB
Benutzter Speicherplatz (gesamt): 148.59 MiB
Von Snapshots belegter Speicherplatz (insgesamt): 0 Byte
Verwendeter Off-Heap-Speicher (gesamt): 5.17 MiB
SSTable-Komprimierungsverhältnis: 0.5720989576459437
Anzahl der Partitionen (Schätzung): 3970323
Anzahl der gespeicherten Zellen: 0
Memtable-Datengröße: 0 Byte
Verwendeter Memtable-Off-Heap-Speicher: 0 Byte
Anzahl der Memtable-Schalter: 5
Lokale Leseanzahl: 2346045
Lokale Leselatenz: NaN ms
Lokale Schreibanzahl: 3865503
Lokale Schreiblatenz: NaN ms
Ausstehende Spülungen: 0
Prozentsatz repariert: 0.0
Fehlalarme des Bloom-Filters: 25
Falsches Verhältnis des Bloom-Filters: 0.00000
Verwendeter Bloom-Filterraum: 4.57 MiB
Verwendeter Bloom-Filter-Off-Heap-Speicher: 4.57 MiB
Indexzusammenfassung: Verwendeter Off-Heap-Speicher: 590.02 KiB
Komprimierungsmetadaten außerhalb des verwendeten Heap-Speichers: 19.45 KiB
Minimale Bytes der komprimierten Partition: 36
Maximale Bytes der komprimierten Partition: 42
Komprimierte Partition, mittlere Bytes: 42
Durchschnittliche lebende Zellen pro Scheibe (letzte fünf Minuten): NaN
Maximale lebende Zellen pro Scheibe (letzte fünf Minuten): 0
Durchschnittliche Tombstones pro Slice (letzte fünf Minuten): NaN
Maximale Grabsteine ​​pro Slice (letzte fünf Minuten): 0
Verworfene Mutationen: 0 Bytes

Ein Versuch, die Größe des Stapels zu reduzieren (auch wenn er einzeln verschickt wurde), hatte keine Wirkung, es wurde nur noch schlimmer. Es ist möglich, dass dies tatsächlich die maximale Leistung für CS ist, da die für CS erhaltenen Ergebnisse denen für DataStax ähneln – etwa Hunderttausende Operationen pro Sekunde. Wenn wir uns außerdem die Ressourcennutzung ansehen, werden wir feststellen, dass CS viel mehr CPU und Festplatten verbraucht:

Kampf zweier Yakozuna oder Cassandra gegen HBase. Erfahrung des Sberbank-Teams
Die Abbildung zeigt die Auslastung während der Ausführung aller Tests hintereinander für beide Datenbanken.

In Bezug auf den starken Lesevorteil von HB. Hier sehen Sie, dass für beide Datenbanken die Festplattenauslastung beim Lesen äußerst gering ist (Lesetests sind der letzte Teil des Testzyklus für jede Datenbank, bei CS beispielsweise von 15:20 bis 15:40). Im Fall von HB ist der Grund klar: Die meisten Daten hängen im Speicher, im Memstore, und einige werden im Blockcache zwischengespeichert. Bei CS ist nicht ganz klar, wie es funktioniert, aber das Festplattenrecycling ist auch nicht sichtbar, aber für alle Fälle wurde versucht, den Cache row_cache_size_in_mb = 2048 zu aktivieren und caching = {'keys': 'ALL' zu setzen, 'rows_per_partition': ' 2000000'}, aber das machte es noch ein bisschen schlimmer.

Erwähnenswert ist auch noch einmal ein wichtiger Punkt zur Anzahl der Regionen in HB. In unserem Fall wurde der Wert mit 64 angegeben. Wenn man ihn reduziert und beispielsweise auf 4 setzt, sinkt die Geschwindigkeit beim Lesen um das Zweifache. Der Grund dafür ist, dass sich der Memstore schneller füllt und Dateien häufiger geleert werden und beim Lesen mehr Dateien verarbeitet werden müssen, was für HB ein ziemlich komplizierter Vorgang ist. Unter realen Bedingungen kann dies durch eine Strategie zur Voraufteilung und Komprimierung behoben werden. Insbesondere verwenden wir ein selbst geschriebenes Dienstprogramm, das im Hintergrund ständig Müll sammelt und HFiles komprimiert. Es ist durchaus möglich, dass sie für DataStax-Tests nur eine Region pro Tabelle zugewiesen haben (was nicht korrekt ist), und dies würde einigermaßen klären, warum HB in ihren Lesetests so schlecht abgeschnitten hat.

Daraus werden die folgenden vorläufigen Schlussfolgerungen gezogen. Unter der Annahme, dass bei den Tests keine größeren Fehler gemacht wurden, sieht Cassandra wie ein Koloss auf tönernen Füßen aus. Genauer gesagt, während sie auf einem Bein balanciert, wie auf dem Bild am Anfang des Artikels, zeigt sie relativ gute Ergebnisse, verliert aber in einem Kampf unter den gleichen Bedingungen völlig. Gleichzeitig haben wir unter Berücksichtigung der geringen CPU-Auslastung unserer Hardware gelernt, zwei RegionServer-HBs pro Host zu installieren und dadurch die Leistung zu verdoppeln. Diese. Unter Berücksichtigung der Ressourcenauslastung ist die Situation für CS noch bedauerlicher.

Natürlich sind diese Tests recht synthetisch und die Datenmenge, die hier verwendet wurde, ist relativ bescheiden. Es ist möglich, dass die Situation anders wäre, wenn wir auf Terabyte umsteigen würden, aber während wir für HB Terabyte laden können, erwies sich dies für CS als problematisch. Auch bei diesen Volumes löste es häufig eine OperationTimedOutException aus, obwohl die Parameter für das Warten auf eine Antwort im Vergleich zu den Standardwerten bereits um ein Vielfaches erhöht waren.

Ich hoffe, dass wir durch gemeinsame Anstrengungen die Engpässe von CS finden und wenn wir es beschleunigen können, werde ich am Ende des Beitrags auf jeden Fall Informationen über die Endergebnisse hinzufügen.

UPD: Dank des Ratschlags von Genossen ist es mir gelungen, das Lesen zu beschleunigen. War:
159 Operationen (644 Tabellen, 4 Streams, Batch 5).
Dobavleno:
.withLoadBalancingPolicy(new TokenAwarePolicy(DCAwareRoundRobinPolicy.builder().build()))
Und ich habe mit der Anzahl der Threads herumgespielt. Das Ergebnis ist folgendes:
4 Tabellen, 100 Threads, Batch = 1 (Stück für Stück): 301 Operationen
4 Tabellen, 100 Threads, Batch = 10: 447 Operationen
4 Tabellen, 100 Threads, Batch = 100: 625 Operationen

Später werde ich weitere Tuning-Tipps anwenden, einen vollständigen Testzyklus durchführen und die Ergebnisse am Ende des Beitrags hinzufügen.

Source: habr.com

Kommentar hinzufügen