本文目录导读:

在 PHP 中进行 MySQL 分区表维护,通常有以下几种方式和最佳实践:
使用 SQL 命令直接维护
添加分区
<?php
$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'password');
// 添加 RANGE 分区
try {
$sql = "ALTER TABLE orders ADD PARTITION (
PARTITION p2024_04 VALUES LESS THAN (TO_DAYS('2024-05-01'))
)";
$pdo->exec($sql);
echo "分区添加成功";
} catch (PDOException $e) {
echo "添加失败: " . $e->getMessage();
}
?>
删除分区
<?php
// 删除旧分区(适合归档历史数据)
try {
$sql = "ALTER TABLE orders DROP PARTITION p2023_01, p2023_02";
$pdo->exec($sql);
echo "分区删除成功";
} catch (PDOException $e) {
echo "删除失败: " . $e->getMessage();
}
?>
合并分区
<?php
// 重新组织分区
$sql = "ALTER TABLE orders REORGANIZE PARTITION p2024_01, p2024_02 INTO (
PARTITION p2024_Q1 VALUES LESS THAN (TO_DAYS('2024-04-01'))
)";
$pdo->exec($sql);
?>
封装分区维护类
<?php
class PartitionManager {
private $pdo;
public function __construct(PDO $pdo) {
$this->pdo = $pdo;
}
/**
* 获取表的所有分区信息
*/
public function getPartitions($tableName) {
$sql = "SELECT
PARTITION_NAME,
PARTITION_METHOD,
PARTITION_EXPRESSION,
PARTITION_DESCRIPTION,
TABLE_ROWS,
DATA_LENGTH
FROM information_schema.PARTITIONS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = :table";
$stmt = $this->pdo->prepare($sql);
$stmt->execute(['table' => $tableName]);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
/**
* 检查分区是否存在
*/
public function partitionExists($tableName, $partitionName) {
$partitions = $this->getPartitions($tableName);
foreach ($partitions as $partition) {
if ($partition['PARTITION_NAME'] === $partitionName) {
return true;
}
}
return false;
}
/**
* 自动添加下月分区
*/
public function addNextMonthPartition($tableName, $dateColumn) {
$nextMonth = date('Y-m', strtotime('+1 month'));
$partitionName = 'p' . str_replace('-', '_', $nextMonth);
if (!$this->partitionExists($tableName, $partitionName)) {
// 获取下下个月的开始日期作为边界
$boundary = date('Y-m-01', strtotime('+2 month'));
$sql = "ALTER TABLE {$tableName} ADD PARTITION (
PARTITION {$partitionName}
VALUES LESS THAN (TO_DAYS('{$boundary}'))
)";
try {
$this->pdo->exec($sql);
return "成功添加分区: {$partitionName}";
} catch (PDOException $e) {
return "添加分区失败: " . $e->getMessage();
}
}
return "分区已存在: {$partitionName}";
}
/**
* 自动清理过期分区(保留最近N个月)
*/
public function cleanupOldPartitions($tableName, $keepMonths = 6) {
$partitions = $this->getPartitions($tableName);
$cutoffDate = date('Y-m-01', strtotime("-{$keepMonths} months"));
foreach ($partitions as $partition) {
if (isset($partition['PARTITION_DESCRIPTION'])) {
$boundary = $partition['PARTITION_DESCRIPTION'];
// 解析分区边界值(假设是 TO_DAYS 格式)
if (is_numeric($boundary)) {
$date = date('Y-m-d', strtotime("1970-01-01 + $boundary days"));
if ($date < $cutoffDate && $partition['PARTITION_NAME'] !== 'p_maxvalue') {
$sql = "ALTER TABLE {$tableName} DROP PARTITION {$partition['PARTITION_NAME']}";
$this->pdo->exec($sql);
echo "已删除过期分区: {$partition['PARTITION_NAME']}\n";
}
}
}
}
}
/**
* 重建分区(优化数据)
*/
public function rebuildPartition($tableName, $partitionName) {
$sql = "ALTER TABLE {$tableName} REBUILD PARTITION {$partitionName}";
try {
$this->pdo->exec($sql);
return "分区重建成功: {$partitionName}";
} catch (PDOException $e) {
return "重建失败: " . $e->getMessage();
}
}
/**
* 分析分区(更新统计信息)
*/
public function analyzePartition($tableName, $partitionName = null) {
if ($partitionName) {
$sql = "ALTER TABLE {$tableName} ANALYZE PARTITION {$partitionName}";
} else {
$sql = "ALTER TABLE {$tableName} ANALYZE PARTITION ALL";
}
try {
$this->pdo->exec($sql);
return "分区分析完成";
} catch (PDOException $e) {
return "分析失败: " . $e->getMessage();
}
}
}
// 使用示例
$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'password');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$manager = new PartitionManager($pdo);
// 查看分区信息
$partitions = $manager->getPartitions('orders');
print_r($partitions);
// 添加下月分区
echo $manager->addNextMonthPartition('orders', 'created_at');
// 清理6个月前的分区
$manager->cleanupOldPartitions('orders', 6);
// 重建某个分区
echo $manager->rebuildPartition('orders', 'p2024_03');
?>
定时任务实现自动维护
<?php
// cron_job.php - 通过 crontab 每天执行
class PartitionScheduler {
private $pdo;
public function __construct() {
$this->pdo = new PDO(
'mysql:host=localhost;dbname=test',
'user',
'password',
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
);
}
public function run() {
$tables = [
'orders' => ['column' => 'created_at', 'keep_months' => 12],
'logs' => ['column' => 'log_date', 'keep_months' => 3]
];
$manager = new PartitionManager($this->pdo);
foreach ($tables as $table => $config) {
echo "处理表: {$table}\n";
// 添加未来3个月的分区
for ($i = 1; $i <= 3; $i++) {
$month = date('Y-m', strtotime("+{$i} month"));
$partitionName = 'p' . str_replace('-', '_', $month);
if (!$manager->partitionExists($table, $partitionName)) {
$boundary = date('Y-m-01', strtotime("+" . ($i + 1) . " month"));
$sql = "ALTER TABLE {$table} ADD PARTITION (
PARTITION {$partitionName}
VALUES LESS THAN (TO_DAYS('{$boundary}'))
)";
try {
$this->pdo->exec($sql);
echo " 添加分区: {$partitionName}\n";
} catch (Exception $e) {
echo " 错误: " . $e->getMessage() . "\n";
}
}
}
// 清理过期分区
$manager->cleanupOldPartitions($table, $config['keep_months']);
// 分析表
$this->pdo->exec("ANALYZE TABLE {$table}");
}
// 记录日志
file_put_contents(
'/var/log/partition_maintenance.log',
date('Y-m-d H:i:s') . " - 分区维护完成\n",
FILE_APPEND
);
}
}
// 执行任务
$scheduler = new PartitionScheduler();
$scheduler->run();
?>
注意事项和最佳实践
安全执行
<?php
// 使用事务和错误处理
function safePartitionOperation(PDO $pdo, $sql) {
try {
$pdo->beginTransaction();
$pdo->exec($sql);
$pdo->commit();
return true;
} catch (Exception $e) {
$pdo->rollBack();
error_log("分区操作失败: " . $e->getMessage());
return false;
}
}
?>
监控和维护策略
<?php
// 分区健康检查
function checkPartitionHealth(PDO $pdo, $tableName) {
$sql = "SELECT
PARTITION_NAME,
TABLE_ROWS,
ROUND(DATA_LENGTH/1024/1024, 2) as '数据大小(MB)',
ROUND(INDEX_LENGTH/1024/1024, 2) as '索引大小(MB)'
FROM information_schema.PARTITIONS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = :table
ORDER BY PARTITION_ORDINAL_POSITION";
$stmt = $pdo->prepare($sql);
$stmt->execute(['table' => $tableName]);
echo "分区健康报告 - {$tableName}\n";
echo str_repeat('-', 50) . "\n";
foreach ($stmt->fetchAll() as $row) {
echo sprintf(
"分区: %-15s 行数: %-10s 数据: %-10s MB 索引: %-10s MB\n",
$row['PARTITION_NAME'],
number_format($row['TABLE_ROWS']),
$row['数据大小(MB)'],
$row['索引大小(MB)']
);
}
}
?>
PHP 维护 MySQL 分区表的关键点:
- 预创建分区 - 提前创建未来分区,避免数据写入失败
- 定期清理 - 删除过期分区,控制表大小
- 监控状态 - 定期检查分区使用情况
- 异常处理 - 所有操作都要有完整的错误处理
- 定时自动化 - 使用 Cron 等工具实现自动化维护
建议在非高峰时段执行分区维护操作,并始终记得备份数据。