Μάχη δύο yakozuna, ή Cassandra vs HBase. Εμπειρία ομάδας Sberbank

Αυτό δεν είναι καν αστείο, φαίνεται ότι αυτή η συγκεκριμένη εικόνα αντικατοπτρίζει με μεγαλύτερη ακρίβεια την ουσία αυτών των βάσεων δεδομένων και στο τέλος θα είναι σαφές γιατί:

Μάχη δύο yakozuna, ή Cassandra vs HBase. Εμπειρία ομάδας Sberbank

Σύμφωνα με την κατάταξη DB-Engines, οι δύο πιο δημοφιλείς βάσεις δεδομένων στηλών NoSQL είναι η Cassandra (εφεξής CS) και η HBase (HB).

Μάχη δύο yakozuna, ή Cassandra vs HBase. Εμπειρία ομάδας Sberbank

Με τη θέληση της μοίρας, η ομάδα διαχείρισης φόρτωσης δεδομένων στη Sberbank έχει ήδη προ πολλού και συνεργάζεται στενά με την HB. Σε αυτό το διάστημα μελετήσαμε αρκετά καλά τα δυνατά και τα αδύνατα σημεία του και μάθαμε πώς να το μαγειρεύουμε. Ωστόσο, η παρουσία μιας εναλλακτικής με τη μορφή CS μας ανάγκαζε πάντα να βασανιζόμαστε λίγο με αμφιβολίες: κάναμε τη σωστή επιλογή; Επιπλέον, τα αποτελέσματα συγκρίσεις, που πραγματοποιήθηκε από την DataStax, είπαν ότι η CS κερδίζει εύκολα την HB με σχεδόν συντριπτικό σκορ. Από την άλλη πλευρά, η DataStax είναι ένα ενδιαφερόμενο μέρος και δεν θα πρέπει να το δεχθείτε. Ήμασταν επίσης σε σύγχυση από τον σχετικά μικρό όγκο πληροφοριών σχετικά με τις συνθήκες δοκιμής, οπότε αποφασίσαμε να μάθουμε μόνοι μας ποιος είναι ο βασιλιάς του BigData NoSql και τα αποτελέσματα που προέκυψαν αποδείχθηκαν πολύ ενδιαφέροντα.

Ωστόσο, πριν προχωρήσουμε στα αποτελέσματα των δοκιμών που πραγματοποιήθηκαν, είναι απαραίτητο να περιγράψουμε τις σημαντικές πτυχές των διαμορφώσεων του περιβάλλοντος. Το γεγονός είναι ότι το CS μπορεί να χρησιμοποιηθεί σε μια λειτουργία που επιτρέπει την απώλεια δεδομένων. Εκείνοι. Αυτό συμβαίνει όταν μόνο ένας διακομιστής (κόμβος) είναι υπεύθυνος για τα δεδομένα ενός συγκεκριμένου κλειδιού και εάν για κάποιο λόγο αποτύχει, τότε η τιμή αυτού του κλειδιού θα χαθεί. Για πολλά καθήκοντα αυτό δεν είναι κρίσιμο, αλλά για τον τραπεζικό τομέα αυτό είναι η εξαίρεση και όχι ο κανόνας. Στην περίπτωσή μας, είναι σημαντικό να έχουμε πολλά αντίγραφα δεδομένων για αξιόπιστη αποθήκευση.

Επομένως, λήφθηκε υπόψη μόνο ο τρόπος λειτουργίας CS σε λειτουργία τριπλής αναπαραγωγής, δηλ. Η δημιουργία του casepace πραγματοποιήθηκε με τις ακόλουθες παραμέτρους:

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

Στη συνέχεια, υπάρχουν δύο τρόποι για να εξασφαλίσετε το απαιτούμενο επίπεδο συνέπειας. Γενικός κανόνας:
ΒΔ + NR > RF

Που σημαίνει ότι ο αριθμός των επιβεβαιώσεων από τους κόμβους κατά την εγγραφή (NW) συν ο αριθμός των επιβεβαιώσεων από τους κόμβους κατά την ανάγνωση (NR) πρέπει να είναι μεγαλύτερος από τον παράγοντα αναπαραγωγής. Στην περίπτωσή μας, RF = 3, που σημαίνει ότι οι ακόλουθες επιλογές είναι κατάλληλες:
2 + 2> 3
3 + 1> 3

Δεδομένου ότι είναι θεμελιωδώς σημαντικό για εμάς να αποθηκεύουμε τα δεδομένα όσο το δυνατόν πιο αξιόπιστα, επιλέχθηκε το σχήμα 3+1. Επιπλέον, το HB λειτουργεί με παρόμοια αρχή, δηλ. μια τέτοια σύγκριση θα είναι πιο δίκαιη.

Πρέπει να σημειωθεί ότι η DataStax έκανε το αντίθετο στη μελέτη τους, έθεσαν RF = 1 και για το CS και για το HB (για το τελευταίο αλλάζοντας τις ρυθμίσεις HDFS). Αυτή είναι μια πραγματικά σημαντική πτυχή, επειδή ο αντίκτυπος στην απόδοση του CS σε αυτήν την περίπτωση είναι τεράστιος. Για παράδειγμα, η παρακάτω εικόνα δείχνει την αύξηση του χρόνου που απαιτείται για τη φόρτωση δεδομένων στο CS:

Μάχη δύο yakozuna, ή Cassandra vs HBase. Εμπειρία ομάδας Sberbank

Εδώ βλέπουμε τα εξής: όσο περισσότερα ανταγωνιστικά νήματα γράφουν δεδομένα, τόσο περισσότερος χρόνος χρειάζεται. Αυτό είναι φυσικό, αλλά είναι σημαντικό η υποβάθμιση της απόδοσης για RF=3 να είναι σημαντικά υψηλότερη. Με άλλα λόγια, αν γράψουμε 4 νήματα σε 5 πίνακες το καθένα (20 συνολικά), τότε το RF=3 χάνει περίπου 2 φορές (150 δευτερόλεπτα για RF=3 έναντι 75 για RF=1). Αν όμως αυξήσουμε το φορτίο φορτώνοντας δεδομένα σε 8 πίνακες με 5 νήματα ο καθένας (40 συνολικά), τότε η απώλεια του RF=3 είναι ήδη 2,7 φορές (375 δευτερόλεπτα έναντι 138).

Ίσως αυτό να είναι εν μέρει το μυστικό της επιτυχούς δοκιμής φόρτωσης που πραγματοποιήθηκε από την DataStax για CS, γιατί για την HB στο περίπτερό μας η αλλαγή του συντελεστή αναπαραγωγής από 2 σε 3 δεν είχε κανένα αποτέλεσμα. Εκείνοι. Οι δίσκοι δεν είναι το σημείο συμφόρησης HB για τη διαμόρφωσή μας. Ωστόσο, υπάρχουν πολλές άλλες παγίδες εδώ, γιατί πρέπει να σημειωθεί ότι η έκδοση του HB μας ήταν ελαφρώς διορθωμένη και τροποποιημένη, τα περιβάλλοντα είναι εντελώς διαφορετικά κ.λπ. Αξίζει επίσης να σημειωθεί ότι ίσως απλώς δεν ξέρω πώς να προετοιμάσω σωστά το CS και υπάρχουν μερικοί πιο αποτελεσματικοί τρόποι να το δουλέψω και ελπίζω να το μάθουμε στα σχόλια. Πρώτα όμως πρώτα.

Όλες οι δοκιμές πραγματοποιήθηκαν σε ένα σύμπλεγμα υλικού που αποτελείται από 4 διακομιστές, ο καθένας με την ακόλουθη διαμόρφωση:

CPU: Xeon E5-2680 v4 @ 2.40 GHz 64 νήματα.
Δίσκοι: 12 τεμάχια SATA HDD
Έκδοση java: 1.8.0_111

Έκδοση CS: 3.11.5

cassandra.yml παράμετροιnum_tokens: 256
hinted_handoff_enabled: αληθές
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
authenticator: AllowAllAuthenticator
εξουσιοδότης: 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 # κάθε κατάλογος dataN είναι ξεχωριστός δίσκος
- /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: διακοπή
commit_failure_policy: σταματήστε
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
saved_caches_directory: /data10/cassandra/saved_caches
commitlog_sync: περιοδικό
commitlog_sync_period_in_ms: 10000
commitlog_segment_size_in_mb: 32
seed_provider:
- class_name: org.apache.cassandra.locator.SimpleSeedProvider
παραμέτρους:
— σπόροι: "*,*"
concurrent_reads: 256 # δοκιμάστηκε 64 - δεν παρατηρήθηκε διαφορά
concurrent_writes: 256 # δοκιμάστηκε 64 - δεν παρατηρήθηκε διαφορά
concurrent_counter_write: 256 # δοκιμάστηκε 64 - δεν παρατηρήθηκε διαφορά
concurrent_materialized_view_write: 32
memtable_heap_space_in_mb: 2048 # δοκιμάστηκε 16 GB - ήταν πιο αργό
memtable_allocation_type: heap_buffers
index_summary_capacity_in_mb:
index_summary_resize_interval_in_mins: 60
trickle_fsync: ψευδής
trickle_fsync_interval_in_kb: 10240
storage_port: 7000
ssl_storage_port: 7001
listen_address: *
εκπομπή_διεύθυνση: *
listen_on_broadcast_address: αλήθεια
internode_authenticator: org.apache.cassandra.auth.AllowAllInternodeAuthenticator
start_native_transport: αλήθεια
native_transport_port: 9042
start_rpc: αληθές
rpc_address: *
rpc_port: 9160
rpc_keepalive: αληθές
rpc_server_type: συγχρονισμός
thrift_framed_transport_size_in_mb: 15
incremental_backups: false
snapshot_before_compaction: false
auto_snapshot: αληθινό
στήλη_ευρετήριο_μέγεθος_σε_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: ψευδής
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: κανένα
client_encryption_options:
ενεργοποιημένο: false
internode_compression: dc
inter_dc_tcp_nodelay: ψευδής
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:
ενεργοποιημένο: false
tombstone_warn_threshold: 1000
επιτύμβια_αποτυχία_κατώφλι: 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: ψευδής
enable_materialized_views: αληθές
enable_sasi_indexes: true

Ρυθμίσεις GC:

### Ρυθμίσεις 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

Η μνήμη jvm.options εκχωρήθηκε 16 Gb (δοκιμάσαμε και 32 Gb, δεν παρατηρήθηκε διαφορά).

Οι πίνακες δημιουργήθηκαν με την εντολή:

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)

Μη προεπιλεγμένες παράμετροι HBasezookeeper.session.timeout: 120000
hbase.rpc.timeout: 2 λεπτά
hbase.client.scanner.timeout.period: 2 λεπτά(α)
hbase.master.handler.count: 10
hbase.regionserver.lease.period, hbase.client.scanner.timeout.period: 2 λεπτά
hbase.regionserver.handler.count: 160
hbase.regionserver.metahandler.count: 30
hbase.regionserver.logroll.period: 4 ώρες
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 ημέρα(ες)
HBase Service Advanced Configuration Snippet (Safety Valve) για 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 για το διακομιστή περιοχής HBase:
-XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=70 -XX:+CMSParallelRemarkEnabled -XX:ReservedCodeCacheSize=256m
hbase.snapshot.master.timeoutMillis: 2 λεπτά
hbase.snapshot.region.timeout: 2 λεπτά
hbase.snapshot.master.timeout.millis: 2 λεπτά
Μέγιστο μέγεθος αρχείου καταγραφής διακομιστή HBase REST: 100 MiB
Μέγιστα αντίγραφα ασφαλείας αρχείων καταγραφής διακομιστή HBase REST: 5
Μέγιστο μέγεθος αρχείου καταγραφής διακομιστή HBase Thrift: 100 MiB
Μέγιστα αντίγραφα ασφαλείας αρχείων καταγραφής διακομιστή HBase Thrift: 5
Master Max Μέγεθος αρχείου καταγραφής: 100 MiB
Κύρια μέγιστα αντίγραφα ασφαλείας αρχείων καταγραφής: 5
Μέγιστο μέγεθος αρχείου καταγραφής RegionServer: 100 MiB
Μέγιστα αντίγραφα ασφαλείας αρχείων καταγραφής RegionServer: 5
Παράθυρο ανίχνευσης Active HBase: 4 λεπτά
dfs.client.hedged.read.threadpool.size: 40
dfs.client.hedged.read.threshold.millis: 10 χιλιοστά του δευτερολέπτου(α)
hbase.rest.threads.min: 8
hbase.rest.threads.max: 150
Μέγιστες περιγραφές αρχείων διεργασίας: 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
Νήματα μετακίνησης περιοχής: 6
Μέγεθος Client Java Heap σε Byte: 1 GiB
Προεπιλεγμένη ομάδα διακομιστή HBase REST: 3 GiB
Προεπιλεγμένη ομάδα διακομιστή HBase Thrift: 3 GiB
Java Heap Μέγεθος του HBase Master σε Byte: 16 GiB
Μέγεθος Java Heap του HBase RegionServer σε Byte: 32 GiB

+ZooKeeper
maxClientCnxns: 601
maxSessionTimeout: 120000
Δημιουργία πινάκων:
hbase org.apache.hadoop.hbase.util.RegionSplitter ns:t1 UniformSplit -c 64 -f βλ.
alter 'ns:t1', {NAME => 'cf', DATA_BLOCK_ENCODING => 'FAST_DIFF', COMPRESSION => 'GZ'}

Υπάρχει ένα σημαντικό σημείο εδώ - η περιγραφή του DataStax δεν αναφέρει πόσες περιοχές χρησιμοποιήθηκαν για τη δημιουργία των πινάκων HB, αν και αυτό είναι κρίσιμο για μεγάλους όγκους. Επομένως, για τις δοκιμές επιλέχθηκε η ποσότητα = 64, η οποία επιτρέπει την αποθήκευση έως και 640 GB, δηλ. τραπέζι μεσαίου μεγέθους.

Κατά τη στιγμή της δοκιμής, το HBase είχε 22 χιλιάδες πίνακες και 67 χιλιάδες περιοχές (αυτό θα ήταν θανατηφόρο για την έκδοση 1.2.0 αν όχι για το έμπλαστρο που αναφέρθηκε παραπάνω).

Τώρα για τον κωδικό. Δεδομένου ότι δεν ήταν σαφές ποιες διαμορφώσεις ήταν πιο συμφέρουσες για μια συγκεκριμένη βάση δεδομένων, οι δοκιμές πραγματοποιήθηκαν σε διάφορους συνδυασμούς. Εκείνοι. Σε ορισμένες δοκιμές, 4 πίνακες φορτώθηκαν ταυτόχρονα (και οι 4 κόμβοι χρησιμοποιήθηκαν για σύνδεση). Σε άλλα τεστ δουλέψαμε με 8 διαφορετικούς πίνακες. Σε ορισμένες περιπτώσεις, το μέγεθος της παρτίδας ήταν 100, σε άλλες 200 (παράμετρος παρτίδας - βλ. κωδικό παρακάτω). Το μέγεθος δεδομένων για την τιμή είναι 10 byte ή 100 byte (dataSize). Συνολικά, 5 εκατομμύρια εγγραφές γράφτηκαν και διαβάζονταν σε κάθε πίνακα κάθε φορά. Ταυτόχρονα, γράφτηκαν/διαβάστηκαν 5 νήματα σε κάθε πίνακα (αριθμός νήματος - thNum), καθένα από τα οποία χρησιμοποιούσε το δικό του εύρος κλειδιών (μέτρηση = 1 εκατομμύριο):

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

Αντίστοιχα, παρείχε παρόμοια λειτουργικότητα για το 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);
}

Τώρα το πιο ενδιαφέρον μέρος - τα αποτελέσματα:

Μάχη δύο yakozuna, ή Cassandra vs HBase. Εμπειρία ομάδας Sberbank

Το ίδιο πράγμα σε μορφή γραφήματος:

Μάχη δύο yakozuna, ή Cassandra vs HBase. Εμπειρία ομάδας Sberbank

Το πλεονέκτημα του HB είναι τόσο εκπληκτικό που υπάρχει η υποψία ότι υπάρχει κάποιο είδος συμφόρησης στη ρύθμιση του CS. Ωστόσο, το Google και η αναζήτηση για τις πιο προφανείς παραμέτρους (όπως concurrent_writes ή memtable_heap_space_in_mb) δεν επιτάχυναν τα πράγματα. Ταυτόχρονα, τα κούτσουρα είναι καθαρά και δεν ορκίζονται σε τίποτα.

Τα δεδομένα κατανεμήθηκαν ομοιόμορφα στους κόμβους, τα στατιστικά στοιχεία από όλους τους κόμβους ήταν περίπου τα ίδια.

Έτσι φαίνονται τα στατιστικά του πίνακα από έναν από τους κόμβουςKeyspace: ks
Αριθμός αναγνωσμένων: 9383707
Αναμονή ανάγνωσης: 0.04287025042448576 ms
Πλήθος γραφών: 15462012
Καθυστέρηση εγγραφής: 0.1350068438699957 ms
Εκροές σε εκκρεμότητα: 0
Πίνακας: t1
Αριθμός SSTable: 16
Χώρος που χρησιμοποιείται (ζωντανά): 148.59 MiB
Χώρος που χρησιμοποιείται (σύνολο): 148.59 MiB
Χώρος που χρησιμοποιείται από στιγμιότυπα (σύνολο): 0 byte
Μνήμη εκτός σωρού που χρησιμοποιείται (σύνολο): 5.17 MiB
Αναλογία συμπίεσης SST: 0.5720989576459437
Αριθμός κατατμήσεων (εκτίμηση): 3970323
Πλήθος κυττάρων Memtable: 0
Μέγεθος δεδομένων Memtable: 0 byte
Χρησιμοποιείται μνήμη Memtable off heap: 0 byte
Αριθμός διακόπτη Memtable: 5
Τοπικός αριθμός αναγνώσεων: 2346045
Τοπική καθυστέρηση ανάγνωσης: NaN ms
Αριθμός τοπικών εγγραφών: 3865503
Τοπική καθυστέρηση εγγραφής: NaN ms
Εκροές σε εκκρεμότητα: 0
Ποσοστό επισκευής: 0.0
Εσφαλμένα θετικά φίλτρο άνθισης: 25
Λανθασμένη αναλογία φίλτρου άνθισης: 0.00000
Χρησιμοποιημένος χώρος φίλτρου Bloom: 4.57 MiB
Χρησιμοποιείται μνήμη σωρού με φίλτρο Bloom: 4.57 MiB
Σύνοψη ευρετηρίου από μνήμη σωρού που χρησιμοποιείται: 590.02 KiB
Μεταδεδομένα συμπίεσης από μνήμη σωρού που χρησιμοποιείται: 19.45 KiB
Ελάχιστα byte συμπιεσμένου διαμερίσματος: 36
Μέγιστα byte συμπιεσμένου διαμερίσματος: 42
Μέση byte συμπιεσμένου διαμερίσματος: 42
Μέσος όρος ζωντανών κυττάρων ανά φέτα (τελευταία πέντε λεπτά): NaN
Μέγιστα ζωντανά κύτταρα ανά φέτα (τελευταία πέντε λεπτά): 0
Μέσος όρος ταφόπλακες ανά φέτα (τελευταία πέντε λεπτά): NaN
Μέγιστες επιτύμβιες στήλες ανά φέτα (τελευταία πέντε λεπτά): 0
Απορριπτόμενες μεταλλάξεις: 0 byte

Μια προσπάθεια μείωσης του μεγέθους της παρτίδας (ακόμα και η αποστολή της μεμονωμένα) δεν είχε κανένα αποτέλεσμα, μόνο χειροτέρεψε. Είναι πιθανό ότι στην πραγματικότητα αυτή είναι πραγματικά η μέγιστη απόδοση για το CS, καθώς τα αποτελέσματα που λαμβάνονται για το CS είναι παρόμοια με αυτά που λαμβάνονται για το DataStax - περίπου εκατοντάδες χιλιάδες λειτουργίες ανά δευτερόλεπτο. Επιπλέον, αν εξετάσουμε τη χρήση πόρων, θα δούμε ότι το CS χρησιμοποιεί πολύ περισσότερη CPU και δίσκους:

Μάχη δύο yakozuna, ή Cassandra vs HBase. Εμπειρία ομάδας Sberbank
Το σχήμα δείχνει τη χρήση κατά τη διάρκεια της εκτέλεσης όλων των δοκιμών στη σειρά και για τις δύο βάσεις δεδομένων.

Όσον αφορά το ισχυρό αναγνωστικό πλεονέκτημα της HB. Εδώ μπορείτε να δείτε ότι και για τις δύο βάσεις δεδομένων, η χρήση του δίσκου κατά την ανάγνωση είναι εξαιρετικά χαμηλή (οι δοκιμές ανάγνωσης είναι το τελευταίο μέρος του κύκλου δοκιμών για κάθε βάση δεδομένων, για παράδειγμα για CS αυτό είναι από 15:20 έως 15:40). Στην περίπτωση του HB, ο λόγος είναι ξεκάθαρος - τα περισσότερα από τα δεδομένα κρέμονται στη μνήμη, στο memstore και μερικά είναι αποθηκευμένα στην κρυφή μνήμη μπλοκ. Όσο για το CS, δεν είναι πολύ σαφές πώς λειτουργεί, αλλά η ανακύκλωση δίσκου δεν είναι επίσης ορατή, αλλά για κάθε περίπτωση, έγινε προσπάθεια να ενεργοποιηθεί η προσωρινή μνήμη row_cache_size_in_mb = 2048 και να οριστεί η προσωρινή αποθήκευση = {'keys': 'ALL', 'rows_per_partition': ' 2000000'}, αλλά αυτό το έκανε ακόμη λίγο χειρότερο.

Αξίζει επίσης να αναφερθεί για άλλη μια φορά ένα σημαντικό σημείο σχετικά με τον αριθμό των περιοχών στην ΗΒ. Στην περίπτωσή μας, η τιμή καθορίστηκε ως 64. Αν τη μειώσουμε και την κάνουμε ίση με, για παράδειγμα, 4, τότε κατά την ανάγνωση, η ταχύτητα πέφτει κατά 2 φορές. Ο λόγος είναι ότι το memstore θα γεμίζει γρηγορότερα και τα αρχεία θα ξεπλένονται πιο συχνά και κατά την ανάγνωση, θα πρέπει να επεξεργαστούν περισσότερα αρχεία, κάτι που είναι μια αρκετά περίπλοκη λειτουργία για το HB. Σε πραγματικές συνθήκες, αυτό μπορεί να αντιμετωπιστεί με τη σκέψη μέσω μιας στρατηγικής εκ των προτέρων διαχωρισμού και συμπύκνωσης· συγκεκριμένα, χρησιμοποιούμε ένα αυτογραφικό βοηθητικό πρόγραμμα που συλλέγει σκουπίδια και συμπιέζει τα HFiles συνεχώς στο παρασκήνιο. Είναι πολύ πιθανό ότι για τις δοκιμές DataStax διέθεσαν μόνο 1 περιοχή ανά πίνακα (κάτι που δεν είναι σωστό) και αυτό θα διευκρίνιζε κάπως γιατί το HB ήταν τόσο κατώτερο στις δοκιμές ανάγνωσης.

Από αυτό εξάγονται τα ακόλουθα προκαταρκτικά συμπεράσματα. Αν υποθέσουμε ότι δεν έγιναν μεγάλα λάθη κατά τη διάρκεια των δοκιμών, τότε η Κασσάνδρα μοιάζει με κολοσσός με πόδια από πηλό. Πιο συγκεκριμένα, ενώ ισορροπεί στο ένα πόδι, όπως στην εικόνα στην αρχή του άρθρου, δείχνει σχετικά καλά αποτελέσματα, αλλά σε έναν αγώνα υπό τις ίδιες συνθήκες χάνει οριστικά. Ταυτόχρονα, λαμβάνοντας υπόψη τη χαμηλή χρήση της CPU στο υλικό μας, μάθαμε να φυτεύουμε δύο RegionServer HB ανά κεντρικό υπολογιστή και έτσι διπλασιάσαμε την απόδοση. Εκείνοι. Λαμβάνοντας υπόψη την αξιοποίηση των πόρων, η κατάσταση για το CS είναι ακόμη πιο θλιβερή.

Φυσικά, αυτές οι δοκιμές είναι αρκετά συνθετικές και η ποσότητα των δεδομένων που χρησιμοποιήθηκε εδώ είναι σχετικά μέτρια. Είναι πιθανό ότι αν αλλάζαμε σε terabytes, η κατάσταση θα ήταν διαφορετική, αλλά ενώ για το HB μπορούμε να φορτώσουμε terabytes, για το CS αυτό αποδείχθηκε προβληματικό. Συχνά έβαζε ένα OperationTimedOutException ακόμη και με αυτούς τους τόμους, αν και οι παράμετροι για την αναμονή για απάντηση είχαν ήδη αυξηθεί αρκετές φορές σε σύγκριση με τις προεπιλεγμένες.

Ελπίζω ότι μέσα από κοινές προσπάθειες θα βρούμε τα σημεία συμφόρησης του CS και αν μπορέσουμε να το επιταχύνουμε, τότε στο τέλος της ανάρτησης σίγουρα θα προσθέσω πληροφορίες για τα τελικά αποτελέσματα.

UPD: Χάρη στις συμβουλές των συντρόφων, κατάφερα να επιταχύνω την ανάγνωση. Ήταν:
159 ops (644 τραπέζια, 4 streams, παρτίδα 5).
Προστέθηκε:
.withLoadBalancingPolicy(new TokenAwarePolicy(DCAwareRoundRobinPolicy.builder().build()))
Και έπαιξα με τον αριθμό των νημάτων. Το αποτέλεσμα είναι το εξής:
4 τραπέζια, 100 κλωστές, παρτίδα = 1 (κομμάτι-κομμάτι): 301 οπ.
4 πίνακες, 100 νήματα, παρτίδα = 10: 447 επ.
4 πίνακες, 100 νήματα, παρτίδα = 100: 625 επ.

Αργότερα θα εφαρμόσω άλλες συμβουλές συντονισμού, θα εκτελέσω έναν πλήρη κύκλο δοκιμών και θα προσθέσω τα αποτελέσματα στο τέλος της ανάρτησης.

Πηγή: www.habr.com

Προσθέστε ένα σχόλιο