分表分库方法总结

案例一:

1,背景:一个地址薄的应用程序,设计的用户量为2亿,统计出每个用户的地址薄为30个左右,整个数据量为60亿,使用mysql数据库
计划分为:1000个表,100个库 

2,分库分表代码

    private function getDbNo($email)  
    {  
        $m = md5($email);  
        $n = hexdec(substr($m, 0, 16));  
        $tableNo = fmod($n, 1000);  
        $dbNo = $tableNo % 100;  
        return array($dbNo, $tableNo);  
    }  

  3,配合的连接访问代码

require_once 'Db/Config.php';

class Db_Adapter
{
    const MASTER    = 0;
    const SLAVE     = 1;

    private static $instances = array();

    private $conf = array();

    private $conns = array();

    private $conn = NULL;
    private $stmt = NULL;
    
    public function __construct($conf)
    {
        $this->conf = $conf;
    }

    public function execute($sql, $params)
    {
        $cmd = substr(strtolower(trim($sql)), 0, 6);
        if ($cmd == 'select') {
            $conn = $this->getConn(self::SLAVE);
        } else {
            $conn = $this->getConn(self::MASTER);
        }

        $conn->prepare($sql);
        $stmt = $conn->execute($params);

        $this->conn = $conn;
        $this->stmt = $stmt;
    }

    public function fetch()
    {
        return $this->stmt->fetch();
    }

    public function fetchAll()
    {
        return $this->stmt->fetchAll();
    }

    public function lastInsertId($name = NULL)
    {
        return $this->conn->lastInsertId($name);
    }

    public function rowCount()
    {
        return $this->stmt->rowCount();
    }

    private function getConn($type)
    {
        if ($type == self::SLAVE && isset($this->conf[self::SLAVE])) {
            $id = 0;
        } else {
            $id = 1;
        }

        if (!isset($this->conns[$id])) {
            $conf = $this->conf[$id];
            $this->conns[$id] = new PDO(
                $conf['dsn'], $conf['user'], $conf['pass'],
                self::dbOptions);
        }

        return $this->conns[$id];
    }

    public static function getInstance($dbName, $dbNo = 0)
    {
        $key = $dbName . '_' . $dbNo;
        if (!isset(self::$instances[$key])) {
            $conf = Db_Config::getConfig($dbName, $dbNo); //连接配置参数
            self::$instances[$key] = new self($conf);
        }

        return self::$instances[$key];
    }
}

  
4,潜在问题
如果某个表中的那些用户的地址薄联系人超多,如每个人1000个,则可能出现该表超大,需要把该表区分为子表,暂时没有配置中心来处理该情况。
(若真的出现该情况,在连接参数这个地方继续作一次hash)。

转于:http://hanyh.iteye.com/blog/431323

 

 案例二:

<?php
include 'config.php';
class Model{
        //用户名
        protected $user;
        //密码
        protected $pwd;
        //主机
        protected $host;
        //库名,是一个数组
        protected $dbName=array();
        //字符集
        protected $charset='utf8';
        //连接资源是一个数组
        protected $_link=array();
        //通用表名
        protected $tabName;
        //真实表名
        protected $trueTabName;
        //表前缀
        protected $prefix;
        //字段缓存
        protected $fields;
        //创建表的sql语句
        protected $createSql='CREATE TABLE IF NOT EXISTS __TABLENAME__(
  `id` mediumint(9) NOT NULL AUTO_INCREMENT,
  `username` char(15) NOT NULL,
  `password` char(32) NOT NULL,
  `createtime` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;';

//1,通过ID取余,得到真实表名    mod
//2,用户名截取前几位   substr
//3,md5                        md5
//4,不带分库分表        none

        protected $partition=array(
                'type'=>'md5',        
                
                'rule'=>1,
        
        );


        public function __construct($tabName=''){
                $this->user=DB_USER;
                $this->host=DB_HOST;
                $this->dbName[0]=DB_NAME;
                $this->charset=DB_CHARSET;
                $this->prefix=DB_PREFIX;
                $this->pwd=DB_PWD;

                if(empty($tabName)){
                //userModel
                //newModel
                        $this->tabName=$this->prefix.ucfirst(strtolower(substr(get_class($this),0,-5)));

                }else{
                        $this->tabName=$this->prefix.$tabName;
                }

                $this->_link[0]=$this->connect($this->host,$this->user,$this->pwd,$this->dbName,$this->charset);

        }

        public function connect($host,$user,$pwd,$dbName,$charset,$linkId=0){
                $conn=mysql_connect($host,$user,$pwd);

                
                if(mysql_errno()){
                        $this->error(-1,$conn);
                        return false;
                }
        

                if(!$this->selectDb($dbName[$linkId],$conn)){
                        $this->error(-2,$conn);
                        return false;        
                }
                
                if(!$this->setCharset($charset,$conn)){
                        $this->error(-3,$conn);
                        return false;
                }
        

                return $conn;



        }


        public function selectDb($dbName,$conn){
                if(mysql_select_db($dbName,$conn)){

                        return true;
                }else{
                        return false;
                }        
        }

        public function setCharset($charset,$conn){
                if(mysql_set_charset($charset,$conn)){
                        return true;
                }else{
                        return false;
                }

        }

        public function addServer($host,$user,$pwd,$dbName,$charset,$linkId){
                $this->dbName[$linkId]=$dbName;
                $this->_link[$linkId]=$this->connect($host,$user,$pwd,$dbName,$charset,$linkId);

        }

        public function getTrueTable($content,$linkId=0){
                switch($this->partition['type']){
                        case 'mod':
                                if(!is_int($content)){
                                        $this->error(-4);
                                        return false;
                                }
                                $string=$content%$this->partition['rule'];
                                break;
                        case 'substr':
                                $string=substr($content,0,$this->partition['rule']);
                                break;
                        case 'md5':
                                $string=substr(md5($content),0,$this->partition['rule']);
                                break;
                        case 'none':
                                $string=null;
                                break;
                }

                if(empty($string)){
                        $this->trueTableName=$this->tabName;

                }else{
                        $this->trueTableName=$this->tabName.'_'.$string;
                }

                //第一,判断表是否存在,存在返回表字段缓存
                //第二,不存在,则创建表,返回字段缓存

                        $this->existsTable($this->trueTableName,$linkId);


        }
        //表是否存在
        //是否缓存了字段

        protected function existsTable($tableName,$linkId=0){
                $database=$this->dbName[$linkId];
                $sql='select `TABLE_NAME` from `INFORMATION_SCHEMA`.`TABLES` where `TABLE_SCHEMA`=\''.$database.'\' and `TABLE_NAME`=\''.$tableName.'\'';
        
                if($this->execute($sql,$linkId)){
                        //表存在
                        if(file_exists('cache/'.md5($this->tabName).'.php')){
                                $this->fields=include 'cache/'.md5($this->tabName).'.php';
                        }else{
                                //暂时留着不写,待会来写
                                $this->fields=$this->getFieldCache($linkId);
                        }

                }else{
                        //表不存在
                        $this->createTable($this->trueTableName,$linkId);
                        $this->fields=$this->getFieldCache($linkId);

                }

        }

        protected function getFieldCache($linkId=0){
                if(file_exists('cache/'.md5($this->tabName).'.php')){
                        $fields=include 'cache/'.md5($this->tabName).'.php';
                        return $fields;        
                }
                $sql="desc $this->trueTableName";
                $f=$this->query($sql,$linkId);
                
                $fields=$this->writeFields($f);

                return $fields;
                

        }

        protected function writeFields($f){
                foreach($f as $key=>$value){
                        $fields[]=$value['Field'];
                        
                        if($value['Key']=='PRI'){
                                $fields['_pk']=$value['Field'];
                        }
                        if($value['Extra']=='auto_increment'){
                                $fields['_auto']=$value['Field'];
                        }
                }
                $string="<?php \n return ".var_export($fields,true)."\n?>";
                
                file_put_contents('cache/'.md5($this->tabName).'.php',$string);
                return $fields;
                        
        }

        protected function createTable($tabName,$linkId=0){
                $sql=str_replace('__TABLENAME__',$tabName,$this->createSql);
        
                $this->execute($sql,$linkId);
        }

        //不需要返回结果集我用execute方法
        public function  execute($sql,$linkId=0){
                $conn=$this->_link[$linkId];
        
                $result=mysql_query($sql,$this->_link[$linkId]);
                if($result&&mysql_affected_rows()){
                
                        return mysql_affected_rows();
                }else{
                        return false;
                }

        }


        //需要返回结果集我用query方法
        public function query($sql,$linkId=0){
                $result=mysql_query($sql,$this->_link[$linkId]);
                
                if($result&&mysql_affected_rows()){
                        while($row=mysql_fetch_assoc($result)){

                                $rows[]=$row;
                        }
                }else{
                        return false;
                }
                return $rows;
        }


        public function error($num,$conn){
                switch($num){
                        case -1:
                                $string='连接数据库服务器失败'.mysql_error($conn);
                                break;
                        case -2:
                                $string='选择数据失败';
                                break;
                        case -3:
                                $string='设置字符集失败';
                                break;
                        case -4:
                                $string='数据库路由时选择的是取余,传入的不是整型';
                                break;
                }
        }

        


        //查最大值
        public function max($field,$linkId=0){
                if(!in_array($field,$this->fields)){
                        return false;
                }
                $sql="select max($field) as re from $this->trueTableName";
                $result=$this->query($sql,$linkId);
                $row=$result['re'];
                return $row;
                

        }        

        

        //查最小值
        public function min($field,$linkId=0){
                if(!in_array($field,$this->fields)){
                        return false;
                }
                $sql="select min($field) as re from $this->trueTableName";
                $result=$this->query($sql,$linkId);
                $row=$result['re'];
                return $row;
                

        }
        //求和
        public function sum($field,$linkId=0){
                if(!in_array($field,$this->fields)){
                        return false;
                }
                $sql="select sum($field) as re from $this->trueTableName";
                $result=$this->query($sql,$linkId);
                $row=$result['re'];
                return $row;
                

        }
        //最平均数
        public function avg($field,$linkId=0){
                if(!in_array($field,$this->fields)){
                        return false;
                }
                $sql="select avg($field) as re from $this->trueTableName";
                $result=$this->query($sql,$linkId);
                $row=$result['re'];
                return $row;
                

        }
        //求总数
        public function count($field='',$linkId=0){
                if(empty($field)){
                        $field=$this->fields['_pk'];
                }
                $sql="select count($field) as re from $this->trueTableName";
                $result=$this->query($sql,$linkId);
                $row=$result['re'];
                return $row;
        }
        //
        //删除
        public function delete($data,$where='',$linkId=0,$order='',$limit=''){
                //delete from 表  where 字段  order by  字段 limit 
                
                if(is_array($data)){
                        $value=join(',',$data);
                }else{
                        $value=(int)$data;
                }
                $fields=$this->fields['_pk'];

                if(empty($where)){

                        $sql="delete from $this->trueTableName where $fields in ($value)";
                }else{
                        $where='where '.$where;
                        if(!empty($order)){
                                $order='order by '.$order;        
                        }
                        if(!empty($limit)){
                                $limit='limit '.$limit;        
                        }

                        $sql="delete from $this->trueTableName $where $order $limit";
                }
                return $this->execute($sql,$linkId);
        }
        //
        //修改
        public function save($data,$where,$linkId=0,$order='',$limit=''){

                //update 表  set 字段=值,字段=值 where 条件 order  limit
                $key=array_keys($data);
                $newKey=array_intersect($key,$this->fields);
        
                foreach($data as $key=>$value){
                        if(!in_array($key,$newKey))
                                continue;
                        $update.=$key.'="'.$value.'",';
                
                }
                $update=rtrim($update,',');
                
                if(!empty($order)){
                        $order='order by '.$order;
                }
                if(!empty($limit)){
                        $limit='limit '.$limit;
                }

                if(!empty($where)){
                        $where='where '.$where;
                }


                $sql="update $this->trueTableName set $update $where $order $limit";

                echo $sql;
                $result=$this->execute($sql,$linkId);
                return $result;

        }



        //增加
        public function add($data,$linkId=0){
                //insert into 表(字段) values(值)
                $key=array_keys($data);
                $newKey=array_intersect($key,$this->fields);
                foreach($data as $key=>$value){
                        if(!in_array($key,$newKey))
                                continue;
                        $values.="'".$value."',";
                }
                $values=trim($values,',');
                $fields=join(',',$newKey);
                $sql="insert into $this->trueTableName($fields) values($values)";
                echo $sql;
                $result=$this->execute($sql,$linkId);
                return $result;
        }


        //单条查询
        public function find($linkId=0,$where='',$order=''){
                //select * from 表 where  order  limit 1
                $field=join(',',$this->fields);
                if(!empty($where)){
                        $where='where '.$where;
                }
                if(!empty($order)){
                        $order='order by '.$order;
                }
                $sql="select $field from $this->trueTableName $where $order limit 1";
                $result=$this->query($sql,$linkId);
                return $result[0];
                
        }

        //多条查询
        public function select($field='',$linkId=0,$where='',$order='',$limit=''){
                //select * from 表 where  order  limit
                if(empty($field)){
                        $fields=join(',',$this->fields);
                }else{
                        if(is_array($field)){
                                $newKey=array_intersect($field,$this->fields);
                                $fields=implode(',',$newKey);
                        }else{
                                $fields=$field;
                        }
                }
                if(!empty($where)){
                        $where='where '.$where;
                }
                if(!empty($order)){
                        $order='order by '.$order;
                }
                if(!empty($limit)){
                        $limit='limit '.$limit;
                }
                $sql="select $fields from $this->trueTableName $where $order $limit";
                $result=$this->query($sql,$linkId);
                return $result;
                
        }
        //按照字段来查询数据


        function __call($name,$param){
                $key=substr($name,0,5);
                if(strtolower($key)=='getby'){
                        $field=strtolower(substr($name,5));
                
                        if(!in_array($field,$this->fields)){
                                return false;
                        }
                        $f=join(',',$this->fields);
                        $value=$param[0];
                        $sql="select $f  from $this->trueTableName where $field='$value'";
        
                        $result=$this->query($sql);
                        return $result[0];

                }
        }



}

?>

  

 案例三:1亿条数据在PHP中实现Mysql数据库分表100张

首先创建100张表:

$i=0;
while($i<=99){
echo "$newNumber \r\n";
$sql="CREATE TABLE `code_".$i."` (
 `full_code` char(10) NOT NULL,
 `create_time` int(10) unsigned NOT NULL,
 PRIMARY KEY  (`full_code`),
) ENGINE=MyISAM DEFAULT CHARSET=utf8";
mysql_query($sql);
$i++; 

 full_code作为主键,我们对full_code做hash

$table_name=get_hash_table('code',$full_code);

function get_hash_table($table,$code,$s=100){
$hash = sprintf("%u", crc32($code));
echo $hash;
$hash1 = intval(fmod($hash, $s));
 return $table."_".$hash1;
} 

 最后我们使用merge存储引擎来实现一张完整的code表

CREATE TABLE IF NOT EXISTS `code` (  
`full_code` char(10) NOT NULL,
`create_time` int(10) unsigned NOT NULL,
INDEX(full_code)  
) TYPE=MERGE UNION=(code_0,code_1,code_2.......) INSERT_METHOD=LAST ; 

 这样我们通过select * from code就可以得到所有的full_code数据了。

posted @ 2014-05-09 11:27  一束光  阅读(609)  评论(0编辑  收藏  举报

友情链接

CFC4N