운영 서버에서 DB index 적용 작업 진행
- 01~07 작업 문서를 운영 측정값 기준으로 정리 (문서의 DB 비밀번호 삭제) - app_highest_record 중복 8,795행 점수 기준 정리, 인덱스 5개 + UNIQUE 키 2개 추가 - 02·04 공통 측정 스크립트와 측정 결과, comparison.txt 추가 - 서버 처리 시간 18~292배 단축, 결과 행 수 동일 - 운영 데이터 백업과 권한(비밀번호 해시) 파일은 커밋에서 제외 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
-- best_record: 랭킹용
|
||||
ALTER TABLE best_record
|
||||
ADD INDEX IF NOT EXISTS idx_maestro_app_dt (MaestroID, AppID, RecordDateTime),
|
||||
ALGORITHM=INPLACE, LOCK=NONE;
|
||||
|
||||
-- best_record: 히스토리/저장용
|
||||
ALTER TABLE best_record
|
||||
ADD INDEX IF NOT EXISTS idx_maestro_player_app_dt (MaestroID, PlayerID, AppID, RecordDateTime),
|
||||
ALGORITHM=INPLACE, LOCK=NONE;
|
||||
|
||||
-- typing_exam_record
|
||||
ALTER TABLE typing_exam_record
|
||||
ADD INDEX IF NOT EXISTS idx_maestro_writing_dt (MaestroID, WritingID, RecordDateTime),
|
||||
ALGORITHM=INPLACE, LOCK=NONE;
|
||||
|
||||
ALTER TABLE typing_exam_record
|
||||
ADD INDEX IF NOT EXISTS idx_maestro_player_writing_dt (MaestroID, PlayerID, WritingID, RecordDateTime),
|
||||
ALGORITHM=INPLACE, LOCK=NONE;
|
||||
|
||||
-- license_score
|
||||
ALTER TABLE license_score
|
||||
ADD INDEX IF NOT EXISTS idx_maestro_player_dt (MaestroID, PlayerID, ScoreDateTime),
|
||||
ALGORITHM=INPLACE, LOCK=NONE;
|
||||
|
||||
-- app_highest_record: UNIQUE 키
|
||||
ALTER TABLE app_highest_record
|
||||
ADD UNIQUE KEY IF NOT EXISTS uk_maestro_player_app (MaestroID, PlayerID, AppID),
|
||||
ALGORITHM=INPLACE, LOCK=NONE;
|
||||
|
||||
-- typing_exam_highest_record: UNIQUE 키
|
||||
ALTER TABLE typing_exam_highest_record
|
||||
ADD UNIQUE KEY IF NOT EXISTS uk_maestro_player_writing (MaestroID, PlayerID, WritingID),
|
||||
ALGORITHM=INPLACE, LOCK=NONE;
|
||||
@@ -0,0 +1,8 @@
|
||||
TABLE_NAME INDEX_NAME NON_UNIQUE cols
|
||||
app_highest_record uk_maestro_player_app 0 MaestroID,PlayerID,AppID
|
||||
best_record idx_maestro_app_dt 1 MaestroID,AppID,RecordDateTime
|
||||
best_record idx_maestro_player_app_dt 1 MaestroID,PlayerID,AppID,RecordDateTime
|
||||
license_score idx_maestro_player_dt 1 MaestroID,PlayerID,ScoreDateTime
|
||||
typing_exam_highest_record uk_maestro_player_writing 0 MaestroID,PlayerID,WritingID
|
||||
typing_exam_record idx_maestro_player_writing_dt 1 MaestroID,PlayerID,WritingID,RecordDateTime
|
||||
typing_exam_record idx_maestro_writing_dt 1 MaestroID,WritingID,RecordDateTime
|
||||
@@ -0,0 +1,64 @@
|
||||
--------------
|
||||
ALTER TABLE best_record
|
||||
ADD INDEX IF NOT EXISTS idx_maestro_app_dt (MaestroID, AppID, RecordDateTime),
|
||||
ALGORITHM=INPLACE, LOCK=NONE
|
||||
--------------
|
||||
|
||||
Query OK, 0 rows affected (4.803 sec)
|
||||
Records: 0 Duplicates: 0 Warnings: 0
|
||||
|
||||
--------------
|
||||
ALTER TABLE best_record
|
||||
ADD INDEX IF NOT EXISTS idx_maestro_player_app_dt (MaestroID, PlayerID, AppID, RecordDateTime),
|
||||
ALGORITHM=INPLACE, LOCK=NONE
|
||||
--------------
|
||||
|
||||
Query OK, 0 rows affected (4.832 sec)
|
||||
Records: 0 Duplicates: 0 Warnings: 0
|
||||
|
||||
--------------
|
||||
ALTER TABLE typing_exam_record
|
||||
ADD INDEX IF NOT EXISTS idx_maestro_writing_dt (MaestroID, WritingID, RecordDateTime),
|
||||
ALGORITHM=INPLACE, LOCK=NONE
|
||||
--------------
|
||||
|
||||
Query OK, 0 rows affected (0.735 sec)
|
||||
Records: 0 Duplicates: 0 Warnings: 0
|
||||
|
||||
--------------
|
||||
ALTER TABLE typing_exam_record
|
||||
ADD INDEX IF NOT EXISTS idx_maestro_player_writing_dt (MaestroID, PlayerID, WritingID, RecordDateTime),
|
||||
ALGORITHM=INPLACE, LOCK=NONE
|
||||
--------------
|
||||
|
||||
Query OK, 0 rows affected (0.370 sec)
|
||||
Records: 0 Duplicates: 0 Warnings: 0
|
||||
|
||||
--------------
|
||||
ALTER TABLE license_score
|
||||
ADD INDEX IF NOT EXISTS idx_maestro_player_dt (MaestroID, PlayerID, ScoreDateTime),
|
||||
ALGORITHM=INPLACE, LOCK=NONE
|
||||
--------------
|
||||
|
||||
Query OK, 0 rows affected (0.042 sec)
|
||||
Records: 0 Duplicates: 0 Warnings: 0
|
||||
|
||||
--------------
|
||||
ALTER TABLE app_highest_record
|
||||
ADD UNIQUE KEY IF NOT EXISTS uk_maestro_player_app (MaestroID, PlayerID, AppID),
|
||||
ALGORITHM=INPLACE, LOCK=NONE
|
||||
--------------
|
||||
|
||||
Query OK, 0 rows affected (1.076 sec)
|
||||
Records: 0 Duplicates: 0 Warnings: 0
|
||||
|
||||
--------------
|
||||
ALTER TABLE typing_exam_highest_record
|
||||
ADD UNIQUE KEY IF NOT EXISTS uk_maestro_player_writing (MaestroID, PlayerID, WritingID),
|
||||
ALGORITHM=INPLACE, LOCK=NONE
|
||||
--------------
|
||||
|
||||
Query OK, 0 rows affected (0.156 sec)
|
||||
Records: 0 Duplicates: 0 Warnings: 0
|
||||
|
||||
Bye
|
||||
@@ -0,0 +1,308 @@
|
||||
=== q1_hour_func ===
|
||||
{
|
||||
"query_optimization": {
|
||||
"r_total_time_ms": 0.303570412
|
||||
},
|
||||
"query_block": {
|
||||
"select_id": 1,
|
||||
"r_loops": 1,
|
||||
"r_total_time_ms": 2.069071756,
|
||||
"nested_loop": [
|
||||
{
|
||||
"read_sorted_file": {
|
||||
"r_rows": 22,
|
||||
"filesort": {
|
||||
"sort_key": "best_record.RecordDateTime, best_record.PlayerID",
|
||||
"r_loops": 1,
|
||||
"r_total_time_ms": 2.050037968,
|
||||
"r_used_priority_queue": false,
|
||||
"r_output_rows": 22,
|
||||
"r_buffer_size": "2047Kb",
|
||||
"r_sort_mode": "sort_key,addon_fields",
|
||||
"table": {
|
||||
"table_name": "best_record",
|
||||
"access_type": "ref",
|
||||
"possible_keys": [
|
||||
"MaestroID",
|
||||
"AppID",
|
||||
"idx_maestro_app_dt",
|
||||
"idx_maestro_player_app_dt"
|
||||
],
|
||||
"key": "idx_maestro_app_dt",
|
||||
"key_length": "8",
|
||||
"used_key_parts": ["MaestroID", "AppID"],
|
||||
"ref": ["const", "const"],
|
||||
"r_loops": 1,
|
||||
"rows": 5851,
|
||||
"r_rows": 22,
|
||||
"r_table_time_ms": 2.024712928,
|
||||
"r_other_time_ms": 0.015343053,
|
||||
"r_engine_stats": {
|
||||
"pages_accessed": 77
|
||||
},
|
||||
"filtered": 100,
|
||||
"r_filtered": 100,
|
||||
"index_condition": "cast(best_record.RecordDateTime as date) = @`day` and hour(best_record.RecordDateTime) = 9",
|
||||
"attached_condition": "best_record.MaestroID <=> @maestro and best_record.AppID <=> @app"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
=== q2_hour_range ===
|
||||
{
|
||||
"query_optimization": {
|
||||
"r_total_time_ms": 0.23481673
|
||||
},
|
||||
"query_block": {
|
||||
"select_id": 1,
|
||||
"r_loops": 1,
|
||||
"r_total_time_ms": 0.259231588,
|
||||
"nested_loop": [
|
||||
{
|
||||
"read_sorted_file": {
|
||||
"r_rows": 22,
|
||||
"filesort": {
|
||||
"sort_key": "best_record.RecordDateTime, best_record.PlayerID",
|
||||
"r_loops": 1,
|
||||
"r_total_time_ms": 0.241578075,
|
||||
"r_used_priority_queue": false,
|
||||
"r_output_rows": 22,
|
||||
"r_buffer_size": "2047Kb",
|
||||
"r_sort_mode": "sort_key,addon_fields",
|
||||
"table": {
|
||||
"table_name": "best_record",
|
||||
"access_type": "range",
|
||||
"possible_keys": [
|
||||
"MaestroID",
|
||||
"AppID",
|
||||
"idx_maestro_app_dt",
|
||||
"idx_maestro_player_app_dt"
|
||||
],
|
||||
"key": "idx_maestro_app_dt",
|
||||
"key_length": "13",
|
||||
"used_key_parts": ["MaestroID", "AppID", "RecordDateTime"],
|
||||
"r_loops": 1,
|
||||
"rows": 22,
|
||||
"r_rows": 22,
|
||||
"r_table_time_ms": 0.143388535,
|
||||
"r_other_time_ms": 0.086477209,
|
||||
"r_engine_stats": {
|
||||
"pages_accessed": 69
|
||||
},
|
||||
"filtered": 100,
|
||||
"r_filtered": 100,
|
||||
"index_condition": "best_record.MaestroID = @maestro and best_record.AppID = @app and best_record.RecordDateTime >= '2026-08-26 09:00:00' and best_record.RecordDateTime < '2026-08-26 10:00:00'"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
=== q3_day_func ===
|
||||
{
|
||||
"query_optimization": {
|
||||
"r_total_time_ms": 0.260151772
|
||||
},
|
||||
"query_block": {
|
||||
"select_id": 1,
|
||||
"r_loops": 1,
|
||||
"r_total_time_ms": 2.440415655,
|
||||
"filesort": {
|
||||
"sort_key": "max(best_record.BestRecord) desc, best_record.PlayerID",
|
||||
"r_loops": 1,
|
||||
"r_total_time_ms": 0.019813943,
|
||||
"r_used_priority_queue": false,
|
||||
"r_output_rows": 61,
|
||||
"r_buffer_size": "1Kb",
|
||||
"r_sort_mode": "sort_key,rowid",
|
||||
"temporary_table": {
|
||||
"nested_loop": [
|
||||
{
|
||||
"table": {
|
||||
"table_name": "best_record",
|
||||
"access_type": "ref",
|
||||
"possible_keys": [
|
||||
"MaestroID",
|
||||
"AppID",
|
||||
"idx_maestro_app_dt",
|
||||
"idx_maestro_player_app_dt"
|
||||
],
|
||||
"key": "idx_maestro_app_dt",
|
||||
"key_length": "8",
|
||||
"used_key_parts": ["MaestroID", "AppID"],
|
||||
"ref": ["const", "const"],
|
||||
"r_loops": 1,
|
||||
"rows": 5851,
|
||||
"r_rows": 99,
|
||||
"r_table_time_ms": 2.331223925,
|
||||
"r_other_time_ms": 0.071694268,
|
||||
"r_engine_stats": {
|
||||
"pages_accessed": 308
|
||||
},
|
||||
"filtered": 100,
|
||||
"r_filtered": 100,
|
||||
"index_condition": "year(best_record.RecordDateTime) = 2026 and month(best_record.RecordDateTime) = 8 and dayofmonth(best_record.RecordDateTime) = 26",
|
||||
"attached_condition": "best_record.MaestroID <=> @maestro and best_record.AppID <=> @app"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
=== q4_day_range ===
|
||||
{
|
||||
"query_optimization": {
|
||||
"r_total_time_ms": 0.335646795
|
||||
},
|
||||
"query_block": {
|
||||
"select_id": 1,
|
||||
"r_loops": 1,
|
||||
"r_total_time_ms": 0.467062948,
|
||||
"filesort": {
|
||||
"sort_key": "max(best_record.BestRecord) desc, best_record.PlayerID",
|
||||
"r_loops": 1,
|
||||
"r_total_time_ms": 0.017993581,
|
||||
"r_used_priority_queue": false,
|
||||
"r_output_rows": 61,
|
||||
"r_buffer_size": "1Kb",
|
||||
"r_sort_mode": "sort_key,rowid",
|
||||
"temporary_table": {
|
||||
"nested_loop": [
|
||||
{
|
||||
"table": {
|
||||
"table_name": "best_record",
|
||||
"access_type": "range",
|
||||
"possible_keys": [
|
||||
"MaestroID",
|
||||
"AppID",
|
||||
"idx_maestro_app_dt",
|
||||
"idx_maestro_player_app_dt"
|
||||
],
|
||||
"key": "idx_maestro_app_dt",
|
||||
"key_length": "13",
|
||||
"used_key_parts": ["MaestroID", "AppID", "RecordDateTime"],
|
||||
"r_loops": 1,
|
||||
"rows": 99,
|
||||
"r_rows": 99,
|
||||
"r_table_time_ms": 0.318203324,
|
||||
"r_other_time_ms": 0.112702428,
|
||||
"r_engine_stats": {
|
||||
"pages_accessed": 300
|
||||
},
|
||||
"filtered": 100,
|
||||
"r_filtered": 100,
|
||||
"index_condition": "best_record.MaestroID = @maestro and best_record.AppID = @app and best_record.RecordDateTime >= '2026-08-26 00:00:00' and best_record.RecordDateTime < '2026-08-27 00:00:00'"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
=== q5_month_func ===
|
||||
{
|
||||
"query_optimization": {
|
||||
"r_total_time_ms": 0.248649482
|
||||
},
|
||||
"query_block": {
|
||||
"select_id": 1,
|
||||
"r_loops": 1,
|
||||
"r_total_time_ms": 4.299725667,
|
||||
"filesort": {
|
||||
"sort_key": "max(best_record.BestRecord) desc, best_record.PlayerID",
|
||||
"r_loops": 1,
|
||||
"r_total_time_ms": 0.068003533,
|
||||
"r_used_priority_queue": false,
|
||||
"r_output_rows": 262,
|
||||
"r_buffer_size": "6Kb",
|
||||
"r_sort_mode": "sort_key,rowid",
|
||||
"temporary_table": {
|
||||
"nested_loop": [
|
||||
{
|
||||
"table": {
|
||||
"table_name": "best_record",
|
||||
"access_type": "ref",
|
||||
"possible_keys": [
|
||||
"MaestroID",
|
||||
"AppID",
|
||||
"idx_maestro_app_dt",
|
||||
"idx_maestro_player_app_dt"
|
||||
],
|
||||
"key": "idx_maestro_app_dt",
|
||||
"key_length": "8",
|
||||
"used_key_parts": ["MaestroID", "AppID"],
|
||||
"ref": ["const", "const"],
|
||||
"r_loops": 1,
|
||||
"rows": 5851,
|
||||
"r_rows": 709,
|
||||
"r_table_time_ms": 3.78789381,
|
||||
"r_other_time_ms": 0.408851363,
|
||||
"r_engine_stats": {
|
||||
"pages_accessed": 2138
|
||||
},
|
||||
"filtered": 100,
|
||||
"r_filtered": 100,
|
||||
"index_condition": "year(best_record.RecordDateTime) = 2026 and month(best_record.RecordDateTime) = 8",
|
||||
"attached_condition": "best_record.MaestroID <=> @maestro and best_record.AppID <=> @app"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
=== q6_month_range ===
|
||||
{
|
||||
"query_optimization": {
|
||||
"r_total_time_ms": 0.306621019
|
||||
},
|
||||
"query_block": {
|
||||
"select_id": 1,
|
||||
"r_loops": 1,
|
||||
"r_total_time_ms": 2.496056728,
|
||||
"filesort": {
|
||||
"sort_key": "max(best_record.BestRecord) desc, best_record.PlayerID",
|
||||
"r_loops": 1,
|
||||
"r_total_time_ms": 0.065773089,
|
||||
"r_used_priority_queue": false,
|
||||
"r_output_rows": 262,
|
||||
"r_buffer_size": "6Kb",
|
||||
"r_sort_mode": "sort_key,rowid",
|
||||
"temporary_table": {
|
||||
"nested_loop": [
|
||||
{
|
||||
"table": {
|
||||
"table_name": "best_record",
|
||||
"access_type": "range",
|
||||
"possible_keys": [
|
||||
"MaestroID",
|
||||
"AppID",
|
||||
"idx_maestro_app_dt",
|
||||
"idx_maestro_player_app_dt"
|
||||
],
|
||||
"key": "idx_maestro_app_dt",
|
||||
"key_length": "13",
|
||||
"used_key_parts": ["MaestroID", "AppID", "RecordDateTime"],
|
||||
"r_loops": 1,
|
||||
"rows": 709,
|
||||
"r_rows": 709,
|
||||
"r_table_time_ms": 2.033224622,
|
||||
"r_other_time_ms": 0.360211684,
|
||||
"r_engine_stats": {
|
||||
"pages_accessed": 2131
|
||||
},
|
||||
"filtered": 0.057728633,
|
||||
"r_filtered": 100,
|
||||
"index_condition": "best_record.MaestroID = @maestro and best_record.AppID = @app and best_record.RecordDateTime >= '2026-08-01 00:00:00' and best_record.RecordDateTime < '2026-09-01 00:00:00'"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"query_block": {
|
||||
"select_id": 1,
|
||||
"nested_loop": [
|
||||
{
|
||||
"read_sorted_file": {
|
||||
"filesort": {
|
||||
"sort_key": "best_record.RecordDateTime, best_record.PlayerID",
|
||||
"table": {
|
||||
"table_name": "best_record",
|
||||
"access_type": "ref",
|
||||
"possible_keys": [
|
||||
"MaestroID",
|
||||
"AppID",
|
||||
"idx_maestro_app_dt",
|
||||
"idx_maestro_player_app_dt"
|
||||
],
|
||||
"key": "idx_maestro_app_dt",
|
||||
"key_length": "8",
|
||||
"used_key_parts": ["MaestroID", "AppID"],
|
||||
"ref": ["const", "const"],
|
||||
"rows": 5851,
|
||||
"filtered": 100,
|
||||
"index_condition": "cast(best_record.RecordDateTime as date) = @`day` and hour(best_record.RecordDateTime) = 9",
|
||||
"attached_condition": "best_record.MaestroID <=> @maestro and best_record.AppID <=> @app"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"query_block": {
|
||||
"select_id": 1,
|
||||
"nested_loop": [
|
||||
{
|
||||
"read_sorted_file": {
|
||||
"filesort": {
|
||||
"sort_key": "best_record.RecordDateTime, best_record.PlayerID",
|
||||
"table": {
|
||||
"table_name": "best_record",
|
||||
"access_type": "range",
|
||||
"possible_keys": [
|
||||
"MaestroID",
|
||||
"AppID",
|
||||
"idx_maestro_app_dt",
|
||||
"idx_maestro_player_app_dt"
|
||||
],
|
||||
"key": "idx_maestro_app_dt",
|
||||
"key_length": "13",
|
||||
"used_key_parts": ["MaestroID", "AppID", "RecordDateTime"],
|
||||
"rows": 22,
|
||||
"filtered": 100,
|
||||
"index_condition": "best_record.MaestroID = @maestro and best_record.AppID = @app and best_record.RecordDateTime >= <cache>(cast(@`day` as datetime) + interval @`hour` hour) and best_record.RecordDateTime < <cache>(cast(@`day` as datetime) + interval @`hour` + 1 hour)"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"query_block": {
|
||||
"select_id": 1,
|
||||
"filesort": {
|
||||
"sort_key": "max(best_record.BestRecord) desc, best_record.PlayerID",
|
||||
"temporary_table": {
|
||||
"nested_loop": [
|
||||
{
|
||||
"table": {
|
||||
"table_name": "best_record",
|
||||
"access_type": "ref",
|
||||
"possible_keys": [
|
||||
"MaestroID",
|
||||
"AppID",
|
||||
"idx_maestro_app_dt",
|
||||
"idx_maestro_player_app_dt"
|
||||
],
|
||||
"key": "idx_maestro_app_dt",
|
||||
"key_length": "8",
|
||||
"used_key_parts": ["MaestroID", "AppID"],
|
||||
"ref": ["const", "const"],
|
||||
"rows": 5851,
|
||||
"filtered": 100,
|
||||
"index_condition": "year(best_record.RecordDateTime) = 2026 and month(best_record.RecordDateTime) = 8 and dayofmonth(best_record.RecordDateTime) = 26",
|
||||
"attached_condition": "best_record.MaestroID <=> @maestro and best_record.AppID <=> @app"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"query_block": {
|
||||
"select_id": 1,
|
||||
"filesort": {
|
||||
"sort_key": "max(best_record.BestRecord) desc, best_record.PlayerID",
|
||||
"temporary_table": {
|
||||
"nested_loop": [
|
||||
{
|
||||
"table": {
|
||||
"table_name": "best_record",
|
||||
"access_type": "range",
|
||||
"possible_keys": [
|
||||
"MaestroID",
|
||||
"AppID",
|
||||
"idx_maestro_app_dt",
|
||||
"idx_maestro_player_app_dt"
|
||||
],
|
||||
"key": "idx_maestro_app_dt",
|
||||
"key_length": "13",
|
||||
"used_key_parts": ["MaestroID", "AppID", "RecordDateTime"],
|
||||
"rows": 99,
|
||||
"filtered": 100,
|
||||
"index_condition": "best_record.MaestroID = @maestro and best_record.AppID = @app and best_record.RecordDateTime >= <cache>(cast(@`day` as datetime)) and best_record.RecordDateTime < <cache>(cast(@`day` as datetime) + interval 1 day)"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"query_block": {
|
||||
"select_id": 1,
|
||||
"filesort": {
|
||||
"sort_key": "max(best_record.BestRecord) desc, best_record.PlayerID",
|
||||
"temporary_table": {
|
||||
"nested_loop": [
|
||||
{
|
||||
"table": {
|
||||
"table_name": "best_record",
|
||||
"access_type": "ref",
|
||||
"possible_keys": [
|
||||
"MaestroID",
|
||||
"AppID",
|
||||
"idx_maestro_app_dt",
|
||||
"idx_maestro_player_app_dt"
|
||||
],
|
||||
"key": "idx_maestro_app_dt",
|
||||
"key_length": "8",
|
||||
"used_key_parts": ["MaestroID", "AppID"],
|
||||
"ref": ["const", "const"],
|
||||
"rows": 5851,
|
||||
"filtered": 100,
|
||||
"index_condition": "year(best_record.RecordDateTime) = 2026 and month(best_record.RecordDateTime) = 8",
|
||||
"attached_condition": "best_record.MaestroID <=> @maestro and best_record.AppID <=> @app"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"query_block": {
|
||||
"select_id": 1,
|
||||
"filesort": {
|
||||
"sort_key": "max(best_record.BestRecord) desc, best_record.PlayerID",
|
||||
"temporary_table": {
|
||||
"nested_loop": [
|
||||
{
|
||||
"table": {
|
||||
"table_name": "best_record",
|
||||
"access_type": "range",
|
||||
"possible_keys": [
|
||||
"MaestroID",
|
||||
"AppID",
|
||||
"idx_maestro_app_dt",
|
||||
"idx_maestro_player_app_dt"
|
||||
],
|
||||
"key": "idx_maestro_app_dt",
|
||||
"key_length": "13",
|
||||
"used_key_parts": ["MaestroID", "AppID", "RecordDateTime"],
|
||||
"rows": 709,
|
||||
"filtered": 0.057728633,
|
||||
"index_condition": "best_record.MaestroID = @maestro and best_record.AppID = @app and best_record.RecordDateTime >= <cache>(cast(date_format(@`day`,'%Y-%m-01') as datetime)) and best_record.RecordDateTime < <cache>(cast(date_format(@`day`,'%Y-%m-01') as datetime) + interval 1 month)"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,6 @@
|
||||
start_time query_time rows_examined sql_text
|
||||
2026-09-15 23:09:13.487911 00:00:04.819872 0 ALTER TABLE best_record \nADD INDEX IF NOT EXISTS idx_maestro_player_app_dt (MaestroID, PlayerID, AppID, RecordDateTime),
|
||||
2026-09-15 23:09:08.684811 00:00:04.790929 0 ALTER TABLE best_record \nADD INDEX IF NOT EXISTS idx_maestro_app_dt (MaestroID, AppID, RecordDateTime),\nALGORITHM=INPLAC
|
||||
2026-09-15 23:09:19.466802 00:00:01.063310 0 ALTER TABLE app_highest_record \nADD UNIQUE KEY IF NOT EXISTS uk_maestro_player_app (MaestroID, PlayerID, AppID),\nALGORIT
|
||||
2026-09-15 23:08:21.126639 00:00:00.929698 935362 DELETE AHR FROM app_highest_record AHR\nJOIN (\n SELECT AppHighestRecordID FROM (\n SELECT AppHighestRecordID,\n
|
||||
2026-09-15 23:09:18.319470 00:00:00.723314 0 ALTER TABLE typing_exam_record \nADD INDEX IF NOT EXISTS idx_maestro_writing_dt (MaestroID, WritingID, RecordDateTime),\nA
|
||||
@@ -0,0 +1,12 @@
|
||||
baseline q1_hour_func server_time_ms=74.61373851
|
||||
baseline q2_hour_range server_time_ms=75.58458171
|
||||
baseline q3_day_func server_time_ms=75.28739257
|
||||
baseline q4_day_range server_time_ms=75.24298373
|
||||
baseline q5_month_func server_time_ms=76.26824776
|
||||
baseline q6_month_range server_time_ms=79.70009072
|
||||
after q1_hour_func server_time_ms=2.069071756
|
||||
after q2_hour_range server_time_ms=0.259231588
|
||||
after q3_day_func server_time_ms=2.440415655
|
||||
after q4_day_range server_time_ms=0.467062948
|
||||
after q5_month_func server_time_ms=4.299725667
|
||||
after q6_month_range server_time_ms=2.496056728
|
||||
@@ -0,0 +1,356 @@
|
||||
=== q1_hour_func ===
|
||||
{
|
||||
"query_optimization": {
|
||||
"r_total_time_ms": 0.175154857
|
||||
},
|
||||
"query_block": {
|
||||
"select_id": 1,
|
||||
"r_loops": 1,
|
||||
"r_total_time_ms": 74.61373851,
|
||||
"nested_loop": [
|
||||
{
|
||||
"read_sorted_file": {
|
||||
"r_rows": 22,
|
||||
"filesort": {
|
||||
"sort_key": "best_record.RecordDateTime, best_record.PlayerID",
|
||||
"r_loops": 1,
|
||||
"r_total_time_ms": 74.59065391,
|
||||
"r_used_priority_queue": false,
|
||||
"r_output_rows": 22,
|
||||
"r_buffer_size": "2047Kb",
|
||||
"r_sort_mode": "sort_key,addon_fields",
|
||||
"table": {
|
||||
"table_name": "best_record",
|
||||
"access_type": "index_merge",
|
||||
"possible_keys": ["MaestroID", "AppID"],
|
||||
"key_length": "4,4",
|
||||
"index_merge": {
|
||||
"intersect": [
|
||||
{
|
||||
"range": {
|
||||
"key": "MaestroID",
|
||||
"used_key_parts": ["MaestroID"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"range": {
|
||||
"key": "AppID",
|
||||
"used_key_parts": ["AppID"]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"r_loops": 1,
|
||||
"rows": 10103,
|
||||
"r_rows": 5851,
|
||||
"r_table_time_ms": 15.56706792,
|
||||
"r_other_time_ms": 52.50187814,
|
||||
"r_engine_stats": {
|
||||
"pages_accessed": 17780
|
||||
},
|
||||
"filtered": 100,
|
||||
"r_filtered": 0.376004102,
|
||||
"attached_condition": "best_record.MaestroID = @maestro and best_record.AppID = @app and cast(best_record.RecordDateTime as date) = @`day` and hour(best_record.RecordDateTime) = 9"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
=== q2_hour_range ===
|
||||
{
|
||||
"query_optimization": {
|
||||
"r_total_time_ms": 0.183126443
|
||||
},
|
||||
"query_block": {
|
||||
"select_id": 1,
|
||||
"r_loops": 1,
|
||||
"r_total_time_ms": 75.58458171,
|
||||
"nested_loop": [
|
||||
{
|
||||
"read_sorted_file": {
|
||||
"r_rows": 22,
|
||||
"filesort": {
|
||||
"sort_key": "best_record.RecordDateTime, best_record.PlayerID",
|
||||
"r_loops": 1,
|
||||
"r_total_time_ms": 75.56186719,
|
||||
"r_used_priority_queue": false,
|
||||
"r_output_rows": 22,
|
||||
"r_buffer_size": "2047Kb",
|
||||
"r_sort_mode": "sort_key,addon_fields",
|
||||
"table": {
|
||||
"table_name": "best_record",
|
||||
"access_type": "index_merge",
|
||||
"possible_keys": ["MaestroID", "AppID"],
|
||||
"key_length": "4,4",
|
||||
"index_merge": {
|
||||
"intersect": [
|
||||
{
|
||||
"range": {
|
||||
"key": "MaestroID",
|
||||
"used_key_parts": ["MaestroID"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"range": {
|
||||
"key": "AppID",
|
||||
"used_key_parts": ["AppID"]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"r_loops": 1,
|
||||
"rows": 10103,
|
||||
"r_rows": 5851,
|
||||
"r_table_time_ms": 15.67992038,
|
||||
"r_other_time_ms": 53.82031051,
|
||||
"r_engine_stats": {
|
||||
"pages_accessed": 17780
|
||||
},
|
||||
"filtered": 100,
|
||||
"r_filtered": 0.376004102,
|
||||
"attached_condition": "best_record.MaestroID = @maestro and best_record.AppID = @app and best_record.RecordDateTime >= '2026-08-26 09:00:00' and best_record.RecordDateTime < '2026-08-26 10:00:00'"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
=== q3_day_func ===
|
||||
{
|
||||
"query_optimization": {
|
||||
"r_total_time_ms": 0.205960987
|
||||
},
|
||||
"query_block": {
|
||||
"select_id": 1,
|
||||
"r_loops": 1,
|
||||
"r_total_time_ms": 75.28739257,
|
||||
"filesort": {
|
||||
"sort_key": "max(best_record.BestRecord) desc, best_record.PlayerID",
|
||||
"r_loops": 1,
|
||||
"r_total_time_ms": 0.028745721,
|
||||
"r_used_priority_queue": false,
|
||||
"r_output_rows": 61,
|
||||
"r_buffer_size": "1Kb",
|
||||
"r_sort_mode": "sort_key,rowid",
|
||||
"temporary_table": {
|
||||
"nested_loop": [
|
||||
{
|
||||
"table": {
|
||||
"table_name": "best_record",
|
||||
"access_type": "index_merge",
|
||||
"possible_keys": ["MaestroID", "AppID"],
|
||||
"key_length": "4,4",
|
||||
"index_merge": {
|
||||
"intersect": [
|
||||
{
|
||||
"range": {
|
||||
"key": "MaestroID",
|
||||
"used_key_parts": ["MaestroID"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"range": {
|
||||
"key": "AppID",
|
||||
"used_key_parts": ["AppID"]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"r_loops": 1,
|
||||
"rows": 10103,
|
||||
"r_rows": 5851,
|
||||
"r_table_time_ms": 15.68132066,
|
||||
"r_other_time_ms": 53.54159505,
|
||||
"r_engine_stats": {
|
||||
"pages_accessed": 17780
|
||||
},
|
||||
"filtered": 100,
|
||||
"r_filtered": 1.692018458,
|
||||
"attached_condition": "best_record.MaestroID = @maestro and best_record.AppID = @app and year(best_record.RecordDateTime) = 2026 and month(best_record.RecordDateTime) = 8 and dayofmonth(best_record.RecordDateTime) = 26"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
=== q4_day_range ===
|
||||
{
|
||||
"query_optimization": {
|
||||
"r_total_time_ms": 0.200909982
|
||||
},
|
||||
"query_block": {
|
||||
"select_id": 1,
|
||||
"r_loops": 1,
|
||||
"r_total_time_ms": 75.24298373,
|
||||
"filesort": {
|
||||
"sort_key": "max(best_record.BestRecord) desc, best_record.PlayerID",
|
||||
"r_loops": 1,
|
||||
"r_total_time_ms": 0.023154608,
|
||||
"r_used_priority_queue": false,
|
||||
"r_output_rows": 61,
|
||||
"r_buffer_size": "1Kb",
|
||||
"r_sort_mode": "sort_key,rowid",
|
||||
"temporary_table": {
|
||||
"nested_loop": [
|
||||
{
|
||||
"table": {
|
||||
"table_name": "best_record",
|
||||
"access_type": "index_merge",
|
||||
"possible_keys": ["MaestroID", "AppID"],
|
||||
"key_length": "4,4",
|
||||
"index_merge": {
|
||||
"intersect": [
|
||||
{
|
||||
"range": {
|
||||
"key": "MaestroID",
|
||||
"used_key_parts": ["MaestroID"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"range": {
|
||||
"key": "AppID",
|
||||
"used_key_parts": ["AppID"]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"r_loops": 1,
|
||||
"rows": 10103,
|
||||
"r_rows": 5851,
|
||||
"r_table_time_ms": 15.79226274,
|
||||
"r_other_time_ms": 52.7619799,
|
||||
"r_engine_stats": {
|
||||
"pages_accessed": 17780
|
||||
},
|
||||
"filtered": 100,
|
||||
"r_filtered": 1.692018458,
|
||||
"attached_condition": "best_record.MaestroID = @maestro and best_record.AppID = @app and best_record.RecordDateTime >= '2026-08-26 00:00:00' and best_record.RecordDateTime < '2026-08-27 00:00:00'"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
=== q5_month_func ===
|
||||
{
|
||||
"query_optimization": {
|
||||
"r_total_time_ms": 0.213092406
|
||||
},
|
||||
"query_block": {
|
||||
"select_id": 1,
|
||||
"r_loops": 1,
|
||||
"r_total_time_ms": 76.26824776,
|
||||
"filesort": {
|
||||
"sort_key": "max(best_record.BestRecord) desc, best_record.PlayerID",
|
||||
"r_loops": 1,
|
||||
"r_total_time_ms": 0.069783887,
|
||||
"r_used_priority_queue": false,
|
||||
"r_output_rows": 262,
|
||||
"r_buffer_size": "6Kb",
|
||||
"r_sort_mode": "sort_key,rowid",
|
||||
"temporary_table": {
|
||||
"nested_loop": [
|
||||
{
|
||||
"table": {
|
||||
"table_name": "best_record",
|
||||
"access_type": "index_merge",
|
||||
"possible_keys": ["MaestroID", "AppID"],
|
||||
"key_length": "4,4",
|
||||
"index_merge": {
|
||||
"intersect": [
|
||||
{
|
||||
"range": {
|
||||
"key": "MaestroID",
|
||||
"used_key_parts": ["MaestroID"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"range": {
|
||||
"key": "AppID",
|
||||
"used_key_parts": ["AppID"]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"r_loops": 1,
|
||||
"rows": 10103,
|
||||
"r_rows": 5851,
|
||||
"r_table_time_ms": 16.3543846,
|
||||
"r_other_time_ms": 53.27444188,
|
||||
"r_engine_stats": {
|
||||
"pages_accessed": 17780
|
||||
},
|
||||
"filtered": 100,
|
||||
"r_filtered": 12.11758674,
|
||||
"attached_condition": "best_record.MaestroID = @maestro and best_record.AppID = @app and year(best_record.RecordDateTime) = 2026 and month(best_record.RecordDateTime) = 8"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
=== q6_month_range ===
|
||||
{
|
||||
"query_optimization": {
|
||||
"r_total_time_ms": 0.215072801
|
||||
},
|
||||
"query_block": {
|
||||
"select_id": 1,
|
||||
"r_loops": 1,
|
||||
"r_total_time_ms": 79.70009072,
|
||||
"filesort": {
|
||||
"sort_key": "max(best_record.BestRecord) desc, best_record.PlayerID",
|
||||
"r_loops": 1,
|
||||
"r_total_time_ms": 0.072174363,
|
||||
"r_used_priority_queue": false,
|
||||
"r_output_rows": 262,
|
||||
"r_buffer_size": "6Kb",
|
||||
"r_sort_mode": "sort_key,rowid",
|
||||
"temporary_table": {
|
||||
"nested_loop": [
|
||||
{
|
||||
"table": {
|
||||
"table_name": "best_record",
|
||||
"access_type": "index_merge",
|
||||
"possible_keys": ["MaestroID", "AppID"],
|
||||
"key_length": "4,4",
|
||||
"index_merge": {
|
||||
"intersect": [
|
||||
{
|
||||
"range": {
|
||||
"key": "MaestroID",
|
||||
"used_key_parts": ["MaestroID"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"range": {
|
||||
"key": "AppID",
|
||||
"used_key_parts": ["AppID"]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"r_loops": 1,
|
||||
"rows": 10103,
|
||||
"r_rows": 5851,
|
||||
"r_table_time_ms": 19.07379578,
|
||||
"r_other_time_ms": 54.36322855,
|
||||
"r_engine_stats": {
|
||||
"pages_accessed": 17780
|
||||
},
|
||||
"filtered": 100,
|
||||
"r_filtered": 12.11758674,
|
||||
"attached_condition": "best_record.MaestroID = @maestro and best_record.AppID = @app and best_record.RecordDateTime >= '2026-08-01 00:00:00' and best_record.RecordDateTime < '2026-09-01 00:00:00'"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
baseline q1_hour_func server_time_ms=74.61373851
|
||||
baseline q2_hour_range server_time_ms=75.58458171
|
||||
baseline q3_day_func server_time_ms=75.28739257
|
||||
baseline q4_day_range server_time_ms=75.24298373
|
||||
baseline q5_month_func server_time_ms=76.26824776
|
||||
baseline q6_month_range server_time_ms=79.70009072
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"query_block": {
|
||||
"select_id": 1,
|
||||
"nested_loop": [
|
||||
{
|
||||
"read_sorted_file": {
|
||||
"filesort": {
|
||||
"sort_key": "best_record.RecordDateTime, best_record.PlayerID",
|
||||
"table": {
|
||||
"table_name": "best_record",
|
||||
"access_type": "index_merge",
|
||||
"possible_keys": ["MaestroID", "AppID"],
|
||||
"key_length": "4,4",
|
||||
"index_merge": {
|
||||
"intersect": [
|
||||
{
|
||||
"range": {
|
||||
"key": "MaestroID",
|
||||
"used_key_parts": ["MaestroID"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"range": {
|
||||
"key": "AppID",
|
||||
"used_key_parts": ["AppID"]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"rows": 10103,
|
||||
"filtered": 100,
|
||||
"attached_condition": "best_record.MaestroID = @maestro and best_record.AppID = @app and cast(best_record.RecordDateTime as date) = @`day` and hour(best_record.RecordDateTime) = 9"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"query_block": {
|
||||
"select_id": 1,
|
||||
"nested_loop": [
|
||||
{
|
||||
"read_sorted_file": {
|
||||
"filesort": {
|
||||
"sort_key": "best_record.RecordDateTime, best_record.PlayerID",
|
||||
"table": {
|
||||
"table_name": "best_record",
|
||||
"access_type": "index_merge",
|
||||
"possible_keys": ["MaestroID", "AppID"],
|
||||
"key_length": "4,4",
|
||||
"index_merge": {
|
||||
"intersect": [
|
||||
{
|
||||
"range": {
|
||||
"key": "MaestroID",
|
||||
"used_key_parts": ["MaestroID"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"range": {
|
||||
"key": "AppID",
|
||||
"used_key_parts": ["AppID"]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"rows": 10103,
|
||||
"filtered": 100,
|
||||
"attached_condition": "best_record.MaestroID = @maestro and best_record.AppID = @app and best_record.RecordDateTime >= <cache>(cast(@`day` as datetime) + interval @`hour` hour) and best_record.RecordDateTime < <cache>(cast(@`day` as datetime) + interval @`hour` + 1 hour)"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"query_block": {
|
||||
"select_id": 1,
|
||||
"filesort": {
|
||||
"sort_key": "max(best_record.BestRecord) desc, best_record.PlayerID",
|
||||
"temporary_table": {
|
||||
"nested_loop": [
|
||||
{
|
||||
"table": {
|
||||
"table_name": "best_record",
|
||||
"access_type": "index_merge",
|
||||
"possible_keys": ["MaestroID", "AppID"],
|
||||
"key_length": "4,4",
|
||||
"index_merge": {
|
||||
"intersect": [
|
||||
{
|
||||
"range": {
|
||||
"key": "MaestroID",
|
||||
"used_key_parts": ["MaestroID"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"range": {
|
||||
"key": "AppID",
|
||||
"used_key_parts": ["AppID"]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"rows": 10103,
|
||||
"filtered": 100,
|
||||
"attached_condition": "best_record.MaestroID = @maestro and best_record.AppID = @app and year(best_record.RecordDateTime) = 2026 and month(best_record.RecordDateTime) = 8 and dayofmonth(best_record.RecordDateTime) = 26"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"query_block": {
|
||||
"select_id": 1,
|
||||
"filesort": {
|
||||
"sort_key": "max(best_record.BestRecord) desc, best_record.PlayerID",
|
||||
"temporary_table": {
|
||||
"nested_loop": [
|
||||
{
|
||||
"table": {
|
||||
"table_name": "best_record",
|
||||
"access_type": "index_merge",
|
||||
"possible_keys": ["MaestroID", "AppID"],
|
||||
"key_length": "4,4",
|
||||
"index_merge": {
|
||||
"intersect": [
|
||||
{
|
||||
"range": {
|
||||
"key": "MaestroID",
|
||||
"used_key_parts": ["MaestroID"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"range": {
|
||||
"key": "AppID",
|
||||
"used_key_parts": ["AppID"]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"rows": 10103,
|
||||
"filtered": 100,
|
||||
"attached_condition": "best_record.MaestroID = @maestro and best_record.AppID = @app and best_record.RecordDateTime >= <cache>(cast(@`day` as datetime)) and best_record.RecordDateTime < <cache>(cast(@`day` as datetime) + interval 1 day)"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"query_block": {
|
||||
"select_id": 1,
|
||||
"filesort": {
|
||||
"sort_key": "max(best_record.BestRecord) desc, best_record.PlayerID",
|
||||
"temporary_table": {
|
||||
"nested_loop": [
|
||||
{
|
||||
"table": {
|
||||
"table_name": "best_record",
|
||||
"access_type": "index_merge",
|
||||
"possible_keys": ["MaestroID", "AppID"],
|
||||
"key_length": "4,4",
|
||||
"index_merge": {
|
||||
"intersect": [
|
||||
{
|
||||
"range": {
|
||||
"key": "MaestroID",
|
||||
"used_key_parts": ["MaestroID"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"range": {
|
||||
"key": "AppID",
|
||||
"used_key_parts": ["AppID"]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"rows": 10103,
|
||||
"filtered": 100,
|
||||
"attached_condition": "best_record.MaestroID = @maestro and best_record.AppID = @app and year(best_record.RecordDateTime) = 2026 and month(best_record.RecordDateTime) = 8"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"query_block": {
|
||||
"select_id": 1,
|
||||
"filesort": {
|
||||
"sort_key": "max(best_record.BestRecord) desc, best_record.PlayerID",
|
||||
"temporary_table": {
|
||||
"nested_loop": [
|
||||
{
|
||||
"table": {
|
||||
"table_name": "best_record",
|
||||
"access_type": "index_merge",
|
||||
"possible_keys": ["MaestroID", "AppID"],
|
||||
"key_length": "4,4",
|
||||
"index_merge": {
|
||||
"intersect": [
|
||||
{
|
||||
"range": {
|
||||
"key": "MaestroID",
|
||||
"used_key_parts": ["MaestroID"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"range": {
|
||||
"key": "AppID",
|
||||
"used_key_parts": ["AppID"]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"rows": 10103,
|
||||
"filtered": 100,
|
||||
"attached_condition": "best_record.MaestroID = @maestro and best_record.AppID = @app and best_record.RecordDateTime >= <cache>(cast(date_format(@`day`,'%Y-%m-01') as datetime)) and best_record.RecordDateTime < <cache>(cast(date_format(@`day`,'%Y-%m-01') as datetime) + interval 1 month)"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
baseline_explain_q1_hour_func.json access_type=index_merge key=MaestroID ∩ AppID rows=10103
|
||||
baseline_explain_q2_hour_range.json access_type=index_merge key=MaestroID ∩ AppID rows=10103
|
||||
baseline_explain_q3_day_func.json access_type=index_merge key=MaestroID ∩ AppID rows=10103
|
||||
baseline_explain_q4_day_range.json access_type=index_merge key=MaestroID ∩ AppID rows=10103
|
||||
baseline_explain_q5_month_func.json access_type=index_merge key=MaestroID ∩ AppID rows=10103
|
||||
baseline_explain_q6_month_range.json access_type=index_merge key=MaestroID ∩ AppID rows=10103
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,6 @@
|
||||
day hour cnt
|
||||
2026-08-26 9 22
|
||||
2026-08-28 9 22
|
||||
2026-08-26 10 21
|
||||
2026-08-31 10 20
|
||||
2026-08-25 10 19
|
||||
@@ -0,0 +1,39 @@
|
||||
=== 성능 개선 효과 (운영 DB, 2026-09-15) ===
|
||||
측정 기준: params.sql (MaestroID 181, AppID 21, @day 2026-08-26, @hour 9)
|
||||
측정 시점: 베이스라인 22:5x (인덱스 추가 전) / 개선 후 23:1x (인덱스 7개 추가 후)
|
||||
|
||||
| 쿼리 | access_type (전→후) | rows (전→후) | 서버 시간 ms (전→후, ANALYZE) | 결과 행 수 |
|
||||
|--------------------|-----------------------|-----------------------|-------------------------------|-----------|
|
||||
| q1 시간별 · 함수 | index_merge → ref | 10,103 → 5,851 (1.7배) | 74.61 → 2.07 (36배) | 22 = 22 |
|
||||
| q2 시간별 · 범위 | index_merge → range | 10,103 → 22 (459배) | 75.58 → 0.26 (292배) | 22 = 22 |
|
||||
| q3 일간 · 함수 | index_merge → ref | 10,103 → 5,851 (1.7배) | 75.29 → 2.44 (31배) | 61 = 61 |
|
||||
| q4 일간 · 범위 | index_merge → range | 10,103 → 99 (102배) | 75.24 → 0.47 (161배) | 61 = 61 |
|
||||
| q5 월간 · 함수 | index_merge → ref | 10,103 → 5,851 (1.7배) | 76.27 → 4.30 (18배) | 262 = 262 |
|
||||
| q6 월간 · 범위 | index_merge → range | 10,103 → 709 (14배) | 79.70 → 2.50 (32배) | 262 = 262 |
|
||||
|
||||
사용 인덱스: 개선 후 6개 모두 idx_maestro_app_dt (MaestroID, AppID, RecordDateTime)
|
||||
클라이언트 시간 (네트워크 포함, 2회차): 약 88~92ms → 13~18ms
|
||||
|
||||
참고 (스테이징): rows 5,804 → 1, cost 8.63 → 0.004
|
||||
참고 (운영 01 사전 측정): 일간 랭킹 index_merge, rows 10,100
|
||||
|
||||
결과 행 수 동일 (4-3): [x] 예 [ ] 아니오
|
||||
|
||||
결론:
|
||||
- 인덱스만의 효과 (q1·q3·q5, 함수형 조건):
|
||||
rows 추정은 1.7배만 줄었지만 서버 처리 시간은 18~36배 단축.
|
||||
기존에는 MaestroID·AppID 단일 인덱스 두 개의 교집합(index_merge)을 구하느라 느렸고,
|
||||
복합 인덱스의 (MaestroID, AppID) 앞부분으로 바로 찾게(ref) 되었기 때문.
|
||||
날짜 조건은 여전히 인덱스로 거르지 못해 해당 선생님·앱의 기록 약 5,851행을 확인함.
|
||||
- 인덱스 + 쿼리 수정 효과 (q2·q4·q6, 범위 조건):
|
||||
날짜까지 인덱스로 걸러(range) 조회 기간의 실제 기록만 읽음.
|
||||
서버 처리 시간 32~292배 단축, 검사 행 수 14~459배 감소.
|
||||
같은 조건에서 함수형 대비 추가로 1.7~9배 빠름 (q1→q2 8배, q3→q4 5배, q5→q6 1.7배).
|
||||
|
||||
중복 정리 (03-0):
|
||||
- app_highest_record 8,795행 삭제 (4,823조합), 220,648행 = 220,648조합
|
||||
- UNIQUE 키 2개 추가 완료 → 이후 중복 저장은 DB가 거부
|
||||
|
||||
Slow query log (04-5, 23:08 이후 1시간):
|
||||
- 0.5초 이상은 작업 SQL 5건(ALTER 4건, 중복 DELETE 1건)뿐, 애플리케이션 쿼리 0건
|
||||
- 새벽이 아닌 밤 시간이고 기간이 짧으므로 24시간 뒤 다시 확인 필요 (07단계)
|
||||
@@ -0,0 +1,2 @@
|
||||
dup_groups rows_to_delete
|
||||
4823 8795
|
||||
@@ -0,0 +1,47 @@
|
||||
--------------
|
||||
DELETE AHR FROM app_highest_record AHR
|
||||
JOIN (
|
||||
SELECT AppHighestRecordID FROM (
|
||||
SELECT AppHighestRecordID,
|
||||
ROW_NUMBER() OVER (
|
||||
PARTITION BY MaestroID, PlayerID, AppID
|
||||
ORDER BY CASE WHEN AppID = 105 THEN HighestRecord ELSE -HighestRecord END,
|
||||
AppHighestRecordID DESC
|
||||
) AS rn
|
||||
FROM app_highest_record
|
||||
) ranked
|
||||
WHERE rn > 1
|
||||
) dup ON dup.AppHighestRecordID = AHR.AppHighestRecordID
|
||||
--------------
|
||||
|
||||
Query OK, 8795 rows affected (0.942 sec)
|
||||
|
||||
--------------
|
||||
SELECT 'app_highest_record' AS table_name,
|
||||
COUNT(*) AS total_rows,
|
||||
COUNT(DISTINCT MaestroID, PlayerID, AppID) AS unique_combos
|
||||
FROM app_highest_record
|
||||
--------------
|
||||
|
||||
+--------------------+------------+---------------+
|
||||
| table_name | total_rows | unique_combos |
|
||||
+--------------------+------------+---------------+
|
||||
| app_highest_record | 220648 | 220648 |
|
||||
+--------------------+------------+---------------+
|
||||
1 row in set (0.190 sec)
|
||||
|
||||
--------------
|
||||
SELECT 'typing_exam_highest_record' AS table_name,
|
||||
COUNT(*) AS total_rows,
|
||||
COUNT(DISTINCT MaestroID, PlayerID, WritingID) AS unique_combos
|
||||
FROM typing_exam_highest_record
|
||||
--------------
|
||||
|
||||
+----------------------------+------------+---------------+
|
||||
| table_name | total_rows | unique_combos |
|
||||
+----------------------------+------------+---------------+
|
||||
| typing_exam_highest_record | 20413 | 20413 |
|
||||
+----------------------------+------------+---------------+
|
||||
1 row in set (0.024 sec)
|
||||
|
||||
Bye
|
||||
@@ -0,0 +1,12 @@
|
||||
baseline_explain_q1_hour_func.json access_type=index_merge key=MaestroID ∩ AppID rows=10103
|
||||
baseline_explain_q2_hour_range.json access_type=index_merge key=MaestroID ∩ AppID rows=10103
|
||||
baseline_explain_q3_day_func.json access_type=index_merge key=MaestroID ∩ AppID rows=10103
|
||||
baseline_explain_q4_day_range.json access_type=index_merge key=MaestroID ∩ AppID rows=10103
|
||||
baseline_explain_q5_month_func.json access_type=index_merge key=MaestroID ∩ AppID rows=10103
|
||||
baseline_explain_q6_month_range.json access_type=index_merge key=MaestroID ∩ AppID rows=10103
|
||||
after_explain_q1_hour_func.json access_type=ref key=idx_maestro_app_dt rows=5851
|
||||
after_explain_q2_hour_range.json access_type=range key=idx_maestro_app_dt rows=22
|
||||
after_explain_q3_day_func.json access_type=ref key=idx_maestro_app_dt rows=5851
|
||||
after_explain_q4_day_range.json access_type=range key=idx_maestro_app_dt rows=99
|
||||
after_explain_q5_month_func.json access_type=ref key=idx_maestro_app_dt rows=5851
|
||||
after_explain_q6_month_range.json access_type=range key=idx_maestro_app_dt rows=709
|
||||
@@ -0,0 +1 @@
|
||||
SET @maestro = 181, @app = 21, @day = '2026-08-26', @hour = 9;
|
||||
@@ -0,0 +1,2 @@
|
||||
dup_groups rows_to_delete
|
||||
4823 8795
|
||||
@@ -0,0 +1,11 @@
|
||||
MaestroID PlayerID AppID cnt
|
||||
223 115830 2 33
|
||||
306 93738 106 25
|
||||
123 86825 106 21
|
||||
82 2245 106 21
|
||||
278 84908 106 20
|
||||
49 114356 106 20
|
||||
184 112221 101 19
|
||||
127 71176 3 19
|
||||
363 105234 106 18
|
||||
75 10923 106 17
|
||||
@@ -0,0 +1,66 @@
|
||||
{
|
||||
"query_block": {
|
||||
"select_id": 1,
|
||||
"filesort": {
|
||||
"sort_key": "max(BR.BestRecord) desc",
|
||||
"temporary_table": {
|
||||
"nested_loop": [
|
||||
{
|
||||
"table": {
|
||||
"table_name": "BR",
|
||||
"access_type": "index_merge",
|
||||
"possible_keys": [
|
||||
"MaestroID",
|
||||
"AppID",
|
||||
"PlayerID"
|
||||
],
|
||||
"key_length": "4,4",
|
||||
"index_merge": {
|
||||
"intersect": [
|
||||
{
|
||||
"range": {
|
||||
"key": "MaestroID",
|
||||
"used_key_parts": [
|
||||
"MaestroID"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"range": {
|
||||
"key": "AppID",
|
||||
"used_key_parts": [
|
||||
"AppID"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"rows": 10100,
|
||||
"filtered": 100,
|
||||
"attached_condition": "BR.MaestroID = 181 and BR.AppID = 21 and year(BR.RecordDateTime) = 2026 and month(BR.RecordDateTime) = 9 and dayofmonth(BR.RecordDateTime) = 15"
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": {
|
||||
"table_name": "U",
|
||||
"access_type": "eq_ref",
|
||||
"possible_keys": [
|
||||
"PRIMARY"
|
||||
],
|
||||
"key": "PRIMARY",
|
||||
"key_length": "4",
|
||||
"used_key_parts": [
|
||||
"PlayerID"
|
||||
],
|
||||
"ref": [
|
||||
"chocomae.BR.PlayerID"
|
||||
],
|
||||
"rows": 1,
|
||||
"filtered": 100
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
ym cnt
|
||||
2022-04 3
|
||||
2022-05 3544
|
||||
2022-06 7730
|
||||
2022-07 7543
|
||||
2022-08 4605
|
||||
2022-09 4164
|
||||
2022-10 4702
|
||||
2022-11 4052
|
||||
2022-12 2446
|
||||
2023-01 3903
|
||||
2023-02 1580
|
||||
2023-03 18646
|
||||
2023-04 22750
|
||||
2023-05 19749
|
||||
2023-06 18620
|
||||
2023-07 16490
|
||||
2023-08 13244
|
||||
2023-09 17892
|
||||
2023-10 19045
|
||||
2023-11 23144
|
||||
2023-12 17608
|
||||
2024-01 15262
|
||||
2024-02 6745
|
||||
2024-03 19414
|
||||
2024-04 27242
|
||||
2024-05 25057
|
||||
2024-06 21613
|
||||
2024-07 23446
|
||||
2024-08 17951
|
||||
2024-09 21469
|
||||
2024-10 22776
|
||||
2024-11 22526
|
||||
2024-12 19082
|
||||
2025-01 12170
|
||||
2025-02 8558
|
||||
2025-03 26788
|
||||
2025-04 30366
|
||||
2025-05 29850
|
||||
2025-06 32421
|
||||
2025-07 28872
|
||||
2025-08 19853
|
||||
2025-09 33908
|
||||
2025-10 22878
|
||||
2025-11 28723
|
||||
2025-12 28649
|
||||
2026-01 16532
|
||||
2026-02 8980
|
||||
2026-03 66105
|
||||
2026-04 70072
|
||||
2026-05 58444
|
||||
2026-06 65606
|
||||
2026-07 56307
|
||||
2026-08 51991
|
||||
2026-09 39366
|
||||
@@ -0,0 +1,6 @@
|
||||
Variable_name Value
|
||||
log_output FILE
|
||||
log_queries_not_using_indexes OFF
|
||||
long_query_time 3.000000
|
||||
slow_query_log ON
|
||||
slow_query_log_file /var/log/mysql/mariadb-slow.log
|
||||
@@ -0,0 +1,67 @@
|
||||
=== 운영 DB 사전 측정 결과 (2026-09-15) ===
|
||||
|
||||
1. 테이블 크기 (premeasure_table_sizes.txt)
|
||||
- best_record data_mb: 72.5
|
||||
- best_record index_mb: 72.1
|
||||
- best_record 행 수: 약 1,152,399 (information_schema 추정치) / 월별 합계 1,210,482
|
||||
- typing_exam_record data_mb: 10.3
|
||||
- typing_exam_record index_mb: 10.9
|
||||
|
||||
2. 월별 적재량 (premeasure_monthly_load.txt)
|
||||
- 최근 1개월 (2026-08): 51,991 행
|
||||
- 2026-09 (15일까지): 39,366 행
|
||||
- 최근 3개월 평균 (2026-06~08): 57,968 행/월
|
||||
- 전년 같은 기간 평균 (2025-06~08): 27,049 행/월 → 약 2.1배 증가
|
||||
|
||||
3. 테스트용 선생님·앱 (premeasure_top_maestro_app.txt)
|
||||
- MaestroID: 181
|
||||
- AppID: 21
|
||||
- 30일 기록 수: 1,089
|
||||
|
||||
4. 일간 랭킹 EXPLAIN (premeasure_explain_daily_ranking.json)
|
||||
- access_type: index_merge (예상했던 ALL 아님)
|
||||
- key: MaestroID ∩ AppID (기존 단일 인덱스 2개의 교집합)
|
||||
- rows: 10,100 (예상했던 1,206,768 아님)
|
||||
- 날짜 조건(YEAR/MONTH/DAYOFMONTH)은 인덱스로 거르지 못하고 10,100행을 하나씩 확인
|
||||
- 참고: 원본 출력은 헤더와 \n 이스케이프 때문에 JSON이 아니었음 → 내용 변경 없이 JSON으로 복원
|
||||
|
||||
5. 최고기록 테이블 중복 (premeasure_*_duplicates.txt, premeasure_app_highest_dup_count.txt)
|
||||
- app_highest_record 중복: 4,823 조합 / 삭제 대상 8,795 행
|
||||
· 테이블 약 229,459행(추정치)의 약 3.8%
|
||||
· 상위 10개 조합은 각 17~33행
|
||||
· 03-0-1에서 작업 당일 다시 세어 비교 (그 사이 조금 늘어날 수 있음)
|
||||
- typing_exam_highest_record 중복: 0 건 (빈 파일 = 조회 결과 없음)
|
||||
|
||||
6. 계정 권한 (premeasure_grants.txt)
|
||||
- jisangs@182.217.174.221: SELECT, ALTER ON chocomae.* 만 있음
|
||||
- 2026-09-15 임시 부여 후 (premeasure_grants_after.txt):
|
||||
+ SUPER ON *.*
|
||||
+ INSERT, DELETE ON chocomae.app_highest_record, chocomae.typing_exam_highest_record
|
||||
+ SELECT ON mysql.slow_log
|
||||
→ 05단계에서 회수 (slow log 복원 후)
|
||||
- 원래 권한으로 할 수 없던 작업:
|
||||
· 02-1 slow query log 변경 (SET GLOBAL → SUPER)
|
||||
· 03-0-3 중복 삭제 (DELETE)
|
||||
· 04-5, 07 mysql.slow_log 조회 (SELECT ON mysql.slow_log)
|
||||
· 05 slow query log 복원 (SET GLOBAL)
|
||||
· 06 삭제 행 복원 (INSERT) → 임시 부여됨
|
||||
· 06 ANALYZE TABLE best_record (best_record INSERT 필요, 부여 안 함) → 관리자 계정
|
||||
|
||||
7. Slow query log 원래 설정 (premeasure_slow_log_settings.txt) — 05단계 복원 기준값
|
||||
- slow_query_log: ON
|
||||
- long_query_time: 3
|
||||
- log_output: FILE
|
||||
- log_queries_not_using_indexes: OFF
|
||||
- slow_query_log_file: /var/log/mysql/mariadb-slow.log
|
||||
- 이전 문서의 1-4 SET GLOBAL은 적용되지 않았음 (SUPER 권한 없음, 적용됐다면 long_query_time=1·log_output=TABLE)
|
||||
|
||||
8. 준비 확인
|
||||
☑ 테이블 크기 확인
|
||||
☑ 월별 적재량 확인
|
||||
☑ EXPLAIN 결과 저장
|
||||
☑ 중복 확인 (전체 개수 포함)
|
||||
☑ 계정 권한 확인
|
||||
☑ Slow query log 현재 설정 저장
|
||||
☑ 작업 권한 임시 부여
|
||||
|
||||
다음: 02-baseline.md
|
||||
@@ -0,0 +1,20 @@
|
||||
TABLE_NAME approx_rows data_mb index_mb
|
||||
best_record 1152399 72.5 72.1
|
||||
app_highest_record 229459 16.5 15.8
|
||||
typing_exam_record 131675 10.3 10.9
|
||||
player 22394 3.5 0.4
|
||||
active_app 17661 1.5 0.6
|
||||
typing_exam_highest_record 21355 1.4 1.2
|
||||
maestro_log 1944 0.4 0.0
|
||||
maestro 418 0.1 0.0
|
||||
license_score 514 0.1 0.0
|
||||
license_maestro_password 1024 0.1 0.0
|
||||
maestro_extension 450 0.0 0.0
|
||||
admin 0 0.0 0.0
|
||||
app 48 0.0 0.0
|
||||
ads_client 0 0.0 0.0
|
||||
license_time 0 0.0 0.0
|
||||
writing 20 0.0 0.0
|
||||
typing_exam_ads 0 0.0 0.0
|
||||
banned_word 22 0.0 0.0
|
||||
maestro_upgrade 109 0.0 0.0
|
||||
@@ -0,0 +1,6 @@
|
||||
MaestroID AppID cnt
|
||||
181 21 1089
|
||||
181 1 591
|
||||
389 103 562
|
||||
230 21 546
|
||||
389 104 541
|
||||
@@ -0,0 +1,6 @@
|
||||
SELECT SQL_NO_CACHE PlayerID, BestRecord, RecordDateTime
|
||||
FROM best_record
|
||||
WHERE MaestroID = @maestro AND AppID = @app
|
||||
AND DATE(RecordDateTime) = @day
|
||||
AND HOUR(RecordDateTime) = @hour
|
||||
ORDER BY RecordDateTime, PlayerID;
|
||||
@@ -0,0 +1,6 @@
|
||||
SELECT SQL_NO_CACHE PlayerID, BestRecord, RecordDateTime
|
||||
FROM best_record
|
||||
WHERE MaestroID = @maestro AND AppID = @app
|
||||
AND RecordDateTime >= CAST(@day AS DATETIME) + INTERVAL @hour HOUR
|
||||
AND RecordDateTime < CAST(@day AS DATETIME) + INTERVAL (@hour + 1) HOUR
|
||||
ORDER BY RecordDateTime, PlayerID;
|
||||
@@ -0,0 +1,8 @@
|
||||
SELECT SQL_NO_CACHE PlayerID, MAX(BestRecord) AS HighScore
|
||||
FROM best_record
|
||||
WHERE MaestroID = @maestro AND AppID = @app
|
||||
AND YEAR(RecordDateTime) = YEAR(@day)
|
||||
AND MONTH(RecordDateTime) = MONTH(@day)
|
||||
AND DAYOFMONTH(RecordDateTime) = DAYOFMONTH(@day)
|
||||
GROUP BY PlayerID
|
||||
ORDER BY HighScore DESC, PlayerID;
|
||||
@@ -0,0 +1,7 @@
|
||||
SELECT SQL_NO_CACHE PlayerID, MAX(BestRecord) AS HighScore
|
||||
FROM best_record
|
||||
WHERE MaestroID = @maestro AND AppID = @app
|
||||
AND RecordDateTime >= CAST(@day AS DATETIME)
|
||||
AND RecordDateTime < CAST(@day AS DATETIME) + INTERVAL 1 DAY
|
||||
GROUP BY PlayerID
|
||||
ORDER BY HighScore DESC, PlayerID;
|
||||
@@ -0,0 +1,7 @@
|
||||
SELECT SQL_NO_CACHE PlayerID, MAX(BestRecord) AS HighScore
|
||||
FROM best_record
|
||||
WHERE MaestroID = @maestro AND AppID = @app
|
||||
AND YEAR(RecordDateTime) = YEAR(@day)
|
||||
AND MONTH(RecordDateTime) = MONTH(@day)
|
||||
GROUP BY PlayerID
|
||||
ORDER BY HighScore DESC, PlayerID;
|
||||
@@ -0,0 +1,7 @@
|
||||
SELECT SQL_NO_CACHE PlayerID, MAX(BestRecord) AS HighScore
|
||||
FROM best_record
|
||||
WHERE MaestroID = @maestro AND AppID = @app
|
||||
AND RecordDateTime >= CAST(DATE_FORMAT(@day, '%Y-%m-01') AS DATETIME)
|
||||
AND RecordDateTime < CAST(DATE_FORMAT(@day, '%Y-%m-01') AS DATETIME) + INTERVAL 1 MONTH
|
||||
GROUP BY PlayerID
|
||||
ORDER BY HighScore DESC, PlayerID;
|
||||
@@ -0,0 +1,41 @@
|
||||
#!/bin/zsh
|
||||
# 02·04단계 공통 측정 스크립트
|
||||
# 사용법: zsh run_measure.sh baseline (02단계, 인덱스 추가 전)
|
||||
# zsh run_measure.sh after (04단계, 인덱스 추가 후)
|
||||
|
||||
PREFIX=$1
|
||||
if [[ $PREFIX != baseline && $PREFIX != after ]]; then
|
||||
echo "사용법: zsh run_measure.sh baseline|after" >&2; exit 1
|
||||
fi
|
||||
cd "${0:A:h}" || exit 1
|
||||
|
||||
printf "Enter password: "; read -rs DBPW; echo
|
||||
|
||||
db() {
|
||||
mariadb --defaults-extra-file=<(printf '[client]\npassword="%s"\n' "$DBPW") \
|
||||
-h chocomae.jinaju.com -u jisangs chocomae "$@"
|
||||
}
|
||||
|
||||
# 1) EXPLAIN: 쿼리마다 JSON 파일 1개
|
||||
for q in queries/*.sql; do
|
||||
out="${PREFIX}_explain_${q:t:r}.json"
|
||||
{ cat params.sql; printf 'EXPLAIN FORMAT=JSON '; cat "$q"; } | db -N -r > "$out" \
|
||||
|| { echo "EXPLAIN 실패: $q" >&2; exit 1; }
|
||||
echo "저장: $out"
|
||||
done
|
||||
|
||||
# 2) 실행 시간: 같은 쿼리 묶음을 2회 실행 (2회차 값 사용)
|
||||
for round in 1 2; do
|
||||
{ cat params.sql
|
||||
for q in queries/*.sql; do
|
||||
echo "SELECT '=== round ${round}: ${q:t:r} ===' AS msg;"
|
||||
cat "$q"
|
||||
done
|
||||
} | db -vvv || { echo "실행 시간 측정 실패" >&2; exit 1; }
|
||||
done > "${PREFIX}_perf.txt"
|
||||
echo "저장: ${PREFIX}_perf.txt"
|
||||
|
||||
# 3) 요약: 쿼리 이름 / 결과 행 수 / 실행 시간 (2회차)
|
||||
echo
|
||||
grep -E '^\| === |rows? in set|Empty set' "${PREFIX}_perf.txt" | paste - - - | grep 'round 2' \
|
||||
| sed -E 's/\| === round 2: (.*) === \|/\1/; s/\t1 row in set \([0-9.]+ sec\)//'
|
||||
@@ -0,0 +1,18 @@
|
||||
# 사용법: python3 summarize_analyze.py baseline [after]
|
||||
import json
|
||||
import sys
|
||||
|
||||
decoder = json.JSONDecoder()
|
||||
for prefix in sys.argv[1:]:
|
||||
text = open(f"{prefix}_analyze.txt", encoding="utf-8").read()
|
||||
names = [line.split("=== ")[1].split(" ===")[0] for line in text.splitlines() if line.startswith("=== ")]
|
||||
docs, pos = [], 0
|
||||
while True:
|
||||
start = text.find("{", pos)
|
||||
if start < 0:
|
||||
break
|
||||
doc, pos = decoder.raw_decode(text, start)
|
||||
docs.append(doc)
|
||||
for name, doc in zip(names, docs):
|
||||
block = doc["query_block"]
|
||||
print(f"{prefix:8} {name:16} server_time_ms={block.get('r_total_time_ms')}")
|
||||
@@ -0,0 +1,40 @@
|
||||
# 사용법: python3 summarize_explain.py baseline [after]
|
||||
import glob
|
||||
import json
|
||||
import sys
|
||||
|
||||
|
||||
def find_table(node):
|
||||
if isinstance(node, dict):
|
||||
if isinstance(node.get("table"), dict):
|
||||
return node["table"]
|
||||
children = node.values()
|
||||
elif isinstance(node, list):
|
||||
children = node
|
||||
else:
|
||||
return None
|
||||
for child in children:
|
||||
found = find_table(child)
|
||||
if found:
|
||||
return found
|
||||
return None
|
||||
|
||||
|
||||
def keys_of(node):
|
||||
if isinstance(node, dict):
|
||||
for k, v in node.items():
|
||||
if k == "key":
|
||||
yield v
|
||||
else:
|
||||
yield from keys_of(v)
|
||||
elif isinstance(node, list):
|
||||
for v in node:
|
||||
yield from keys_of(v)
|
||||
|
||||
|
||||
for prefix in sys.argv[1:]:
|
||||
for path in sorted(glob.glob(f"{prefix}_explain_*.json")):
|
||||
with open(path, encoding="utf-8") as f:
|
||||
table = find_table(json.load(f))
|
||||
key = table.get("key") or " ∩ ".join(keys_of(table.get("index_merge", {}))) or "-"
|
||||
print(f"{path:40} access_type={str(table.get('access_type')):12} key={key:28} rows={table.get('rows')}")
|
||||
Reference in New Issue
Block a user