当一个表数据记录过大时就会出现性能瓶颈,而一般对应的解决办法是要么做分区表,要么分表,分区表就不说了,分表又分为垂直分割和水平分割,具体区别请自行搜索。一般而言,分库分表属于水平分割,按照一定的规则将数据插入到不同的表中去。而分库则可以很方便的转移数据库的压力,比如将一个很大库的分别放在不同的服务器上。
下面是我写的一个分库分表的实现:
<?php /** * User: jing.liu * 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: jing.liu * 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: jing.liu * 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: jing.liu * 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);
如果看官您有任何疑问或者有更好的实现,欢迎交流~