MySQLと正規形のはなし 申し訳ない、あんまりMySQLのはなし出てこなかったDeath 2016/07/02 yoku0825 YAP(achimon)C::Asia Hachioji 2016 mid in Shinagawa
MySQLと正規形のはなし申し訳ない、あんまりMySQLのはなし出てこなかったDeath
2016/07/02
yoku0825YAP(achimon)C::Asia Hachioji 2016 mid in Shinagawa
\こんにちは/
yoku0825@とある企業のDBAオラクれない-ポスグれない-マイエスキューエる-
家に帰ると妻の夫-せがれの⽗-ムスメの⽗-
⽣息域Twitter: @yoku0825-Blog: ⽇々の覚書-MyNA ML: ⽇本MySQLユーザ会-MySQL Casualʼs Slack: MySQL Casual-
1/87
あらすじ
MySQLで正規化すると遅くなるあるある-
節⼦、それ正規化が原因やない、内側のテーブルでソートしてるやんか
YAPC::Asia Tokyo 2014 WHERE狙いのキー、ORDER BY狙いのキー
-
「正規化したら遅くなった」テーブルを⾒せてもらったら正規形じゃなかったテーブル分割≠正規化-イマココ-
3/87
正規化 #とは
関係の正規化(かんけいのせいきか)は、関係データベ
ース (リレーショナル・データベース) において、正規
形と呼ばれる形式に関係(リレーション)を準拠させる
ことにより、データの⼀貫性の維持と効率的なデータア
クセスを可能にする関係設計を導くための⽅法である。
関係の正規化 - Wikipedia
5/87
正規化 #とは
更新箇所を減らす1. 参照効率を上げる2. データの整合性を守りやすくする3. ためのテーブル設計のプラクティス集“Normal Form” だから “標準系” とか “平坦化” とかでもいい気がする(正規っていうと正誤の問題ぽく聞こえるから)
7/87
MySQLerが絶対に押えておくべき正規形
第1正規形(1NF)第2正規形(2NF)第3正規形(3NF)ボイスコッド正規形(BCNF)第4正規形(4NF)第5正規形(5NF)第6正規形(6NF)
9/87
NOT NULLと候補キー
第1正規形の定義に⼊ってることもある候補キーが無いと第2正規化のステップで詰まる
3NFを除いてこれ以降の正規化は全て候補キーを軸にテーブル分割する
-
NULLABLE(NOT NULLしてない)と、第3正規化のステップで詰まる⼈間的な理屈で無理⽮理それっぽく分割することはできるけれど-
個⼈的にはこれらは第1正規形の前だと思ってる
12/87
第2正規形
ある関係が、第1正規形で、かつ、すべての非キー属性
が、すべての候補キーに対して完全従属するとき、第2
正規形 (second normal form; 2NF) であるという
関係の正規化 - Wikipedia
13/87
第2正規形
候補キー(PRIMARY KEYとUNIQUE KEYだけど、何らかの事情で制約をかけていないものも含む)が複数のカラムで構成されているときに(複合候補キー)複合候補キーの⼀部が決まれば値が決まるカラム がないとき
18/87
第3正規形
ある関係が、第2正規形で、かつ、非キー属性があるな
らば、それら全てが候補キーに非推移的に関数従属する
とき、第3正規形 (third normal form; 3NF) であると
いう
関係の正規化 - Wikipedia
20/87
第3正規形
非キー属性(候補キーでないカラム)があるときある非キー属性の値が決まると、値が決まるカラム がないとき
NULLABLEなカラムがあると、そもそも「値が決まらなくなる」ので何とも⾔えなくなる
-
21/87
ボイスコッド正規形
ある関係上に存在する⾃明でない全ての関数従属性の決
定項が候補キーであるとき、かつそのときに限り、その
関係はボイス・コッド正規形 (Boyce/Codd normal
form; BCNF) であるという
関係の正規化 - Wikipedia
23/87
ボイスコッド正規形
第3正規形であってBCNFでない例(︖)PRIMARY KEY (ipaddr, port, process) に対して、protocolがわかればprocessは⼀意に決まる場合
memcachedとInnoDB memcached Pluginが混在するとこの前提は崩れる
-
25/87
ボイスコッド正規形
複合候補キーが複数 (ipaddr, port, process), (ipaddr, port, protocol) あって、それぞれの真部分集合 (ipddr, port) が重なってる場合にしか発⽣しない候補キーの⼀部 と 非キー属性 (の全部または⼀部) から候補キーの別の⼀部が決まるケース が ないこのへんから「制約」の側⾯が強まってくる
26/87
第4正規形
第4正規形 (fourth normal form; 4NF) では候補キーで
はない属性への多値従属性をもった属性があってはなら
ない。
関係の正規化 - Wikipedia
27/87
第5正規形
第5正規形 (fifth normal form; 5NF) を満たす関係は、
その関係が第4正規形であり、さらにその関係に含まれ
る結合従属性の決定項が候補キーのみである場合、かつ
その場合だけである。
関係の正規化 - Wikipedia
28/87
先に⾔っておくと
BCNFを満たすリレーションのうち 非キー属性を持つものは⾃動的に第5正規形テーブルが 3カラム以上の複合候補キーだけで構成されている場合のみ 第4正規化と第5正規化を考える必要があるそんなテーブル作ったことあるお客様はいらっしゃいますか
29/87
第6正規形
A relvar R [table] is in sixth normal form
(abbreviated 6NF) if and only if it satisfies no
nontrivial join dependencies at all ̶ where, as
before, a join dependency is trivial if and only if at
least one of the projections (possibly
U̲projections) involved is taken over the set of all
attributes of the relvar [table] concerned.
Sixth normal form
33/87
第6正規形
5NFのこんなテーブルを
+------------------------------------------+------+------+------+| _hash_ | flg1 | flg2 | flg3 |+------------------------------------------+------+------+------+| a12483faa7f1c90c568017314a7ffdc84b544bb1 | 0 | 1 | 0 || da0e9d085e8f3ac208a7c80567914c097a9cc72a | 0 | 0 | 0 || 8458992edfd5e49f99bafacd33b9cac30abb967b | 0 | 0 | 0 |+------------------------------------------+------+------+------+
35/87
第6正規形
こうじゃ+------------------------------------------+------+| _hash_ | flg1 |+------------------------------------------+------+| a12483faa7f1c90c568017314a7ffdc84b544bb1 | 0 || da0e9d085e8f3ac208a7c80567914c097a9cc72a | 0 || 8458992edfd5e49f99bafacd33b9cac30abb967b | 0 |+------------------------------------------+------+
+------------------------------------------+------+| _hash_ | flg2 |+------------------------------------------+------+| a12483faa7f1c90c568017314a7ffdc84b544bb1 | 1 || da0e9d085e8f3ac208a7c80567914c097a9cc72a | 0 || 8458992edfd5e49f99bafacd33b9cac30abb967b | 0 |+------------------------------------------+------+
+------------------------------------------+------+| _hash_ | flg3 |+------------------------------------------+------+| a12483faa7f1c90c568017314a7ffdc84b544bb1 | 0 || da0e9d085e8f3ac208a7c80567914c097a9cc72a | 0 || 8458992edfd5e49f99bafacd33b9cac30abb967b | 0 |+------------------------------------------+------+
36/87
正規形
2NF, BCNFとそれ以降への正規化は複合候補キーがある場合のみ4NF以降(ただし6NFを除く)への正規化は3カラム以上の複合候補キーのみで構成される場合だからだいたい1NF〜3NFまで(本当はBCNFまで)きっちり把握してればプラクティスの使い⼼地(学習コストと得られるメリットの割合)としては上々
39/87
ここまでのまとめ
正規化は更新箇所を減らす&参照効率&データの整合性を上げるためのテーブル設計のプラクティス集
-
1NF〜3NFは割と簡単かつ効果が⾼い4NF以降は3カラム以上の複合候補キーのみのテーブルに対してのみ考える
-
40/87
アトミック #とは
それ以上分割できない1⾏に複数の(候補キーで識別されるべき)主体の情報が⼊ってはいけない
-
1カラムに複数の種類の情報が⼊ってはいけない-
分割しすぎてもとの意味が失われていない「⽂字列型は配列だから1⽂字ずつがアトミック(キリ)」とかいうことは起こらない
-
44/87
1⾏に複数の主体
mysql57> SELECT * FROM t1\G*************************** 1. row ***************************v: [email protected]: せがれが扇風機に向かってですます調で「ワレワレハ、ウチュウジンデス」って言っててちょっと面白い季節がやってきました。[email protected]: "Noted in 8.0.0 changelog." \n\nMySQL Bugs: #81827: no stack trace on crash in freebsd\nhttps://bugs.mysql.com/[email protected]: Workbench使いにとっては大事なことかもしれない。\n\nMySQL Bugs: #81971: Needs HiDPI support\nhttp://bugs.mysql.com/bug.php?id=819711 row in set (0.00 sec)
45/87
⾏を分割
mysql57> SELECT * FROM t1\G*************************** 1. row ***************************v: [email protected]: せがれが扇風機に向かってですます調で「ワレワレハ、ウチュウジンデス」って言っててちょっと面白い季節がやってきました。*************************** 2. row ***************************v: [email protected]: "Noted in 8.0.0 changelog." \n\nMySQL Bugs: #81827: no stack trace on crash in freebsd\nhttps://bugs.mysql.com/bug.php?id=81827*************************** 3. row ***************************v: [email protected]: Workbench使いにとっては大事なことかもしれない。\n\nMySQL Bugs: #81971: Needs HiDPI support\nhttp://bugs.mysql.com/bug.php?id=819713 rows in set (0.00 sec)
46/87
それでもまだ1カラムに複数の情報
mysql57> SELECT * FROM t1\G*************************** 1. row ***************************v: [email protected]: せがれが扇風機に向かってですます調で「ワレワレハ、ウチュウジンデス」って言っててちょっと面白い季節がやってきました。*************************** 2. row ***************************v: [email protected]: "Noted in 8.0.0 changelog." \n\nMySQL Bugs: #81827: no stack trace on crash in freebsd\nhttps://bugs.mysql.com/bug.php?id=81827*************************** 3. row ***************************v: [email protected]: Workbench使いにとっては大事なことかもしれない。\n\nMySQL Bugs: #81971: Needs HiDPI support\nhttp://bugs.mysql.com/bug.php?id=819713 rows in set (0.00 sec)
47/87
カラム分割
mysql57> SELECT * FROM t1\G*************************** 1. row ***************************u: [email protected]: せがれが扇風機に向かってですます調で「ワレワレハ、ウチュウジンデス」って言っててちょっと面白い季節がやってきました。*************************** 2. row ***************************u: [email protected]: "Noted in 8.0.0 changelog." \n\nMySQL Bugs: #81827: no stack trace on crash in freebsd\nhttps://bugs.mysql.com/bug.php?id=81827*************************** 3. row ***************************u: [email protected]: Workbench使いにとっては大事なことかもしれない。\n\nMySQL Bugs: #81971: Needs HiDPI support\nhttp://bugs.mysql.com/bug.php?id=819713 rows in set (0.00 sec)
48/87
[email protected] はアトミックか
ほとんどのサービスにとってはアトミックTwitter, Slack, GitHub, Oracle Single Signin On, [email protected] をメールアドレスだとしか認識してない⼈たち
アカウント名が yoku0825 なのは俺がそう設定したからであって、関連はない(彼らにとっては)
-
51/87
[email protected] はアトミックか
たまに、アトミックじゃないサービスがいるGMail, その他MTAさんたち[email protected] は gmail.com の yoku0825 ユーザー
DNSさんにとっては gmail.com もアトミックじゃない
-
52/87
つまりアトミック #とは
設計次第なんだけどLIKE 演算⼦や SUBSTR 関数を使ってるなら、それは多分アトミックじゃない⇔ アトミックなら LIKE 演算⼦や SUBSTR 関数を使ってない
配列型(MySQLにはない)やJSON型もBLOB型も、SQLからはPKで引いてアプリケーション側でゴニョゴニョするならリレーショナルモデル的には正規形
-
SET型(こんなデータ型MySQLくらい︖)さん…-
53/87
第2正規形(again)
ある関係が、第1正規形で、かつ、すべての非キー属性
が、すべての候補キーに対して完全従属するとき、第2
正規形 (second normal form; 2NF) であるという
関係の正規化 - Wikipedia
54/87
第2正規形(again)
候補キー(PRIMARY KEYとUNIQUE KEYだけど、何らかの事情で制約をかけていないものも含む)が複数のカラムで構成されているときに(複合候補キー)複合候補キーの⼀部が決まれば値が決まるカラム がないとき
55/87
第2正規形
+--------------+----------------------+| _prefecture_ | _city_ |+--------------+----------------------+| 北海道 | 札幌市中央区 || 北海道 | 札幌市北区 || 北海道 | 札幌市東区 |+--------------+----------------------+
+--------------+-----------------------+| _prefecture_ | prefecture_kana |+--------------+-----------------------+| 北海道 | ホッカイドウ |+--------------+-----------------------+
+--------------------+--------------------------------------+| _city_ | city_kana |+--------------------+--------------------------------------+| 札幌市中央区 | サッポロシチュウオウク || 札幌市北区 | サッポロシキタク || 札幌市東区 | サッポロシヒガシク |+--------------------+--------------------------------------+
58/87
正規化の醍醐味
北海道の読みが「でっかいどう」に変わった時にテーブルまるごと更新しなくていい更新箇所を減らす-
prefecture vs. prefecture̲kana を⾒れば容量がコンパクトになっているのは⼀目瞭然参照効率を上げる-city̲kanaはこの⾯微妙。。カーディナリティー依存
-
北海道の読みが「ほっかいどう」であることを保証できる(整合性の保証)元のテーブルで SELECT DISTINCT prefecture_kana FROM .. WHERE prefecture = '北海道' が2⾏返ってきちゃったら、どっちが正しいのかは機械は判断できなくなる
-
59/87
第3正規形(again)
ある関係が、第2正規形で、かつ、非キー属性があるな
らば、それら全てが候補キーに非推移的に関数従属する
とき、第3正規形 (third normal form; 3NF) であると
いう
関係の正規化 - Wikipedia
60/87
第3正規形︖
+----------------------+------------+| _name_ | birthday |+----------------------+------------+| yoku0825 | 1982-08-25 || yoku0825の妻 | 19xx-01-17 || yoku0825のせがれ | 20xx-05-27 || yoku0825のムスメ | 20xx-01-05 |+----------------------+------------+
+------------+-----------------+| _birthday_ | birthday_stone |+------------+-----------------+| 1982-08-25 | ペリドット || 19xx-01-17 | ガーネット || 20xx-05-27 | エメラルド || 20xx-01-05 | ガーネット |+------------+-----------------+
64/87
第1非正規形になってしまった
birthdayがアトミックじゃない
+----------------------+------------+| _name_ | birthday |+----------------------+------------+| yoku0825 | 1982-08-25 || yoku0825の妻 | 19xx-01-17 || yoku0825のせがれ | 20xx-05-27 || yoku0825のムスメ | 20xx-01-05 |+----------------------+------------+
+------------+-----------------+| _birthday_ | birthday_stone |+------------+-----------------+| 1982-08-25 | ペリドット || 19xx-01-17 | ガーネット || 20xx-05-27 | エメラルド || 20xx-01-05 | ガーネット |+------------+-----------------+
65/87
改めて第1正規系のプロセスを通すと第3正規形に
+----------------------+------------+| _name_ | birthday |+----------------------+------------+| yoku0825 | 1982-08-25 || yoku0825の妻 | 19xx-01-17 || yoku0825のせがれ | 20xx-05-27 || yoku0825のムスメ | 20xx-01-05 |+----------------------+------------+
+------------+------------+| _birthday_ | birthmonth |+------------+------------+| 1982-08-25 | 8 || 19xx-01-17 | 1 || 20xx-05-27 | 5 || 20xx-01-05 | 1 |+------------+------------+
+--------------+-----------------+| _birthmonth_ | birthday_stone |+--------------+-----------------+| 1 | ガーネット || 5 | エメラルド || 8 | ペリドット |+--------------+-----------------+
67/87
現実問題、中間テーブルはかっ⾶ばす
name_birthday JOIN birthmonth_stone ON MONTH
(birthday) = birthmonth で仕留めるリレーショナルモデルの世界には MONTH 関数なんてないので、中間テーブルを作らないと birthday と birthmonth の対応が取れない
そういう世界線なのだと割り切る-
現実世界では birthday が判れば birthmonth を⼀意に識別できる
意味的に MONTH 関数が中間テーブルを提供してくれる-
69/87
候補キーで分かれるという意味
そのリレーション(テーブル)で表現されるもの(=⾏)が分けられるということ
name̲birthday は「⼈間」が主体(名前で識別される “⾏”)だろうし
-
birthmonth̲stoneは「誕⽣⽉」が主体(1〜12で識別される “⾏”)だろう
-
何の「属性」だかわからなくなったものを、それぞれ正しい「クラス」に割り振り直すのが正規化
-
72/87
候補キーで分かれることが実際に起こすもの
というわけでJOINのインデックスはPK狙いになるMySQLはPK狙いのJOINならそれなりに速い-
というか PKで結合できないなら分割の仕⽅間違ってる
あるいは、正規化とは関係ない別のテーブル分割の問題-過剰分割とかね-
73/87
候補キーで分かれることが実際に起こすもの
そして出てくる 内側テーブルの “ORDER BY狙いのキー” が選べない問題真⾯目に(︖)正規化すると、サブクエリーやGROUP BYとのJOINをしたくなる
SELECT user_id, tweet_count FROM user JOIN (SELECT user_id, COUNT(\*) AS tweet_count FROM tweet GRUOP BY user_id) USING(user_id) ORDER BY tweet_count DESC ..
-
こんなことするとしんでしまいます「正規化すると遅い」が真になる瞬間
-
74/87
正規化かどうかは別として、同じフレームで評価すると
参照効率を上げる成功してる-
更新箇所を減らす2テーブル更新しなきゃいけなくなったから失敗してる-
整合性の保証トランザクションがあるとはいえ制約は失った(=整合性は保証されない)正しくトランザクションが使われている限りは 整合性があるけれど、 正しくトランザクションが使われていることそのものは誰も保証してくれない
-
81/87
インデックス(ただし禁書目録ではない)
ソート済みのデータの複製予めソートした状態でデータのサブセットを作っておくことで、検索時の探索効率を上げる
-
重複をなくした設計にしましょうって⾔ってるRDBMSが基本的な機能として、「データを敢えて重複させている」というのは業が深くて考えさせられる重複させても不整合が出ないように、重複させるオーバーヘッドを取り除くために、裏側の実装はすんごくがんばってる
-
それと同じだと割り切れば、上⼿い使い⽅として付き合うことはできる
FriendFeed では MySQL を使いどのようにスキーマレスのデータを保存しているのか (⽇本語訳)
-
FriendFeedがFacebookに買収されてサービスを終了したので、原⽂はリンク切れ-84/87
後半のまとめ
正規化は “⾏” が表すものをクラス化する作業
候補キーはインスタンス、非キー属性はプロパティーみたいな感じ-
⼿順通りに3NFまで正規化するとJOINはPK結合になるはずPK結合にならなかったら正しく分割できていないか、正規化とは別のテーブル分割をしたのか
-
別のクラスのプロパティーとして再定義することで(せめて)正規形を保ちつつ冗⻑な⽅向に倒すインデックスと同じ考え⽅-正規化と同じ評価軸を使うと、どこまで論理的にアレな感じかの尺度として⾒られる
-
85/87
参考情報
あなたが知らない リレーショナルモデル理論から学ぶデータベース実践⼊門Amazon.co.jp︓ SQL and Relational TheoryFriendFeed では MySQL を使いどのようにスキーマレスのデータを保存しているのかWhere狙いのキー、order by狙いのキー
86/87