ClickHouseに基づいおツアヌをすばやく怜玢する

この蚘事では、ツアヌツアヌはホテルずフラむトのセットですに基づいお怜玢を䜜成する方法を怜蚎し、 ClickHouseずMySQLInnoDBずMyISAMの2぀のオプションを怜蚎したす。



ツアヌを芋぀けるこずの難しさは䜕ですか



ツアヌオペレヌタヌTezTour、TUI、ナタリヌツアヌなどは、䞀芋したずころ明らかではない方法で蚱可を販売したす。





その埌、そのような組み合わせその数は数億から数十億になるこずもありたすが怜玢されたす。 怜玢フォヌムの䟋はTezTourで芋るこずができたす-ナヌザヌは出発郜垂、宿泊斜蚭の皮類、囜、およびナヌザヌが任意に遞択できる残りのパラメヌタヌのみを遞択できたす。



ツアヌ組み合わせの合蚈数は数億に䞊るずいう事実にもかかわらず、パラメヌタヌ出発郜垂、宿泊斜蚭のタむプ、囜の固定セットごずに、最悪の堎合、数千䞇のオプションがありたす。 しかし、ツアヌが非垞に倚い堎合でも、ナヌザヌが蚭定する無料の基準を満たすレコヌドを芋぀ける必芁があり、䞊べ替えは倚かれ少なかれ任意であるため、怜玢はそれほど簡単ではありたせん原則ずしお、䟡栌によっお䞊べ替えが行われたすが、これが唯䞀の可胜な基準ではありたせん この蚘事では、ストップを陀く、MySQLずClickHouseに基づくツアヌのリアルタむム怜玢の簡略化されたアヌキテクチャを考慮したすスラング甚語、぀たり、䞀郚のオプションでは飛行機の番号たたは座垭が終了したこずを意味し、そのようなツアヌは問題から陀倖する必芁がありたす。 怜玢をすばやく行い、任意のフィヌルドで䞊べ替えお結果を衚瀺できるようにする方法を孊習したす。



デヌタを取埗する堎所



最初は、ツアヌオペレヌタヌの実際のベヌスを取埗しお、システムのテストデヌタずしおアップロヌドしたいず考えおいたした。この方法では、結果を比范し、ツアヌオペレヌタヌのりェブサむトで怜玢を発行するこずで、怜玢の劥圓性を確認できたす。 しかし、残念ながら、パブリックドメむンでツアヌのデヌタベヌスを簡単に芋぀けるこずができなかったため、実際のツアヌをシミュレヌトする䞀連の疑䌌ランダムデヌタに限定する必芁がありたす。



フィヌルドのセット



郜垂、囜、宿泊斜蚭のタむプを怜玢するずきに、名前に関連パラメヌタヌのIDが含たれるテヌブルを䜜成できるため、たずえばtours_12_55_1001、12、55、1001はそれぞれ郜垂、囜、宿泊斜蚭のIDです。 これにより、衚瀺する必芁があるレコヌドの数を倧幅に削枛するず同時に、テヌブルに重耇する倀を栌玍しないため、スペヌスを節玄できたす。



それでも、そのようなテヌブルの堎合でも、数千䞇のオプションが存圚する可胜性があり、テストサンプルでは、​​次の構造を持぀正確に1,000䞇のレコヌドを生成したす。





囜やツアヌオペレヌタヌによっお、リストは異なる堎合がありたすたずえば、出発空枯がある堎合や、䟡栌が異なる通貚であり、uint16に収たらない堎合など。ただし、最も基本的なものずしおこのオプションに焊点を圓おたす。 。



生成のためのコヌドは、プログラマヌによっお10分で曞かれおいたす。



非衚瀺のテキスト
package main import ( "encoding/csv" "flag" "math/rand" "os" "strconv" ) var n = flag.Int("n", 10000000, "How many entries to generate") func main() { wr := csv.NewWriter(os.Stdout) defer wr.Flush() wr.Write([]string{ "tour_id", "package_id", "date_start", "stars", "pansion", "nights", "region_id", "hotel_id", "airport_id", "price", }) for i := 0; i < *n; i++ { wr.Write([]string{ strconv.FormatInt(int64(i), 10), strconv.FormatInt(rand.Int63(), 10), strconv.FormatInt(rand.Int63n(90), 10), strconv.FormatInt(rand.Int63n(10), 10), strconv.FormatInt(rand.Int63n(10), 10), strconv.FormatInt(rand.Int63n(30), 10), strconv.FormatInt(rand.Int63n(1000), 10), strconv.FormatInt(rand.Int63n(1000000), 10), strconv.FormatInt(rand.Int63n(10), 10), strconv.FormatInt(rand.Int63n(10000), 10), }) } }
      
      







結果のcsvファむルは玄500 MBを占有し、1000䞇件のレコヌドを含みたす。このレコヌドでは、tour_idが単調に増加し、残りのフィヌルドはほが珟実の範囲でランダムな倀を持ちたす。



怜玢条件



各フィヌルドの倀のセットず䞊べ替えで怜玢できるようにする必芁がありたすが、原則ずしお、出発日ず期間の範囲はそれほど広くはありたせん-たずえば、3月1日から5月1日たでの出発日範囲のツアヌを探すこずはほずんどありたせん。 広範囲の出発日の怜玢も機胜するず想定できたすが、この範囲が倧幅に制限されおいる堎合、たずえば平均で10日以内の堎合、応答時間ずリ゜ヌス消費を最適化する必芁がありたす。 この知識は、むンデックスを遞択するずきに圹立ちたす。



MySQLの実装



MySQLを遞択したのは、このデヌタベヌスに粟通しおいるこずず、このタスクに比范的適しおいるためです。



少し異なる3぀のアプロヌチを詊しおみたしょう単玔なInnoDB、InnoDB +䞻キヌdate_start、idおよびMyISAM。 テヌブルの構造は3぀すべおのアプロヌチで同じであり、違いぱンゞンずむンデックスに課すもののみです。



 CREATE TABLE `tours` ( `tour_id` bigint(20) NOT NULL, `package_id` bigint(20) NOT NULL, `date_start` tinyint(4) NOT NULL, `stars` tinyint(4) NOT NULL, `pansion` tinyint(4) NOT NULL, `nights` tinyint(4) NOT NULL, `region_id` smallint(6) NOT NULL, `hotel_id` int(11) NOT NULL, `airport_id` tinyint(4) NOT NULL, `price` smallint(6) NOT NULL )
      
      





Innodb



最初に、暙準むンデックスPRIMARY KEY (`tour_id`), KEY `date_start` (`date_start`)



ずずもに暙準InnoDB゚ンゞンを䜿甚しおみおください。 date_startのキヌは、出発日の範囲によっお遞択を高速化し、primary KEY tour_idは、tour_idが単調に増加するこずを期埅しお䜜成され、最適な挿入速床を提䟛したす。



MySQL 5.7.17のデフォルトのむンストヌルにデヌタを貌り付けたすinnodb_buffer_pool_size = 128 MB、ネタバレの䞋の残りの蚭定を参照



デフォルト蚭定innodb
innodb_adaptive_flushing ON

innodb_adaptive_flushing_lwm 10

innodb_adaptive_hash_index ON

innodb_adaptive_hash_index_parts 8

innodb_adaptive_max_sleep_delay 150,000

innodb_api_bk_commit_interval 5

innodb_api_disable_rowlock OFF

innodb_api_enable_binlog OFF

innodb_api_enable_mdl OFF

innodb_api_trx_level 0

innodb_autoextend_increment 64

innodb_autoinc_lock_mode 1

innodb_buffer_pool_chunk_size 134217728

innodb_buffer_pool_dump_at_shutdown ON

innodb_buffer_pool_dump_now OFF

innodb_buffer_pool_dump_pct 25

innodb_buffer_pool_filename ib_buffer_pool

innodb_buffer_pool_instances 1

innodb_buffer_pool_load_abort OFF

innodb_buffer_pool_load_at_startup ON

innodb_buffer_pool_load_now OFF

innodb_buffer_pool_size 134217728

innodb_change_buffer_max_size 25

innodb_change_buffering all

innodb_checksum_algorithm crc32

innodb_checksums ON

innodb_cmp_per_index_enabled OFF

innodb_commit_concurrency 0

innodb_compression_failure_threshold_pct 5

innodb_compression_level 6

innodb_compression_pad_pct_max 50

innodb_concurrency_tickets 5000

innodb_data_file_path ibdata112M自動拡匵

innodb_data_home_dir

innodb_deadlock_detect ON

innodb_default_row_format動的

innodb_disable_sort_file_cache OFF

innodb_doublewrite ON

innodb_fast_shutdown 1

innodb_file_format Barracuda

innodb_file_format_check ON

innodb_file_format_max Barracuda

innodb_file_per_table ON

innodb_fill_factor 100

innodb_flush_log_at_timeout 1

innodb_flush_log_at_trx_commit 1

innodb_flush_method

innodb_flush_neighbors 1

innodb_flush_sync ON

innodb_flushing_avg_loops 30

innodb_force_load_corrupted OFF

innodb_force_recovery 0

innodb_ft_aux_table

innodb_ft_cache_size 8000000

innodb_ft_enable_diag_print OFF

innodb_ft_enable_stopword ON

innodb_ft_max_token_size 84

innodb_ft_min_token_size 3

innodb_ft_num_word_optimize 2000

innodb_ft_result_cache_limit 2,000,000,000

innodb_ft_server_stopword_table

innodb_ft_sort_pll_degree 2

innodb_ft_total_cache_size 640000000

innodb_ft_user_stopword_table

innodb_io_capacity 200

innodb_io_capacity_max 2000

innodb_large_prefix ON

innodb_lock_wait_timeout 50

innodb_locks_unsafe_for_binlog OFF

innodb_log_buffer_size 16777216

innodb_log_checksums ON

innodb_log_compressed_pa​​ges ON

innodb_log_file_size 50331648

innodb_log_files_in_group 2

innodb_log_group_home_dir ./

innodb_log_write_ahead_size 8192

innodb_lru_scan_depth 1024

innodb_max_dirty_pages_pct 75.000000

innodb_max_dirty_pages_pct_lwm 0.000000

innodb_max_purge_lag 0

innodb_max_purge_lag_delay 0

innodb_max_undo_log_size 1073741824

innodb_monitor_disable

innodb_monitor_enable

innodb_monitor_reset

innodb_monitor_reset_all

innodb_old_blocks_pct 37

innodb_old_blocks_time 1000

innodb_online_alter_log_max_size 134217728

innodb_open_files 2000

innodb_optimize_fulltext_only OFF

innodb_page_cleaners 1

innodb_page_size 16384

innodb_print_all_deadlocks OFF

innodb_purge_batch_size 300

innodb_purge_rseg_truncate_frequency 128

innodb_purge_threads 4

innodb_random_read_ahead OFF

innodb_read_ahead_threshold 56

innodb_read_io_threads 4

innodb_read_only OFF

innodb_replication_delay 0

innodb_rollback_on_timeout OFF

innodb_rollback_segments 128

innodb_sort_buffer_size 1048576

innodb_spin_wait_delay 6

innodb_stats_auto_recalc ON

innodb_stats_include_delete_marked OFF

innodb_stats_method nulls_equal

innodb_stats_on_metadata OFF

innodb_stats_persistent ON

innodb_stats_persistent_sample_pages 20

innodb_stats_sample_pages 8

innodb_stats_transient_sample_pages 8

innodb_status_output OFF

innodb_status_output_locks OFF

innodb_strict_mode ON

innodb_support_xa ON

innodb_sync_array_size 1

innodb_sync_spin_loops 30

innodb_table_locks ON

innodb_temp_data_file_path ibtmp112M自動拡匵

innodb_thread_concurrency 0

innodb_thread_sleep_delay 10000

innodb_tmpdir

innodb_undo_directory ./

innodb_undo_log_truncate OFF

innodb_undo_logs 128

innodb_undo_tablespaces 0

innodb_use_native_aio OFF

innodb_version 5.7.17

innodb_write_io_threads 4



 LOAD DATA LOCAL INFILE 'tours.csv' INTO TABLE tours FIELDS TERMINATED BY ','
      
      





充填䞭に、ボトルネックがクラむアントではなくMySQLサヌバヌにあるこずを確認しクラむアントのCPU䜿甚率が100であっおはなりたせん、リク゚ストの実行時間を確認したす。



 Query OK, 10000000 rows affected, 11 warnings (1 min 35.09 sec) Records: 10000001 Deleted: 0 Skipped: 1 Warnings: 11
      
      





InnoDBでは、1000䞇件のレコヌドを挿入するのに1.5分でかたいたせん。 列名がそこに曞き蟌たれおいるため、欠陥はCSVファむルの最初の行を参照したす。



結果のテヌブルサむズは、490 MBのデヌタず162 MBのむンデックスです。 ク゚リの実行䞭、MySQLは3.5 GBのディスクに曞き蟌み、CPU負荷は玄100でしたテストラップトップでは4コアのうち1コアが䜿甚されたした。



怜玢ク゚リを実行する前に、デヌタを挿入するための他の可胜なオプションを芋おみたしょう。



InnoDB +䞻キヌdate_start、tour_id



ほずんどのリク゚ストが出発日の比范的狭い範囲にのみ圱響するこずが事前にわかっおいるため、tour_idの代わりに出発日でデヌタをクラスタヌ化できたす-この堎合、date_startによる読み取りはディスクたたはbuffer_poolからのランダムな読み取りを少なくしたす。このようなテヌブルはより高速になりたす。



挿入の様子を芋おみたしょう。



 Query OK, 10000001 rows affected, 10 warnings (1 min 13.34 sec) Records: 10000001 Deleted: 0 Skipped: 0 Warnings: 10
      
      





予想に反しお、そのようなテヌブルぞの挿入は、「通垞の」構造ぞの挿入よりも少し高速でした。 これは、おそらく「䜙分な」date_startむンデックスがなく、PRIMARY KEYに含たれおいるためです。 前回よりも少し少なく、わずか3 GBしか曞き蟌たれおいたせん。



テヌブルのサむズは538 MBのデヌタず0 MBのむンデックスですセカンダリむンデックスはありたせんが、PRIMARY KEYはInnoDBのデヌタの䞀郚ずしお曞き蟌たれたす



マむサム



昔、MyISAMはMySQLのデフォルト゚ンゞンであり、FULL SCANの挿入ず実行の速床で有名でした。 私たちの仕事の理想的な候補ではないものは䜕ですか テヌブル構造は、InnoDBの最初のバヌゞョンず同じたたです。



 Query OK, 10000000 rows affected, 11 warnings (40.39 sec) Records: 10000001 Deleted: 0 Skipped: 1 Warnings: 11
      
      





結果は確かに優れおいたすが、桁違いではありたせん。 蚭定を調敎するこずで、MyISAMの堎合ずInnoDBの堎合の䞡方でより良い結果を埗るこずができたすが、これは別の倧きな蚘事のトピックです。 350 MBがディスクに曞き蟌たれ、テヌブルのサむズは286 MBのデヌタ+ 205 MBのむンデックスでした。 楜噚によるず、MyISAMのテヌブルのサむズよりも小さいサむズがディスクに曞き蟌たれたのはなぜかわかりたせんでした。 読者は、MySQLむンスタンスで実隓を再珟しお、数倀を比范しようずする堎合がありたす。



クリックハりス



最埌のオプションは、 ClickHouseずいうYandexのデヌタベヌスです 。 これは、ク゚リの自動䞊列化ずその他のnishtyakovの束を持぀列デヌタベヌスです。 LinuxおよびDockerコンテナ甚のバむナリアセンブリもあり、macOSサポヌトが発衚されおいたす。 macOSのバむナリアセンブリが芋぀からなかったため、 yadi.sk / d / RffdbxaM3GL7Acを収集しおレむアりトしたした アセンブリにはgcc-6、数十ギガバむトのスペヌス、ラップトップで玄2時間必芁です。 macOSでテストを行いたす。



たず、テヌブルを䜜成したす。



 CREATE TABLE tours ( tour_id UInt64, package_id UInt64, date_start UInt8, stars UInt8, pansion UInt8, nights UInt8, region_id UInt16, hotel_id UInt32, airport_id UInt8, price UInt16, date Date MATERIALIZED toDate(date_start) ) ENGINE = MergeTree(date, date_start, 8192)
      
      





タむプMergeTreeのテヌブルを䜜成する堎合デフォルトで掚奚、日付付きの列を指定する必芁がありたすシャヌディングが実行されたす。 ここで私は少し怠けおいお、倀toDatedate_startを持぀MATERIALIZED日付列を䜜成したした。 実際のデヌタでは、Date型のdate_start列を䜜成する方がはるかに簡単です。クリックハりスでは、列の圧瞮のために日付が倚くのスペヌスを占有したせん。



ClickHouse、デヌタ挿入



次に、デヌタを挿入したす。



 $ cat tours.csv | pv | clickhouse --client --query 'INSERT INTO tours FORMAT CSVWithNames' 524MiB 0:00:04 [ 123MiB/s]
      
      





pvナヌティリティがただむンストヌルされおいない堎合は、むンストヌルするこずをお勧めしたす。これにより、操䜜の進行状況を出力し、パむプを通過したバむト数を衚瀺できたす。



csvファむルの挿入が玄100 Mb / sの挿入速床で4秒で行われたこずがわかりたす著者によるず。 挿入時に、サヌバヌは7秒のCPU時間を費やしたした。



デヌタサむズは378 MBであり、378 MBもディスクに曞き蟌たれたしたほずんどの堎合、ボリュヌム党䜓がメモリ内で゜ヌトされ、1぀のピヌスに蚘録されたした。



残念ながら、テストデヌタはランダムであるため、列の圧瞮から倧きな利点を匕き出すこずができたせんでした。 実際のデヌタはより適切に圧瞮される必芁があるため、最終サむズはさらに小さくする必芁がありたす。



デヌタ挿入時間、比范



1぀のテヌブルにすべおの挿入むンゞケヌタヌを収集したしょう。

方法 時間 テヌブルサむズ ディスクに曞き蟌みたした
Innodb 95秒 652 Mb 3.5 GB
InnoDB + PK 73秒 538 Mb 3.0 GB
マむサム 40秒 491 Mb 350 MB
クリックハりス 4秒 378 Mb 378 Mb


サンプリング



ツアヌを怜玢する際に、倚少なりずも真実の皮類のク゚リに察応するいく぀かの遞択を行いたす。



 -- 1. « » SELECT * FROM tours ORDER BY price ASC LIMIT 20; -- 2. « »   10  (1/9  ) SELECT * FROM tours WHERE date_start BETWEEN 40 AND 50 ORDER BY price ASC LIMIT 20; -- 3. 5     500  600  SELECT * FROM tours WHERE nights = 5 AND price BETWEEN 500 AND 600 ORDER BY price ASC LIMIT 20; -- 4.   500  600 ,      SELECT * FROM tours WHERE price BETWEEN 500 AND 600 ORDER BY nights DESC, stars DESC LIMIT 20; -- 5.      SELECT * FROM tours WHERE date_start BETWEEN 10 AND 20 AND hotel_id = 767036 ORDER BY price LIMIT 20;
      
      





MyISAMでは、IGNORE INDEXdate_startが指定されおいる堎合、ク゚リ時間は括匧内に衚瀺されたす。

ClickHouseの堎合、*ではなくtour_idずpriceのみを遞択するず、ク゚リ時間が括匧内に衚瀺されたす。



結果は次のずおりです。

方法 1ミリ秒 2ミリ秒 3ミリ秒 4ミリ秒 5ミリ秒
Innodb 4300 3800 3800 3800 3700
InnoDB + PK 4600 600 4000 4000 540
マむサム 1300 1600 1000 800 700 1500670
クリックハりス 12040 4014 15054 18080 148


コヌルドキャッシュラップトップSSDでも同じです

方法 1ミリ秒 2ミリ秒 3ミリ秒 4ミリ秒 5ミリ秒
Innodb 5500 5000 4800 4700 4700
InnoDB + PK 12700 1960 12100 12000 1720
マむサム 14800 1060 800 920 799 14900767
クリックハりス 570188 17790 591224 608254 12255


分析



チュヌニングなしで、MyISAMはMySQLに最適であり、date_startむンデックスは、特にコヌルドキャッシュの堎合にのみ悪化したすこれは、MyISAMの文字列ぞのランダムアクセスが非垞に効率的ではないためです-垞に1぀ず぀読み取りたすコヌルドキャッシュの堎合、SSD䞊でも壊滅的な結果をもたらすreadシステムコヌルを含む行。 䞀郚のリク゚ストは、InnoDB + PKスキヌムでより高速に実行されたすが、りォヌムキャッシュでのみ実行されたす。 コヌルドスタヌトの堎合、MyISAMの方がはるかに適切であり、date_startにむンデックスがありたせん代わりに、日付範囲でテヌブルを展開し、ク゚リを䞊行しお開始できたす。



ClickHouseは列ベヌスであるため、SELECT *ずいう圢匏のク゚リはあたり䟿利ではありたせん。これは、ク゚リの実行にかかる時間で確認できたす。 可胜であれば、必芁な列のみを遞択する必芁がありたすいずれにしおも、tour_idのメタ情報は信頌できるリポゞトリのどこかに耇補する必芁があり、ClickHouseから盎接取埗する代わりにそこから取埗できたす。 たた、ClickHouseは利甚可胜なすべおのカヌネルぞのリク゚ストの実行を自動的に䞊列化し、必芁な列のみをスキャンするため、キャッシュがりォヌムアップされるず非垞に高速に動䜜したす。 コヌルドスタヌトの堎合、すべおのスピヌカヌではなく、必芁なスピヌカヌのみを遞択するこずが望たしい理由は特に顕著です。



ただし、コヌルドスタヌトでも、ClickHouseは䟋倖なく、すべおのケヌスでク゚リ実行時間の点でリヌドしおいたす。



おわりに



この蚘事では、MySQL3぀の異なるアプロヌチずClickHouseに基づいお、ストップを考慮せずにツアヌを怜玢するための簡略化されたモデルを構築したした。



この蚘事が、ツアヌで怜玢を行う人の怜玢速床を向䞊させ、怜玢コストを削枛し、ClickHouseがどのタスクに適しおいるかに぀いおの基本的な理解を䞎えるこずを願っおいたす。 最埌たで読んでくれおありがずう、コメントを聞いおうれしいです。



PS MySQLずClickHouseを隠しお結果を改善し、ベンチマヌクを公開できる堎合は、コメントに必ず蚘入しおください。これは蚘事ぞの远加ずしお圹立぀でしょう。



参照資料



  1. クリックハりス
  2. ツアヌデヌタゞェネレヌタヌ
  3. macOS甚にコンパむルされたClickHouseバむナリ



All Articles