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

ThinkPHP 分表機制實作:百萬級用戶資料水平切分

     編輯:PHP 相關知識

場景

單表超過百萬筆資料時,查詢與寫入效能下降。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();
    }
}

核心分表邏輯解析(getPartitionTableName)

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)分布均勻,支援非數值欄位無法範圍查詢

遷移與擴容建議

  1. 雙寫遷移:新舊分表邏輯並行,校驗一致後切換。
  2. 一致性 Hash:引入中間層(Proxy/ShardingSphere)實現無縫擴容。
  3. 唯一鍵處理:分表後全域唯一鍵需額外機制(雪花演算法、UID Generator)。
  4. 跨分表查詢:盡量避免;必要時在應用層聚合或引入 ES 同步索引。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved