- プロダクトマネージャー
- アーキテクト(バックエンド)
- カスタマーサポートMFK
- 他91件の職種
-
開発
- アーキテクト(バックエンド)
- Railsエンジニア
- アーキテクト(フロントエンド)
- Webエンジニア
- エンジニアリングマネージャー
- バックエンドエンジニア/MFK
- フルスタックエンジニア
- MOpsエンジニア
- フロントエンドマネージャー
- バックエンドエンジニア
- Webフロントエンド
- Webエンジニア
- サーバーサイドエンジニア
- フロントエンジニア
- エンジニア@京都
- エンジニア@大阪
- エンジニア オープンポジション
- エンジニアマネージャー
- Rails@京都
- バックエンド@BFW
- Androidエンジニア
- iOSエンジニア
- SRE
- クラウドエンジニア
- SRE、インフラエンジニア
- テスト自動化エンジニア
- QAエンジニア
- エンジニアリング
- エンジニア職
- コーポレートエンジニア
- マーケティングエンジニア
- Webアナリティクスエンジニア
- SDET
- QA関連職種オープンポジション
- データアナリスト
- セキュリティエンジニア
- コミュニケーションデザイナー
- UIデザイナー
- プロダクトデザイナー
- デザイナーオープンポジション
- グラフィックデザイナー
-
ビジネス
- プロダクトマネージャー
- スクラムマスター
- プロダクトマネージャー
- 人事/人材開発・組織開発担当
- リスク管理
- グローバル採用担当者
- グローバル採用担当
- 金融コンプライアンス
- 新卒採用リクルーター
- エンジニア採用担当
- 中途採用担当
- 労務
- システム監査
- ビジネス採用担当
- 経営企画(予実・IR)
- HRBP
- 法務
- 債権管理/MFK
- セールス・事業開発
- 新規事業開発
- ビジネス職
- フィールドセールス
- セールスマネージャー候補
- インサイドセールス SDR
- インサイドセールス企画
- オンラインセールス
- SaaS営業、MFBC
- インサイドセールス MFBC
- セールス MFBC
- マーケター
- マーケティング
- サービス企画
- データマーケター
- BtoBマーケティングリーダー
- CRMスペシャリスト
- WEBマーケティング(B2B)
- Webマーケティング
- デジタルマーケター
- イベントマーケター
- コンテンツマーケ MFBC
- SEO MFBC
- その他
MySQL パーティショニングで高速化
こんにちは。エンジニアの中山です。
最近、ふるさと納税を初体験しました。
手間がかかりそうという先入観を持っていたのですが、実際にはネットで申し込めて、決済はクレジットカードで、美味しい特産品がすぐに届きました。
こんなに簡単なら、もっと早くから知っていればよかったのにと、知識の大切さを再認識しました。
マネトク!にはそんなお得な情報がたくさんあります。
さて、本題。
サービスが成長してデータベースが大規模化・高使用頻度化してくると、やはり遅いクエリが気になってきます。
対策として、インデックスの工夫、クエリのチューニング、キャッシュ設定、高速なハードウェアへの置き換え等の施策を行うことで、改善をすることができます。
しかし、さらに成長が進むと、それらの手段だけでは足りず、次の手が欲しくなってきます。
高速化する手段の1つとして、パーティショニングが手軽で効果的なので試してみました。
パーティショニングとは
条件によってレコードを振り分け、1つのテーブルを分割する機能です。
データファイルは複数に分かれるのですが、データベースのユーザーから見ると1つのテーブルのままなので、SELECT/UPDATE/INSERT/DELETEのクエリは変えず、そのまま使うことができます。
【参考】
MySQL :: MySQL 5.6 リファレンスマニュアル :: 19 パーティション化
計測用データの準備
パーティショニングの効果を試すために、次のような計測用データを用意しました。
3テーブル。データは同一 パーティション無し(テーブル名 samples)4パーティション(テーブル名 samples_4_partitions)64パーティション(テーブル名 samples_64_partitions)各テーブル約1677万レコード(2の24乗)カラム id : オートインクリメントのIDfoo_id : 1~1000の範囲の乱数content : 乱数から生成した32文字の文字列
テーブル・データ作成のクエリ
CREATE TABLE `samples` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`foo_id` int(11) DEFAULT NULL,
`content` varchar(64) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `index_foo_id` (`foo_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `samples_4_partitions` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`foo_id` int(11) NOT NULL,
`content` varchar(64) DEFAULT NULL,
PRIMARY KEY (`id`,`foo_id`),
KEY `index_foo_id` (`foo_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
PARTITION BY HASH (foo_id) PARTITIONS 4;
CREATE TABLE `samples_64_partitions` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`foo_id` int(11) NOT NULL,
`content` varchar(64) DEFAULT NULL,
PRIMARY KEY (`id`,`foo_id`),
KEY `index_foo_id` (`foo_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
PARTITION BY HASH (foo_id) PARTITIONS 64;
INSERT INTO samples () VALUES ();
INSERT INTO samples (id) SELECT 0 FROM samples;
INSERT INTO samples (id) SELECT 0 FROM samples;
INSERT INTO samples (id) SELECT 0 FROM samples;
INSERT INTO samples (id) SELECT 0 FROM samples;
INSERT INTO samples (id) SELECT 0 FROM samples;
INSERT INTO samples (id) SELECT 0 FROM samples;
INSERT INTO samples (id) SELECT 0 FROM samples;
INSERT INTO samples (id) SELECT 0 FROM samples;
INSERT INTO samples (id) SELECT 0 FROM samples;
INSERT INTO samples (id) SELECT 0 FROM samples;
INSERT INTO samples (id) SELECT 0 FROM samples;
INSERT INTO samples (id) SELECT 0 FROM samples;
INSERT INTO samples (id) SELECT 0 FROM samples;
INSERT INTO samples (id) SELECT 0 FROM samples;
INSERT INTO samples (id) SELECT 0 FROM samples;
INSERT INTO samples (id) SELECT 0 FROM samples;
INSERT INTO samples (id) SELECT 0 FROM samples;
INSERT INTO samples (id) SELECT 0 FROM samples;
INSERT INTO samples (id) SELECT 0 FROM samples;
INSERT INTO samples (id) SELECT 0 FROM samples;
INSERT INTO samples (id) SELECT 0 FROM samples;
INSERT INTO samples (id) SELECT 0 FROM samples;
INSERT INTO samples (id) SELECT 0 FROM samples;
INSERT INTO samples (id) SELECT 0 FROM samples;
UPDATE samples SET foo_id = CEIL(RAND()*1000), content = MD5(RAND());
INSERT INTO samples_4_partitions SELECT * FROM samples;
INSERT INTO samples_64_partitions SELECT * FROM samples;
空のテーブルを作り、1行INSERTし、レコード数を倍増させるINSERTを23回実行することにより、約1677万レコードに増加させたあと、内容をUPDATEしています。それをパーティション数の違う各テーブルにコピーしました。
【参考】
MySQLで簡単にランダムなテストデータを作成する方法 – Qiita
計測結果
1パーティションのみアクセスする例
> SELECT SQL_NO_CACHE count(*) FROM samples where foo_id = 500 and content < '8';
+----------+
| count(*) |
+----------+
| 8271 |
+----------+
1 row in set (0.39 sec)
> SELECT SQL_NO_CACHE count(*) FROM samples_4_partitions where foo_id = 500 and content < '8';
+----------+
| count(*) |
+----------+
| 8271 |
+----------+
1 row in set (0.28 sec)
> SELECT SQL_NO_CACHE count(*) FROM samples_64_partitions where foo_id = 500 and content < '8';
+----------+
| count(*) |
+----------+
| 8271 |
+----------+
1 row in set (0.05 sec)
パーティション数が多く、細かく分割するほどほど速くなりました。
パーティション無しでも、foo_idのインデックスが効いているので、アクセスするレコード数は変わらないように思えますが、実際は速くなりました。参照の局所性が高まって、ストレージへのアクセス等が効率的になることが要因なのではと考えています。
多数のパーティションにアクセスする例
> SELECT SQL_NO_CACHE count(*) FROM samples where foo_id between 501 and 800;
+----------+
| count(*) |
+----------+
| 5033677 |
+----------+
1 row in set (0.90 sec)
> SELECT SQL_NO_CACHE count(*) FROM samples_4_partitions where foo_id between 501 and 800;
+----------+
| count(*) |
+----------+
| 5033677 |
+----------+
1 row in set (0.82 sec)
> SELECT SQL_NO_CACHE count(*) FROM samples_64_partitions where foo_id between 501 and 800;
+----------+
| count(*) |
+----------+
| 5033677 |
+----------+
1 row in set (0.85 sec)
あまり差が無い結果になりました。
パーティショニングの効果を発揮させるには、アクセス対象が特定のパーティションのみで済むようにすることが大事です。
最後に
マネーフォワードでは、急成長するプロダクトを基盤から支えるエンジニアを募集しています。
是非ご応募お待ちしております!
【採用サイト】
■『マネーフォワード採用サイト』 https://recruit.moneyforward.com/
■『Wantedly』 https://www.wantedly.com/companies/moneyforward
【公開カレンダー】
■マネーフォワード公開カレンダー
【プロダクト一覧】
■家計簿アプリ・クラウド家計簿ソフト『マネーフォワード』 https://moneyforward.com/
■家計簿アプリ・クラウド家計簿ソフト『マネーフォワード』 iPhone,iPad
■家計簿アプリ・クラウド家計簿ソフト『マネーフォワード』 Android
■クラウド型会計ソフト『MFクラウド会計』 https://biz.moneyforward.com/
■クラウド型請求書管理ソフト『MFクラウド請求書』 https://invoice.moneyforward.com/
■クラウド型給与計算ソフト『MFクラウド給与』 https://payroll.moneyforward.com/
■消込ソフト・システム『MFクラウド消込』 https://biz.moneyforward.com/reconciliation/
■マイナンバー対応『MFクラウドマイナンバー』 https://biz.moneyforward.com/mynumber