Upgrade to Pro — share decks privately, control downloads, hide ads and more …

DBスペシャリスト午後2問1で覚えるRDBMSの機能(2)

hmatsu47
March 17, 2020

 DBスペシャリスト午後2問1で覚えるRDBMSの機能(2)

インフラ勉強会 2020/03/20 FRI 22:00-23:30

hmatsu47

March 17, 2020
Tweet

More Decks by hmatsu47

Other Decks in Technology

Transcript

  1. 自己紹介 松久裕保(@hmatsu47) https://qiita.com/hmatsu47 名古屋でWebインフラのお守り係(非DBA) ◦ 会社ではAmazon Aurora(MySQL 5.6互換版)を 使ってます MySQL

    8.0:趣味・遊び ◦ MySQL 8.0の薄い本を作って配っています https://qiita.com/hmatsu47/items/ceb75caf46e3c761095d ◦ 現在8.0.19対応版です ◦ GitHubリポジトリのほか、印刷版を勉強会などでタダで配っています 2
  2. 今日のテーマ IPAのデータベーススペシャリスト試験問題をもとに、 • PostgreSQLやMySQL • 関連/派生プロダクト での機能利用例を参照しながら、 • 実務と共通する部分 •

    実務とは違う部分 を読み解いていきます 前回(3/6)分スライド : https://twitter.com/hmatsu47/status/1234490770134007809 ※今日の範囲は、仕様面では「違う部分」が多いです 3
  3. 伝えたいこと(再掲) • IPA試験の問題も、使い方次第で実務に役立ちます • 実務とは違うところもあるので、その違いを意識して役立てま しょう ◦ RDBMSによって実装されている機能は異なります ◦ 同じ機能が実装されていても、仕様や挙動が違うこともあります

    • とはいえ、このセッションでは厳密なことは説明しないので、 雰囲気がわかればOKです ※一部文字が小さいページがあります  スライドを手元にダウンロードしてお使いください  https://twitter.com/hmatsu47/status/1239176917732839426 4
  4. おしながき • 平成31年春期試験・午後2問1の概要(再掲) • ④テーブルパーティショニング ◦ 試験問題とPostgreSQL/MySQLを比較する ◦ PostgreSQLとMySQLで確認してみる •

    ⑤パラレルクエリ ◦ 試験問題とPostgreSQLを比較する ◦ PostgreSQLで確認してみる • ⑥クラスタリング ◦ 試験問題とMySQL/PostgreSQL関連(派生)プロダクトを比較する ◦ RDBMSクラスタリングの問題点とNewSQLについて 5
  5. ④テーブルパーティショニング PostgreSQL 12の場合 • パーティションの種類(区分方法)は3種類 ◦ RANGE レンジ(範囲) ◦ LIST

    リスト ◦ HASH ハッシュ • 親テーブルの下に子テーブルを作成する形でパーティショニングを行う • サブパーティションも作成可能 • PostgreSQL 11.5文書/5.10. テーブルのパーティショニング https://www.postgresql.jp/document/11/html/ddl-partitioning.html • PostgreSQLインサイド(富士通)/パーティショニングの概要 https://www.fujitsu.com/jp/products/software/resources/feature-stories/postgres/article-index/partitioning-overview/ 10
  6. ④テーブルパーティショニング MySQL 8.0の場合 • パーティションの種類(区分方法)は4種類(バリエーション含め8種類) ◦ RANGE [COLUMNS] レンジ(範囲) ◦

    LIST [COLUMNS] リスト ◦ [LINEAR] HASH ハッシュ(列または式) ◦ [LINEAR] KEY キー(キー列) ◦ COLUMNS : 複数列を使用して区分 ◦ LINEAR : 線形二乗アルゴリズムを使用して区分 • パーティションの下にサブパーティションを作成することができる • MySQL 8.0 Reference Manual/23.2 Partitioning Types https://dev.mysql.com/doc/refman/8.0/en/partitioning-types.html 11
  7. ④テーブルパーティショニング PostgreSQL 12/MySQL 8.0の場合 • (5)のパラレルクエリを除いて試験問題とほぼ同じ • WHERE句の条件指定でパーティションの刈り込み(Pruning)が可能 ◦ 特にパーティションキーのカーディナリティが低い(小さい)場合に有効

    ◦ 主キーやインデックスで十分に絞り込める場合はあまり意味がない • パーティションを削除することでDELETE文よりも高速な行削除が可能 ◦ MySQLではALTER TABLE テーブル名 DROP PARTITION パーティション名 ▪ MySQL 8.0 Reference Manual/23.3 Partition Management https://dev.mysql.com/doc/refman/8.0/en/partitioning-management.html ◦ PostgreSQLではDROP TABLE 子テーブル名 13
  8. ④テーブルパーティショニング カーディナリティとは • カラム(列)値のばらつき(値が何種類あるか?) ◦ [例1] 0と1 : 2 ◦

    [例2] 1~10 : 10 • カーディナリティが高い(大きい)列ほどインデックスで絞り込みやすい ◦ カーディナリティが2、かつ偏りがない→インデックスでは半分にしか絞り込めない ▪ インデックス→データ行の順にアクセスするよりもデータ行を直接フルスキャンしたほうが効率的 ◦ 例外として、カーディナリティが低くても値の偏りが大きければインデックスも有効 https://qiita.com/hmatsu47/items/2d44c173a9114fd06853 【注】ER図におけるリレーションのカーディナリティ(多重度)とは別概念 14
  9. ④テーブルパーティショニング PostgreSQL 12の例(レンジパーティショニング) 16 postgres=# CREATE DATABASE partition_test; CREATE DATABASE

    postgres=# \c partition_test You are now connected to database "partition_test" as user "postgres". partition_test=# CREATE TABLE log_record ( partition_test(# log_id SERIAL NOT NULL, partition_test(# log_datetime TIMESTAMP NOT NULL, partition_test(# log_text VARCHAR(500) NOT NULL partition_test(# ) partition_test-# PARTITION BY RANGE (log_datetime); CREATE TABLE partition_test=# CREATE TABLE log_record_default PARTITION OF log_record DEFAULT; CREATE TABLE partition_test=# CREATE TABLE log_record_2015 PARTITION OF log_record partition_test-# FOR VALUES FROM ('2015-01-01 00:00:00') TO ('2016-01-01 00:00:00'); CREATE TABLE 日時(タイムスタンプ)で パーティショニングを試す ↓範囲外はデフォルトパーティション
  10. ④テーブルパーティショニング PostgreSQL 12の例(レンジパーティショニング) 17 partition_test=# CREATE TABLE log_record_2016 PARTITION OF

    log_record partition_test-# FOR VALUES FROM ('2016-01-01 00:00:00') TO ('2017-01-01 00:00:00'); CREATE TABLE partition_test=# CREATE TABLE log_record_2017 PARTITION OF log_record partition_test-# FOR VALUES FROM ('2017-01-01 00:00:00') TO ('2018-01-01 00:00:00'); CREATE TABLE partition_test=# CREATE TABLE log_record_2018 PARTITION OF log_record partition_test-# FOR VALUES FROM ('2018-01-01 00:00:00') TO ('2019-01-01 00:00:00'); CREATE TABLE partition_test=# CREATE TABLE log_record_2019 PARTITION OF log_record partition_test-# FOR VALUES FROM ('2019-01-01 00:00:00') TO ('2020-01-01 00:00:00'); CREATE TABLE partition_test=# ALTER TABLE log_record ADD PRIMARY KEY (log_id, log_datetime); ALTER TABLE (データINSERT) ↑主キーにパーティションキーを含める
  11. ④テーブルパーティショニング PostgreSQL 12の例(レンジパーティショニング) 18 postgres=# CREATE DATABASE no_partition_test; CREATE DATABASE

    partition_test=# \c no_partition_test You are now connected to database "no_partition_test" as user "postgres". no_partition_test=# CREATE TABLE log_record ( no_partition_test(# log_id SERIAL NOT NULL, no_partition_test(# log_datetime TIMESTAMP NOT NULL, no_partition_test(# log_text VARCHAR(500) NOT NULL no_partition_test(# ); CREATE TABLE no_partition_test=# ALTER TABLE log_record ADD PRIMARY KEY (log_id); ALTER TABLE no_partition_test=# CREATE INDEX ON log_record (log_datetime); CREATE INDEX (データINSERT) 比較用に非パーティショニング  のテーブルも用意する 全く同じデータをINSERTする
  12. ④テーブルパーティショニング PostgreSQL 12の例(SELECTの場合) 19 partition_test=# EXPLAIN VERBOSE SELECT COUNT(*) FROM

    log_record partition_test-# WHERE log_datetime BETWEEN '2019-01-01 00:00:00' AND '2019-12-31 23:59:59'; QUERY PLAN --------------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------- Aggregate (cost=2135.00..2135.01 rows=1 width=8) Output: count(*) -> Seq Scan on public.log_record_2019 (cost=0.00..2075.00 rows=24000 width=0) Filter: ((log_record_2019.log_datetime >= '2019-01-01 00:00:00'::timestamp without time zone) AND (log _record_2019.log_datetime <= '2019-12-31 23:59:59'::timestamp without time zone)) (4 rows) partition_test=# \timing Timing is on. partition_test=# SELECT COUNT(*) FROM log_record partition_test-# WHERE log_datetime BETWEEN '2019-01-01 00:00:00' AND '2019-12-31 23:59:59'; count ------- 24000 (1 row) Time: 5.854 ms 2019年のパーティションから検索されている
  13. ④テーブルパーティショニング PostgreSQL 12の例(SELECTの場合) 20 no_partition_test=# EXPLAIN VERBOSE SELECT COUNT(*) FROM

    log_record no_partition_test-# WHERE log_datetime BETWEEN '2019-01-01 00:00:00' AND '2019-12-31 23:59:59'; QUERY PLAN --------------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------- Aggregate (cost=2638.89..2638.90 rows=1 width=8) Output: count(*) -> Index Only Scan using log_record_log_datetime_idx on public.log_record (cost=0.42..2576.84 rows=24821 w idth=0) Output: log_datetime Index Cond: ((log_record.log_datetime >= '2019-01-01 00:00:00'::timestamp without time zone) AND (log_ record.log_datetime <= '2019-12-31 23:59:59'::timestamp without time zone)) (5 rows) (中略) no_partition_test=# SELECT COUNT(*) FROM log_record no_partition_test-# WHERE log_datetime BETWEEN '2019-01-01 00:00:00' AND '2019-12-31 23:59:59'; count ------- 24000 (1 row) Time: 7.361 ms インデックスで絞り込まれている パーティショニングのほうが若干速い
  14. ④テーブルパーティショニング PostgreSQL 12の例(一括削除の場合) 21 partition_test=# DROP TABLE log_record_2015; DROP TABLE

    Time: 7.023 ms partition_test=# SELECT COUNT(*) FROM log_record partition_test-# WHERE log_datetime BETWEEN '2015-01-01 00:00:00' AND '2015-12-31 23:59:59'; count ------- 0 (1 row) no_partition_test=# DELETE FROM log_record no_partition_test-# WHERE log_datetime BETWEEN '2015-01-01 00:00:00' AND '2015-12-31 23:59:59'; DELETE 24000 Time: 123.978 ms no_partition_test=# SELECT COUNT(*) FROM log_record no_partition_test-# WHERE log_datetime BETWEEN '2015-01-01 00:00:00' AND '2015-12-31 23:59:59'; count ------- 0 (1 row) パーティショニングの場合は子テーブルをDROPする パーティショニングを利用してDROPしたほうが速い 非パーティショニングの場合はDELETEする
  15. ④テーブルパーティショニング MySQL 8.0の例(ハッシュパーティショニング) 23 mysql> CREATE DATABASE partition_test; Query OK,

    1 row affected (0.00 sec) mysql> USE partition_test Database changed mysql> CREATE TABLE flag_test ( -> id INT NOT NULL AUTO_INCREMENT, -> str VARCHAR(500) NOT NULL, -> flag INT NOT NULL, -> PRIMARY KEY (id, flag) -> ) -> PARTITION BY HASH(flag) -> PARTITIONS 2; Query OK, 0 rows affected (0.04 sec) (データINSERT) flag=0または1しかないテーブル  でパーティショニングを試す
  16. ④テーブルパーティショニング MySQL 8.0の例(ハッシュパーティショニング) 24 mysql> CREATE DATABASE no_partition_test; Query OK,

    1 row affected (0.01 sec) mysql> USE no_partition_test Database changed mysql> CREATE TABLE flag_test ( -> id INT NOT NULL AUTO_INCREMENT, -> str VARCHAR(500) NOT NULL, -> flag INT NOT NULL, -> PRIMARY KEY (id), -> INDEX idx_flag (flag) -> ); Query OK, 0 rows affected (0.02 sec) (データINSERT) 比較用に非パーティショニング  のテーブルも用意する 全く同じデータをINSERTする
  17. ④テーブルパーティショニング MySQL 8.0の例(SELECTの場合) 25 mysql> EXPLAIN SELECT COUNT(*) FROM partition_test.flag_test

    WHERE flag=1\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: flag_test partitions: p1 type: index possible_keys: PRIMARY key: PRIMARY key_len: 8 ref: NULL rows: 96444 filtered: 10.00 Extra: Using where; Using index 1 row in set, 1 warning (0.00 sec) 約10万行=全体の半分 (サンプリング統計なので誤差あり)
  18. ④テーブルパーティショニング MySQL 8.0の例(SELECTの場合) 26 mysql> EXPLAIN SELECT COUNT(*) FROM no_partition_test.flag_test

    WHERE flag=1\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: flag_test partitions: NULL type: ref possible_keys: idx_flag key: idx_flag key_len: 4 ref: const rows: 96444 filtered: 100.00 Extra: Using index 1 row in set, 1 warning (0.00 sec) こちらも約10万行=全体の半分
  19. ④テーブルパーティショニング MySQL 8.0の例(SELECTの場合) 27 mysql> SELECT COUNT(*) FROM partition_test.flag_test WHERE

    flag=1\G *************************** 1. row *************************** COUNT(*): 100000 1 row in set (0.03 sec) mysql> SELECT COUNT(*) FROM no_partition_test.flag_test WHERE flag=1\G *************************** 1. row *************************** COUNT(*): 100000 1 row in set (0.01 sec) SELECTでは誤差の範囲 (むしろパーティショニングのほうが遅い)
  20. ④テーブルパーティショニング MySQL 8.0の例(UPDATEの場合) 28 mysql> EXPLAIN UPDATE partition_test.flag_test SET flag=0

    WHERE flag=1\G *************************** 1. row *************************** id: 1 select_type: UPDATE table: flag_test partitions: p1 type: index possible_keys: NULL key: PRIMARY key_len: 8 ref: NULL rows: 96444 filtered: 100.00 Extra: Using where; Using temporary 1 row in set, 1 warning (0.00 sec) 約10万行=全体の半分 (変化なし)
  21. ④テーブルパーティショニング MySQL 8.0の例(UPDATEの場合) 29 mysql> EXPLAIN UPDATE no_partition_test.flag_test SET flag=0

    WHERE flag=1\G *************************** 1. row *************************** id: 1 select_type: UPDATE table: flag_test partitions: NULL type: index possible_keys: idx_flag key: PRIMARY key_len: 4 ref: NULL rows: 192888 filtered: 100.00 Extra: Using where 1 row in set, 1 warning (0.00 sec) こちらは約20万行=フルスキャンに
  22. ④テーブルパーティショニング MySQL 8.0の例(UPDATEの場合) 30 mysql> UPDATE partition_test.flag_test SET flag=0 WHERE

    flag=1\G Query OK, 100000 rows affected (6.26 sec) Rows matched: 100000 Changed: 100000 Warnings: 0 mysql> UPDATE no_partition_test.flag_test SET flag=0 WHERE flag=1\G Query OK, 100000 rows affected (9.90 sec) Rows matched: 100000 Changed: 100000 Warnings: 0 UPDATEでは明確な差が出た (パーティショニングのほうが速い)
  23. ⑤パラレルクエリ パラレルクエリとは • 1つのクエリ(SQL)の処理を分割して並列で処理する機能 • データ/インデックスのスキャンを並列で行う • MySQL 8.0ではごく一部の限られたSQLのみ対応 ◦

    SELECT COUNT(*) と CHECK TABLE • 試験問題とは違い、テーブルパーティショニングに関係なく使える ◦ 1つのテーブルでも有効 • PostgreSQL 11.5文書/第15章 パラレルクエリ https://www.postgresql.jp/document/11/html/parallel-query.html 32
  24. ⑤パラレルクエリ PostgreSQL 12の例(さきほどの非パーティショニングテーブルにて) 34 no_partition_test=# SET max_parallel_workers_per_gather=2; SET no_partition_test=# EXPLAIN

    VERBOSE SELECT log_datetime, COUNT(*) FROM log_record GROUP BY log_datetime; QUERY PLAN ---------------------------------------------------------------------------------------------------------- Finalize GroupAggregate (cost=13902.96..13921.21 rows=72 width=16) Output: log_datetime, count(*) Group Key: log_record.log_datetime -> Gather Merge (cost=13902.96..13919.77 rows=144 width=16) Output: log_datetime, (PARTIAL count(*)) Workers Planned: 2 -> Sort (cost=12902.94..12903.12 rows=72 width=16) Output: log_datetime, (PARTIAL count(*)) Sort Key: log_record.log_datetime -> Partial HashAggregate (cost=12900.00..12900.72 rows=72 width=16) Output: log_datetime, PARTIAL count(*) Group Key: log_record.log_datetime -> Parallel Seq Scan on public.log_record (cost=0.00..12600.00 rows=60000 width=8) Output: log_id, log_datetime, log_text (14 rows) Time: 0.597 ms Workerプロセスを2に (デフォルトで有効)
  25. ⑤パラレルクエリ PostgreSQL 12の例 35 no_partition_test=# SET max_parallel_workers_per_gather=0; SET Time: 0.206

    ms no_partition_test=# EXPLAIN VERBOSE SELECT log_datetime, COUNT(*) FROM log_record GROUP BY log_datetime; QUERY PLAN -------------------------------------------------------------------------------- HashAggregate (cost=14160.00..14160.72 rows=72 width=16) Output: log_datetime, count(*) Group Key: log_record.log_datetime -> Seq Scan on public.log_record (cost=0.00..13440.00 rows=144000 width=8) Output: log_id, log_datetime, log_text (5 rows) Time: 0.443 ms Workerプロセスを0に(パラレルクエリ無効) このケースでは実際のSQL実行でも パラレルクエリのほうが遅い(サーバスペックの問題)
  26. ⑤パラレルクエリ まとめ • 1つのクエリの処理を分割して並列で処理するのがパラレルクエリ • MySQL 8.0では(ほぼ)利用できない ◦ 今後に期待 •

    PostgreSQL 12ではデフォルトで有効になっている ◦ あまり気にせずに使える ◦ サーバのスペックによってはかえって遅くなることもあるのでその場合は無効にする • パーティショニングと併用しなくても使える 36
  27. ⑥クラスタリング クラスタリングとは • 複数のノードでクエリ(SQL)を処理/データを保存する構成 シェアードエブリシングとシェアードナッシング • シェアードエブリシング:データ保存先のストレージを全ノードで共有 ◦ Oracle RACなど

    • シェアードナッシング :データ保存先のストレージを共有しない ◦ 一般的にはこちらが多い シャーディング(分散)とレプリケーション(複製) • シャーディング :データを行単位または列単位で分割して保存 • レプリケーション:ノード間でデータを複製して保存(同期/非同期) 38
  28. ⑥クラスタリング プロキシで振り分け 40 • 主にレプリケーションを利用 するクラスタで採用 ◦ 例:MySQL+ProxySQL • Proxy自体も冗長化する

    ◦ SPOF防止 • セキュリティ目的の配置も ◦ Firewall(不正クエリ防止) ◦ クエリログ採取
  29. ⑥クラスタリング 参考情報(レプリケーション・シャーディング) • ProxySQL(公式)※プロキシのみ https://www.proxysql.com/ • MySQL InnoDB Cluster(MySQL公式+Smart Style)

    https://dev.mysql.com/doc/refman/8.0/en/mysql-innodb-cluster-userguide.html https://www.s-style.co.jp/blog/2018/11/2722/ • Citus(Microsoft Azure公式+篠田さんスライド) https://docs.microsoft.com/ja-jp/azure/postgresql/concepts-hyperscale-nodes https://www.slideshare.net/noriyoshishinoda/lets-scaleout-postgresql-using-citus-japanese-123590324 • Vitess(公式) https://vitess.io/ 43
  30. ⑥クラスタリング Citus(PostgreSQLベースのシャーディングクラスタ) • Microsoftが開発元のCitus Data社を買収 • Azure Database for PostgreSQL

    - Hyperscale (Citus) としてサービス提供 https://azure.microsoft.com/ja-jp/services/postgresql/ • Microsoft社内でWindows Updateのログデータ分析に利用されている https://www.atmarkit.co.jp/ait/articles/1911/06/news003.html Vitess(MySQLベースのシャーディングクラスタ) • YouTube内部で利用するために開発 • Cloud Native Computing Foundation (CNCF) がプロジェクトをホスト ◦ 2019/11にGraduationをアナウンス https://www.cncf.io/announcement/2019/11/05/cloud-native-computing-foundation-announces-vitess-graduation/ 44
  31. ⑥クラスタリング レプリケーションを利用したクラスタリングの問題点 • レプリケーションラグが発生する(非同期レプリケーションの場合) • 更新性能が悪くなる(同期レプリケーションの場合) • 更新性能がスケールしない ◦ マルチマスター構成にすると更新の競合でかえって性能が下がる

    シャーディング方式のクラスタリングの問題点 • レプリケーションを併用しない場合、データ冗長化ができない ◦ 一部のSQLが実行できないなど、機能面の制約もある • レプリケーションを併用しないとシャードをまたぐ処理が遅い場合がある ◦ 通信処理が必要な分オーバーヘッドが発生する(JOINなど/試験問題と同じ) • シャードの再構成時に性能が低下しやすい 45
  32. ⑥クラスタリング 最近の流れ:NewSQL • 更新を含めた性能のスケールと、データの整合性を両立 • 地理冗長性も意識 主なNewSQL(と呼ばれるもの) • Google Spanner

    https://cloud.google.com/spanner?hl=ja • CockroachDB https://www.cockroachlabs.com/product/ • YugaByteDB https://www.yugabyte.com/ • TiDB(分散KVS「TiKV」をバックエンドに利用したSQL対応DB) https://pingcap.com/en/ 46
  33. まとめ • テーブルパーティショニング:テーブル内のデータを分割保持 ◦ 有効なケースはかなり限られる ◦ パーティション単位での一括削除が高速 • パラレルクエリ:クエリを並列処理して高速化 ◦

    通常はパーティショニングと併用しなくても利用可能 • クラスタリング:複数ノードでクエリを処理 ◦ レプリケーションを利用したクラスタリングとシャーディングがある ◦ レプリケーションでは読み取り整合性と書き込み性能向上の問題がある ◦ シャーディングではノードまたぎの処理性能と再構成の問題がある ◦ 書き込み性能向上などを目指して進化(NewSQL) 49