41から始めました

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

インデックスマージ、とても良い

MySQLのインデックス

世の中がMySQL8.0.18のハッシュJOINに盛り上がってる中、オジサンは古いけど自分にとっては新しいマージインデックスの話をします。

複数のインデックスがテーブルに作られていても、使えるのは一つ

というのがMySQLでは原則ですが、これの 例外 があります。

それがタイトルのインデックスマージというものです。

インデックスマージとは?

名前の通りインデックス(の結果)をマージするというものです。

MySQLのドキュメント曰く

複数の range スキャンによって、行を取得しそれらの結果を 1 つにマージするために使用されます。

The Index Merge access method retrieves rows with multiple range scans and merges their results into one.

MySQL5.1では使える機能なんで、それ以降の5.5、5.6、5.7、8.0でも使えます。

1 つのテーブルからのインデックススキャンをマージします。複数のテーブルにわたるスキャンはマージしません。

This access method merges index scans from a single table only, not scans across multiple tables.

というわけで、JOINしたテーブル間からはマージしないとのこと。

単体のテーブルの条件内でのみ発動すると。

詳しくはMySQLのドキュメント読んでもらえばわかりますが、要するに1つのテーブル内で2つのインデックスを使ってORで条件指定している場合にこれが発動する可能性があるということです。

社内のDBのスロークエリを眺めていたところ…

WHERE句にこんな感じの条件が書かれているクエリがありました。

SELECT count(*)
FROM A_table
WHERE
(A_table.last_update_time >= '2019-10-16' OR A_table.saigono_koushin_jikan >= '2019-10-16')
AND A_table.last_update_time <= '2019-10-17 17:00:00'
AND A_table.saigono_koushin_jikan <= '2019-10-17 17:00:00'
AND …
;

(※本当はもうちょっと複雑なんですが、大人の事情でカラム名とか色々変えて、雰囲気だけ伝わるようにしていますw)

last_update_time というカラムと saigono_koushin_jikan というカラムでいずれかが前日以降の日付が入っていて、いずれのカラムも今日の取得時点の日付までというレンジスキャンをしています。

いずれのカラムにもインデックスは張られておらず、別のインデックスを使っていました。

(ぶっちゃけほぼインデックスフルスキャンでした)

EXPLAINを見るとこんな感じ

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

(見せられない色々なものを隠していますが、ご了承ください)

この2つのインデックス、張るとマージインデックス効くんじゃね?と見つけて思った自分はさっそくステージング環境にインデックスを張ってみました。

alter table A_table add key i_saigono_koushin_jikan(saigono_koushin_jikan);
alter table A_table add key i_last_update_time(last_update_time);

そして実行計画確認したところ、コスト爆下がりでした!

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

(今回のクエリでは17万が7になりましたw)

本番で4.4秒かかってたクエリなんですが、ステージングではこのくらいでした。

追加前(秒) 追加後(秒)
3.234 0.0072

これは良い!

まとめ

もちろん、インデックスマージで結果が大きかったらインデックスを張った意味はあまりないかもしれないですし、逆にインデックスを張ったことで更新コストが上がる可能性もあります。

(テーブルにあれこれインデックスが増えないほうが良いと思っている人でもあるので)

(インデックスが増えすぎているということはテーブルが正規化されるべきなのかもしれないというのは別の話)

とはいえ、OR句を使ってて、絞り込まれる条件がそこにあるのなら検討する余地はあります。

インデックスのテクニックとして

  • PKやUKで検索するのが最速
  • セカンダリインデックスはインデックス内にPK情報を持つ
  • カバリングインデックスで複合インデックスは効率よく全て使えるようにしましょう
  • 実行計画(EXPLAIN)を見てExtraにUsing whereがあったらインデックスをうまく使えてないかも?
  • インデックスは張りすぎると更新処理が遅くなる

といったことに加え、今回のインデックスマージを使えるようになるとまたMySQLが好きになるかもしれません。

原則(使えるインデックスは1つ)は頭に入れつつも、ORを見かけたら「もしかして?」と立ち止まって見てみましょう。

MySQL Technology Cafe #4参加してきた

昨日行ってきたやつのお話共有します。 松信さん(Facebook)の話がすごすぎて聞いてて頭プスプス言ってた。 それを思い返しながらまたプスプス言ってたんですが、なんとかかきおこしました。 正直、まだ理解が追いついてないです…。

MySQL Technology Cafe #4

https://oracle-code-tokyo-dev.connpass.com/event/135081/

タイムスケジュール *逐次通訳有

時間 内容 登壇者
18:00-18:25 受付 -
18:25-18:30 はじめに MySQL GBU 梶山 隆輔 氏
18:30-18:55 State of the Dolphin - What's new in MySQL Oracle Corporation Philip Antoniades (逐次通訳あり)
18:55-19:55 MySQL Replication and HA at Facebook Facebook, Database Engineer, 松信嘉範 氏
19:55-20:30 ネットワーキング

State of the Dolphin - What's new in MySQL

セッション概要 MySQLの最新の開発動向についてご紹介するセッションです。MySQL 8.0のGA以降も機能強化が続けられ、MySQL Cluster 8.0やMySQL Cloud Serviceもリリースに向けて開発が進められています。

登壇者 Philip Antoniades Oracle Corporation, Senior Director MySQL Sales Consulting プロフィール MySQL社の古参メンバーの一人で、現在はMySQLの技術営業部門のを統括。2003年にMySQL ABに入社、現在はオラクルにてワールドワイドのMySQLソリューションエンジニアチームのディレクターを務める。ニューヨーク在住。

Top 8 Best Features in MySQL 8

新機能であるInnoDB ClusterやJSONGIS関数の強化の話をされていました。

  1. SQL+NoSQL = MySQL ロゴにもなってるよね!
  2. MySQL InnoDB Cluster みなさんご存知。
  3. MySQLShell (SQL, Python, Java(Scriptですね))
  4. ROLEとかパスワードとかセキュリティ強化
  5. GIS関数
  6. データディクショナリ
  7. ユニコード改善
  8. SQLのアナライズ

Oracle Open World 2019でこのあたりの話はもちろん、今回はメルカリ社がMySQL Analytics Serviceの話をするらしい。 MySQL Analytics Serviceって、Nipunさんの話(この動画参照くらいでしか知らないけど、当時聞いたときにまだLaboの研究段階だったと思うし、それをどう実用に活かすかみたいな話が聞けるのかと思うとちょっと今からwkwkする。

MySQL Replication and HA at Facebook

セッション概要 FacebookにおけるMySQLの高可用化技術について紹介します。具体的には、Semi-Synchronous Replicationを始めとするレプリケーション技術の話や、そこに追加した機能の話をします。また、高可用化を実現するための内製ソフトウェアのDBStatus、Binlog Server、Logtailerなどを紹介し、どのような背景で、どのような課題を解決することを目指したのかを説明します。

登壇者 松信嘉範 氏 Facebook, Database Engineer プロフィール Yoshinori is a database engineer at Facebook. Before joining Facebook, Yoshinori was a database and infrastructure architect at DeNA, living in Tokyo. Yoshinori’s primary responsibility at DeNA is to make our database infrastructure more reliable, faster and more scalable. Before joining DeNA, Yoshinori worked at MySQL/Sun/Oracle as a lead consultant in APAC for four years. Yoshinori has written eight MySQL related technical books so far and has published technical articles about MySQL, Linux, and Java for a monthly database magazine since 2004.

MySQL replication and HA @Facebook

以下松信さんの話したことを箇条書きでメモしてる分だけですが書き起こします

  • MySQLレプリケーションをどうやって使っているか?について話す
  • 資料はPerconaLive(5月)で使ったもの(別の人が作った)

今日の話(目次)

レプリケーションの話

  • グローバルな構成(複数リージョンでMySQLのマスタースレーブを構成)
  • Facebookでは1リージョンに1つのMySQLインスタンスしか持ちません
  • マルチマスターはやりません
  • ある程度シンプルにシステムを組まないとトラブルの時に大変なので
  • チェインもしません
  • 孫スレーブはない
  • カスケードもしません
  • R1=リージョン1という意味
  • ちなみに、普段、マスタースレーブって言っちゃうんですが、プライマリー・レプリカっていうべきじゃないという話ありますよね。だけどslaveってソースに書いてるじゃん!ってことで(松信さんが)スレーブって言ったらレプリカのことだと思ってください
  • マスターと同じリージョンにスレーブが必要
  • Facebookではレプリケーションの主要な点として、リードをスケールアウトできるようにしたい
  • リードをスケールアウトするためには、Writeはクロスリージョンにするのはしょうがないとしている
  • 単一(同一)リージョンの中では準同期レプリケーション
  • 準同期レプリケーションの仕組みは、ローカルのバイナリログを書いて、
  • LBU ロジカルバックアップユニット >binlogを書いてackだけ返す
  • Master capable regions マスター候補となるスレーブ
    • マスター候補にならないスレーブもいる
  • binlogを読んで通知する機能:Wormhole
    • バイナリログの更新情報を元にMySQL以外のデータを更新する
    • キャッシュとかDWHにBinlogの更新情報を通知する Wormholeの参考

binlog BinlogServerの話

  • Live Master Promotion
    • マスターを切り替えたい時
    • アップグレード(マイナーバージョン)をFacebookでは頻繁に行っている(ので必要)
    • 重要なのはダウンタイム(500ms)
    • マスターをリードオンリーにした後、同期されたところで切り替え先をRWに書き換え、切り替える
    • リージョンが違うのでそのくらいかかる
  • Dead Master Promotion
    • どっかのRACが常に落ちるんで切り替えるのを自動化してる
    • ダウンタイムは30sくらい
    • ダウンしてもちゃんと復活するように
    • 多少切り替え時間がかかっても
  • 一個もセミシンククライアントがいないときは書き込みを止めるということができる=書き込みをさせない状態にする
  • リージョンがまるごと落ちるということもないことはない
  • そういう時は人が対応する
  • データがロストしたか(自動では)わからないので、人が介入する

Binlog Server Role and Features

ちなみにBinlog Serverの参考資料 もあげときます RippleについてはSmartStyleの説明が分かりやすかった yoku0825さんも検証をやってた

  • (マスター→binlog server→スレーブの流れ)
  • この機構により別のオートメーションが(スレーブやマスターを)構築する

Dependency replication

  • MySQLレプリケーションを使っているほぼすべての人は遅延を経験したことがあると思うんですけど
  • MySQLレプリケーション自体はすごく進化していて、並列性が上がって早くなっていますが課題はある
  • Facebookでは特定のアカウントの特定の投稿、例えばオバマが写真をアップロードした時に、その社員へのアクセスが集中して遅延が発生しました
  • こういう場合、特定のDBにデータがあるので、DBの並列性を上げても1つなんで限界がある
  • Facebookでは RBR を使ってbinlogから並列に更新可能なトランザクションを判定するグラフを作っている
  • トランザクション単位ではなく、レコード単位で並列度を上げていく

RBRって何? と思ってtom__boさんの記事に行く

  • RBR (Row based replication)ではRW/WWコンフリクトを見つけるためのすべてのデータが入っている。
  • conflictをDAGに書き換えて、コンフリクトがないものは部分的にであっても(例ではrowレベルで分解して競合しない部分を並列化していた)すぐに適用する
  • 最終的には適用でCommit orderingが保持されるようにしている
  • この方法でコンフリクトが少ない環境ではsysbenchのoltp_write_onlyのシナリオで最大4~5倍の性能改善

ああ、Row Based Replのことね。 とはいえ、言ってることはわかるけど、3行目の話とかどうやって実現しているのかわけわかめ・・・。

  • 非同期なスレーブが準同期なスレーブよりも多くのバイナリログを持っている可能性はある
  • Facebookでは、node fenceは準同期レプリケーションを組んでいるスレーブをすべて止める
  • スレーブよりもbinlog serverが正としている
  • binlog serverが再構築されないようにしている
  • 特定のUIDを持ったサーバにしかアクセスできないようにする
  • Rows Queryにコメントが書けるので、ほかのシステムから判別できるようにしている
    • シャードのIDとかレプリカセットとか
  • ネットワークの優先度を調整できるようにしてる
  • 準同期しているbinlog serverが最も進んだ状態にすることでフェールオーバーなどをシンプルにできた

RBR COMPLETE image format

  • binlog_row_imageについて
    • FULLに比べて元の値しか書かない
    • MINIMALだと微妙
  • FULLだと多いのでその中間のCOMPLETEを追加

Key Learnings/observations

  • binlog自体がハックされて壊されると深刻な問題
  • checksum機能が役に立ってる
  • SBRはやめてる
  • RACが部分的に遅延していると、特にリージョン間のネットワークが遅延してると書き込みは完結してても、レプリケーションが遅延する
  • SQL Thread をどんなに早くしても遅れてしまう
  • できるだけそういう状況を早く検知できるようにしている

今後

あたりが今後のチャレンジ

  • あと、パッチの管理、いわゆる技術的負債として残っているものをできるだけなくしていきたい

Q&A

Q: NICの速度の重要性が共感してもらいにくい。松信さんはどうお考えか?

  • MySQL だと優先度を変えるというのが大事
  • バックアップとかはそうかもしれないけど
  • あまりバンドル間での帯域については気にしてない

Q:リストアはどうしている?

  • こないだ梶山さんと話したのはバックアップかHAで今回の話はHAにしたんですけど、
  • mysqldumpでバックアップとってる
  • consistent restore
    • 自動的にバックアップをリストアしたものが正しいかを検証する機構がある

Q:変えたくても変えられないものはあるとは思うんですが、もし変えてもいいものがあればどこを変えたいですか?

  • MySQLじゃないものを使うこともできますが、レプリも使えてSQLも使えて気軽に使えるMySQLはイイ
  • RocksDBエンジンもあるし
  • パッチの管理が大変なので、できるだけその改造がしなくていいようにしたいんだけどそうはいかなくて、
  • でもMongoDBでどうする?ってのはそれはそれであるし
  • KVSでSQLが使えないとめんどい
  • だからRDBMSがいいよね、というのでMySQL使ってます
  • とにかくパッチを取り入れてほしい
  • Group ReplicationとかMySQLClusterの場合、同一リージョンにないといけないのでリージョンをまたがりたいFacebookの方針と合わない
  • Group Replicationがリージョンまたがるといいなと思ってる

授賞式

当日出席されていたMySQL ACEのとみたさん、yoku0825さん、三谷さんと、DB Techで登壇された北川さんにイルカの授与式

受賞後のyoku0825さんを撮っちゃいました f:id:next4us-ti:20190719145654j:plain

最後は残った人で写真撮影 f:id:next4us-ti:20190719145749j:plain

資料について

後日上がるとのことなので、イメージわきづらい人はそれを待ちましょう

最後に

mysql_cafeのスレッドに書かれていることをだいぶ参考に書かせてもらいました。

みなさんありがとうございました(特にyoku0825さん) そして、美味しい食事と飲み物、きれいな会場と、Philipさんと松信さんの貴重なお話を提供してくれたOracle社の関係者の皆様に感謝です。

データを適当に作成するmysql_random_data_loadを試してみた

テストデータ作りたいな

  • 自分で手で作るのもめんどいし、定義変わるとアレ(メンテが必要)やしな。
  • せや、型から適当にデータ作るツールとかないんかな?
  • あったわ→mysql_random_data_load

さすぺる!(さすがPercona!)

試してみよう

use test;
-- 適当なテーブル作る
CREATE TABLE `person` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `guid` varchar(36) COLLATE utf8mb4_bin NOT NULL COMMENT 'GUID',
  `name` varchar(150) COLLATE utf8mb4_bin NOT NULL COMMENT '名前',
  `age` tinyint(4) NOT NULL DEFAULT '0' COMMENT '年齢',
  `money` int(11) DEFAULT NULL COMMENT '手持ちのお金',
  `marriage_flag` tinyint(1) NOT NULL DEFAULT '0' COMMENT '結婚フラグ',
  `prefecture` varchar(30) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '住所(都道府県)',
  `tel_number` varchar(20) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '電話番号',
  `fax_number` varchar(20) COLLATE utf8mb4_bin DEFAULT NULL COMMENT 'FAX番号',
  `email` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL COMMENT 'Eメールアドレス',
  `address` varchar(300) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '住所',
  `company` varchar(300) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '会社名',
  PRIMARY KEY (`id`),
  UNIQUE KEY `u_1` (`guid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='誰かさんの情報';

すぐ使いたいんで、precompiled binariesから取ってくる。

解凍して、mysql_random_data_loadを$PATHで見えるところに置いて、

$ which mysql_random_data_load 
/home/xxx/bin/mysql_random_data_load

ってな感じで見えるようになったんでOK。

使い方はhttps://github.com/Percona-Lab/mysql_random_data_loadに書いてある。

デバッグモードで100行くらい入れて試してみるか。

$ mysql_random_data_load test person 100 --debug --user=root --password=xxx --host=127.0.0.1 --port=3307
DEBU[2019-07-06T10:30:48+09:00] &tableparser.Table{
    Schema: "test",
    Name:   "person",
    Fields: {
        {
            TableCatalog:           "def",
            TableSchema:            "test",
            TableName:              "person",
            ColumnName:             "id",
            OrdinalPosition:        1,
            ColumnDefault:          sql.NullString{},
            IsNullable:             false,
            DataType:               "bigint",
            CharacterMaximumLength: sql.NullInt64{},
            CharacterOctetLength:   sql.NullInt64{},
            NumericPrecision:       sql.NullInt64{Int64:19, Valid:true},
            NumericScale:           sql.NullInt64{Int64:0, Valid:true},
            DatetimePrecision:      sql.NullInt64{},
            CharacterSetName:       sql.NullString{},
            CollationName:          sql.NullString{},
            ColumnType:             "bigint(20)",
            ColumnKey:              "PRI",
            Extra:                  "auto_increment",
            Privileges:             "select,insert,update,references",
            ColumnComment:          "ID",
            GenerationExpression:   "",
            SetEnumVals:            {},
            Constraint:             (*tableparser.Constraint)(nil),
            SrsID:                  sql.NullString{},
        },
        {
            TableCatalog:           "def",
            TableSchema:            "test",
            TableName:              "person",
            ColumnName:             "guid",
            OrdinalPosition:        2,
            ColumnDefault:          sql.NullString{},
            IsNullable:             false,
            DataType:               "varchar",
            CharacterMaximumLength: sql.NullInt64{Int64:36, Valid:true},
            CharacterOctetLength:   sql.NullInt64{Int64:144, Valid:true},
            NumericPrecision:       sql.NullInt64{},
            NumericScale:           sql.NullInt64{},
            DatetimePrecision:      sql.NullInt64{},
            CharacterSetName:       sql.NullString{String:"utf8mb4", Valid:true},
            CollationName:          sql.NullString{String:"utf8mb4_bin", Valid:true},
            ColumnType:             "varchar(36)",
            ColumnKey:              "UNI",
            Extra:                  "",
            Privileges:             "select,insert,update,references",
            ColumnComment:          "GUID",
            GenerationExpression:   "",
            SetEnumVals:            {},
            Constraint:             (*tableparser.Constraint)(nil),
            SrsID:                  sql.NullString{},
        },
        {
            TableCatalog:           "def",
            TableSchema:            "test",
            TableName:              "person",
            ColumnName:             "name",
            OrdinalPosition:        3,
            ColumnDefault:          sql.NullString{},
            IsNullable:             false,
            DataType:               "varchar",
            CharacterMaximumLength: sql.NullInt64{Int64:150, Valid:true},
            CharacterOctetLength:   sql.NullInt64{Int64:600, Valid:true},
            NumericPrecision:       sql.NullInt64{},
            NumericScale:           sql.NullInt64{},
            DatetimePrecision:      sql.NullInt64{},
            CharacterSetName:       sql.NullString{String:"utf8mb4", Valid:true},
            CollationName:          sql.NullString{String:"utf8mb4_bin", Valid:true},
            ColumnType:             "varchar(150)",
            ColumnKey:              "",
            Extra:                  "",
            Privileges:             "select,insert,update,references",
            ColumnComment:          "名前",
            GenerationExpression:   "",
            SetEnumVals:            {},
            Constraint:             (*tableparser.Constraint)(nil),
            SrsID:                  sql.NullString{},
        },
        {
            TableCatalog:           "def",
            TableSchema:            "test",
            TableName:              "person",
            ColumnName:             "age",
            OrdinalPosition:        4,
            ColumnDefault:          sql.NullString{String:"0", Valid:true},
            IsNullable:             false,
            DataType:               "tinyint",
            CharacterMaximumLength: sql.NullInt64{},
            CharacterOctetLength:   sql.NullInt64{},
            NumericPrecision:       sql.NullInt64{Int64:3, Valid:true},
            NumericScale:           sql.NullInt64{Int64:0, Valid:true},
            DatetimePrecision:      sql.NullInt64{},
            CharacterSetName:       sql.NullString{},
            CollationName:          sql.NullString{},
            ColumnType:             "tinyint(4)",
            ColumnKey:              "",
            Extra:                  "",
            Privileges:             "select,insert,update,references",
            ColumnComment:          "年齢",
            GenerationExpression:   "",
            SetEnumVals:            {},
            Constraint:             (*tableparser.Constraint)(nil),
            SrsID:                  sql.NullString{},
        },
        {
            TableCatalog:           "def",
            TableSchema:            "test",
            TableName:              "person",
            ColumnName:             "money",
            OrdinalPosition:        5,
            ColumnDefault:          sql.NullString{},
            IsNullable:             false,
            DataType:               "int",
            CharacterMaximumLength: sql.NullInt64{},
            CharacterOctetLength:   sql.NullInt64{},
            NumericPrecision:       sql.NullInt64{Int64:10, Valid:true},
            NumericScale:           sql.NullInt64{Int64:0, Valid:true},
            DatetimePrecision:      sql.NullInt64{},
            CharacterSetName:       sql.NullString{},
            CollationName:          sql.NullString{},
            ColumnType:             "int(11)",
            ColumnKey:              "",
            Extra:                  "",
            Privileges:             "select,insert,update,references",
            ColumnComment:          "手持ちのお金",
            GenerationExpression:   "",
            SetEnumVals:            {},
            Constraint:             (*tableparser.Constraint)(nil),
            SrsID:                  sql.NullString{},
        },
        {
            TableCatalog:           "def",
            TableSchema:            "test",
            TableName:              "person",
            ColumnName:             "marriage_flag",
            OrdinalPosition:        6,
            ColumnDefault:          sql.NullString{String:"0", Valid:true},
            IsNullable:             false,
            DataType:               "tinyint",
            CharacterMaximumLength: sql.NullInt64{},
            CharacterOctetLength:   sql.NullInt64{},
            NumericPrecision:       sql.NullInt64{Int64:3, Valid:true},
            NumericScale:           sql.NullInt64{Int64:0, Valid:true},
            DatetimePrecision:      sql.NullInt64{},
            CharacterSetName:       sql.NullString{},
            CollationName:          sql.NullString{},
            ColumnType:             "tinyint(1)",
            ColumnKey:              "",
            Extra:                  "",
            Privileges:             "select,insert,update,references",
            ColumnComment:          "結婚フラグ",
            GenerationExpression:   "",
            SetEnumVals:            {},
            Constraint:             (*tableparser.Constraint)(nil),
            SrsID:                  sql.NullString{},
        },
        {
            TableCatalog:           "def",
            TableSchema:            "test",
            TableName:              "person",
            ColumnName:             "prefecture",
            OrdinalPosition:        7,
            ColumnDefault:          sql.NullString{},
            IsNullable:             false,
            DataType:               "varchar",
            CharacterMaximumLength: sql.NullInt64{Int64:30, Valid:true},
            CharacterOctetLength:   sql.NullInt64{Int64:120, Valid:true},
            NumericPrecision:       sql.NullInt64{},
            NumericScale:           sql.NullInt64{},
            DatetimePrecision:      sql.NullInt64{},
            CharacterSetName:       sql.NullString{String:"utf8mb4", Valid:true},
            CollationName:          sql.NullString{String:"utf8mb4_bin", Valid:true},
            ColumnType:             "varchar(30)",
            ColumnKey:              "",
            Extra:                  "",
            Privileges:             "select,insert,update,references",
            ColumnComment:          "住所(都道府県)",
            GenerationExpression:   "",
            SetEnumVals:            {},
            Constraint:             (*tableparser.Constraint)(nil),
            SrsID:                  sql.NullString{},
        },
        {
            TableCatalog:           "def",
            TableSchema:            "test",
            TableName:              "person",
            ColumnName:             "tel_number",
            OrdinalPosition:        8,
            ColumnDefault:          sql.NullString{},
            IsNullable:             false,
            DataType:               "varchar",
            CharacterMaximumLength: sql.NullInt64{Int64:20, Valid:true},
            CharacterOctetLength:   sql.NullInt64{Int64:80, Valid:true},
            NumericPrecision:       sql.NullInt64{},
            NumericScale:           sql.NullInt64{},
            DatetimePrecision:      sql.NullInt64{},
            CharacterSetName:       sql.NullString{String:"utf8mb4", Valid:true},
            CollationName:          sql.NullString{String:"utf8mb4_bin", Valid:true},
            ColumnType:             "varchar(20)",
            ColumnKey:              "",
            Extra:                  "",
            Privileges:             "select,insert,update,references",
            ColumnComment:          "電話番号",
            GenerationExpression:   "",
            SetEnumVals:            {},
            Constraint:             (*tableparser.Constraint)(nil),
            SrsID:                  sql.NullString{},
        },
        {
            TableCatalog:           "def",
            TableSchema:            "test",
            TableName:              "person",
            ColumnName:             "fax_number",
            OrdinalPosition:        9,
            ColumnDefault:          sql.NullString{},
            IsNullable:             false,
            DataType:               "varchar",
            CharacterMaximumLength: sql.NullInt64{Int64:20, Valid:true},
            CharacterOctetLength:   sql.NullInt64{Int64:80, Valid:true},
            NumericPrecision:       sql.NullInt64{},
            NumericScale:           sql.NullInt64{},
            DatetimePrecision:      sql.NullInt64{},
            CharacterSetName:       sql.NullString{String:"utf8mb4", Valid:true},
            CollationName:          sql.NullString{String:"utf8mb4_bin", Valid:true},
            ColumnType:             "varchar(20)",
            ColumnKey:              "",
            Extra:                  "",
            Privileges:             "select,insert,update,references",
            ColumnComment:          "FAX番号",
            GenerationExpression:   "",
            SetEnumVals:            {},
            Constraint:             (*tableparser.Constraint)(nil),
            SrsID:                  sql.NullString{},
        },
        {
            TableCatalog:           "def",
            TableSchema:            "test",
            TableName:              "person",
            ColumnName:             "email",
            OrdinalPosition:        10,
            ColumnDefault:          sql.NullString{},
            IsNullable:             false,
            DataType:               "varchar",
            CharacterMaximumLength: sql.NullInt64{Int64:255, Valid:true},
            CharacterOctetLength:   sql.NullInt64{Int64:1020, Valid:true},
            NumericPrecision:       sql.NullInt64{},
            NumericScale:           sql.NullInt64{},
            DatetimePrecision:      sql.NullInt64{},
            CharacterSetName:       sql.NullString{String:"utf8mb4", Valid:true},
            CollationName:          sql.NullString{String:"utf8mb4_bin", Valid:true},
            ColumnType:             "varchar(255)",
            ColumnKey:              "",
            Extra:                  "",
            Privileges:             "select,insert,update,references",
            ColumnComment:          "Eメールアドレス",
            GenerationExpression:   "",
            SetEnumVals:            {},
            Constraint:             (*tableparser.Constraint)(nil),
            SrsID:                  sql.NullString{},
        },
        {
            TableCatalog:           "def",
            TableSchema:            "test",
            TableName:              "person",
            ColumnName:             "address",
            OrdinalPosition:        11,
            ColumnDefault:          sql.NullString{},
            IsNullable:             false,
            DataType:               "varchar",
            CharacterMaximumLength: sql.NullInt64{Int64:300, Valid:true},
            CharacterOctetLength:   sql.NullInt64{Int64:1200, Valid:true},
            NumericPrecision:       sql.NullInt64{},
            NumericScale:           sql.NullInt64{},
            DatetimePrecision:      sql.NullInt64{},
            CharacterSetName:       sql.NullString{String:"utf8mb4", Valid:true},
            CollationName:          sql.NullString{String:"utf8mb4_bin", Valid:true},
            ColumnType:             "varchar(300)",
            ColumnKey:              "",
            Extra:                  "",
            Privileges:             "select,insert,update,references",
            ColumnComment:          "住所",
            GenerationExpression:   "",
            SetEnumVals:            {},
            Constraint:             (*tableparser.Constraint)(nil),
            SrsID:                  sql.NullString{},
        },
        {
            TableCatalog:           "def",
            TableSchema:            "test",
            TableName:              "person",
            ColumnName:             "company",
            OrdinalPosition:        12,
            ColumnDefault:          sql.NullString{},
            IsNullable:             false,
            DataType:               "varchar",
            CharacterMaximumLength: sql.NullInt64{Int64:300, Valid:true},
            CharacterOctetLength:   sql.NullInt64{Int64:1200, Valid:true},
            NumericPrecision:       sql.NullInt64{},
            NumericScale:           sql.NullInt64{},
            DatetimePrecision:      sql.NullInt64{},
            CharacterSetName:       sql.NullString{String:"utf8mb4", Valid:true},
            CollationName:          sql.NullString{String:"utf8mb4_bin", Valid:true},
            ColumnType:             "varchar(300)",
            ColumnKey:              "",
            Extra:                  "",
            Privileges:             "select,insert,update,references",
            ColumnComment:          "会社名",
            GenerationExpression:   "",
            SetEnumVals:            {},
            Constraint:             (*tableparser.Constraint)(nil),
            SrsID:                  sql.NullString{},
        },
    },
    Indexes: {
        "u_1": {
            Name:    "u_1",
            Fields:  {"guid"},
            Unique:  true,
            Visible: true,
        },
        "PRIMARY": {
            Name:    "PRIMARY",
            Fields:  {"id"},
            Unique:  true,
            Visible: true,
        },
    },
    Constraints: {
    },
    Triggers: {
    },
    conn: (*sql.DB)(nil),
} 
INFO[2019-07-06T10:30:48+09:00] Starting                                     
DEBU[2019-07-06T10:30:48+09:00] Must run 1 bulk inserts having 100 rows each 
INFO[2019-07-06T10:30:49+09:00] 100 rows inserted

お、入ってる感じやな。どれどれ?

id guid name age money marriage_flag prefecture tel_number fax_number email address company
1 recusandae et consequatur eius aut a consequatur nihil error eius ut. 5 1342458479 6 rerum unde qui perferendis nob Terry Sanchez Tammy Torres eum culpa nam minima maxime. et non nisi voluptas temporibus aliquam magnam. eius deleniti nam et sint voluptates id.
2 aut odio soluta ut dolores. omnis accusamus est voluptatem consequuntur quisquam quae quia. 11 914091476 5 fugiat magnam aut culpa ut rep Janet Oliver Juan King nemo suscipit doloremque porro error et qui tenetur ut asperiores corporis! nemo quia magnam hic dolor. adipisci architecto animi vitae deleniti rerum.
3 aut dolor consequatur soluta exercit perspiciatis sit odio fugit quasi non. 3 1255569388 11 ullam facere exercitationem il Janet Murphy Fred Grant ut ea nisi recusandae quo inventore tenetur et. et blanditiis maxime est quam quidem id. illo ea voluptas vitae repudiandae architecto saepe.
4 quidem omnis iste ea a impedit odio quod velit ut earum dolore molestiae! 14 2003588754 12 et qui et id voluptas voluptat Tina Garrett I II II Joe Edwards quo explicabo eum qui alias sequi. dolorem quaerat nihil aut perspiciatis qui asperiores nihil. alias nisi ullam quo debitis iusto animi.
5 illo earum est pariatur aut in digni consequatur molestias dicta omnis. 4 1698116023 5 doloribus natus distinctio nos Brenda Hunt Sean Carter assumenda et deserunt quidem. consequatur occaecati non libero accusantium reprehenderit quam suscipit. ratione eum beatae laboriosam quo enim.
6 consequuntur quibusdam nihil soluta accusamus similique cum eos quidem. 8 1153628287 1 voluptas est et temporibus aut Paula Parker Kathryn Watkins quis voluptatem at perferendis labore. reiciendis voluptates itaque voluptatem optio ea qui hic. ab quibusdam illum.
7 voluptas animi vero tenetur adipisci et ut et! 5 1640670520 8 quod corporis qui rerum culpa Joshua Hart Louise Fernandez quia et molestiae distinctio quaerat quasi soluta aut voluptatem et recusandae! doloribus dignissimos ut hic tempora. temporibus eveniet vel quisquam repellendus aut animi fugit.
8 quae quod ratione pariatur repellend suscipit omnis aut consequatur numquam laboriosam non repellendus. 11 1218842385 7 dolorem quia qui accusamus eni Kenneth Burns Terry Bryant consequatur ex aliquam beatae qui et consequatur et. vitae ratione quaerat rerum facilis. animi sit ut nobis quae ea.
9 laudantium odit et facere minima. ea quis voluptas ipsum dignissimos asperiores sequi aut. 13 601894044 12 id non consequuntur iste disti Charles Anderson Raymond Diaz sequi impedit nihil ea vel et nihil rerum aut. sit commodi et autem eius. odit qui vitae facilis modi quia harum amet aspernatur.
10 magni ullam dignissimos praesentium dicta qui sunt dolor ut repellat hic. 10 1408984034 11 quia tempora consequatur delec Stephanie Garcia Patricia Carroll repudiandae fuga dignissimos odio voluptate quia. quia necessitatibus molestiae dolorem natus accusantium. sunt consectetur est amet doloremque qui autem quae.

そりゃそうだよね・・・。

だって型で見てるだけなんだもん。

カラム名も見てくれるツール欲しい!

  • 日本語で入ってほしいものもあるよね。
  • コメントを参考にしたりしてほしいよね。
  • MySQL8.0でも動いて欲しい(このツールはMySQL5.7までしか動かない)

自分で作るか、こいつをいじるかな〜

追記

github.com

を見るとわかりますが、型のフィールド値に制限が有ります。

decimal(25,0)

とかやると、

panic: invalid argument to Int63n

ってメッセージが出るのでご注意を!

♡♡♡主キー大主キー♡♡♡(MySQL Casual Talks vol.11)

名言生まれる!

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

タイトルにもある大主キーという単語がtwitterのTLに一瞬溢れました。

後日もことあるごとに大主キーという人が続出。

もうこれは今年の流行語対象 (in MySQL)といっても過言ではありません。

MySQL Casual Talksとは?

もっと深く浅く、広く狭くMySQLを使っていこうという趣旨のイベントです。 多方面から多様なMySQLの使い方、運用、TipsなどなどのTalkを集めたいと思っております。

https://mysql-casual.connpass.com/event/131551/

というイベントの趣旨の通りvol. 11 でも非常に個性的かつDeepな話が繰り広げられました。

togetter

tmtmsさんがまとめたこちらをどうぞ

発表者 内容 時間 資料URL
TBD オープニング 5分くらい -
do_aki TritonnからElasticSearchへの移行話 30分くらい https://www.slideshare.net/do_aki/tritonn-elasticsearch
awache DBRE(Database Reliability Engineering) 始めました
~ Make it visible / No Ops, More Code ~
30分くらい https://speakerdeck.com/_awache/no-ops-more-code
tom__bo テーブル定義から見積もるストレージサイズと8.0以降のベンチマーク自動化について 20分くらい https://speakerdeck.com/tombo/about-table-size-estimator
t_kyt オンプレMySQLをRDSにして解決したこと/しなかったこと(仮) 15分か20分くらい
lhfukamachi TBD 10分くらい https://www.slideshare.net/hidemifukamachi/sql-require-primarykey
keny_lala TBD 5~10分くらい https://www.slideshare.net/kenken0807/mysql-149112359
tmtms MySQLと令和 10分くらい https://speakerdeck.com/tmtms/mysql-on-reiwa

TritonnからElasticSearchへの移行話

do_akiさんによる全文検索のお話。

す、すみません。にわかなMySQLerなんでTritonnSennaも初耳です…。

このTritonn、2009年で開発が止まっており、脆弱性等を考えるとMySQLも5.0から移行しなくてはならず脱Tritonnをする必要が出たことがきっかけらしい。

ここで移行案を色々検討されたところはTritonnに限らず全文検索を使う人には本当に参考になる話。

https://www.slideshare.net/do_aki/tritonn-elasticsearch#10

Elasticsearchを採用されるが、そこでも試行錯誤しているところもやはり参考になる。

DBRE(Database Reliability Engineering) 始めました ~ Make it visible / No Ops, More Code ~

会場を貸してくれたBizreachの_awacheさんのトーク

「DBREとは何ぞや?」を数か月前に教えてくれたその人の話ということで参加前から非常に楽しみにしてました。

DBREとは、については

を読んだほうが確実なのですが、二つを読んだうえでの自分のいい加減な理解では 「DBを使って開発の手助けをするスペシャリスト」 です。

DBAとの違いがややこしいというか、今までのDBAはDBRE的なこともやってる場合が多いんだと思っていて、

  • DBAはデータベースを守るために尽力する(セキュリティ、バックアップ・リストア(本番)、レプリケーション、HA設定等)のに対し、
  • DBREはバックアップを開発機に利用するためのパイプを作ったり、そのままでは使えない場合はマスキングが必要なのでその機構を作ったりするイメージ。

まあ、DB大好きな人はどっちの素養も大抵持ってるとは思うんですが、DBAが僧侶、DBREが戦士・魔法使い的な感じでしょうかね?(どっちもやってる人は勇者ですねw)

資料を見るとわかりますが、DBREの存在意義として 3つの非機能要件の共通化 を行い、それを各事業へ提供することで会社のMissionとVisionを具現化しようとしています。

3つの非機能要件の共通化とは

  • Provisioning(DBのパラメータセッティング、ユーザ設計等)
  • Monitoring(定量モニタリング⇒人材の横断的な移動を可能に)
  • Backup(開発環境の構築、定期的バックアップ、マスキングによる安全な開発)

なるほど、DBREとは安心してDBを使えるように積極的に活動することだな!(雑な理解)

そして面白いのはBizreach社のDBREは守備(結局DBA的なことも全部やってる)時もやはりオフェンシブで、例えば

  • リストア時に監査ログを全出力
  • リストアをする時点でランダムパスワード
  • 必要な処理が終わったらインスタンスもスナップショット(バックアップファイル)も全て削除

と言った守備的なところもイケイケ。

  • ダンプもパラレル処理(テーブル単位でダンプする)

とか、

  • 事業側にマスキングルールを決めさせて、そのルールとなる設定ファイルをcommitしてもらって、
  • 欲しい形でデータをTSV形式で作る仕組みを作った。
  • これにより責任範囲を明確にして自分たちを守る

とか、

かっこよすぎます!

少しマネさせてもらいます。

テーブル定義から見積もるストレージサイズと8.0以降のベンチマーク自動化について

今回のトークの中で一番みんなが楽しんだのがtom__boさんの話だったと思うw

2年目の子がMySQLのパーサー作るという偉業にチャレンジするもんだから、おじさんたちの目はキラキラしちゃって、見えないはずのマサカリが具現化してた気すらしましたw

テーブルサイズを見積もるためにここまでやろうということすら思わない凡人の自分にはtom__boさんは眩しすぎて、

「ぜひ、うちの会社に来ないか?」(代わりに俺がリストラされるだろうけど)

オンプレMySQLをRDSにして解決したこと/しなかったこと(仮)

t_kytさんのこの話はスライドが上がってないんですが、内容は結構濃くて、メモを後述しておきますが要するにMySQLのことというかDBすらあんまり知らなかったけど、1年間ひたすらバージョンアップ等を繰り返して詳しくなっちゃいました!というもの。

MySQL4.xをRDSで上げるのにまず5.6まで持っていかなければいけないので、その色々な苦労をDBあまり知らない人が悪戦苦闘しながらすることは話としては参考になります。

ここからはLT

Sql require primary_keyを使って主キーを必須にさせる

lhfukamachiさんのLT。

このLTで冒頭の名言が生まれました。

https://www.slideshare.net/hidemifukamachi/sql-require-primarykey#4

スライドがちょっとyokuさんの影響受けてるのはさすがw

MySQLのリアルタイムモニタリングツールを作った話

keny_lalaさんのモニタリングツールの話。

Go製のMySQLのリアルタイムモニタリングツールですが、最後に

「これって需要あります?」

とあったんですが、

めっちゃほしいです!

MySQLと令和

最後の締めはACEのtmtmsさんの文字コードのお話。

これについては既に先日のOracleで聴いてたので知ってはいましたがやはり濃いなぁ…。

まとめ

MySQL Casual Talks、2回目の参加(前回はVol.10)でしたが、今回も非常に面白かったです。

特に@_awacheさんの話は今後の自分の仕事の方向にも影響を与えてくれたいいお話でした。

あとは若い人たちが本当に活気というか才能があって、羨ましいというか頑張らねばという気持ちになりました。

yoku0825さんの幻のLT聞けなかったのはちょっぴり残念でした。

最後に

開場・飲食物を提供してくれたBizreach社と運営を手伝ってくださってた社員の方々と@_awacheさん、 トークしていただいた皆様、 開催してくれた管理人のお二人、 ありがとうございました。

Vol.12も楽しみにしています。

AWS Loft TokyoのMiddlewares Deep Talks行ってきました

これもあんまりまとめる感じでは無いですが、とりあえず後から追記できる程度には雑記します。


iddlewares Deep Talks(AWS Loft Tokyo)

Amazonさんがありがたいことにミドルウェア関連の話≒DB系の話やってくれたんで、目黒のビルに行ってきた。

ぼくらが8.0に至ったみちのり(踏破)

https://speakerdeck.com/yoku0825/bokuraga8-dot-0nizhi-tutamitifalseri-ta-po

遅刻したので半分聞けず…(´;ω;`)

道に迷ったり、3階のエレベーター乗り場で別の会社の人に違う場所に案内されたり、入り口で登録したはずのメールアドレスが無いよーと言われたり…。

f:id:next4us-ti:20190524191624j:plain (着いたときは汗だく…)

でも、こないだのOracle Code2019とその辺は話が重複してたので結果オーライ。

後半の話は実際にMySQL8使っての今のところの感想ということで、とても興味深いものだった。

(スライド見るともしかすると微妙そうに見えちゃうかもしれないけど、新規に立てるならMySQL8.0は超おススメだと俺も思う)

PostgreSQL 12の話

https://www.slideshare.net/masahikosawada98/postgresql-12

一番気になったのはパーティションの話。

プルーニングでパフォーマンス超上がったとのこと。

休憩時間に

気になったんで間の休憩時間に澤田さん捕まえて聞いてみたら、パーティションした場合別のところ(複数あるらしい)でメモリ食うので数千レベルのテーブルで使うのはまだ危険とのこと。

(そこにYahoo! の三谷さんも来たので巻き込んで3人でMySQLPostgreSQLの話で「へー」「ほー」と言い合う) (三谷さん、ACEポイントは6月かららしい!5月はタダ働き…。mjsk)

そのあと、

  • MySQLの良さはクエリからコストが予想できること。
    • (↑ヒストグラムが使われないから)
    • じゃあ、範囲検索の行推定とかどうやってたんだ?(澤田さん)
    • MySQLは総行数(見積もり)/カーディナリティー(見積もり)が均等に分布していると見做します(by yoku0825さん)
    • そしてsh2さんのスライドが共有される→これ

という温かいMySQLPostgreSQLの交流会がTwitter上で行われた。(sh2さんのスライド、メッチャためになった!)

PostgreSQLパーティションについては13で更に改良する(澤田さんも参加)ので、お楽しみに!とのこと。(12がまだRC出てないのにw)

What's new in Slastic Stack 7.0?

https://noti.st/johtani/psVhfT/whats-new-in-elastic-stack-7-1

  • Elasticsearchのインデックスはお前(RDB使い)のインデックスじゃない
  • 今のElastic Searchは本当に何もしなくてもいい感じの設定になってる
  • 可用性めっちゃ高い
  • むしろ設定触るな
  • seed node触るな
  • なんかあったらログを見ろ!
  • PainlessでPainfull

というメッセージが印象に残った。

(ちなみに、p37のnumber_indicesってのはnumber_of_shardsの間違い)

Cassandra vs ScyllaDB 性能比較

資料がまだアップされてないのでメモを乗せとく。

資料がアップされました→こちら

要するにCassandra使うんなら、ScyllaDBスゲー早いよおススメ、Yahoo!では切り替え始めてるって話。

Yahoo! JapanでのCassandra

  • ヤフーの主要サービスのほとんどで利用
  • 5500ノード!
  • クラスタ数280!
  • コンテンツのメタデータ等を入れている

Cassandraの限界

特定サイズを超えたデータにアクセスが集中すると、クラスタが不安定になる

Cassandra VS ScyllaDB

  • 99.9%読み書きレイテンシ
  • Cassandraは早い段階で跳ね上がり
  • ScyllaDBは2倍程度のPF
  • IO waitがScyllaDBではほとんどでない
  • ScyllaDBはSATA3の性能限界に到達したが、Cassandraは使いきれず

https://www.scylladb.com/2019/05/22/yahoo-japan-using-scylla-and-cassandra-at-scale/

S3 整合性モデルと Hadoop/Spark の話

https://www.slideshare.net/ssuserca76a5/s3-hadoopspark

分かったこと。

『俺はまだS3を知らない』

でも、S3の使い方ちょっと知れて雰囲気だけ感じました。 S3にログとか置いて遊ぼうかな。

Introducing Algolia with Demo

https://speakerdeck.com/shinodogg/introducing-algolia-with-demo

すみません、俺本当に何もわからないおバカさんです。 俺には難しかったです…。

Middlewares Deep Talks 楽しかった!

軽食(サンドイッチ)もドリンク(俺はコーラばっかりだけど、みんなはビール)も旨かった。

スピーカーの方々の軽妙なトークに笑い声も何度も上がり、壇上のスピーカーと聞いてる人たちとのやり取りなんかもあって和気あいあいとしてました。

最後の懇親会までいたかったんですが、妻のご機嫌も気にしてたので昨日は早々に帰宅。

開催者の皆様、場所と食事を提供してくださったAmazon様、そしてスピーカーの皆様

ありがとうございました

f:id:next4us-ti:20190524191618j:plain f:id:next4us-ti:20190524191621j:plain

次回もあればぜひ行きたいです!

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!でした。