feat: sam_stat 최적화 및 안정화 (Phase 5)
- StatBackfillCommand: 과거 데이터 일괄 백필 (일간+월간, 프로그레스바, 에러 리포트) - StatVerifyCommand: 원본 DB vs sam_stat 정합성 교차 검증 (--fix 자동 재집계) - 파티셔닝 준비: 7개 일간 테이블 RANGE COLUMNS(stat_date) 마이그레이션 - Redis 캐싱: StatQueryService Cache::remember TTL 5분 + invalidateCache() - StatMonitorService: 집계 실패/누락/불일치 시 stat_alerts 알림 기록 - StatAggregatorService: 모니터링 알림 + 캐시 무효화 연동 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
/**
|
||||
* 일간 통계 테이블 RANGE 파티셔닝 준비
|
||||
*
|
||||
* 실행 방법: php artisan migrate --path=database/migrations/stats/2026_01_29_300001_prepare_partitioning_daily_tables.php
|
||||
*
|
||||
* 주의: MySQL에서 파티셔닝 적용 시 UNIQUE KEY에 파티션 컬럼(stat_date)이 포함되어야 합니다.
|
||||
* 이 마이그레이션은 기존 데이터를 보존하며 파티셔닝을 적용합니다.
|
||||
*/
|
||||
return new class extends Migration
|
||||
{
|
||||
protected $connection = 'sam_stat';
|
||||
|
||||
/**
|
||||
* 파티셔닝 대상 테이블과 고유 키 정의
|
||||
*/
|
||||
private function getTargetTables(): array
|
||||
{
|
||||
return [
|
||||
'stat_sales_daily' => [
|
||||
'unique_columns' => 'tenant_id, stat_date',
|
||||
'unique_name' => 'uk_tenant_date',
|
||||
],
|
||||
'stat_finance_daily' => [
|
||||
'unique_columns' => 'tenant_id, stat_date',
|
||||
'unique_name' => 'uk_tenant_date',
|
||||
],
|
||||
'stat_production_daily' => [
|
||||
'unique_columns' => 'tenant_id, stat_date',
|
||||
'unique_name' => 'uk_tenant_date',
|
||||
],
|
||||
'stat_inventory_daily' => [
|
||||
'unique_columns' => 'tenant_id, stat_date',
|
||||
'unique_name' => 'uk_tenant_date',
|
||||
],
|
||||
'stat_quote_pipeline_daily' => [
|
||||
'unique_columns' => 'tenant_id, stat_date',
|
||||
'unique_name' => 'uk_tenant_date',
|
||||
],
|
||||
'stat_hr_attendance_daily' => [
|
||||
'unique_columns' => 'tenant_id, stat_date',
|
||||
'unique_name' => 'uk_tenant_date',
|
||||
],
|
||||
'stat_system_daily' => [
|
||||
'unique_columns' => 'tenant_id, stat_date',
|
||||
'unique_name' => 'uk_tenant_date',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 연도별 파티션 정의 생성 (2024~2028 + MAXVALUE)
|
||||
*/
|
||||
private function getPartitionDefinitions(): string
|
||||
{
|
||||
$partitions = [];
|
||||
for ($year = 2024; $year <= 2028; $year++) {
|
||||
$partitions[] = "PARTITION p{$year} VALUES LESS THAN ('{$year}-01-01')";
|
||||
}
|
||||
$partitions[] = 'PARTITION p_future VALUES LESS THAN MAXVALUE';
|
||||
|
||||
return implode(",\n ", $partitions);
|
||||
}
|
||||
|
||||
public function up(): void
|
||||
{
|
||||
$tables = $this->getTargetTables();
|
||||
$partitionDefs = $this->getPartitionDefinitions();
|
||||
|
||||
foreach ($tables as $table => $config) {
|
||||
// 테이블 존재 여부 확인
|
||||
$exists = DB::connection('sam_stat')
|
||||
->select("SHOW TABLES LIKE '{$table}'");
|
||||
|
||||
if (empty($exists)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 이미 파티셔닝되어 있는지 확인
|
||||
$partitionInfo = DB::connection('sam_stat')
|
||||
->select('SELECT PARTITION_NAME FROM INFORMATION_SCHEMA.PARTITIONS
|
||||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? AND PARTITION_NAME IS NOT NULL',
|
||||
[$table]);
|
||||
|
||||
if (! empty($partitionInfo)) {
|
||||
continue; // 이미 파티셔닝됨
|
||||
}
|
||||
|
||||
// AUTO_INCREMENT PK를 일반 PK로 변경 (파티션 키 포함)
|
||||
// MySQL 파티셔닝 제약: UNIQUE KEY에 파티션 컬럼 포함 필수
|
||||
DB::connection('sam_stat')->statement("
|
||||
ALTER TABLE `{$table}`
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`id`, `stat_date`),
|
||||
DROP INDEX `{$config['unique_name']}`,
|
||||
ADD UNIQUE KEY `{$config['unique_name']}` ({$config['unique_columns']})
|
||||
");
|
||||
|
||||
// RANGE 파티셔닝 적용
|
||||
DB::connection('sam_stat')->statement("
|
||||
ALTER TABLE `{$table}`
|
||||
PARTITION BY RANGE COLUMNS(`stat_date`) (
|
||||
{$partitionDefs}
|
||||
)
|
||||
");
|
||||
}
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
$tables = $this->getTargetTables();
|
||||
|
||||
foreach ($tables as $table => $config) {
|
||||
$exists = DB::connection('sam_stat')
|
||||
->select("SHOW TABLES LIKE '{$table}'");
|
||||
|
||||
if (empty($exists)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 파티션 제거
|
||||
DB::connection('sam_stat')->statement("
|
||||
ALTER TABLE `{$table}` REMOVE PARTITIONING
|
||||
");
|
||||
|
||||
// PK 원복
|
||||
DB::connection('sam_stat')->statement("
|
||||
ALTER TABLE `{$table}`
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`id`)
|
||||
");
|
||||
}
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user