Appearance
db2023 台別データ 読み取りパフォーマンス調査(Issue #2293)
参考 / 調査メモ: 2026-06 時点の性能調査記録です。現役の本番作業手順ではありません。現在のテーブル定義は カスタムアプリケーションテーブル と各 Installer を優先してください。
目的
wp_db2023(DatabaseTableConstants::DAILY_DATA)の読み取りパフォーマンスを調査し、インデックス見直し・DAY 型変換・MySQL パーティショニング・年別テーブル分割の各案を比較する。本ドキュメントは 調査/設計 の成果物であり、スキーマ変更の実装は Issue #2135(kishu_id バックフィル)完了後を推奨する。Issue #2341 の本番導入手順は db2023 RANGE パーティショニング本番導入手順 を参照。
現状: 本ドキュメント内の
kishu_idバックフィル記述は、2026-06 時点の調査メモです。機種マスタ移行に伴う本番バックフィルは完了済みであり、現状の本番運用で追加対応する前提ではありません。
調査環境
| 項目 | 値 |
|---|---|
| 環境 | Local(slotkouryaku.local) |
| DB | MySQL(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 記載(本番想定) | ローカル実測 |
|---|---|---|
DAY 型 | varchar(10)、Y/n/j 形式 | date 型、YYYY-MM-DD 格納 |
| 日付リテラル | '2025/1/10' 等 | '2026-03-16' 等(MySQL が date に解釈) |
注意(調査当時): Issue #2293 時点では本番が varchar の可能性があった。2026-07 再確認では本番も date 型であり、varchar → date の DDL 移行は不要。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 = 2) | select_daily_data_per_unit_by_ymd | WHERE DAY IN (3日) → PHP で hall フィルタ |
| 期間差枚サマリー(SC-010) | 最大 366 日 | select_daily_data_group_by_day_and_hall_and_kishu_by_ymd | WHERE 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;| 環境 | total | null_count | filled_count |
|---|---|---|---|
| ローカル(2026-06-12) | 1,439,926 | 1,439,926 | 0 |
調査時点のローカルでは 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 temporary3. DAY + hall クエリ — DailyDataRepository
WHERE DAY = '2026-03-16' AND hall = 'アイランド秋葉原':
type=ref, key=PRIMARY, rows=911, Extra=Using wherePRIMARY KEY (DAY, dainum, hall) の先頭列 DAY で絞り込み後、hall は 3 列目のためインデックス上では完全カバーされない。
コード影響範囲調査
DAY バインド形式(date 列)
進捗(Issue #2297): 本番・ローカルとも
DAYはdate型。アプリは Repository 境界でDateUtil::normalize_date_for_db()(Y-m-d)に統一(暗黙変換依存を解消)。varchar → date の DDL 移行は不要。
| レイヤ | ファイル | 影響 |
|---|---|---|
| インポート | DailyDataImportService | 行の DAY はアプリ内で Y/n/j のまま。INSERT 時に Repository が Y-m-d |
| リポジトリ | DailyDataPerUnitRepository | Y-m-d でバインド。Entity 読取は Y/n/j に正規化 |
| リポジトリ | DailyDataRepository | Y-m-d でバインド |
| ユーティリティ | DateUtil::normalize_date_for_import | アプリ内契約用。DB 用は normalize_date_for_db |
| Entity | DailyDataPerUnitEntity | アプリ契約は 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 フィルタDailyArticleResultService—select_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 Count | 15 以下 | 本調査対象外(別途 run-performance-test.sh で計測) |
| Execution Time | 0.05 秒以下 | 3 日 select_daily_data_per_unit(JOIN なし): 約 0.012 秒 ✓ |
| Memory Usage | 2 MB 以下 | 本調査対象外 |
| EXPLAIN rows 改善 | 改善していること | 366 日 GROUP BY: 推定 55,480 / 実 303,586 行 — パーティション+hall 条件で削減見込み |
次のアクション(実装 Issue 用)
- 本番:
SHOW CREATE TABLE wp_db2023とkishu_idNULL 件数を確認 - #2135 完了後:
DailyDataPerUnitRepositoryに hall オプション引数を追加する設計 Issue を起票 - #2135 完了後:
DAY型統一 + RANGE パーティション追加のマイグレーションを db2023 RANGE パーティショニング本番導入手順 に従って実施(Issue #2341) - 計測: 変更前後で
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 を再取得すること。
参照
- Issue #2293(本調査)
- Issue #2135(
kishu_idバックフィル) daily-data-per-unit-schema.mdDailyDataPerUnitRepositoryPeriodKishuSamaiRankingServicePeriodSamaiSummaryService