41から始めました

文字通り41歳から始めたブログです。DB(MySQL)を使ってお仕事してるので、DB周りの話を中心に最近始めたこととかをTwitterのノリで書いています。なお、本サイトにおいて示されている見解は私個人の見解であり、所属団体や組織を代表するものではありません。

Oracle Code 2019最高でした!ありがとうございました!

※勢いで書いて、まとめようとしてなかったので読みにくいと思います。(伝えたいことはタイトルの一行)

今年は参加できたOracle Code。

https://www.oracle.co.jp/events/code/2019/

毎年この時期なんやかんやあったんだけど、今年は時間取れたし、聞きたい話も盛りだくさんだったので。

場所

シェラトン都ホテル

家から近いので自転車で行っちゃいました。(良かったのかな?)

あの辺、何もないので自転車があって本当に助かった。

(お昼食べずに行ったから、20分の休憩時間で何か食べようと思ったときコンビニが近くにないのでセッションを無駄にしかねない)

以下セッションについての感想等

Oracle ACEが語る MySQL 8

資料はこちら

Oracle ACE から見た、MySQL 8の便利な新機能、ハマりどころを紹介します!
・降順インデックスによるSQLチューニング方法
・InnoDB Cluster / Group Replication における一貫性のコントロール方法とその使い分け
・スロークエリログの拡張
・MySQL 8のクエリキャッシュ・・・など
アプリケーション開発者の方にとって有益な機能を紹介予定です。

【講演者紹介】
ヤフー株式会社 サービスプラットフォーム本部 データベース部 MySQL 三谷 智史 氏
Yahoo JAPAN! におけるRDBの第一人者として活動。日本MySQLユーザ会のメンバーとしてコミュニティ活動にも従事。

MySQL ACEとして初の登壇。

しかし登壇慣れしてるからか緊張は見られない。

裏でやってた @t_wada さんの「過去を知り、未来に備える - 技術選定の審美眼2019」も聞きたかったけど、体は一つだし、何より最初のセッションはMySQLの話が聞きたかったw

三谷さん(右)と目次

f:id:next4us-ti:20190522182933p:plain

今回のは8.0.15までの話らしく、CHECK制約の話はここにはない(残念!)

三谷 さんのセッション、すべて良かったんですが特に良かったのが、

これ、凄いです。本当によく調べたなあ、と。

最後にyoku0825さんから

「実際導入するとして、consistencyってどれを選びます?」

という質問があり(俺も質問出なかったら聞こうと思ってたのでありがたす!)、

三谷さん:「EVENTUALかなぁ……特定の、古いデータを読みたくない処理だけ、セッション単位でConsistencyを設定するように案内するかなあと思います」

という回答。この辺は地雷キュアことyoku0825さんか運用キュアの三谷さんにおススメパターンを導いてほしい!(お前がやれ)

まあ、MySQLっぽいのはEVENTUALなんだけど、BEFORE_ON_PRIMARY_FAILOVERとかBEFORE_AND_AFTERあたりも場合によっては使われそうな気がするなー、と個人的には思いました。

(AFTERはキツそうなんだが、デフォじゃないんだ?と今までのMySQLの感じからすると意外?)

  • 上記Consistency Levelのデモ動画が超分かりやすかった

デモ動画、あった場合と無い場合で上のまとめの理解が全然違う。 いずれ公開されるらしいんですが、早く見たい!

Kubernetesで実現する運用自動化の新しいアプローチとは

Kubernetesというと、小規模コンテナ群で構成されたマイクロサービスのための基盤というイメージをお持ちかもしれません。しかし、Kubernetesが元来備えている拡張性と、コンテナ自動管理機能を利用することによって、Enterpriseの重量級ワークロードでも運用の自動化・効率化の恩恵を受けることが可能です。
このセッションでは、そんなKubernetesの可能性を活かした、Enterpriseでのコンテナ活用の手法について、デモを交えて解説致します。

【講演者紹介】
日本オラクル株式会社 ソリューションエンジニアリング統括 クラウドプラットフォーム本部 茂(しげる) こと
新卒で日本オラクルに入社後、Oracle Databaseのプリセールスエンジニアを経て、現在はアプリケーション開発を支援するクラウドサービスの提案やContainer、KubernetesといったCloud Nativeテクノロジーの活用を日本のお客様に広めるため日々模索し奔走しています。

この時間は本当にどれを選ぶか迷った。

f:id:next4us-ti:20190522182943p:plain

ここを選んだのは

というからであった。

ちなみに資料はこちら

helmでワンライナーで立っちゃうMySQL InnoDB Clusterとかもうヤバい。

ちなみにk8sでコンテナがデプロイされるまでの話は本人曰くこの資料をデフォルメしたものとのこと。

MySQL Operator使いてえ!試してえ!って勝手にほざいてましたが、その前提となる話のKubernetes Operatorのことが知れて本当に聞けて良かった!

茂 こと(@cotoc)さん、スゲーっす!

世界はグラフ構造でできている? 〜 超高速クエリから機械学習まで

資料はこちら

データベースの中でもとりわけ柔軟で直感的なデータの管理を実現するため、グラフ構造を用いた「グラフ・データベース」の発展が長らく期待されてきました。ここ最近になって、ユーザー行動などの関係性に着目した分析のニーズが高まり、新たなクエリ言語の標準化の取り組みが活発化し、更にはグラフそのものを学習するという AI 技術が生まれ、 金融不正検知から製造における部品表に至るあらゆる分野で、グラフというビッグデータを活用する機が熟しつつあります。このセッションでは「世界」を記述するグラフの検索から機械学習まで、オラクルの最新のデータベース技術を用いて、デモを交えながら解説します。

【講演者紹介】
Oracle Corporation Thailand Solutions Consultant, Big Data and Analytics 山中 遼太
オラクルのコンサルティング部門にてデータベースのエンジニアとして従事した後、退職してバイオインフォマティクスとゲノム科学の学位を取得。その後、オラクルに復帰し、機械学習やグラフ分析の製品担当として、ビッグデータ活用ソリューションの提案をリードしている。2018年よりバンコクに在住。

このセッション直前に食事買いに出かけたため、セッション開始直前に入ることになり、席が埋まってて予約したにもかかわらず立ち見。

さらにはモニターの故障により、セッションが10分程度中断するアクシデント。

でも山中さん、それにも明るい感じで、「アハハ、どうしましょう?」と言いながら機器トラブルを笑いに変える大胆さとトークのうまさ。それでいてスタッフへの気遣いもしてて、この人マジ素敵やな!と男ながらに惚れてしまいました。

トラブル解消後、とてつもないスピードでグラフDBについて説明してましたが、

  • グラフDBはNoSQLの中では今一番ホットかつ断トツの利用量
  • RDBMSでは速度が出ない関係性のデータも素早く出せる
    • 例えば求職サイトのマッチング f:id:next4us-ti:20190522182947p:plain
    • 重要度の評価 f:id:next4us-ti:20190522182953p:plain
  • 機械学習にも応用できる f:id:next4us-ti:20190522182959p:plain

(こういうのって自社でも使えそうだなー)

グラフDB、少しずつ勉強してたけどもう少し触る機会を自分に作ったほうがいいかも。

GraphPipe and TensorFlow, Serverless and Neural Networks with Fn Project

GraphPipeとは、Oracleがオープンソースとして公開する、機械学習およびディープラーニングのモデルのデプロイを単純化し標準化するツールです。このセッションでは、GraphPipe概要、GraphPipeおよびTensorFlowを使用した学習済みモデルのデプロイと推論の実装についてご紹介します。さらに、OSSのサーバーレスプラットフォームであるFn Projectと組み合わせたデプロイについてもご紹介予定です。 

【講演者紹介:ABeam Consulting Ltd. 澤田 哲史 氏】
SIerを経て、現在アビームコンサルティングに所属。
アプリケーション開発からインフラ領域まで幅広いレイヤーに精通したアーキテクトとして、
様々な領域の基幹システム構築や、エンタープライズITインフラ整備に数多く従事。
企業のDigital Transformationを支える、未来のITアーキテクチャの在り方を模索すべく、活動中。 
【講演者紹介:日本オラクル株式会社 河内 美樹】
日本オラクル クラウド・プラットフォーム本部で、データ分析・機械学習・Deep Learningに関わる製品とお客様への提案活動を担当。
データ蓄積については、特にOracle DatabaseとHadoopの世界を繋ぐBig Data SQLや、Oracle Exadataの性能検証などを過去に担当していた。日本オラクルのBig Data & Data Integration BLOGで情報発信を行っている。

今回、一番自分になじみのない話をここでは聞いてみた。

こういう複合セッション系で一つは全く知らない話を聞きに行くと、後で「あぁ、そういえば!」となることが多いので面白い。

このセッションの資料については https://blogs.oracle.com/bigdata-dataintegration-jp/graphpipe_intro にすべてアップされているという事前準備の良さ!(ありがたい)

オジサンからすると凄いありがたいお話だった。

あと、もう一つオジサンにはたまらなかったのが、もう一人の登壇者 澤田 哲史さんのGundam Face 色塗りアプリケーションの話。

f:id:next4us-ti:20190522183005p:plain

このソースがgithubにあるので、誰でも試せますw

https://github.com/scpepper69/ml-image-generator

ここがへんだよMySQL ここが凄いよMySQL

そして、メインディッシュ!これを聞かずには帰れない。

f:id:next4us-ti:20190522183008p:plain

以下5人の資料がまとめてここに上がってる (それぞれのサイズが違うのでちょっと見づらい・・・)

それにしても豪華!

日本人でMySQL関連のOracle ACEは計6人。

そのうち、Facebookの松信さん以外がすべて集まってMySQLについての思い出等を話してくれたのがこのセッション。

sh2さんのここがヘンだったよMySQL

さすがMySQLを4から触ってるだけあって、それは変だなと思いましたw

  • CPUコア数を増やしても性能が上がらなかった > 5.5で改善
  • バイナリログ形式のデフォルトがSTATEMENTだった > 5.7で改善
  • トランザクションログの同期書き込みをサボっていた > 5.7で改善

結論:MySQL5.7以降良いですね

kamipoさんのMySQLとActive Recordの話

kamipoさんの資料だけ外だし

f:id:next4us-ti:20190522183013p:plain

基本、sh2さんと同じで、変なところはなくなってる!って話なんですが、一番変なのは、Maintenance Releaseといいつつ、

  • GROUP BY ... DESCの削除 8.0.13
  • デフォルト式 8.0.13
  • 関数インデックス 8.0.13
  • LATERAL句 8.0.14
  • CHECK制約 8.0.16

と全然メンテナンスじゃねー!というところだと。(ですよねー)

yoku0825さんのHUGっと!Oracle ACE(MySQL)

  • すごいプリ〇ュア=キュア松信さん
  • InnoDBのプリ〇ュア=キュアSH2さん
  • Active Recordのプリ〇ュア=キュアkamipoさん
  • 運用のプリ〇ュア=キュアmita2さん
  • 文字化けのプリ〇ュア=キュアとみたさん
  • 地雷のプリ〇ュア=キュアyoku0825さん

強いわ、このプリ〇ュア達w

この人たちに加えて、Oracle ACEじゃないけどMySQLに関しては戦闘力53万みたいな人がまだまだいるので、

MySQLの未来は安泰!

ちなみに、「罠、トラウマとなるほどのものはMySQL8にはない」と話されてますが、

スライド見ると、十分に踏んでますね・・・(感謝の意)

三谷さんのここがヘンだよMySQL&ここがスゴイよMySQL

データベースの一番重要な要素・・・Durability/耐久性

それに対し、

  • 壊れるデータファイル(ver <= 5.1)
  • クラッシュセーフでないスレーブ(ver <= 5.5)
  • 非同期レプリケーション(ver <= 5.6)
  • クラッシュセーフでないDDL(ver <= 5.7)

バージョンが上がるごとに良くなったね!(MySQL8.0最強)

とみたさんのMySQLと令和

https://speakerdeck.com/tmtms/mysql-and-reiwa

やはり文字化けのプリ〇ュアw

読むとわかりますが、マニアック!(でも面白い)

ちなみに合字の㋿と令和は明治〜平成までとは違って=(イコール)じゃなくなってるそうです。

今回のセッションの資料

https://www.oracle.co.jp/campaign/code/2019/ に聞けなかったセッションの資料も上がってるんですが、どれも興味深い。

これらをすべて読んでたら、また積読が増える・・・。(でも読みたい)

今回の戦利品

f:id:next4us-ti:20190522182926p:plain

タオルとMyNAクリアフォルダーと@hmatsu47さんによるMySQL 8.0の薄い本の物理本!

@hmatsu47さんありがとうございました!

Oracle Code2019 最高だった!

来年も行きたい!!

登壇した方々、お疲れさまでした。

DB設計したいNight #4 そーだいさんと失敗から学びながらDB設計したいnight参加してきました

募集内容

枠名 参加条件
通常参加枠 300円(会場払い)
本購入枠 無料。但し本を持参する
絶対来る枠 300円(会場払い)
ブログ枠 無料

最後のブログ枠って、参加報告ブログを書いて資料としてアップするってことかな?

とにかく、自分は本購入してる(フフフ、サインも入ってるぞ!)ので本購入枠で参加してきました。

スケジュール

開場

19時開場、19時半開始ということでなるべく早く行って良い席とろうと思ったが、当日定時で上がれず、 参加枠も本購入枠もまあまあ当日までいい感じで埋まってて、増席すらあったので

「これは微妙な席になっても仕方ないかも…」

と覚悟してましたが、まだ19時5分時点でそこまで混んでおらず、前から2列目の正面席を取れて自分的にはベストポジションでした。

(すでに@kaibaさん、@Nakunaruさん、@yuyasatさん、@bringer1092さん達が楽しそうに雑談されてましたが)

ゴザ席というなかなか初心者には 座りにくい 勇気のいる席があり、誰もそこには座れずw

あれはもしやるのであれば映画館形式の席の並べ方じゃなく、コロシアムというかスタジアムのようにメインの人を囲むような感じにしたほうが良かったのかなあ?なんて思ったりしました。

ちなみに会場はピクスタ株式会社から提供していただきました。ありがとうございました。

始めて来ましたが、とても綺麗なオフィスで羨ましいです。(うちも引越ししたい)

そーだいさん到着

19:25頃、本日のメインご登場。

本当に「ワタシ"ポスグレ"チョットデキル」 Tシャツが眩しいです。

イントロダクション

開場説明と今回の進め方について簡単にお話。

あとで懇親会で知ったが、過去の回とは今回の回の進め方全然違ったらしく、過去のやり方はお題があってそれに対してみんなでERDとか書きながら発表し合い、ゴールを探すやり方だったらしいけど、今回はお題の失敗談をいくつか用意しておき、それについてそーだいさんとNakunaruさんが答えていくというディスカッション形式でやりますよー、とのこと。

でも、イベントページの概要をもう一回よく見たら

迷えるDB設計初心者達のための勉強会です。 普段はハンズオン形式ですが、今回は そーだいさん と nakunaruさんのパネルディスカッション形式です。

って書いてあって、うん、なるほどw

一つ目のお悩み(お題①)

Kさんという匿名の方からのお悩みだそうでw

  • 電話帳のような企業向けアプリのお話
  • 支店、部署、人名のように絞り込みをする
  • 支店番号は数値か昇順にしてほしい
  • それなのに東京支店は30aと30bがあって文字列
  • 対応として支店コードを36進数にした

型変換に悩んでいる様子

一つ目の悩みへの回答

  • 支店名が入るマスターを作ればよかったのでは?
  • テーブル追加で対応できる
  • テクニックで逃げるのではなく、基本を大事に

といった回答がありました。

自分だったらどうしてただろう?

少なくとも36進数は思いつかないw

自分は正規化推進派(一回小さく、少なくとも第三正規化まではしておきたい)なので、おそらくテーブルを作って対応するだろうな、と思いました。

もちろん、それによりDBMSによっては結合回数が多くなって遅くなることも考えられるのですが、これはそーだいさんも言ってた通り、テーブルを合わせるのは分割するよりも簡単なので、その時は合体すればいいので、マスターテーブル化がベターなんだと思われます。

(そうすればあとはマスターみたいな情報なんだから、速度云々みたいな話になるんならAWSならS3みたいなところに入れといて、DB使わずにそこからキャッシュに入れるんでもいいんじゃとかもある)

二つ目のお悩み(お題②)

なんとこちらもKさんからのお悩みだそうでw

  • 賃貸物件と売買物件の両方を扱うサービス
  • 数多くの項目があるのだが、異なるのは一部
  • 現在は全く別のテーブル
  • 別のソースコードだが、処理はほぼ同じ
  • だがそれぞれ別のサービスなので、テーブルは分割されていたほうが使い勝手はよさそう
  • というわけでテーブル構造もSTI(単一テーブル継承)構造にした
  • そうしたら賃貸されてるのに売買されているという変なデータができた

というお話。

二つ目の悩みへの回答

  • 賃貸・売買物件を親にしてしまうのはどうか?
  • その二つの親から共通テーブルが見える形にすれば
  • そこで親テーブルのIDの排他を担保するためのCHECK制約(MySQLだと8.0.16から)を使う
  • もしくは共通テーブルのIDを親が持つ
  • この時、共通テーブルにフラグを持たせがちだが、1つのテーブルに複数のステータスや状態を持たせないほうがいい
    • そうなるのなら別テーブルに分割
  • 共通項目は本当に共通かはちゃんと考えたほうがいい

自分だったらどうしてただろう?

これ、うちも業種が同じなのでわかるんですが、基本的には元々設計した人がしっかりDB分かってる人だったんで既にそーだいさんが言ったようなことが自社のDBではなされててます。

それでも後から新規に作られるテーブルやカラムは上記のような基本を考えていないことがあるので、その辺に目を見張らせるのが今の自分の仕事だったりします。 (数が多すぎて見れてないこと多々ありますが・・・。)

まあ、Check制約については無い場合がMySQLの場合ほとんどだと思うので、API側にやらせるんでしょうね。

あとはJOINで速度が出ない場合はこの共通テーブルに色々入れていきたくなり、遅くなりがちになるので注意です。

三つ目のお悩み(お題③)

こちらはNさんからのお悩みだそうですw

KとかNとか、あれー、管理者さんのイニシャル…

  • 元々はこんな感じだった
    • 飲食店向け求人サイト
    • 店舗テーブル
      • 店舗情報、掲載期間、審査ステータス、編集ステータス
    • 現行、エリア、路線、駅
      • 店舗テーブルとJOINして検索
    • 店舗ビュー
      • 店舗テーブルと関連があるすべてのテーブルをJOINする
  • パフォーマンスが悪い!
  • というわけでなんでも取れるビューはダメだ、分割しよう!と考えた
  • 画面上のブロックで必要なデータだけを取るビューを作成
  • その結果ビュー同士の依存が4階層になり、ビューの数が200以上に
  • どのビューを変更するとどこが壊れるかわからない!!!
  • 開発速度が遅くなってしまった

三つ目の悩みへの回答

  • サマリテーブルやマテリアライズドビューを作ろう
  • キャッシュも使おう
    • 更新頻度が少ないマスター系データはキャッシュに入れたり、S3にファイルを置いといてそこから見よう
    • とはいえ、キャッシュ中毒の章(16章)はよく読んで使いましょう
  • ビューの多段は絶対ダメ(2階層以上)
  • ビューは1段構成(Not 多段)であれば作る順番は気にしなくて済む
  • ビューでビューを作ったりすると、更新順序を守らないと古いキャッシュが残り、ビューのデータがおかしくなる
  • 分けたものをくっつけるのは楽だけど、くっついたものを分けるのはツライ
  • 論理設計を最初にちゃんとやって、細かく割ってから、とりあえず動かしてチューニングするのがいい
  • ちなみにビューやマテビューを使いたくなったら、それは設計をミスってて、それをDBMSで直す飛び道具としてビューやMビューを考える
  • あと、ビューの使いどころとしては、以下の3つがある
    • テーブルを分けたいときに、その分割前の状態をビューにしておくパターン
    • ビューに参照権限付与して、元のテーブルを見せない
    • 設計が悪いパターンで変えづらいやつに対し、結合した結果を見せたビューを作る(前述のやつ)

自分だったらどうしてただろう?

まあ、やっぱり正規化して細かくテーブル分割して、見たかったらJOINしたり、パフォーマンス出ないなら結合テーブルっていうのでいいんじゃないかな?

ビューをそもそもアプリ側が吸収してくれるならそれで良くて、それがツライっていうんなら仕方なくビューを用意するって意識なので、それを多段にするってのは考えない。

MySQLここ2年半ほど触ってきて、思った以上にちゃんと作ったりちゃんとSQL組まないとすぐ遅くなるので(Oracleとは大違い)、その辺は全然意識が変わった。

それでもビューを多段にするのは20代で一回失敗してからOracleでもやらないようにしてるけど。

ライトな質問①

ここからはお悩みというか、質問タイム。 まずは、

不要になったカラムは消すか?

これに対してはいらないという判断がつきづらいので消せないという話が多かったけど、そういう人も要らないのが判ってたら消すんでしょ?と思いきやそうでもなかった。

怖いから念のため取っておくという人が結構いた。

OracleMariaDBではinvisible columnができるから、そういうので試したらいいと思うし、インデックスでも同様の話があるのでそれも確認する方法を使って消せばいい。

ログテーブルって作ってる?

作ってる人はほとんどいなかった。

RDBに入れておくと便利だけど、そのメンテがめんどくさい。

不要になったログデータをS3などにファイル化して退避させてから削除する仕組みを作ればDBに入れとくのもありでは?という話。

懇親会

フード・ドリンク等の費用サポートのForkwell様ありがとうございました。

お酒飲めないんですが、コーラをたくさん飲みましたw

同じテーブルにいた人達が結構個性的で、その人たちとも交流できて面白かったです。

本当はそーだいさんや最近DBREという言葉を教えてくれた@_awacheさんとお話したかったんですが、あまり動き回るのもアレかな~、と思ってるうちに家族都合のタイムアップ。

次回は積極的に動こうかな?と思ってます。

あ、でも帰りにそーだいさんがぐーぜんエレベーターホールまで一緒になって、帰りに色々温かい言葉かけてくれたり、握手までしてもらったりしたし、

@_awacheさんからも

こんな優しい言葉かけてもらって、

行って良かったDB設計したいnight!でした。

Changes in MySQL 8.0.16 (2019-04-25, General Availability) を読む(1回目)

リリースノートを読んでみる

英語の勉強もかねてちょっとずつ読んでみる。

Account Management Notes

DROP ROLE権限について

> Previously, users who had the DROP ROLE privilege could use the DROP ROLE statement to drop locked or unlocked accounts. 
以前は、DROP ROLE権限を持っていたユーザーはこのDROP ROLEステートメントを使用してロックまたはロックされていないアカウントを削除できました。

> Now, users who have the DROP ROLE privilege can use DROP ROLE only to drop accounts that are locked 
現在、DROP ROLE 権限を持つユーザーは、ロックされているアカウントを削除するためだけに使用できます

> (unlocked accounts are presumably user accounts used to log in to the server and not just as roles). 
(ロック解除されたアカウントは、おそらくロールとしてではなく、サーバーへのログインに使用されるユーザーアカウントです)。

> Users who have the CREATE USER privilege can use DROP ROLE to drop accounts that are locked or unlocked.
CREATE USER権限を持つユーザーは、ロックされているまたはロックされていないアカウントを削除できます。

8.0.16からはDROP ROLE権限だとロックされているアカウントだけを削除できるように制限され、 それ以前のDROP ROLE権限と同じようにロックされていないユーザを削除するにはCREATE USER権限が必要ということですね。

ユーザーアカウントカテゴリ

> Several changes have been made to MySQL account-management capabilities:
MySQLのアカウント管理機能にいくつかの変更が加えられました。

> MySQL now incorporates the concept of user account categories, with system and regular users distinguished according to whether they have the new SYSTEM_USER privilege:
MySQLは現在、ユーザーアカウントカテゴリの概念を取り入れており、システムユーザーと一般ユーザーは新しいSYSTEM_USER権限を持っているかどうかによって区別されます 。

> System users are users who possess the SYSTEM_USER privilege. A system user can perform operations on both system and regular accounts.
システムユーザーは、SYSTEM_USER権限を持つユーザーです 。システムユーザーは、システムアカウントと通常のアカウントの両方で操作を実行できます。

> Regular users are ordinary users who do not possess the SYSTEM_USER privilege. A regular user can perform operations on regular accounts, but not system accounts.
一般ユーザーは、SYSTEM_USER権限を持たない一般ユーザーです 。通常のユーザーは通常のアカウントに対して操作を実行できますが、システムアカウントに対しては実行できません。

とりあえず分けたということは分かるが、ここまでの説明だといまいち権限の差が見えにくい。

もう少し読み進める。

> Other operational implications of the SYSTEM_USER privilege:
SYSTEM_USER権限の その他の運用上の影響

> A session that has the SYSTEM_USER privilege can be killed only by users who have the SYSTEM_USER privilege, in addition to any other required privileges.
SYSTEM_USER権限 を持つセッションは、必要とされる権限に加え、SYSTEM_USER権限を持つユーザーだけが強制終了できます 。

> An account that has the SYSTEM_USER privilege can be specified as the DEFINER for a stored object only by users who have the SYSTEM_USER privilege, in addition to any other required privileges.
SYSTEM_USER権限 を持つアカウントは、必要とされる権限に加え、SYSTEM_USER権限を持つユーザーによってのみストアドオブジェクトのDEFINERとして指定できます。

> A role that has the SYSTEM_USER privilege cannot be listed in the value of the mandatory_roles system variable.
SYSTEM_USER権限 を持つ役割 は、mandatory_rolesシステム変数の値にリストすることはできません 。

mandatory_rolesってなんじゃ?と思う方もいると思う。(自分も使ったことないのでよくは知らない)

要するにサーバで必須のロールとして設定できるようになってる(8.0.2~)のだが、それにはSYSTEM_USERは入れられないよということっぽい。

あんまりちゃんと理解できていないものの、SYSTEM_USER権限は他のユーザ用権限とは一線を画しておく管理者ユーザ用にのみ与えるものってことでいいのかしら?

続きは後日。

【祝】MySQL8.0.16登場!MySQLはその先に向かう。

とうとう来ました、待望のバージョンが!

僕はdocker-composeで遊んでるのでYAML

   mysql_8.0:
     image: mysql:8.0.16

と書いて再起動しただけでできましたが、中の人が

と教えてくれたリンク先にも上がっていますので、公式が8.0.16になるまではリンクのほうで試してみると良いかと思います。

ftp.iij.ad.jp

(⇒4/25 21:30時点で確認したら公式にも上がってました) f:id:next4us-ti:20190425213137p:plain

(リリースノートも更新されてますね。) dev.mysql.com

さて、さっそく遊んでみよう

何はともあれCHECK制約を触ってみる

MySQL :: MySQL 8.0 Reference Manual :: 13.1.20.7 CHECK Constraints

に書かれているテーブルをそのまま使ってみる。

f:id:next4us-ti:20190425132910p:plain

おお、とうとうMySQLにもCHECK制約が入ったんやなあ!

(ちなみに過去のバージョンはそもそもcreate tableできない)

f:id:next4us-ti:20190425133044p:plain

ほかの機能も遊びたい

8.0.16の機能についてはhmatsu47さんの

github.com

を読みながらこれから遊んでみようかと思う。 (その前にyoku0825さんがガンガン濃厚な実験+記事書いてくれそうな気はしてるけど)

追記(NOT ENFORCEDは誰がためにある)

f:id:next4us-ti:20190425173317p:plain

↑これを見て、「なるほど?」

試す

f:id:next4us-ti:20190425174024p:plain

挿入できたし、データも入ってる。

ちなみに間のところで見えてますが、

select * from information_schema.CHECK_CONSTRAINTS;

でCHECK制約一覧見れます。

NOT ENFORCED 使いたくなる場面が知りたいなあ。

『失敗から学ぶ RDBの正しい歩き方』発刊記念!そーだいさんから直接学ぶ会 に参加してきました

行ってきました

先日、『失敗から学ぶ RDBの正しい歩き方』発刊記念!そーだいさんから直接学ぶ会 に参加してきたのでそのレポートというか雑記です。

https://mixi.connpass.com/event/124948/

3部構成で、最初にこの会を開いてくれたミクシィの担当者(しょっさん)さんから会社説明と会場説明、そしてこの会を開くきっかけが5分ほど話され、 次にミクシィの2年目のエンジニアであるさのひろゆきさんから今年の新人研修資料の発表が同じく5分ほどあり、 最後に1時間ほどそーだいさんの話という流れでした。

内容についてはありがたいことに動画をミクシィさんがアップしてくれているのでそちらを見たほうが 自分が書くよりも確実なものが得られると思うので大半は割愛しますが、メモ的な感じでとった感想等を 書いておきたいと思います。

1部

開催小話

そーだいさんが本を出されて、それがいい本だったので社内のSlackで話題に挙がった ⇒しょっさんがTwitterで今回の会についてつぶやいてみたら本人が快諾してくれた

奇跡の上に成り立った会!

引き受けてくれたそーだいさんにしょっさんさんが感謝してましたが、それを実現したしょっさんさんとミクシィ社にも感謝です。

2部

新卒DB研修

さのひろゆきさん 出てきた瞬間、ベテラン感あふれる風貌なのに、プロフィールで「2017年度新卒」ってマジか!(失礼)

2年目に研修資料作らせてるのはまあいいとして、その内容が

  • SQLチュートリアル
    • Jupyter Notebookを用いてSQLの機能確認 ⇒わかる
  • インデックスの演習
    • ISUCONの過去問題を題材にpt-query-digest/EXPLAIN/インデックスの使い方を学ぶ …うん、まあわかる。(ISUCONの過去問題を題材にするところとpt-query-digestはちょっと早い気もするが)
  • ストレージエンジンの実装
    • MySQLのストレージエンジンの実装を通してデータベースの基本機能を自作してみる

ミクシィの新人研修、新人のレベルが凄すぎる気がするの自分だけ? 俺もストレージエンジンの実装なんてしたことないわ…。

3部

そーだいさんのお話

序 なぜこの本を書いたか

https://soudai.hatenablog.com/entry/2019/03/06/080000

『失敗から学ぶ RDBの正しい歩き方』 この本、とてもわかりやすいし『SQLアンチパターン』を参考にしつつも内容が被らないように気を付けてていい本だなあと思ってましたが 作るのに「2年くらいかかった」というコメントからもこういう良書を作るにはやはりそれだけの労力が必要なのには納得でした。

何のためにこの本を書いたかという目的として「知識をつないでいくため」というフレーズはハッとさせられました。

  • インターネットに置いておくと、古い話や失敗事例なんかは色々なパターンで消えていく
    • サイトがなくなったり
    • 著者がなくなるとクレジットカードを止めてサイトがとまるとか

言われてみると確かにそうです。 (ネット上の情報は生き物じゃないから永遠に残ってそうだと思ってたけど、それを管理するのは生身の人間なので永遠じゃない)

先日Microsoft Storeで電子書籍の終了があったが、書籍という形であれば書店はもちろん、電子書籍も複数のメディアで販売できるのでブログという形よりは確実に次につなげられる。

データベースのノウハウは10年レベルで使えるし、必要であれば改定すればいい。

今回のこの本はSQLアンチパターンと同じように定期的に読んで、自分の血肉にしていかなければ意味がないなと思いました。

本題

https://speakerdeck.com/soudai/learn-from-failure-1

今回は『失敗から学ぶ RDBの正しい歩き方』の中の2章、15章、7章についての話でしたが、Q&Aではそこ以外の章とも絡んだ話がされました。(8章 JSONの甘い罠)

失われた事実

  • 履歴の保存をすることで過去の情報が失われることを防ぐ
  • But、履歴の保持はパフォーマンスに影響する
  • 設計時に事実の履歴をしっかり検討することが大事

簡単すぎる不整合

  • 正しく正規化することが大事
    • RDBを使うのなら正規化は絶対にするべき(おそらくここで言われている正規化は第3までだと勝手に思ってる)でそれ以上はRDBの責務の範囲外に来ている。
    • ログデータについてはKinesisで受けてもDynamoDBで受けたりS3に吐いたりすればいい。
    • 正規化すればRDBの90%の問題は解決する。

隠された状態

  • アンチパターンのポイント

    • データに複数の意味を持たせない
    • 削除フラグ・・・フラグなのに0,1,2,...99,NULLってあったことがある。
    • 友人のyoku0825さんがTwitterでこんなことをつぶやいてました
SQL> SELECT COUNT(*) FROM t1 WHERE c1 = 'null';

  COUNT(*)
----------
      1231

SQL> SELECT COUNT(*) FROM t1 WHERE c1 IS NULL;

  COUNT(*)
----------
      3531

(これはよく見るw)

  • テーブルやレコードを分ければシンプルになります。
  • 一つのデータの責務を小さくする
    • 正規化すると自然とそうなる。パフォーマンスも上がる。
  • 常に状態が見えるようにするために事実のみを保存する
  • こちらを参考にしてください⇒https://speakerdeck.com/soudai/rdb-the-right-way

Q&A

Q:JSONについての章と隠された状態の話ってつながりがありますか?

A:あります

  • JSON型の便利なところ:スキーマレス
  • だが、本来のデータを正規化というか制約してない
  • そういうのをちゃんと正規化していきましょう
  • でかいデータをJSONにする
  • あまりの部分をJSONにしましょう

Q:隠された状態でユーザテーブルが作られていた場合ユーザごとにカラムを分解した方が良いのか?それとも管理者と普通のユーザのようにテーブルを分けたほうがいいのか?

A:結論ケースバイケースです。

そこでは - ステータス,フラグごとにカラムを二つ、三つ作って管理するのは絶対にダメ。 - ステータスカラムを持つべきだが、その場合はテーブルが肥大化しやすい。 - テーブルを分解すると肥大化が抑えられるが、同じユーザが存在するテーブルが複数存在する形になる - 会員はどれくらいで考えているか?を元に最初に設計しましょう。 - テーブルを増やすのは楽だけど、分けるのはしんどいです。

追加で自分が質問した内容

本にサインをもらいに行ったんですが、その際に以下の質問をしてみました。

Q:正規化した場合のパフォーマンスはどのように上げられるのか?

MySQLで正規化を進めていくとJOIN結合が発生した場合に遅くなりがちと言われるが、それでも正規化はすべきで、それ以上のパフォーマンス劣化はRDBの責務の範囲外に来ているから別の対応をした方が良いといわれましたが、具体的にはどのような方法がありますか?

A:レプリケーション・必要な情報を集めたテーブル作成・NoSQLを活用してみてください

  • レプリケーションで必要なデータだけを特定のスレーブに集める
  • 必要なデータを結合したビューテーブルを作る
  • NoSQL上にキャッシュしておく

最後に

懇親会に家族都合で最後までいられませんでしたが、非常に有意義な時間を過ごせました。(そーだいさんに追加質問できたり、懇親会で知人ができたり) 現場を提供してくださったミクシィ様、スピーチに立ったミクシィのさのさん、この会を企画してくださったしょっさんさん、そして10年物のノウハウを残してくれたそーだいさんには本当に感謝しています。

定期的にこの本読みます!

decimal(n,0)の使い道

MySQLの数値型

整数型 (真数値)だと - INTEGER - INT - SMALLINT - TINYINT - MEDIUMINT - BIGINT

固定小数点型 (真数値)だと - DECIMAL - NUMERIC

浮動小数点型 (概数値) - FLOAT - DOUBLE

ビット値型 - BIT

という風になっています。

社内のDBでdecimal(31,0)というのがいた。

mysql> select TABLE_SCHEMA,TABLE_NAME,COLUMN_NAME,COLUMN_TYPE from information_schema.columns where table_schema = 'xxxxxxxxxxxxxxxx';
+------------------+-------------------------+------------------+---------------+
| TABLE_SCHEMA     | TABLE_NAME              | COLUMN_NAME      | COLUMN_TYPE   |
+------------------+-------------------------+------------------+---------------+
| xxxxxxxxxxxxxxxx | xxxxxxx_xxxxxxxxx       | xxxxxxxxxxxxx_id | decimal(31,0) |
+------------------+-------------------------+------------------+---------------+
1 rows in set (0.01 sec)

これ、連携元のデータがvarchar(31)なんですが、連携先ではこいつをキーにしたいのでxxxxxxxxxxxxx_idとして数字型にして使ってるようです。 (そもそも数字型である理由はよくわからん)

僕は勉強(確認)不足だったなーとあとで反省したのでこれを書いているんですが、bigintでいいじゃん!と思い込んでました。 だって、bigなint なんだからてっきり整数型のかなりの桁のところまで補ってくれるんだと勝手に思ってたんです。

だから、なんで固定小数点型の小数点を使わない形でdecimalなんて使ってるんだ?変なの、とも。

bigintじゃ桁不足。

先に言っちゃうと、bigint は
-9223372036854775808~9223372036854775807
もしくは
0~18446744073709551615 までしか入りません。

f:id:next4us-ti:20190412224526p:plain

dev.mysql.com

つまり、19桁もしくは20桁の途中までということです。

OneのDBではvarchar(31)

当然bigintじゃ入らない、ということになります。

検証してみます

まず符号付きでbigint を検証します。

mysql> use test;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> drop table if exists ketasu;
Query OK, 0 rows affected (0.25 sec)

mysql> CREATE TABLE `ketasu`(
    -> `keta_bigint` bigint NOT NULL,
    -> `keta_varchar20` varchar(20) NOT NULL,
    -> `keta_decimal19` decimal(19,0) NOT NULL
    -> ) ENGINE=InnoDB ;
Query OK, 0 rows affected (0.36 sec)

※マイナス記号が入るのでvarchar(20)にしています

データを入れてみます。

mysql> insert into `ketasu` values(-9223372036854775808,'-9223372036854775808',-9223372036854775808);
Query OK, 1 row affected (0.07 sec)

これは範囲内なので当然入ります。 でも、次のが入りません。

mysql> insert into `ketasu` values(-9223372036854775809,'-9223372036854775809',-9223372036854775809);
ERROR 1264 (22003): Out of range value for column 'keta_bigint' at row 1

bigint であふれてるから駄目だよというメッセージが出ています。

同じように正の数値でもbigintのカラムであふれます。

mysql> insert into `ketasu` values(9223372036854775807,'9223372036854775807',9223372036854775807);
Query OK, 1 row affected (0.06 sec)

mysql> insert into `ketasu` values(9223372036854775808,'9223372036854775808',9223372036854775808);
ERROR 1264 (22003): Out of range value for column 'keta_bigint' at row 1

符号無しでもやってみましょう。

mysql> drop table if exists ketasu_not_fugo;
Query OK, 0 rows affected (0.20 sec)

mysql> CREATE TABLE `ketasu_not_fugo`(
    -> `keta_bigint` bigint UNSIGNED NOT NULL,
    -> `keta_varchar20` varchar(20) NOT NULL,
    -> `keta_decimal20` decimal(20,0) NOT NULL
    -> ) ENGINE=InnoDB ;
Query OK, 0 rows affected (0.29 sec)

やはりbigintで桁あふれしました。

mysql> insert into `ketasu_not_fugo` values(18446744073709551615,'18446744073709551615',18446744073709551615);
Query OK, 1 row affected (0.05 sec)

mysql> insert into `ketasu_not_fugo` values(18446744073709551616,'18446744073709551616',18446744073709551616);
ERROR 1264 (22003): Out of range value for column 'keta_bigint' at row 1

decimal(n,0)の使い道

varchar等の文字列などでbigintでは入りきらない桁の番号の場合、小数点以上の部分については65桁までOKのようです。
https://dev.mysql.com/doc/refman/5.6/ja/precision-math-decimal-characteristics.html

文字列型から数値型に変換したいときはdecimal(n,0)を使うといいです(いいのか?)

でも

正直気持ち悪いんですよね。 Oracle等のRDBMSを触ってきてると、整数値型の上限低くない?と思っちゃいました。 まあ、そもそもうちの使い方(データ連携)もおかしいような気もしますが…。

3つの「れいわ」がMySQLにちゃんと入るか確認しといた

https://qiita.com/tfukumori/items/959ed9c751734b73675fで上がってた話をMySQLで一応調査してみた。

やったこと

https://qiita.com/tfukumori/items/959ed9c751734b73675f であがってた文字コード「U+4EE4」と「U+F9A8」、あと「U+32FF」はいずれもMySQLに入れても大丈夫だよね?文字化けしないよね?(U+32FFは今は保留)という検証。

カラムの型

  • char
  • varchar
  • text

の3つで試した。

また、全文検索が使えるかも確認したかったのでbigramのFULLTEXT INDEXを作成。

検証環境

現場の開発環境で検証。

以下の二つのバージョンで行った。

  • MySQL5.5.20
  • MySQL5.7.17

検証用クエリ

/*
U+4EE4 令
U+F9A8 令
U+32FF ㋿
*/
drop database if exists test;
create database test;

use test;
drop table if exists aaa_utf8mb4;

CREATE TABLE `aaa_utf8mb4` (
  `id` int(11) AUTO_INCREMENT NOT NULL,
  `a` varchar(10) NOT NULL,
  `b` char(10) NOT NULL,
  `c` text     NOT NULL,
  `d` varchar(100) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `i_aaa_utf8mb4_1` (`a`),
  KEY `i_aaa_utf8mb4_2` (`b`),
  KEY `i_aaa_utf8mb4_3` (`c`(10)),
  FULLTEXT KEY `fi_aaa_utf8mb4_4` (`d`) /*!50100 WITH PARSER `ngram` */
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

insert into aaa_utf8mb4(a,b,c,d) values ('令和(U+4EE4)','令和(U+4EE4)','令和(U+4EE4)','明治-大正-昭和-平成-令和(U+4EE4)');
insert into aaa_utf8mb4(a,b,c,d) values ('令和(U+F9A8)','令和(U+F9A8)','令和(U+F9A8)','明治-大正-昭和-平成-令和(U+F9A8)');
insert into aaa_utf8mb4(a,b,c,d) values ('㋿(U+32FF)','㋿(U+32FF)','㋿(U+32FF)','㍾-㍽-㍼-㍻-㋿(U+32FF)');

select * from aaa_utf8mb4;

explain select * from aaa_utf8mb4 where a = '令和(U+4EE4)';
select * from aaa_utf8mb4 where a = '令和(U+4EE4)';
select * from aaa_utf8mb4 where a like '令和%';
explain select * from aaa_utf8mb4 where b = '令和(U+4EE4)';
select * from aaa_utf8mb4 where b = '令和(U+4EE4)';
select * from aaa_utf8mb4 where b like '令和%';
explain select * from aaa_utf8mb4 where c = '令和(U+4EE4)';
select * from aaa_utf8mb4 where c = '令和(U+4EE4)';
select * from aaa_utf8mb4 where c like '令和%';
explain select * from aaa_utf8mb4 where match( d ) against ('+令和' in boolean mode);
select * from aaa_utf8mb4 where match( d ) against ('+令和' in boolean mode);

explain select * from aaa_utf8mb4 where a = '令和(U+F9A8)';
select * from aaa_utf8mb4 where a = '令和(U+F9A8)';
select * from aaa_utf8mb4 where a like '令和%';
explain select * from aaa_utf8mb4 where b = '令和(U+F9A8)';
select * from aaa_utf8mb4 where b = '令和(U+F9A8)';
select * from aaa_utf8mb4 where b like '令和%';
explain select * from aaa_utf8mb4 where c = '令和(U+F9A8)';
select * from aaa_utf8mb4 where c = '令和(U+F9A8)';
select * from aaa_utf8mb4 where c like '令和%';
explain select * from aaa_utf8mb4 where match( d ) against ('+令和' in boolean mode);
select * from aaa_utf8mb4 where match( d ) against ('+令和' in boolean mode);

explain select * from aaa_utf8mb4 where a = '㋿(U+32FF)';
select * from aaa_utf8mb4 where a = '㋿(U+32FF)';
select * from aaa_utf8mb4 where a like '㋿%';
explain select * from aaa_utf8mb4 where b = '㋿(U+32FF)';
select * from aaa_utf8mb4 where b = '㋿(U+32FF)';
select * from aaa_utf8mb4 where b like '㋿%';
explain select * from aaa_utf8mb4 where c = '㋿(U+32FF)';
select * from aaa_utf8mb4 where c = '㋿(U+32FF)';
select * from aaa_utf8mb4 where c like '㋿%';
explain select * from aaa_utf8mb4 where match( d ) against ('+㋿' in boolean mode);
select * from aaa_utf8mb4 where match( d ) against ('+㋿' in boolean mode);


drop table if exists aaa_utf8;

CREATE TABLE `aaa_utf8` (
  `id` int(11) AUTO_INCREMENT NOT NULL,
  `a` varchar(10) NOT NULL,
  `b` char(10) NOT NULL,
  `c` text     NOT NULL,
  `d` varchar(100) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `i_aaa_utf8_1` (`a`),
  KEY `i_aaa_utf8_2` (`b`),
  KEY `i_aaa_utf8_3` (`c`(10)),
  FULLTEXT KEY `fi_aaa_utf8_4` (`d`) /*!50100 WITH PARSER `ngram` */
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert into aaa_utf8(a,b,c,d) values ('令和(U+4EE4)','令和(U+4EE4)','令和(U+4EE4)','明治-大正-昭和-平成-令和(U+4EE4)');
insert into aaa_utf8(a,b,c,d) values ('令和(U+F9A8)','令和(U+F9A8)','令和(U+F9A8)','明治-大正-昭和-平成-令和(U+F9A8)');
insert into aaa_utf8(a,b,c,d) values ('㋿(U+32FF)','㋿(U+32FF)','㋿(U+32FF)','㍾-㍽-㍼-㍻-㋿(U+32FF)');

select * from aaa_utf8;

explain select * from aaa_utf8 where a = '令和(U+4EE4)';
select * from aaa_utf8 where a = '令和(U+4EE4)';
select * from aaa_utf8 where a like '令和%';
explain select * from aaa_utf8 where b = '令和(U+4EE4)';
select * from aaa_utf8 where b = '令和(U+4EE4)';
select * from aaa_utf8 where b like '令和%';
explain select * from aaa_utf8 where c = '令和(U+4EE4)';
select * from aaa_utf8 where c = '令和(U+4EE4)';
select * from aaa_utf8 where c like '令和%';
explain select * from aaa_utf8 where match( d ) against ('+令和' in boolean mode);
select * from aaa_utf8 where match( d ) against ('+令和' in boolean mode);

explain select * from aaa_utf8 where a = '令和(U+F9A8)';
select * from aaa_utf8 where a = '令和(U+F9A8)';
select * from aaa_utf8 where a like '令和%';
explain select * from aaa_utf8 where b = '令和(U+F9A8)';
select * from aaa_utf8 where b = '令和(U+F9A8)';
select * from aaa_utf8 where b like '令和%';
explain select * from aaa_utf8 where c = '令和(U+F9A8)';
select * from aaa_utf8 where c = '令和(U+F9A8)';
select * from aaa_utf8 where c like '令和%';
explain select * from aaa_utf8 where match( d ) against ('+令和' in boolean mode);
select * from aaa_utf8 where match( d ) against ('+令和' in boolean mode);

explain select * from aaa_utf8 where a = '㋿(U+32FF)';
select * from aaa_utf8 where a = '㋿(U+32FF)';
select * from aaa_utf8 where a like '㋿%';
explain select * from aaa_utf8 where b = '㋿(U+32FF)';
select * from aaa_utf8 where b = '㋿(U+32FF)';
select * from aaa_utf8 where b like '㋿%';
explain select * from aaa_utf8 where c = '㋿(U+32FF)';
select * from aaa_utf8 where c = '㋿(U+32FF)';
select * from aaa_utf8 where c like '㋿%';
explain select * from aaa_utf8 where match( d ) against ('+㋿' in boolean mode);
select * from aaa_utf8 where match( d ) against ('+㋿' in boolean mode);
  • 元のクエリからそのまま貼ると、レイがU+4EE4っぽく見えていますが、実際は文字コードは変わっているので大丈夫です。
  • U+32FFは今は豆腐だけど、令和になるはず。

実行コマンド

一応クライアント接続時の文字コードも気にしておく(大丈夫だと思うけど)

# MySQL5.7で作業
$ mysql -uhoge -p -hhogehoge57 -Pxxxx --default-character-set=utf8 -t -v <reiwatest.sql  > reiwatest_utf8.log
$ mysql -uhoge -p -hhogehoge57 -Pxxxx --default-character-set=utf8mb4 -t -v <reiwatest.sql  > reiwatest_utf8mb4.log
# MySQL5.5で作業
$ mysql -uhoge -p -hhogehoge55 -PXXXX --default-character-set=utf8 -t -v <reiwatest_55.sql  > reiwatest_utf8_55.log
$ mysql -uhoge -p -hhogehoge55 -PXXXX --default-character-set=utf8mb4 -t -v <reiwatest_55.sql  > reiwatest_utf8mb4_55.log

実行結果

まず全件

--------------
select * from aaa_utf8mb4
--------------

+----+----------------+----------------+----------------+--------------------------------------------+
| id | a              | b              | c              | d                                          |
+----+----------------+----------------+----------------+--------------------------------------------+
|  1 | 令和(U+4EE4)   | 令和(U+4EE4)   | 令和(U+4EE4)   | 明治-大正-昭和-平成-令和(U+4EE4)           |
|  2 | 令和(U+F9A8)   | 令和(U+F9A8)   | 令和(U+F9A8)   | 明治-大正-昭和-平成-令和(U+F9A8)           |
|  3 | ㋿(U+32FF)      | ㋿(U+32FF)      | ㋿(U+32FF)      | ㍾-㍽-㍼-㍻-㋿(U+32FF)                      |
+----+----------------+----------------+----------------+--------------------------------------------+

こんな感じで入ってる。

まず、U+4EE4側の「令」和で検索してみる。

U+4EE4のレイ

--------------
explain select * from aaa_utf8mb4 where a = '令和(U+4EE4)'
--------------

+----+-------------+-------------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------+
| id | select_type | table       | partitions | type | possible_keys   | key             | key_len | ref   | rows | filtered | Extra |
+----+-------------+-------------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | aaa_utf8mb4 | NULL       | ref  | i_aaa_utf8mb4_1 | i_aaa_utf8mb4_1 | 42      | const |    1 |   100.00 | NULL  |
+----+-------------+-------------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------+
--------------
select * from aaa_utf8mb4 where a = '令和(U+4EE4)'
--------------

+----+----------------+----------------+----------------+--------------------------------------------+
| id | a              | b              | c              | d                                          |
+----+----------------+----------------+----------------+--------------------------------------------+
|  1 | 令和(U+4EE4)   | 令和(U+4EE4)   | 令和(U+4EE4)   | 明治-大正-昭和-平成-令和(U+4EE4)           |
+----+----------------+----------------+----------------+--------------------------------------------+
--------------
select * from aaa_utf8mb4 where a like '令和%'
--------------

+----+----------------+----------------+----------------+--------------------------------------------+
| id | a              | b              | c              | d                                          |
+----+----------------+----------------+----------------+--------------------------------------------+
|  1 | 令和(U+4EE4)   | 令和(U+4EE4)   | 令和(U+4EE4)   | 明治-大正-昭和-平成-令和(U+4EE4)           |
+----+----------------+----------------+----------------+--------------------------------------------+
--------------
explain select * from aaa_utf8mb4 where b = '令和(U+4EE4)'
--------------

+----+-------------+-------------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------+
| id | select_type | table       | partitions | type | possible_keys   | key             | key_len | ref   | rows | filtered | Extra |
+----+-------------+-------------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | aaa_utf8mb4 | NULL       | ref  | i_aaa_utf8mb4_2 | i_aaa_utf8mb4_2 | 40      | const |    1 |   100.00 | NULL  |
+----+-------------+-------------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------+
--------------
select * from aaa_utf8mb4 where b = '令和(U+4EE4)'
--------------

+----+----------------+----------------+----------------+--------------------------------------------+
| id | a              | b              | c              | d                                          |
+----+----------------+----------------+----------------+--------------------------------------------+
|  1 | 令和(U+4EE4)   | 令和(U+4EE4)   | 令和(U+4EE4)   | 明治-大正-昭和-平成-令和(U+4EE4)           |
+----+----------------+----------------+----------------+--------------------------------------------+
--------------
select * from aaa_utf8mb4 where b like '令和%'
--------------

+----+----------------+----------------+----------------+--------------------------------------------+
| id | a              | b              | c              | d                                          |
+----+----------------+----------------+----------------+--------------------------------------------+
|  1 | 令和(U+4EE4)   | 令和(U+4EE4)   | 令和(U+4EE4)   | 明治-大正-昭和-平成-令和(U+4EE4)           |
+----+----------------+----------------+----------------+--------------------------------------------+
--------------
explain select * from aaa_utf8mb4 where c = '令和(U+4EE4)'
--------------

+----+-------------+-------------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------------+
| id | select_type | table       | partitions | type | possible_keys   | key             | key_len | ref   | rows | filtered | Extra       |
+----+-------------+-------------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------------+
|  1 | SIMPLE      | aaa_utf8mb4 | NULL       | ref  | i_aaa_utf8mb4_3 | i_aaa_utf8mb4_3 | 42      | const |    1 |   100.00 | Using where |
+----+-------------+-------------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------------+
--------------
select * from aaa_utf8mb4 where c = '令和(U+4EE4)'
--------------

+----+----------------+----------------+----------------+--------------------------------------------+
| id | a              | b              | c              | d                                          |
+----+----------------+----------------+----------------+--------------------------------------------+
|  1 | 令和(U+4EE4)   | 令和(U+4EE4)   | 令和(U+4EE4)   | 明治-大正-昭和-平成-令和(U+4EE4)           |
+----+----------------+----------------+----------------+--------------------------------------------+
--------------
select * from aaa_utf8mb4 where c like '令和%'
--------------

+----+----------------+----------------+----------------+--------------------------------------------+
| id | a              | b              | c              | d                                          |
+----+----------------+----------------+----------------+--------------------------------------------+
|  1 | 令和(U+4EE4)   | 令和(U+4EE4)   | 令和(U+4EE4)   | 明治-大正-昭和-平成-令和(U+4EE4)           |
+----+----------------+----------------+----------------+--------------------------------------------+
--------------
explain select * from aaa_utf8mb4 where match( d ) against ('+令和' in boolean mode)
--------------

+----+-------------+-------------+------------+----------+------------------+------------------+---------+-------+------+----------+-----------------------------------+
| id | select_type | table       | partitions | type     | possible_keys    | key              | key_len | ref   | rows | filtered | Extra                             |
+----+-------------+-------------+------------+----------+------------------+------------------+---------+-------+------+----------+-----------------------------------+
|  1 | SIMPLE      | aaa_utf8mb4 | NULL       | fulltext | fi_aaa_utf8mb4_4 | fi_aaa_utf8mb4_4 | 0       | const |    1 |   100.00 | Using where; Ft_hints: no_ranking |
+----+-------------+-------------+------------+----------+------------------+------------------+---------+-------+------+----------+-----------------------------------+
--------------
select * from aaa_utf8mb4 where match( d ) against ('+令和' in boolean mode)
--------------

+----+----------------+----------------+----------------+--------------------------------------------+
| id | a              | b              | c              | d                                          |
+----+----------------+----------------+----------------+--------------------------------------------+
|  1 | 令和(U+4EE4)   | 令和(U+4EE4)   | 令和(U+4EE4)   | 明治-大正-昭和-平成-令和(U+4EE4)           |
+----+----------------+----------------+----------------+--------------------------------------------+

インデックスも利いてるし、WHERE句でちゃんと使えてるし、何より全文検索でも表示結果に問題ない。

U+F9A8のレイ

--------------
explain select * from aaa_utf8mb4 where a = '令和(U+F9A8)'
--------------

+----+-------------+-------------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------+
| id | select_type | table       | partitions | type | possible_keys   | key             | key_len | ref   | rows | filtered | Extra |
+----+-------------+-------------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | aaa_utf8mb4 | NULL       | ref  | i_aaa_utf8mb4_1 | i_aaa_utf8mb4_1 | 42      | const |    1 |   100.00 | NULL  |
+----+-------------+-------------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------+
--------------
select * from aaa_utf8mb4 where a = '令和(U+F9A8)'
--------------

+----+----------------+----------------+----------------+--------------------------------------------+
| id | a              | b              | c              | d                                          |
+----+----------------+----------------+----------------+--------------------------------------------+
|  2 | 令和(U+F9A8)   | 令和(U+F9A8)   | 令和(U+F9A8)   | 明治-大正-昭和-平成-令和(U+F9A8)           |
+----+----------------+----------------+----------------+--------------------------------------------+
--------------
select * from aaa_utf8mb4 where a like '令和%'
--------------

+----+----------------+----------------+----------------+--------------------------------------------+
| id | a              | b              | c              | d                                          |
+----+----------------+----------------+----------------+--------------------------------------------+
|  2 | 令和(U+F9A8)   | 令和(U+F9A8)   | 令和(U+F9A8)   | 明治-大正-昭和-平成-令和(U+F9A8)           |
+----+----------------+----------------+----------------+--------------------------------------------+
--------------
explain select * from aaa_utf8mb4 where b = '令和(U+F9A8)'
--------------

+----+-------------+-------------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------+
| id | select_type | table       | partitions | type | possible_keys   | key             | key_len | ref   | rows | filtered | Extra |
+----+-------------+-------------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | aaa_utf8mb4 | NULL       | ref  | i_aaa_utf8mb4_2 | i_aaa_utf8mb4_2 | 40      | const |    1 |   100.00 | NULL  |
+----+-------------+-------------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------+
--------------
select * from aaa_utf8mb4 where b = '令和(U+F9A8)'
--------------

+----+----------------+----------------+----------------+--------------------------------------------+
| id | a              | b              | c              | d                                          |
+----+----------------+----------------+----------------+--------------------------------------------+
|  2 | 令和(U+F9A8)   | 令和(U+F9A8)   | 令和(U+F9A8)   | 明治-大正-昭和-平成-令和(U+F9A8)           |
+----+----------------+----------------+----------------+--------------------------------------------+
--------------
select * from aaa_utf8mb4 where b like '令和%'
--------------

+----+----------------+----------------+----------------+--------------------------------------------+
| id | a              | b              | c              | d                                          |
+----+----------------+----------------+----------------+--------------------------------------------+
|  2 | 令和(U+F9A8)   | 令和(U+F9A8)   | 令和(U+F9A8)   | 明治-大正-昭和-平成-令和(U+F9A8)           |
+----+----------------+----------------+----------------+--------------------------------------------+
--------------
explain select * from aaa_utf8mb4 where c = '令和(U+F9A8)'
--------------

+----+-------------+-------------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------------+
| id | select_type | table       | partitions | type | possible_keys   | key             | key_len | ref   | rows | filtered | Extra       |
+----+-------------+-------------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------------+
|  1 | SIMPLE      | aaa_utf8mb4 | NULL       | ref  | i_aaa_utf8mb4_3 | i_aaa_utf8mb4_3 | 42      | const |    1 |   100.00 | Using where |
+----+-------------+-------------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------------+
--------------
select * from aaa_utf8mb4 where c = '令和(U+F9A8)'
--------------

+----+----------------+----------------+----------------+--------------------------------------------+
| id | a              | b              | c              | d                                          |
+----+----------------+----------------+----------------+--------------------------------------------+
|  2 | 令和(U+F9A8)   | 令和(U+F9A8)   | 令和(U+F9A8)   | 明治-大正-昭和-平成-令和(U+F9A8)           |
+----+----------------+----------------+----------------+--------------------------------------------+
--------------
select * from aaa_utf8mb4 where c like '令和%'
--------------

+----+----------------+----------------+----------------+--------------------------------------------+
| id | a              | b              | c              | d                                          |
+----+----------------+----------------+----------------+--------------------------------------------+
|  2 | 令和(U+F9A8)   | 令和(U+F9A8)   | 令和(U+F9A8)   | 明治-大正-昭和-平成-令和(U+F9A8)           |
+----+----------------+----------------+----------------+--------------------------------------------+
--------------
explain select * from aaa_utf8mb4 where match( d ) against ('+令和' in boolean mode)
--------------

+----+-------------+-------------+------------+----------+------------------+------------------+---------+-------+------+----------+-----------------------------------+
| id | select_type | table       | partitions | type     | possible_keys    | key              | key_len | ref   | rows | filtered | Extra                             |
+----+-------------+-------------+------------+----------+------------------+------------------+---------+-------+------+----------+-----------------------------------+
|  1 | SIMPLE      | aaa_utf8mb4 | NULL       | fulltext | fi_aaa_utf8mb4_4 | fi_aaa_utf8mb4_4 | 0       | const |    1 |   100.00 | Using where; Ft_hints: no_ranking |
+----+-------------+-------------+------------+----------+------------------+------------------+---------+-------+------+----------+-----------------------------------+
--------------
select * from aaa_utf8mb4 where match( d ) against ('+令和' in boolean mode)
--------------

+----+----------------+----------------+----------------+--------------------------------------------+
| id | a              | b              | c              | d                                          |
+----+----------------+----------------+----------------+--------------------------------------------+
|  2 | 令和(U+F9A8)   | 令和(U+F9A8)   | 令和(U+F9A8)   | 明治-大正-昭和-平成-令和(U+F9A8)           |
+----+----------------+----------------+----------------+--------------------------------------------+

こっちもオールオッケー。

U+32FFのレイワ

--------------
explain select * from aaa_utf8mb4 where a = '㋿(U+32FF)'
--------------

+----+-------------+-------------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------+
| id | select_type | table       | partitions | type | possible_keys   | key             | key_len | ref   | rows | filtered | Extra |
+----+-------------+-------------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | aaa_utf8mb4 | NULL       | ref  | i_aaa_utf8mb4_1 | i_aaa_utf8mb4_1 | 42      | const |    1 |   100.00 | NULL  |
+----+-------------+-------------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------+
--------------
select * from aaa_utf8mb4 where a = '㋿(U+32FF)'
--------------

+----+-------------+-------------+-------------+-----------------------------+
| id | a           | b           | c           | d                           |
+----+-------------+-------------+-------------+-----------------------------+
|  3 | ㋿(U+32FF)   | ㋿(U+32FF)   | ㋿(U+32FF)   | ㍾-㍽-㍼-㍻-㋿(U+32FF)       |
+----+-------------+-------------+-------------+-----------------------------+
--------------
select * from aaa_utf8mb4 where a like '㋿%'
--------------

+----+-------------+-------------+-------------+-----------------------------+
| id | a           | b           | c           | d                           |
+----+-------------+-------------+-------------+-----------------------------+
|  3 | ㋿(U+32FF)   | ㋿(U+32FF)   | ㋿(U+32FF)   | ㍾-㍽-㍼-㍻-㋿(U+32FF)       |
+----+-------------+-------------+-------------+-----------------------------+
--------------
explain select * from aaa_utf8mb4 where b = '㋿(U+32FF)'
--------------

+----+-------------+-------------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------+
| id | select_type | table       | partitions | type | possible_keys   | key             | key_len | ref   | rows | filtered | Extra |
+----+-------------+-------------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | aaa_utf8mb4 | NULL       | ref  | i_aaa_utf8mb4_2 | i_aaa_utf8mb4_2 | 40      | const |    1 |   100.00 | NULL  |
+----+-------------+-------------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------+
--------------
select * from aaa_utf8mb4 where b = '㋿(U+32FF)'
--------------

+----+-------------+-------------+-------------+-----------------------------+
| id | a           | b           | c           | d                           |
+----+-------------+-------------+-------------+-----------------------------+
|  3 | ㋿(U+32FF)   | ㋿(U+32FF)   | ㋿(U+32FF)   | ㍾-㍽-㍼-㍻-㋿(U+32FF)       |
+----+-------------+-------------+-------------+-----------------------------+
--------------
select * from aaa_utf8mb4 where b like '㋿%'
--------------

+----+-------------+-------------+-------------+-----------------------------+
| id | a           | b           | c           | d                           |
+----+-------------+-------------+-------------+-----------------------------+
|  3 | ㋿(U+32FF)   | ㋿(U+32FF)   | ㋿(U+32FF)   | ㍾-㍽-㍼-㍻-㋿(U+32FF)       |
+----+-------------+-------------+-------------+-----------------------------+
--------------
explain select * from aaa_utf8mb4 where c = '㋿(U+32FF)'
--------------

+----+-------------+-------------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------------+
| id | select_type | table       | partitions | type | possible_keys   | key             | key_len | ref   | rows | filtered | Extra       |
+----+-------------+-------------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------------+
|  1 | SIMPLE      | aaa_utf8mb4 | NULL       | ref  | i_aaa_utf8mb4_3 | i_aaa_utf8mb4_3 | 42      | const |    1 |   100.00 | Using where |
+----+-------------+-------------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------------+
--------------
select * from aaa_utf8mb4 where c = '㋿(U+32FF)'
--------------

+----+-------------+-------------+-------------+-----------------------------+
| id | a           | b           | c           | d                           |
+----+-------------+-------------+-------------+-----------------------------+
|  3 | ㋿(U+32FF)   | ㋿(U+32FF)   | ㋿(U+32FF)   | ㍾-㍽-㍼-㍻-㋿(U+32FF)       |
+----+-------------+-------------+-------------+-----------------------------+
--------------
select * from aaa_utf8mb4 where c like '㋿%'
--------------

+----+-------------+-------------+-------------+-----------------------------+
| id | a           | b           | c           | d                           |
+----+-------------+-------------+-------------+-----------------------------+
|  3 | ㋿(U+32FF)   | ㋿(U+32FF)   | ㋿(U+32FF)   | ㍾-㍽-㍼-㍻-㋿(U+32FF)       |
+----+-------------+-------------+-------------+-----------------------------+
--------------
explain select * from aaa_utf8mb4 where match( d ) against ('+㋿' in boolean mode)
--------------

+----+-------------+-------------+------------+----------+------------------+------------------+---------+-------+------+----------+-----------------------------------+
| id | select_type | table       | partitions | type     | possible_keys    | key              | key_len | ref   | rows | filtered | Extra                             |
+----+-------------+-------------+------------+----------+------------------+------------------+---------+-------+------+----------+-----------------------------------+
|  1 | SIMPLE      | aaa_utf8mb4 | NULL       | fulltext | fi_aaa_utf8mb4_4 | fi_aaa_utf8mb4_4 | 0       | const |    1 |   100.00 | Using where; Ft_hints: no_ranking |
+----+-------------+-------------+------------+----------+------------------+------------------+---------+-------+------+----------+-----------------------------------+
--------------
select * from aaa_utf8mb4 where match( d ) against ('+㋿' in boolean mode)
--------------

全文検索では合字使えないね。

とはいえちゃんと登録されているようだし、検索時もidを見る限り正しいそれを取得できている様子。(今は豆腐だけどさ・・・)

ちなみにutf8テーブルのほうも結果は上記と全く同じだし、--default-character-setがutf8とutf8mb4で変わることはなかった。 (あったらまずいんだけどね)

追記

collationのこと忘れてた。 MySQL5.5系はデフォがutf8_general_ci、MySQL5.7系はutf8mb4_general_ciなのでそれらで作られたと思ってください。