ThinkPHP 內置分表功能支援按 ID 範圍、取模等策略自動路由到不同物理表。本文詳細解析配置參數含義、getPartitionTableName 使用陷阱、實戰調試技巧。
<?php
// Model 中配置
protected $partition = array(
'field' => 'id', // 分表依據字段
'type' => 'id', // 分表類型:id(範圍)/mod(取模)/year/月/week/day
'expr' => '', // type=id 時的步長(如 10000 表示每 1 萬條一張表)
'num' => 10, // type=mod 時的表數量
);
?>
| type | 適用場景 | 算法邏輯 | expr/num 作用 |
|---|---|---|---|
| id | 自增 ID、時間遞增 | floor(id/step)+1 | expr=步長(必填) |
| mod | 均勻分布、無序 ID | (id%num)+1 | num=表數量(必填) |
| year/month/week/day | 時間序列數據 | 按時間格式化 | expr=日期格式 |
<?php
$model = M('User');
// 錯誤:未傳遞分表字段值
$table = $model->getPartitionTableName();
// 或傳遞錯誤鍵名
$table = $model->getPartitionTableName(array('uid' => 100)); // 分表字段是 id!
?>
<?php
$model = M('User');
// 必須包含分表字段(此處為 id)
$data = array('id' => 10001); // 或從表單/請求獲取
$table = $model->getPartitionTableName($data);
// 返回:user_2 (假設 expr=10000,floor(10001/10000)+1=2)
// 實際表名 = 表前綴 + 表名 + '_' + 序號
?>
<?php
$model = M('User');
$data = array(
'id' => 10001, // 必須包含分表字段
'username' => 'zhangsan',
'email' => 'zhangsan@example.com'
);
// 方式一:手動指定表(不推薦)
$table = $model->getPartitionTableName($data);
$model->table($table)->add($data);
// 方式二:框架自動路由(推薦,需 ThinkPHP 3.2+)
$model->add($data); // 內部自動調用 getPartitionTableName
?>
<?php
$model = M('User');
// 錯誤:無分表條件 → 聯合查詢所有分表(效能極差)
$list = $model->select();
// 正確:WHERE 條件包含分表字段
$list = $model->where('id=10001')->find(); // 自動路由到 user_2
// 手動指定表查詢
$table = $model->getPartitionTableName(array('id' => 10001));
$list = $model->table($table)->where('id=10001')->find();
?>
<?php
// 業務確實需要跨表查詢時
$model = M('User');
// 方法 1:union 查詢(需自己拼 SQL)
$sql = "SELECT * FROM user_1 WHERE status=1
UNION ALL
SELECT * FROM user_2 WHERE status=1";
$list = $model->query($sql);
// 方法 2:遍歷分表(小數量表可行)
$allData = array();
for ($i = 1; $i <= 10; $i++) {
$table = "user_$i";
$data = $model->table($table)->where('status=1')->select();
$allData = array_merge($allData, $data);
}
?>
getPartitionTableName 傳遞的數組鍵名不匹配(如分表字段是 id,傳了 uid)$partition 定義位置)<?php
// 你的表:table_1 ~ table_10,共 10 張
// ID 範圍:1-1000000
// 方案 A:按 ID 範圍(推薦,範圍查詢優化好)
'partition' => array(
'field' => 'id',
'type' => 'id',
'expr' => 100000, // 每 10 萬條一張表
'num' => 10
)
// ID 10001 → table_1, ID 110001 → table_2
// 方案 B:按取模(數據分布均勻,但範圍查詢需掃全表)
'partition' => array(
'field' => 'id',
'type' => 'mod',
'num' => 10
)
// ID 10001 → table_2 (10001%10+1=2), ID 10002 → table_3
?>
<?php
$model = M('User');
$model->startTrans();
try {
// 同一事務內操作必須落在同一分表
$data1 = array('id' => 10001, 'name' => 'A');
$data2 = array('id' => 10002, 'name' => 'B'); // 同表
$model->add($data1);
$model->add($data2);
$model->commit();
} catch (Exception $e) {
$model->rollback();
}
?>
跨分表事務 ThinkPHP 不支援,需自行實現分佈式事務或業務層補償。
<?php
// 開啟 SQL 日誌
'DB_SQL_LOG' => true,
'DB_LOG_FILE' => RUNTIME_PATH . 'sql.log',
// 手動驗證表名
$model = M('User');
echo $model->getPartitionTableName(array('id' => 10001)); // 輸出實際表名
// 查看生成 SQL
$model->where('id=10001')->find();
echo $model->getLastSql(); // 確認表名正確
?>
掌握分表核心邏輯與調試方法,即可輕鬆應對千萬級數據存儲挑戰。