mysql分库分表实战及php代码操作完整实例
生活随笔
收集整理的這篇文章主要介紹了
mysql分库分表实战及php代码操作完整实例
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
當單表達到幾千萬時,查詢一次要很久,如果有聯合查詢,有可能會死在那
分庫分表主要就是解決這個問題,減小數據庫的負擔,縮短查詢時間
分庫:
1)按功能分
用戶類庫、商品類庫、訂單類庫、日志類、統計類庫...
1)按地區分
每個城市或省市一個同樣的庫,加上后綴或前綴如:db_click_bj、db_click_sh...
分表:
1、橫向分表 解決表記錄太大問題
1)按某個字段分,
如:discuz的附件表分成10個附件分表pre_forum_attachment_0到pre_forum_attachment_9,還有1個附件索引表pre_forum_attachment存儲tid和附件id關系
根據主題的tid最后一位來決定附件要保存在哪個分表,
2)按日期分表
一些日志、統計類的可以按年、月、日、周分表
如:點擊量統計click_201601、click_201602
3)使用mysql的merge
先把分表創建好,然后創建總表指定engine= MERGE UNION=(table1,table2) INSERT_METHOD = LAST;
1)經常組合查詢的列放在一個表,常用字段的表可考慮Memory引擎
2)不經常使用的字段單獨成表
3)把text、blob等大字段拆分放在附表
如:phpcms的文章表分成主表v9_news和從表v9_news_data,主表存標題、關鍵字、瀏覽量等,從表存具體內容、模板等
$this->db(1)->query("查詢sql");//下次直接使用編號1定位
[php]?view plaincopy <?php?? ini_set('memory_limit',?'-1');?? $con=mysql_connect("192.168.1.198","open","123456");?? if($con){????? ????for($i=0;$i<10;$i++){//10個庫?? ????????$sql="drop?database?cloude_{$i};";//刪庫?謹慎?? ????????mysql_query($sql);?? ????????$sql="create?database?cloude_{$i}?default?character?set?utf8?collate?utf8_general_ci;";?? ????????$do=mysql_query($sql,$con)or?die(mysql_error());?? ????????if($do){?? ????????????mysql_select_db("cloude_{$i}",$con);?? ????????????mysql_query("set?name?gtf8");?? ????????????for($j=0;$j<10;$j++){????????//10個表?? ????????????????$sql="drop?table?if?exists?user_{$j};";?? ????????????????mysql_query($sql);?? ????????????????$sql="create?table?user_{$j}?? ????????????????(?? ????????????????????id?char(36)?not?null?primary?key,?? ????????????????????name?char(15)?not?null?default?'',?? ????????????????????password?char(32)?not?null?default?'',?? ????????????????????sex?char(1)?not?null?default?'男'?? ????????????????)engine=InnoDB;";?? ????????????????$do=mysql_query($sql,$con)?or?die(mysql_error());?? ????????????????if($do){?? ????????????????????//echo?"create?table?user_{$j}?successful!?<br/>";?? ????????????????}else{?? ????????????????????//echo?"create?error!";?? ????????????????}?? ????????????}?? ????????}?? ????}?? }else{?? ????echo?"connect?error!!!!";?? }??
2、分庫分表路由實現
<?php?? class?Config{???? ????public?$dsn;???? ????public?$user;???? ????public?$password;???? ????public?$dbname;?//分庫分表后得到的數據庫名?? ????public?$table;?//分庫分表后得到的表名?? ????private?static?$config;//mysql配置數組?? ????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'];???? ????}???? ?????? }??
mysql.php
[php]?view plaincopy <?php?? $default?=?array(???? ????'unix_socket'?=>?null,???? ????'host'?=>?'192.168.1.198',???? ????'port'?=>?'3306',???? ????'user'?=>?'open',???? ????'password'?=>?'123456',???? );???? $db_199?=?array(???? ????'unix_socket'?=>?null,???? ????'host'?=>?'192.168.1.199',???? ????'port'?=>?'3306',???? ????'user'?=>?'open',???? ????'password'?=>?'123456',???? );??????? $config?=?array(???? ????//?不進行分庫分表的數據庫???? ????'db'?=>?array(???? ????????'hadoop'?=>?$default,???? ????),???? ????//?分庫分表???? ????'shared'?=>?array(???? ????????'cloude'?=>?array(???? ????????????'host'?=>?array(???? ????????????????/**?? ?????????????????*?編號為?0?到?4?的庫使用的鏈接配置?? ?????????????????*/??? ????????????????'0-4'?=>?$default,???? ????????????????/**?? ?????????????????*?編號為?5?到?9?的庫使用的鏈接配置?? ?????????????????*/??? ????????????????'5-9'?=>?$db_199,????? ?????? ????????????),???? ?????? ????????????//?分庫分表規則???? ????????????/**?? ?????????????*?下面的配置對應10庫10表? ?????????????*?如果根據?uid?進行分表,假設?uid?為?224,對應的庫表為:?? ?????????????*??(224?/?1)?%?10?=?4?為編號為?4?的庫?? ?????????????*??(224?/?10)?%?10?=?1?為編號為?2?的表?? ?????????????*/??? ????????????'database_split'?=>?array(1,?10),???? ????????????'table_split'?=>?array(10,?10),???? ????????),???? ????),???? );???? return?$config;??
4、模型類操作數據庫
<?php?? require_once?'Config.php';//引入配置信息?? class?Model{???? ????public?$config;?????//數據庫配置?? ????public?$connection;?//pdo?? ????protected?$dbnamePrefix;?//庫前綴如cloude_50?前綴為cloude??? ????protected?$tablePrefix;??//表前綴?? ????protected?$dbname;??//分庫分表后對應的庫?? ????protected?$table;???//分庫分表后對應的庫表?? ?????? ????public?function?__construct($id){???? ????????$this->config?=?new?Config($this->dbnamePrefix,?$this->tablePrefix,?$id);??????????????????//根據id找到對應庫和表?? ????????$this->connection?=?new?Pdo($this->config->dsn,?$this->config->user,?$this->config->password);//實例化pdo???? ????????$this->connection->exec("set?names?utf8");????? ????????$this->dbname?=?$this->config->dbname;?? ????????$this->table?=?$this->config->table;???? ????}???? ?????? ????public?function?update(array?$data,?array?$where?=?array()){???? ?????? ????}???? ?????? ????public?function?select(array?$condition){??? ????????$sqlwhere='';?? ????????if(!empty($condition)){????? ????????????foreach?($condition?as?$field?=>?$value)?{???? ????????????????$where[]?=?'`'.$field.'`='."'".addslashes($value)."'";???? ????????????}???? ????????????$sqlwhere?.=?'?'.implode('?and?',?$where);????? ????????}?? ????????$sql="select?*?from?".$this->dbname.'.'.$this->table;?? ????????if($sqlwhere){?? ????????????$sql.="?where?$sqlwhere";?? ????????}?? ????????$res=$this->connection->query($sql);?? ????????$data['data']=$res->fetchAll(PDO::FETCH_ASSOC);?? ????????$data['info']=array("dsn"=>$this->config->dsn,"dbname"=>$this->dbname,"table"=>$this->table,"sql"=>$sql);?? ????????return?$data;????? ????}???? ????public?function?insert(array?$arrData)?{?? ????????$name?=?$values?=?'';?? ????????$flag?=?$flagV?=?1;?? ????????$true?=?is_array(?current($arrData)?);//判斷是否一次插入多條數據?? ????????if($true)?{?? ????????????//構建插入多條數據的sql語句?? ????????????foreach($arrData?as?$arr)?{?? ????????????????$values?.=?$flag???'('?:?',(';?? ????????????????foreach($arr?as?$key?=>?$value)?{?? ????????????????????if($flagV)?{?? ????????????????????????if($flag)?$name?.=?"$key";?? ????????????????????????$values?.=?"'$value'";?? ????????????????????????$flagV?=?0;?? ????????????????????}?else?{?? ????????????????????????if($flag)?$name?.=?",$key";?? ????????????????????????$values?.=?",'$value'";?? ????????????????????}?? ????????????????}?? ????????????????$values?.=?')?';?? ????????????????$flag?=?0;?? ????????????????$flagV?=?1;?? ????????????}?? ????????}?else?{?? ????????????//構建插入單條數據的sql語句?? ????????????foreach($arrData?as?$key?=>?$value)?{?? ????????????????if($flagV)?{?? ????????????????????$name?=?"$key";?? ????????????????????$values?=?"('$value'";?? ????????????????????$flagV?=?0;?? ????????????????}?else?{?? ????????????????????$name?.=?",$key";?? ????????????????????$values?.=?",'$value'";?? ????????????????}?? ????????????}?? ????????????$values?.=?")?";?? ????????}?? ??????????? ????????$sql?=?"insert?into?".$this->dbname.'.'.$this->table."?($name)?values?$values";?? ????????if(?($rs?=?$this->connection->exec($sql)?)?>?0?)?{?? ????????????return?array("dsn"=>$this->config->dsn,"dbname"=>$this->dbname,"table"=>$this->table,"sql"=>$sql);?? ????????}?? ????????return?false;?? ????}?? ????public?function?query($sql){???? ????????return?$this->connection->query($sql);???? ????}???? }??
5、測試
<?php?? require?'Config.php';???? require?'Model.php';???? class?User?extends?Model???? {???? ????protected?$dbnamePrefix?=?'cloude';???? ????protected?$tablePrefix?=?'user';???? }??? //生成唯一uuid?? function?create_uuid($prefix?=?""){????//可以指定前綴?? ????$str?=?md5(uniqid(mt_rand(),?true));????? ????$uuid??=?substr($str,0,8)?.?'-';????? ????$uuid?.=?substr($str,8,4)?.?'-';????? ????$uuid?.=?substr($str,12,4)?.?'-';????? ????$uuid?.=?substr($str,16,4)?.?'-';????? ????$uuid?.=?substr($str,20,12);????? ????return?$prefix?.?$uuid;?? }?? ?? $userId=create_uuid();?? $user?=?new?User($userId);?? $data=array('id'=>$userId,'name'=>'大明'.$userId,'password'=>'14e1b600b1fd579f47433b88e8d85291','sex'=>'男');???? if($result=$user->insert($data)){?? ????echo?'插入成功:','<pre/>';?? ????print_r($result);?? }?? ?? $condition=array("id"=>$userId);?? $list=$user->select($condition);?? if($list){?? ????echo?'查詢成功:','<pre/>';?? ????print_r($list);?? }??
6、結果
插入成功會返回插入到哪個庫哪個表,查詢成功返回從哪個庫哪個表查的
1、維度問題
假如用戶購買了商品,需 要將交易記錄保存取來,如果按照用戶的緯度分表,則每個用戶的交易記錄都保存在同一表中,所以很快很方便的查找到某用戶的購買情況,但是某商品被購買的情 況則很有可能分布在多張表中,查找起來比較麻煩。反之,按照商品維度分表,可以很方便的查找到此商品的購買情況,但要查找到買人的交易記錄比較麻煩。
所以常見的解決方式有:
通過掃表的方式解決,此方法基本不可能,效率太低了。
記錄兩份數據,一份按照用戶緯度分表,一份按照商品維度分表。
通過搜索引擎解決,但如果實時性要求很高,又得關系到實時搜索
2、避免分表join操作 因為關聯的表有可能不在同一數據庫中
3、避免跨庫事務
避免在一個事務中修改db0中的表的時候同時修改db1中的表,一個是操作起來更復雜,效率也會有一定影響
4、分表宜多不宜少;這樣做主要是為了盡量避免后期可能遇到的二次拆分
5、盡量把同一組數據放到同一DB服務器上
分庫分表主要就是解決這個問題,減小數據庫的負擔,縮短查詢時間
分庫:
1)按功能分
用戶類庫、商品類庫、訂單類庫、日志類、統計類庫...
1)按地區分
每個城市或省市一個同樣的庫,加上后綴或前綴如:db_click_bj、db_click_sh...
分表:
1、橫向分表 解決表記錄太大問題
1)按某個字段分,
如:discuz的附件表分成10個附件分表pre_forum_attachment_0到pre_forum_attachment_9,還有1個附件索引表pre_forum_attachment存儲tid和附件id關系
根據主題的tid最后一位來決定附件要保存在哪個分表,
2)按日期分表
一些日志、統計類的可以按年、月、日、周分表
如:點擊量統計click_201601、click_201602
3)使用mysql的merge
先把分表創建好,然后創建總表指定engine= MERGE UNION=(table1,table2) INSERT_METHOD = LAST;
2、縱向分表 解決列過多問題
1)經常組合查詢的列放在一個表,常用字段的表可考慮Memory引擎
2)不經常使用的字段單獨成表
3)把text、blob等大字段拆分放在附表
如:phpcms的文章表分成主表v9_news和從表v9_news_data,主表存標題、關鍵字、瀏覽量等,從表存具體內容、模板等
很多主流mvc框架都提供了切換數據庫訪問方法
thinkphp切換數據庫
$this->db(1)->query("查詢sql");//下次直接使用編號1定位
分表的話 在查詢前先根據分表規則把表名查出
這里用兩臺機子簡單以同個業務庫分庫,同個表分表,演示插入、查詢如何定位庫和表并最終成功執行
兩臺機子:
server1:192.168.1.198
server2:192.168.1.199
兩臺機子都執行下面操作
1、先創建10個數據庫,每個數據庫10個表
當然也可以改成百庫百表,也可手動創建,我為了方便寫了個腳本批量創建
create.php[php]?view plaincopy
2、分庫分表路由實現
Config.PHP
[php]?view plaincopy
3、數據庫配置文件
mysql.php
[php]?view plaincopy
4、模型類操作數據庫
Model.PHP
[php]?view plaincopy
5、測試
使用主鍵id作為分表字段,那最好就不要使用自增了,可使用uuid
User.php
[php]?view plaincopy6、結果
插入成功會返回插入到哪個庫哪個表,查詢成功返回從哪個庫哪個表查的
1、維度問題
假如用戶購買了商品,需 要將交易記錄保存取來,如果按照用戶的緯度分表,則每個用戶的交易記錄都保存在同一表中,所以很快很方便的查找到某用戶的購買情況,但是某商品被購買的情 況則很有可能分布在多張表中,查找起來比較麻煩。反之,按照商品維度分表,可以很方便的查找到此商品的購買情況,但要查找到買人的交易記錄比較麻煩。
所以常見的解決方式有:
通過掃表的方式解決,此方法基本不可能,效率太低了。
記錄兩份數據,一份按照用戶緯度分表,一份按照商品維度分表。
通過搜索引擎解決,但如果實時性要求很高,又得關系到實時搜索
2、避免分表join操作 因為關聯的表有可能不在同一數據庫中
3、避免跨庫事務
避免在一個事務中修改db0中的表的時候同時修改db1中的表,一個是操作起來更復雜,效率也會有一定影響
4、分表宜多不宜少;這樣做主要是為了盡量避免后期可能遇到的二次拆分
5、盡量把同一組數據放到同一DB服務器上
例如將賣家a的商品和交易信息都放到db0中,當db1掛了的時候,賣家a相關的東西可以正常使用。也就是說避免數據庫中的數據依賴另一數據庫中的數據
文章來源http://blog.csdn.NET/nuli888/article/details/52143065
來源:http://blog.csdn.net/weixin_29778143/article/details/72236453
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的mysql分库分表实战及php代码操作完整实例的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 聚影行车记录仪s9怎么样?
- 下一篇: 基于mysqli封装的数据库类