合併不同伺服器的資料表(thinkphp5+mysql)
阿新 • • 發佈:2018-12-15
本認第一次寫部落格,可能有些地方表達不清晰,望大家見諒;
最近業務有個需求,需要將不同渠道的會員資訊合併在一起,然後插入一張表中(每個渠道的會員資料在不同伺服器,這點是操蛋的)
自己先百度了一哈,網上好像沒有此類的文章,所以自己決定把這次的經驗分享出來;
先說哈自己的整體思路:
- 先把其它伺服器的資料庫表複製對映到本地
- 合併成一張表
- 迴圈合併的資料插入到本地資料庫表中並處理自己業務的邏輯
下面圖文講解
- 首先需要把mysql的FEDERATED引擎開啟,預設是關閉的,可以通過show engines 命令檢視;
在mysql配置檔案加上紅色標註的
準備工作已ok,下面貼上程式碼
- 1.配置資訊
- 2.把其它伺服器資料表對映到本地
public function syncMemberTable() { // thinking :不同伺服器的資料表 通過fea引擎 把其它伺服器的表 對映到本地資料庫 在把所有的平臺的member資料 合併成 在迴圈寫入更新資料 $merge_sql = 'create table ' . $this->member_table_merge . ' as '; //連結池 foreach ($this->dsns as $key => $dsn) { $channel_table = $this->member_table . '_' . $key; // 建立對映關係 $show_create_sql = ' show create table ' . $this->member_table; $create_table_sql_result = Db::connect($dsn)->query($show_create_sql); $create_table_sql = null; $create_table_sql = $create_table_sql_result[0]['Create Table']; // 把 table 名和engine 修改。並增加 connect 語句 $fed_channel_table = $channel_table . '_FED'; //本地是否已經存在 $fed_channel_table_has_sql = ' show tables like ' . '\'' . $fed_channel_table . '\'';//show tables like 後面表名比需要引號 $fed_channel_table_has_result = Db::execute($fed_channel_table_has_sql);//有 1 沒有 0 if ($fed_channel_table_has_result == 0) { Log::record("原來的sql:" . $create_table_sql, 'INFO'); $new_create_fed_sql = ''; $new_create_fed_sql = str_replace($this->member_table, $fed_channel_table, $create_table_sql); $new_create_fed_sql = str_replace("InnoDB", 'FEDERATED ', $new_create_fed_sql); $user = $dsn['username']; $password = $dsn['password']; $host = $dsn['hostname']; $database = $dsn['database']; $connection = " CONNECTION='mysql://$user:
[email protected]$host:3306/$database/$this->member_table'"; $new_create_fed_sql = $new_create_fed_sql . $connection; Log::record("替換FED的sql:" . $new_create_fed_sql, 'INFO'); $new_create_result = Db::execute($new_create_fed_sql);//執行對映sql if ($new_create_result !== false) { $msg = "$key 渠道的$this->member_table" . '表 對映到本地資料庫成功'; echo $msg; Log::record($msg, 'INFO'); } else { $msg = "$key 渠道的$this->member_table" . '表 對映到本地資料庫失敗'; echo $msg; Log::record($msg, 'INFO'); exit();//出錯不執行 } } //對映成功後 在本地在新建一張表 因為對映的表不能操作表 //先檢視是否存在 $new_create_table_has_sql = ' show tables like ' . '\'' . $channel_table . '\''; $result = Db::execute($new_create_table_has_sql);//存在1 不存在0 if ($result == 0) { /*--------------------------------------------------*/ Log::record('---------------starting create table ------------------', 'debug'); $new_create_sql = ''; //$new_create_sql = str_replace($this->member_table, $channel_table, $create_table_sql); $new_create_sql = ' create table ' . $channel_table . ' as select * from ' . $fed_channel_table; Log::record("create的sql:" . $new_create_sql, 'INFO'); $result = Db::execute($new_create_sql);//執行建表 if ($result != false) { $msg = "$key 渠道的$this->member_table" . '表 copy到本地資料庫成功'; echo $msg; Log::record($msg, 'INFO'); } else { $msg = "$key 渠道的$this->member_table" . '表 copy到本地資料庫失敗,Error:' . $result; echo $msg; Log::record($msg, 'INFO'); exit(); } } //判斷欄位是否存在 $fields = Db::getTableInfo($channel_table, 'fields'); if (!in_array('channel_id', $fields)) { //複製成功後新增渠道欄位 後面做邏輯處理 渠道欄位直接放在id後面 $channel_id = $dsn['channel_id']; $channel_sql = " ALTER TABLE `$channel_table` ADD COLUMN `channel_id` int(10) UNSIGNED NOT NULL DEFAULT $channel_id AFTER `id`"; $channel_result = Db::execute($channel_sql); if ($channel_result != false) { $msg = "$key 渠道的$this->member_table" . '表 的渠道ID欄位新增成功'; echo $msg; Log::record($msg, 'INFO'); //新增成功後可以刪除多餘的對映表 Db::execute('DROP TABLE ' . $fed_channel_table); } else { $msg = "$key 渠道的$this->member_table" . '表 的渠道ID欄位新增失敗'; echo $msg; Log::record($msg, 'INFO'); exit(); } } $keys = array_keys($this->dsns); $merge_sql .= ' select distinct * from ' . $channel_table . ($key != end($keys) ? ' union all ' : ''); } Log::record($merge_sql, 'INFO'); //不渠道的表合併成 $result = Db::execute($merge_sql); if ($result == false) { echo $msg = $this->member_table . '合併表建立失敗!'; Log::record($msg, 'INFO'); } else { echo $msg = $this->member_table . '合併表建立成功!'; Log::record($msg, 'INFO'); } }
上面大概的思路是通過show create table 把需要合併的表的建立mysql 獲取,然後把引擎改成FEDERATED,然後在本地再次建立一份檢視或者表,因為我這裡需要新增一個channel_id欄位到其它伺服器對映到本地的表中,但是FEDERATED引擎不能操作表,所以需要在多一步步驟;
表建立過後,然後通過mysql 的 union 或者 union all 合併成一張表,到此其它伺服器的表在本地資料庫已合併在一起,有資料了接下來就好辦了;
效果:
某個伺服器的表
合併過後的表
- 3.同步資料
//同步會員表資料
public function SynMemberTableData()
{
//把會員插入的資訊另寫一個日誌
Log::init([
'type' => 'File',
'path' => LOG_PATH . 'MemberLog' . DS,
]);
$ts = microtime(true);
Log::record("-----------------start寫入會員資訊---------執行時間-----------" . date("H:i:s"), 'INFO');
Debug::remark('begin');
//統計一共有多少使用者
$total = Db::table($this->member_table_merge)->group('mobile,channel_id')->count();
Log::record('一共有' . $total . '個使用者', 'INFO');
if (!empty($total)) {
//程序處理資料條數
$limit = 10000;
$pages = ceil($total / $limit);
for ($i = 1; $i <= $pages; $i++) {
$members = Db::table($this->member_table_merge)->group('mobile,channel_id')->order('channel_id')->page($i, $limit)->select();
//多程序
foreach ($members as $k => $member) {
//同個渠道的人是否已經有了
$has = $this->member_model->where(['mobile' => $member['mobile'], 'channel_id' => $member['channel_id']])->count();
if (empty($has)) {
unset($member['id']);
$insert_id = $this->member_model->insertGetId($member);
if ($insert_id) {
$msg = '使用者:' . $member['username'] . '[channel_id=' . $member['channel_id'] . ']資料插入成功,插入ID為:' . $insert_id;
echo $msg;
Log::record($msg, 'INFO');
} else {
$msg = '使用者:' . $member['username'] . '[channel_id=' . $member['channel_id'] . ']資料插入失敗';
echo $msg;
Log::record($msg, 'Error');
continue;
}
} else {
continue;
}
unset($member[$k]);//unset()
}
//
// $reserveProcess = new \Swoole\Process(function ($worker) use ($i, $limit, $member_model, $member_dis_model) {
//
//
//
// }, false);
Debug::remark('end');
Log::record("-----------------會員資訊寫入end-------------結束時間-------" . date("H:i:s"), 'INFO');
Log::record('寫入耗時' . (microtime(true) - $ts) . 's ', 'debug');
Log::record('所耗記憶體' . Debug::getRangeMem('begin', 'end') . 'kb', 'debug');
Log::save();
echo "end SynMemberTableData task..." . (microtime(true) - $ts) . "s " . "\r\n";
}
}
$this->SynMemberTableData();//
}
插入到本地資料庫的表
到此大致上已完成了自己的業務需求了;
希望本文能夠對你有些幫助