開發環境
- Yii1版本
- MySQL
- PHP5.6.27
前言
物流規則匹配日志表記錄訂單匹配規則相關日志信息,方便管理員維護和查閱不匹配的訂單,四個月時間,該日志表數據就有174G,當前,這么大的數據量,不僅對數據庫造成了很大的負載壓力,同時查詢等維護也緩慢,所以采取將日志記錄移出到文件進行存儲。但是短期內,還需要數據庫中的部分日志記錄,故而有了下面的刪除記錄、優化表操作。
日志表大小一覽
表本身有六七百萬條數據,從六七百萬刪到五百多萬,發現數據占用空間大小一點也沒變,如下圖所示。網上查到需要釋放刪除了的數據占用的空間、也就是優化表或碎片整理,使用到的命令是:
OPTIMIZE TABLE tableName。

問題出現原因
在刪除sql語句中,寫法如下:DELETE FROM ueb_logistics_rule_logs WHERE type=0 LIMIT 100; 凡是這樣,delete帶有where條件的,都不是真刪除,只是MySQL給記錄加了個刪除標識,自然這樣操作后表數據占有空間也不會變小了
注意:DELETE FROM
ueb_logistics_rule_logs; 這條sql語句執行后,就清空了表數據,占有空間就變為0了

解決方法
主要就是執行下面三條sql語句(輪詢刪除delete,避免一次性刪除數據太多造成MySQL負載崩潰,另外數據量大的時候需要等待網站訪問流量小的時候執行)
- DELETE FROM
ueb_logistics_rule_logsWHERE type=0 LIMIT 100; - OPTIMIZE TABLE
ueb_logistics_rule_logs;
源碼
基於yii1版本框架,命令行下執行,可以加入定時腳本任務里,自動執行
<?php
/**
* 定時清理相關表數據
* Class Logisticstable
*/
class LogisticstableCommand extends CConsoleCommand
{
public $before15Days;
public $oneMonthAgo;
public $halfYearAgo;
/**
* 清理表數據
*
* windows環境:
* >yiic.bat Logisticstable Execute
* >yiic.bat Logisticstable Execute 10
* linux環境:
* >./yiic Logisticstable Execute
* >./yiic Logisticstable Execute 10
*
* @param int $limit 清理的條數
*/
public function actionExecute($limit = 100)
{
$sTime = microtime(true);
$this->initDaytime();
$dbList = $this->tableData();
$cacheKey = 'command:logisticstable:%s:%s';
foreach ($dbList as $db=>$tableList){
foreach ($tableList as $tableInfo){
$tableName = $tableInfo['tableName'];
$cacheKeyTmp = sprintf($cacheKey, $db, $tableName);
$isRunning = Redis::getCache($cacheKeyTmp);
if (!empty($isRunning)){
//當前表正在清理中……
continue;
}
Redis::setCache($cacheKeyTmp, 1, 36000);
//輪詢刪除表數據
while(!$this->myDeleteData($db, $tableName, $tableInfo['whereStr'], $limit)){
sleep(3);
}
//優化表空間
$this->myOptimizeTable($db, $tableName);
Redis::deleteCache($cacheKeyTmp);
sleep(10);
}
}
echo '共計耗時:'. (microtime(true)-$sTime).' s'.PHP_EOL;
exit(0);
}
/**
* 刪除表數據
* @param $db
* @param $tableName
* @param $where
* @param int $limit
* @return bool
*/
protected function myDeleteData($db, $tableName, $where, $limit = 1000)
{
//連接數據庫
$connection = Yii::app()->$db;
$findSql = "SELECT * FROM `".$tableName."` WHERE $where ";
$arrRow = $connection->createCommand($findSql)->queryRow();
if (empty($arrRow)){
//沒有要刪除的數據了
return true;
}
//刪除
$deleteSql = "DELETE FROM `".$tableName."` WHERE $where LIMIT $limit ";
$connection->createCommand($deleteSql)->execute();
return false;
}
/**
* 釋放被刪除數據占用的空間
* @param $db
* @param $tableName
* @return mixed
*/
protected function myOptimizeTable($db, $tableName)
{
//連接數據庫
$connection = Yii::app()->$db;
//優化表空間
$optimizeSql = "OPTIMIZE TABLE `".$tableName."`";
return $connection->createCommand($optimizeSql)->execute();
}
/**
* 初始化時間
*/
protected function initDaytime()
{
$this->before15Days = date('Y-m-d H:i:s', strtotime('-15 day'));
$this->oneMonthAgo = date('Y-m-d H:i:s', strtotime('-1 month'));
$this->halfYearAgo = date('Y-m-d H:i:s', strtotime('-6 month'));
}
/**
* 相關表數據
*/
protected function tableData()
{
return [
'db_logistics'=>[
[
//check_task_is_run 保留最新15天
'tableName'=>'check_task_is_run',
'whereStr'=> "create_time < '{$this->before15Days}'"
],
[
//check_task_run_get_logistict_cost 30天
'tableName'=>'check_task_run_get_logistict_cost',
'whereStr'=> "create_time < '{$this->oneMonthAgo}'"
],
[
//ueb_logistics_rule_opration_log 半年
'tableName'=>'ueb_logistics_rule_opration_log',
'whereStr'=> "create_time < '{$this->before15Days}'"
],
]
];
}
}
總結
慚愧,今天才碰到這類問題,MySQL delete表數據,之前一直的感覺就是直接刪除掉了,數據所占的空間也自然會釋放,但是今天才發現,delete sql語句后加where條件刪除的數據MySQL會自動加個delete標識而不會真的刪掉。學的太淺了,還需多多努力!
