1. 程式人生 > >分享一個 MySQL 分庫分表類(php)

分享一個 MySQL 分庫分表類(php)

當一個表資料記錄過大時就會出現效能瓶頸,而一般對應的解決辦法是要麼做分割槽表,要麼分表,分割槽表就不說了,分表又分為垂直分割和水平分割,具體區別請自行搜尋。一般而言,分庫分表屬於水平分割,按照一定的規則將資料插入到不同的表中去。而分庫則可以很方便的轉移資料庫的壓力,比如將一個很大庫的分別放在不同的伺服器上。

下面是我寫的一個分庫分表的實現:

<?php
/**
 * User: guoyu
 * Date: 14-8-12
 * Time: 下午3:16
 */
 
namespace App\Model\Database;
 
class Config
{
    public $dsn;
    public $user;
    public $password;
    /**
     * @var string 分庫分表後得到的資料庫名
     */
    public $dbname;
    /**
     * @var string 分庫分表後得到的表名
     */
    public $table;
 
    /**
     * @var array MySQL 配置陣列
     */
    private static $config;
 
    /**
     * @var string 配置檔案路徑
     */
    private static $configFile = 'mysql.php';
 
    public function __construct($dbname, $table, $id = 0)
    {
        if (is_null(static::$config)) {
            $config = include(static::$configFile);
            static::$config = $config;
        }
 
        $config = static::$config;
        if (isset($config['shared']) && isset($config['shared'][$dbname])) {
            $dbconfig = $config['shared'][$dbname];
            $id = is_numeric($id) ? (int)$id : crc32($id);
            $database_id = ($id / $dbconfig['database_split'][0]) % $dbconfig['database_split'][1];
            $table_id = ($id / $dbconfig['table_split'][0]) % $dbconfig['table_split'][1];
 
            foreach ($dbconfig['host'] as $key => $conf) {
                list($from, $to) = explode('-', $key);
                if ($from <= $database_id && $database_id <= $to) {
                    $the_config = $conf;
                }
            }
 
            $this->dbname = $dbname . '_' . $database_id;
            $this->table = $table . '_' . $table_id;
        } else {
            $this->dbname = $dbname;
            $this->table = $table;
            $the_config = $config['db'][$dbname];
        }
        $c = $the_config;
        if (isset($c['unix_socket']) && $c['unix_socket']) {
            $this->dsn = sprintf('mysql:dbname=%s;unix_socket=%s', $this->dbname, $c['unix_socket']);
        } else {
            $this->dsn = sprintf('mysql:dbname=%s;host=%s;port=%s', $this->dbname, $c['host'], $c['port']);
        }
        $this->user = $c['user'];
        $this->password = $c['password'];
    }
 
}

Config 類就做一個事情,根據配置檔案,去拿到對應的庫和表的連結配置,然後客戶可以根據 dsn 去連結對應的資料庫。對應的配置檔案如下:
<?php
/**
 * User: guoyu
 * Date: 14-8-6
 * Time: 上午11:19
 */
 
$default = array(
    'unix_socket' => null,
    'host' => 'localhost',
    'port' => '3306',
    'user' => 'root',
    'password' => '',
);
 
$config = array(
    // 不進行分庫分表的資料庫
    'db' => array(
        'my_site' => $default,
    ),
    // 分庫分表
    'shared' => array(
        'user' => array(
            'host' => array(
                /**
                 * 編號為 0 到 10 的庫使用的連結配置
                 */
                '0-10' => $default,
                /**
                 * 編號為 11 到 28 的庫使用的連結配置
                 */
                '11-28' => $default,
                /**
                 * 編號為 29 到 99 的庫使用的連結配置
                 */
                '29-99' => $default,
 
            ),
 
            // 分庫分表規則
            /**
             * 下面的配置對應百庫百表
             * 如果根據 uid 進行分表,假設 uid 為 543234678,對應的庫表為:
             *  (543234678 / 1) % 100 = 78 為編號為 78 的庫
             *  (543234678 / 100) % 100 = 46 為編號為 46 的表
             */
            'database_split' => array(1, 100),
            'table_split' => array(100, 100),
        ),
    ),
);
 
 
return $config;

給出一個使用這個分庫分表的例子:
<?php
/**
 * User: guoyu
 * Date: 14-8-6
 * Time: 上午10:23
 */
 
namespace App\Model;
 
use App\Model\Database\Config;
use \PDO;
 
abstract class Model
{
    /**
     * @var Config
     */
    public $config;
 
    /**
     * @var PDO
     */
    public $connection;
 
    protected $dbnamePrefix;
    protected $tablePrefix;
 
    /**
     * @var string 分庫分表後對應的表
     */
    protected $table;
 
    public function __construct($id)
    {
        $this->config = new Config($this->dbnamePrefix, $this->tablePrefix, $id);
        $this->connection = new Pdo($this->config->dsn, $this->config->user, $this->config->password);
        $this->table = $this->config->table;
    }
 
    public function update(array $data, array $where = array())
    {
 
    }
 
    public function select(array $where)
    {
 
    }
 
    public function insert(array $data)
    {
 
    }
 
    public function query($sql)
    {
        return $this->connection->query($sql);
    }
}

下面這個例子展示瞭如何使用上述的 Model 類:
<?php
/**
 * User: guoyu
 * Date: 14-8-12
 * Time: 下午4:06
 */
 
require 'Config.php';
require 'Model.php';
 
use App\Model\Model;
 
class User extends Model
{
    protected $dbnamePrefix = 'user';
    protected $tablePrefix = 'userinfo';
}
 
$user = new User(4455345345);
 
print_r($user);