首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

ThinkPHP 分表實戰:從配置到查詢的完整避坑指南

     編輯:PHP 相關知識

前言

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)+1expr=步長(必填)
mod均勻分布、無序 ID(id%num)+1num=表數量(必填)
year/month/week/day時間序列數據按時間格式化expr=日期格式

二、核心陷阱:getPartitionTableName 參數傳遞

錯誤用法(導致聯合查詢/找不到表)

<?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)
// 實際表名 = 表前綴 + 表名 + '_' + 序號
?>

三、實戰場景演示

場景 1:寫入數據(自動路由)

<?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
?>

場景 2:查詢數據(必須指定分表條件)

<?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();
?>

場景 3:跨分表查詢(慎用)

<?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);
}
?>

四、常見問題與解決

1. 為什麼總是聯合查詢?

  • 查詢條件中缺少分表字段
  • getPartitionTableName 傳遞的數組鍵名不匹配(如分表字段是 id,傳了 uid)
  • 模型未正確載入分表配置(檢查 $partition 定義位置)

2. type=id 還是 type=mod?

<?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
?>

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();  // 確認表名正確
?>

掌握分表核心邏輯與調試方法,即可輕鬆應對千萬級數據存儲挑戰。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved