Skip to content

db2023 台別データ 読み取りパフォーマンス調査(Issue #2293)

目的

wp_db2023DatabaseTableConstants::DAILY_DATA)の読み取りパフォーマンスを調査し、インデックス見直し・DAY 型変換・MySQL パーティショニング・年別テーブル分割の各案を比較する。本ドキュメントは 調査/設計 の成果物であり、スキーマ変更の実装は Issue #2135(kishu_id バックフィル)完了後を推奨する。

調査環境

項目
環境Local(slotkouryaku.local
DBMySQL(Local run: 5pYRuxwSB
テーブルwp_db2023
調査日2026-06-12

現状スキーマ(ローカル DB)

sql
CREATE TABLE `wp_db2023` (
  `DAY` date NOT NULL,
  `kishu` varchar(255) NOT NULL,
  `dainum` varchar(255) NOT NULL,
  `kaiten` int(11) NOT NULL,
  `samai` int(11) NOT NULL,
  `BB` int(11) NOT NULL,
  `RB` int(11) NOT NULL,
  `hall` varchar(255) NOT NULL,
  `kishu_id` bigint(20) unsigned DEFAULT NULL,
  PRIMARY KEY (`DAY`,`dainum`,`hall`) USING BTREE,
  KEY `idx_kishu_id` (`kishu_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

ローカルと Issue 記載の差異

項目Issue #2293 記載(本番想定)ローカル実測
DAYvarchar(10)Y/n/j 形式date 型、YYYY-MM-DD 格納
日付リテラル'2025/1/10''2026-03-16' 等(MySQL が date に解釈)

注意: 本番環境では Issue 記載どおり varchar の可能性がある。実装前に本番で SHOW CREATE TABLE wp_db2023 を再確認すること。DBML(custom-tables.dbml)は date 型を正としている。

テーブル規模(ローカル)

指標
総行数1,439,926
データサイズ約 151 MB
インデックスサイズ約 64 MB
合計約 215 MB
日付範囲2022-07-29 〜 2026-03-16
1 日あたり行数(直近)約 519〜911 行/日

用途別の読み取りパターン

用途読み取り範囲主要メソッドSQL パターン
日別記事・台別ランキング直近 3 日(DAYS_BACK_FOR_RANKING_DATA = 2select_daily_data_per_unit_by_ymdWHERE DAY IN (3日) → PHP で hall フィルタ
期間差枚サマリー(SC-010)最大 366 日select_daily_data_group_by_day_and_hall_and_kishu_by_ymdWHERE DAY IN (最大366日) → PHP で hall・kishu フィルタ
期間機種別差枚ランキング(SC-016)最大 366 日同上WHERE DAY IN (最大366日) → PHP で hall フィルタ

機種別ランキングは db_daily_article_kishu_single_day_summary を優先参照(Issue #2076)。台別 raw(db2023)の直接参照は上記 3 用途が中心。

kishu_id バックフィル状況(Issue #2135)

sql
SELECT COUNT(*) AS total,
       SUM(kishu_id IS NULL) AS null_count,
       SUM(kishu_id IS NOT NULL) AS filled_count
FROM wp_db2023;
環境totalnull_countfilled_count
ローカル(2026-06-12)1,439,9261,439,9260

ローカルでは kishu_id が全行 NULL。現行 SQL は INNER JOIN wp_db_kishu_master ON t.kishu_id = m.id を前提とするため、JOIN 付きクエリは 0 件 を返す。本番のバックフィル完了状況は別途確認が必要。

依存関係: 大規模 ALTER(パーティション追加・型変更)と kishu_id バックフィルが重複すると全行スキャンが二重化する。調査は先行可、実装は #2135 完了後を推奨

EXPLAIN 結果

1. 台別ランキング(直近 3 日)— select_daily_data_per_unit

対象日: 2026-03-14, 2026-03-15, 2026-03-16(実データ 1,949 行)

kishu_master JOIN あり(現行アプリ SQL)

id  select_type  table  type   key       rows     Extra
1   SIMPLE       m      index  uk_name   1        Using index; Using temporary; Using filesort
1   SIMPLE       t      range  PRIMARY   3        Using where; Using join buffer (Block Nested Loop)

kishu_id が NULL のため JOIN が成立せず、推定 rows が実態と乖離。

JOIN なし(テーブル単体の読み取り性能)

id  select_type  table  type   key       rows     Extra
1   SIMPLE       t      range  PRIMARY   1949     Using where; Using filesort

実行時間(profiling): 約 0.012 秒(1,949 行取得)

2. 期間集計(SC-010 / SC-016)— select_daily_data_group_by_day_and_hall_and_kishu

3 日 GROUP BY(JOIN なし)

type=range, key=PRIMARY, rows=1949, Extra=Using where; Using temporary

年跨ぎ 121 日(2024-12-01 〜 2025-03-31)

type=range, key=PRIMARY, rows=108053(推定), 実際の取得行数=108,053
Extra=Using where; Using temporary

最大 365 日(2025-03-17 〜 2026-03-16)

type=range, key=PRIMARY, rows=55480(推定), 実際の取得行数=303,586
Extra=Using where; Using temporary

3. DAY + hall クエリ — DailyDataRepository

WHERE DAY = '2026-03-16' AND hall = 'アイランド秋葉原':

type=ref, key=PRIMARY, rows=911, Extra=Using where

PRIMARY KEY (DAY, dainum, hall) の先頭列 DAY で絞り込み後、hall は 3 列目のためインデックス上では完全カバーされない。

コード影響範囲調査

DAY varchar → date 変換の影響

レイヤファイル影響
インポートDailyDataImportServiceDateUtil::normalize_date_for_import()YYYY/M/D 文字列を生成し INSERT
リポジトリDailyDataPerUnitRepository入力日付を DateFormatEnum::Y_N_J_SLASHY/n/j)に変換して WHERE DAY IN (...)
リポジトリDailyDataRepositoryWHERE DAY = %s AND hall = %sY/n/j または DB 依存の文字列)
ユーティリティDateUtil::normalize_date_for_importYYYYMMDDYYYY/M/DYYYY/M/D はそのまま
SQLinsert_or_update_batch.sqlDAY%s でバインド。date 型 DB では MySQL が暗黙変換
EntityDailyDataPerUnitEntityDAY を文字列として保持(DB からの読み取り値をそのまま利用)

変換時の作業:

  1. 本番スキーマ確認(varchardate か)
  2. 既存データの Y/n/jYYYY-MM-DD 正規化マイグレーション
  3. インポート・リポジトリの日付フォーマット統一(Y-m-d または date 型バインド)
  4. PHPUnit・結合テストの更新(RankingKishuDataTest 等で Y/n/j 前提のテストあり)

(DAY, hall) 複合インデックスの効果

クエリパターンSQL に hall 条件(DAY, hall) の効果
select_daily_data_per_unit(台別ランキング)なし(PHP フィルタ) — 3 日分全ホール取得のため
select_daily_data_group_by_*(SC-010/016)なし(PHP フィルタ) — 最大 366 日×全ホール取得
DailyDataRepository::get_daily_data_by_kishuあり(DAY = ? AND hall = ? — PRIMARY の DAY 先頭で部分利用、専用索引で hall 絞り込み改善の余地
select_count_by_day_and_titleオプション(hall IN (...)(hall 指定時のみ)
delete_by_date_and_hall / batch_delete_by_date_hall_pairsあり中〜高

結論: (DAY, hall) インデックス単体では SC-010/SC-016 のボトルネック(全ホール DB 転送)は解消しない。リポジトリ/SQL に hall 条件を追加するアプリ変更とセットで検討する価値がある。

Period 系サービスの全ホール取得(確認済み)

  • PeriodKishuSamaiRankingService::get_ranking()select_daily_data_group_by_day_and_hall_and_kishu_by_ymd($ymd_list) で全ホール取得後、if (!in_array($dto->hall, $halls, true)) で PHP フィルタ
  • PeriodSamaiSummaryService::get_summary() — 同上取得後、array_filter で hall・kishu フィルタ
  • DailyArticleResultServiceselect_daily_data_per_unit_by_ymd で 3 日分全ホール取得後、PHP で hall フィルタ

改善案の比較

概要年跨ぎ対応アプリ変更運用コストローカル調査所見
A: インデックス追加(DAY, hall) 複合インデックスそのまま不要(ただし効果限定)SC-010/016 には効果薄。DailyDataRepository 系には有効
A': SQL に hall 条件追加リポジトリに hall パラメータを追加そのまま必要(Interface・Service・SQL)転送量削減に最も効果的(1 ホール指定時)
B: MySQL パーティショニング年別 RANGE パーティションMySQL が自動横断不要低〜中DAY が date 型なら PARTITION BY RANGE (YEAR(DAY)) が適用可能。varchar 時は式パーティションが必要
C: 年別テーブル分割db_daily_data_{year}リポジトリで UNION必要(ルーティング)高(毎年作業)年跨ぎクエリの UNION コスト・アプリ複雑化が大きい

案 B のパーティション設計(DAY が date 型の場合)

sql
ALTER TABLE wp_db2023
PARTITION BY RANGE (YEAR(`DAY`)) (
  PARTITION p2022 VALUES LESS THAN (2023),
  PARTITION p2023 VALUES LESS THAN (2024),
  PARTITION p2024 VALUES LESS THAN (2025),
  PARTITION p2025 VALUES LESS THAN (2026),
  PARTITION p2026 VALUES LESS THAN (2027),
  PARTITION p_future VALUES LESS THAN MAXVALUE
);
  • WHERE DAY IN (...) は該当パーティションのみスキャン
  • 年跨ぎ期間(例: 2024-12 〜 2025-03)は複数パーティション横断だが、アプリ変更なし
  • 大テーブル ALTER は pt-online-schema-change 等のオンライン DDL を検討

案 B の制約(DAY が varchar の場合)

式ベースパーティションが必要:

sql
PARTITION BY RANGE (YEAR(STR_TO_DATE(`DAY`, '%Y/%c/%e')))
  • パーティション pruning が式に依存し、プランナの挙動が複雑化
  • DAY を date 型へ正規化してからパーティション適用が望ましい

推奨実装順序

現時点の推奨: 案 B(MySQL パーティショニング) を中長期の主軸とし、短期では 案 A'(SQL hall 条件追加) で SC-010/SC-016 の DB 転送量を削減する。案 C(年別テーブル分割)は運用コスト・アプリ変更量の観点から優先度を下げる。

DoD 計測基準との対照

docs/performance/README.md の目標値:

指標目標(2 ホール)本調査での観測
Query Count15 以下本調査対象外(別途 run-performance-test.sh で計測)
Execution Time0.05 秒以下3 日 select_daily_data_per_unit(JOIN なし): 約 0.012 秒 ✓
Memory Usage2 MB 以下本調査対象外
EXPLAIN rows 改善改善していること366 日 GROUP BY: 推定 55,480 / 実 303,586 行 — パーティション+hall 条件で削減見込み

次のアクション(実装 Issue 用)

  1. 本番: SHOW CREATE TABLE wp_db2023kishu_id NULL 件数を確認
  2. #2135 完了後: DailyDataPerUnitRepository に hall オプション引数を追加する設計 Issue を起票
  3. #2135 完了後: DAY 型統一 + RANGE パーティション追加のマイグレーション Issue を起票(pt-online-schema-change 手順含む)
  4. 計測: 変更前後で scripts/run-performance-test.sh および SC-010/016 の代表クエリで EXPLAIN・実行時間を比較

再現用 SQL(ローカル)

bash
# Local MySQL ソケット例(環境によりパスが異なる)
MYSQL_SOCK="/Users/a/Library/Application Support/Local/run/<site-id>/mysql/mysqld.sock"

# スキーマ・行数
mysql -uroot -proot -S "$MYSQL_SOCK" local -e "SHOW CREATE TABLE wp_db2023;"
mysql -uroot -proot -S "$MYSQL_SOCK" local -e "SELECT COUNT(*) FROM wp_db2023;"

# 3 日クエリ EXPLAIN
mysql -uroot -proot -S "$MYSQL_SOCK" local -e "
EXPLAIN SELECT t.\`DAY\`, t.kishu, t.dainum, t.kaiten, t.samai, t.BB, t.RB, t.hall
FROM wp_db2023 AS t
WHERE t.\`DAY\` IN ('2026-03-14','2026-03-15','2026-03-16')
ORDER BY t.dainum ASC, t.\`DAY\` ASC;
"

kishu_id バックフィル完了後は、JOIN 付きクエリ(select_daily_data_per_unit.sql 相当)でも EXPLAIN を再取得すること。

参照