單表超過百萬筆資料時,查詢與寫入效能下降。ThinkPHP 內建分表支援,可透過配置自動路由至對應子表(house_member_0~_3)。
<?php
namespace app\common\model;
use think\Model;
class MemberModel extends Model
{
// 分表配置:依 username 進行 hash 取模 4
protected $partition = [
'field' => 'username',
'type' => 'md5',
'num' => 4,
];
// 取得對應分表的查詢物件
public function getDao($data = [])
{
$data = empty($data) ? request()->post() : $data;
$table = $this->getPartitionTableName($data);
return $this->table($table);
}
}
<?php
namespace app\controller;
use app\common\model\MemberModel;
use think\facade\Request;
class MemberController
{
public function login()
{
if (Request::isPost()) {
$this->validateCsrfToken();
$dao = (new MemberModel())->getDao();
$user = $dao->where('username', Request::post('username'))->find();
return $this->jsonResponse($user ? ['status' => 1, 'data' => $user] : ['status' => 0, 'msg' => '帳號不存在']);
}
return view();
}
}
public function getPartitionTableName($data = [])
{
if (isset($data[$this->partition['field']])) {
$field = $data[$this->partition['field']];
switch ($this->partition['type']) {
case 'id':
$step = $this->partition['expr'] ?? 1000000;
$seq = floor($field / $step) + 1;
break;
case 'year':
$field = is_numeric($field) ? $field : strtotime($field);
$expr = $this->partition['expr'] ?? date('Y');
$seq = date('Y', $field) - $expr + 1;
break;
case 'mod':
$seq = ($field % $this->partition['num']) + 1;
break;
case 'md5':
$seq = (ord(substr(md5($field), 0, 1)) % $this->partition['num']) + 1;
break;
default:
if (function_exists($this->partition['type'])) {
$fun = $this->partition['type'];
$seq = (ord(substr($fun($field), 0, 1)) % $this->partition['num']) + 1;
} else {
$seq = (ord($field[0]) % $this->partition['num']) + 1;
}
}
return $this->getTableName() . '_' . $seq;
} else {
// 無分表欄位條件:聯合查詢所有分表(需謹慎使用)
$tables = [];
for ($i = 0; $i < $this->partition['num']; $i++) {
$tables[] = 'SELECT * FROM ' . $this->getTableName() . '_' . $i;
}
return '(' . implode(' UNION ALL ', $tables) . ') AS ' . $this->name;
}
}
| 策略 | 適用場景 | 優點 | 缺點 |
|---|---|---|---|
| id 範圍 | 自增 ID,按時間自然增長 | 範圍查詢高效,易歸檔 | 熱點集中在最新分表 |
| 年份 | 日誌、訂單等強時間屬性 | 天然按年隔離,便於歸檔刪除 | 跨年查詢需 UNION |
| 取模 | ID 分布均勻 | 資料均勻分佈 | 擴容需重新分表 |
| MD5 Hash | 字串欄位(用戶名、Email) | 分布均勻,支援非數值欄位 | 無法範圍查詢 |