Битка на две јакозуни, или Касандра против HBase. Искуство во тимот на Сбербанк

Ова не е ни шега, се чини дека оваа конкретна слика најпрецизно ја отсликува суштината на овие бази на податоци, а на крајот ќе биде јасно зошто:

Битка на две јакозуни, или Касандра против HBase. Искуство во тимот на Сбербанк

Според рангирањето на DB-Engines, двете најпопуларни NoSQL колонозни бази на податоци се Cassandra (во понатамошниот текст CS) и HBase (HB).

Битка на две јакозуни, или Касандра против HBase. Искуство во тимот на Сбербанк

По волја на судбината, нашиот тим за управување со вчитување податоци во Сбербанк веќе го направи одамна и тесно соработува со HB. За тоа време, доста добро ги проучувавме неговите јаки и слаби страни и научивме како да го готвиме. Сепак, присуството на алтернатива во форма на CS секогаш нè принудуваше малку да се измачуваме со сомнежи: дали го направивме вистинскиот избор? Покрај тоа, резултатите споредби, во изведба на DataStax, тие рекоа дека CS лесно го победува HB со речиси скршен резултат. Од друга страна, DataStax е заинтересирана страна и не треба да го прифаќате нивниот збор за тоа. Бевме збунети и од прилично малата количина на информации за условите за тестирање, па решивме сами да дознаеме кој е кралот на BigData NoSql, а добиените резултати се покажаа многу интересни.

Сепак, пред да се премине на резултатите од извршените тестови, неопходно е да се опишат значајните аспекти на конфигурациите на околината. Факт е дека CS може да се користи во режим кој овозможува губење на податоци. Оние. ова е кога само еден сервер (јазол) е одговорен за податоците на одреден клуч, и ако поради некоја причина не успее, тогаш вредноста на овој клуч ќе се изгуби. За многу задачи ова не е критично, но за банкарскиот сектор ова е исклучок наместо правило. Во нашиот случај, важно е да имате неколку копии на податоци за сигурно складирање.

Затоа, се разгледуваше само режимот на работа CS во режим на тројна репликација, т.е. Создавањето на просторот за случаи беше извршено со следните параметри:

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

Следно, постојат два начини да се обезбеди потребното ниво на конзистентност. Општо правило:
NW + NR > RF

Што значи дека бројот на потврди од јазли при пишување (NW) плус бројот на потврди од јазли при читање (NR) мора да биде поголем од факторот на репликација. Во нашиот случај, RF = 3, што значи дека следните опции се соодветни:
2 + 2 > 3
3 + 1 > 3

Бидејќи е фундаментално важно за нас да ги складираме податоците што е можно посигурно, беше избрана шемата 3+1. Покрај тоа, HB работи на сличен принцип, т.е. ваквата споредба ќе биде поправедна.

Треба да се напомене дека DataStax го направи спротивното во нивната студија, тие поставија RF = 1 и за CS и за HB (за второто со менување на поставките за HDFS). Ова е навистина важен аспект бидејќи влијанието врз перформансите на CS во овој случај е огромно. На пример, сликата подолу го покажува зголемувањето на времето потребно за вчитување податоци во CS:

Битка на две јакозуни, или Касандра против HBase. Искуство во тимот на Сбербанк

Овде го гледаме следново: колку повеќе конкурентни нишки пишуваат податоци, толку подолго е потребно. Ова е природно, но важно е дека деградацијата на перформансите за 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 сервери, секој со следнава конфигурација:

Процесор: Xeon E5-2680 v4 @ 2.40 GHz 64 нишки.
Дискови: 12 парчиња SATA HDD
Java верзија: 1.8.0_111

CS верзија: 3.11.5

cassandra.yml параметриброј_токени: 256
hinted_handoff_enabled: точно
hinted_handoff_throttle_in_kb: 1024
max_hints_delivery_threads: 2
совети_директориум: /data10/cassandra/hints
совети_флеш_период_во_ms: 10000
max_hints_file_size_in_mb: 128
batchlog_replay_throttle_in_kb: 1024
автентикатор: AllowAllAuthenticator
автор: AllowAllAuthorizer
role_manager: CassandraRoleManager
roles_validity_in_ms: 2000 година
permissions_validity_in_ms: 2000 година
credentials_validity_in_ms: 2000 година
партиционер: org.apache.cassandra.dht.Murmur3Partitioner
податоци_датотеки_директориуми:
- /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: неточно
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_writes: 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_minutes: 60
trickle_fsync: неточно
trickle_fsync_interval_in_kb: 10240
складирање_порта: 7000
ssl_storage_port: 7001
слушај_адреса: *
емитува_адреса: *
listen_on_broadcast_address: точно
internode_authenticator: org.apache.cassandra.auth.AllowAllInternodeAuthenticator
start_native_transport: точно
мајчин_транспорт_порт: 9042
start_rpc: точно
rpc_address: *
rpc_port: 9160
rpc_keepalive: точно
rpc_server_type: синхронизирање
thrift_framed_transport_size_in_mb: 15
инкрементални_резервни копии: неточно
слика_пред_набивање: неточно
auto_snapshot: точно
колона_индекс_големина_во_кб: 64
column_index_cache_size_in_kb: 2
истовремени_компактори: 4
набивање_пропуст_mb_по_сек: 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: нема
клиент_енкрипција_опции:
овозможено: неточно
internode_compression: dc
inter_dc_tcp_nodelay: неточно
tracetype_query_ttl: 86400
tracetype_repair_ttl: 604800
enable_user_defined_functions: неточно
enable_scripted_user_defined_functions: неточно
windows_timer_interval: 1
transparent_data_encryption_options:
овозможено: неточно
надгробна плоча_предупредување_праг: 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: точно

Поставки за GC:

### Поставки за CMS-XX:+UseParNewGC
-XX:+КористетеConcMarkSweepGC
-XX:+CMSParallelRemark Овозможено
-XX:Сооднос на преживеан=8
-XX:MaxTenuringThreshold=1
-XX:CMSInitiating OccupancyFraction=75
-XX:+UseCMSInitiating OccupancyOnly
-XX:CMSWaitDuration=10000
-XX:+CMSParallelInitialMarkEnabled
-XX:+CMSEdenChunksRecordAlways
-XX:+CMSClassUnloadingEnabled

На jvm.options меморијата и беше доделена 16Gb (пробавме и 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)

Нестандардни параметри на HBaseчувар на зоолошката градина.сесија.време: 120000
hbase.rpc.timeout: 2 минути
hbase.client.scanner.timeout.период: 2 минути(и)
hbase.master.handler.број: 10
hbase.regionserver.lease.period, hbase.client.scanner.timeout.период: 2 минути
hbase.regionserver.handler.count: 160
hbase.regionserver.metahandler.count: 30
hbase.regionserver.logroll.период: 4 часа
hbase.regionserver.maxlogs: 200
hbase.hregion.memstore.flush.големина: 1 GiB
hbase.hregion.memstore.block.мултипликатор: 6
hbase.hstore.compactionThreshold: 5
hbase.hstore.blockingStoreFiles: 200
hbase.hregion.major compaction: 1 ден(и)
Парче за напредна конфигурација на услугата HBase (безбедносен вентил) за hbase-site.xml:
hbase.regionserver.wal.codecorg.apache.hadoop.hbase.regionserver.wal.IndexedWALEditCodec
hbase.master.namespace.init.timeout3600000
hbase.regionserver.опционален cacheflushinterval18000000
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:CMSInitiating OccupancyFraction=70 -XX:+CMSParallelRemarkEnabled -XX:ReservedCodeCacheSize=256m
hbase.snapshot.master.timeoutМилис: 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
РегионСервер Максимална големина на дневник: 100 MiB
Максимални резервни копии на датотеката за дневник на RegionServer: 5
HBase Active Master Detection прозорец: 4 минути
dfs.client.hedged.read.threadpool.големина: 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.големина: 20
Теми за движење на регионот: 6
Големина на Java Heap на клиентот во бајти: 1 GiB
Стандардна група на серверот HBase REST: 3 GiB
Стандардна група на серверот HBase Thrift: 3 GiB
Java Heap Големина на HBase Master во бајти: 16 GiB
Java Heap Големина на HBase RegionServer во бајти: 32 GiB

+ZooKeeper
maxClientCnxns: 601
maxSessionTimeout: 120000
Креирање табели:
hbase org.apache.hadoop.hbase.util.RegionSplitter ns:t1 UniformSplit -c 64 -f cf
измени '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 бајти или 100 бајти (големина на податоци). Вкупно, 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);
}

Сега најинтересниот дел - резултатите:

Битка на две јакозуни, или Касандра против HBase. Искуство во тимот на Сбербанк

Истото во форма на графикон:

Битка на две јакозуни, или Касандра против HBase. Искуство во тимот на Сбербанк

Предноста на HB е толку изненадувачка што постои сомневање дека има некаков вид на тесно грло во поставувањето на CS. Сепак, гуглањето и пребарувањето на најочигледните параметри (како concurrent_writes или memtable_heap_space_in_mb) не ги забрза работите. Во исто време, трупците се чисти и не пцујат ништо.

Податоците беа распределени рамномерно низ јазлите, статистиката од сите јазли беше приближно иста.

Вака изгледа статистиката на табелата од еден од јазлитеКлучен простор: ks
Број на прочитани: 9383707
Латентност на читање: 0.04287025042448576 ms
Број на пишување: 15462012
Латентност на пишување: 0.1350068438699957 ms
Испуштања на чекање: 0
Табела: t1
Број на SST: 16
Искористен простор (во живо): 148.59 MiB
Искористен простор (вкупно): 148.59 MiB
Простор што го користат снимките (вкупно): 0 бајти
Исклучена меморија искористена (вкупно): 5.17 MiB
Сооднос на компресија на SST: 0.5720989576459437
Број на партиции (проценка): 3970323
Број на мемтабилни клетки: 0
Големина на податоци на Memtable: 0 бајти
Искористена меморија со исклучување на Memtable: 0 бајти
Број на прекинувачи на Memtable: 5
Локален број на читани: 2346045
Локална латентност на читање: NaN ms
Локален број на записи: 3865503
Локална латентност на пишување: NaN ms
Исплакувања на чекање: 0
Процент поправен: 0.0
Лажни позитиви на филтерот за цут: 25
Лажен однос на филтерот за цут: 0.00000
Искористен простор за филтер за цут: 4.57 MiB
Искористена меморија со филтер за цут: 4.57 MiB
Индекс резиме на искористена меморија на куп: 590.02 KiB
Метаподатоци за компресија искористена меморија од грамада: 19.45 KiB
Минимум бајти на збиена партиција: 36
Максимални бајти на збиена партиција: 42
Средни бајти на збиена партиција: 42
Просечни живи клетки по парче (последните пет минути): NaN
Максимални живи клетки по парче (последните пет минути): 0
Просечни надгробни споменици по парче (последни пет минути): NaN
Максимални надгробни споменици по парче (последните пет минути): 0
Испуштени мутации: 0 бајти

Обидот да се намали големината на серијата (дури и да се испрати поединечно) немаше ефект, само се влоши. Можно е всушност ова да е навистина максимална изведба за CS, бидејќи резултатите добиени за CS се слични на оние добиени за DataStax - околу стотици илјади операции во секунда. Дополнително, ако го погледнеме искористувањето на ресурсите, ќе видиме дека CS користи многу повеќе процесор и дискови:

Битка на две јакозуни, или Касандра против HBase. Искуство во тимот на Сбербанк
Сликата ја прикажува искористеноста за време на извршувањето на сите тестови по ред за двете бази на податоци.

Во однос на моќната предност за читање на HB. Овде можете да видите дека и за двете бази на податоци, искористеноста на дискот за време на читањето е исклучително мала (тестовите за читање се последниот дел од циклусот на тестирање за секоја база на податоци, на пример за CS ова е од 15:20 до 15:40). Во случајот со HB, причината е јасна - повеќето од податоците висат во меморијата, во мемориската продавница, а дел се кеширани во блок-кешот. Што се однесува до CS, не е многу јасно како функционира, но рециклирањето на дискот исто така не е видливо, но за секој случај, беше направен обид да се овозможи кешот row_cache_size_in_mb = 2048 и да се постави кеширање = {'keys': 'ALL', 'rows_per_partition': '2000000'}, но тоа го направи уште малку полошо.

Исто така, вреди да се спомене уште еднаш важна точка за бројот на региони во HB. Во нашиот случај, вредноста беше наведена како 64. Ако ја намалите и ја направите еднаква на, на пример, 4, тогаш кога читате, брзината паѓа за 2 пати. Причината е што мемсторот побрзо ќе се полни и датотеките ќе се мијат почесто и при читање ќе треба да се обработат повеќе датотеки, што е прилично комплицирана операција за HB. Во реални услови, ова може да се третира со размислување преку стратегија за претходно раздвојување и компактирање; особено, ние користиме самонапишана алатка која собира ѓубре и ги компресира HFiles постојано во позадина. Сосема е можно за DataStax тестовите да одвоиле само 1 регион по табела (што не е точно) и ова донекаде би разјаснило зошто HB бил толку инфериорен во нивните тестови за читање.

Од ова се извлечени следните прелиминарни заклучоци. Ако се претпостави дека не биле направени поголеми грешки при тестирањето, тогаш Касандра изгледа како колос со глинени стапала. Поточно, додека балансира на едната нога, како на сликата на почетокот на статијата, покажува релативно добри резултати, но во борба под исти услови целосно губи. Во исто време, земајќи ја предвид малата искористеност на процесорот на нашиот хардвер, научивме да поставиме два регионални сервери HB по домаќин и со тоа да ги удвоиме перформансите. Оние. Имајќи ја предвид искористеноста на ресурсите, состојбата за ЦС е уште пожалосна.

Се разбира, овие тестови се прилично синтетички и количината на податоци што се користеа овде е релативно скромна. Можно е ако се префрливме на терабајти ситуацијата да беше поинаква, но додека за HB можеме да вчитаме терабајти, за CS ова се покажа како проблематично. Честопати фрлаше OperationTimedOutException дури и со овие томови, иако параметрите за чекање одговор веќе беа зголемени неколку пати во споредба со стандардните.

Се надевам дека со заеднички напори ќе ги најдеме тесните грла на CS и ако можеме да го забрзаме, тогаш на крајот од објавата дефинитивно ќе додадам информации за конечните резултати.

УПД: Благодарение на советите на другарите, успеав да го забрзам читањето. Беше:
159 операции (644 табели, 4 потоци, серија 5).
Додадено од:
.withLoadBalancingPolicy(new TokenAwarePolicy(DCAwareRoundRobinPolicy.builder().build()))
И си поиграв со бројот на нишки. Резултатот е следниот:
4 маси, 100 нишки, серија = 1 (парче по парче): 301 оп.
4 табели, 100 нишки, серија = 10: 447 оп.
4 табели, 100 нишки, серија = 100: 625 оп.

Подоцна ќе применам други совети за подесување, ќе извршам целосен тест циклус и ќе ги додадам резултатите на крајот од објавата.

Извор: www.habr.com

Додадете коментар