1. 程式人生 > >億級資料遷移實戰方案,真實案例

億級資料遷移實戰方案,真實案例

背景:
公司某業務模組的使用者通訊記錄和通話記錄的表,單表記錄過億了,mysql很堅挺好不好!!!!
其中某表是205773235 約2.0億
另外某表是575213155 約5.7億
使用者數量 約100W
其中通訊錄有的使用者是有很多重複記錄的,之前設計資料庫的同事按條來存的,也就是說一個使用者會有多條記錄,還有重複的記錄(沒有去重),歷史遺留問題那就不廢話了,我們存在的意義就是把不合理的設計去規範化來
如: id:1 uid:10086 name: 馬雲爸爸 num:88888
id:1 uid:10086 name: 馬雲爸爸 num:88888
這樣的資料表設計在資料量少的時候是沒有問題的,不去重這點會佔用額外的空間,其實這些不太常用而且有很多一對多關係的資料可以存json或者直接用OSS來存的(ps我們技術顧問推薦的儲存方式)
大概思路:
1.是把100W的使用者拆分成10份,每次迴圈10W個使用者的資料上傳
2.打通oss介面,檔案儲存按照日期+小時這樣子儲存檔案
3.儲存方式json檔案方式儲存
4.資料安全方面,用動態密匙加密json,動態的密匙需要按規律儲存好(因為檔案要開放公共讀的,處於對使用者資料的安全考慮得加密)
5.上傳成功返回url儲存好
異常處理:
1.檔案上傳必然會有上傳失敗的,每個檔案迴圈上傳,如果上傳成功,則跳出迴圈,否則程式休息0.5秒繼續上傳,最大限度迴圈10次,如果還是上傳失敗,把這個使用者的uid push到redis的連結串列裡面
2.檔案上傳完成後儲存上傳路徑,同理儲存也有可能儲存失敗,也是上面的套路,做一個迴圈,如果儲存一次成功了就跳出迴圈,否則資訊0.5秒繼續發起儲存sql,成功推出迴圈,失敗10次儲存uid到redis上面
3.每10分鐘檢查一次redis裡面的上傳失敗或者儲存使用者資料失敗的使用者,如果超過500條則繼續上傳,這裡的套路和1.2點同理,如果還是失敗,到這裡還失敗的,其實使用者已經失敗了20次了,那麼應該屬於重災區,把這些使用者的uid儲存到資料庫裡面
4.1.2.3步驟都有和檔案日誌結合的,我的目標是不要有問題,少有問題!
最終實踐:
1.其實我沒有直接拿使用者表的uid,因為有小部分的使用者是沒有資料的,我建立了一張表,專門來儲存符合調教的使用者的uid,匯入適合的資料的命令如花:

INSERT INTO 某臨時表 (uid) 
select * from (SELECT ct.uid FROM (select max(id) as id from  某符合條件的表啦 group by uid ) pct join  某符合條件的表啦 ct on ct.id=pct.id ) as xxx

2.一步步找到之前的儲存資料的方式改成現在的模式
3.各種測試方法的可行性

具體遷移方法:

    /**
     *  通話記錄遷移 written : yangxingyi date: 2017-09-11
     */
    public function send_phone
() {
ini_set('memory_limit','10240M');//限制記憶體10GB $start = trim(I('start')); $end = trim(I('end'));//2017-09-18 15:47 區間1000-100000 ($start>$end)&&exit("輸入範圍有誤!"); (!$start||!$end)&&exit("沒有輸入條件!"); $uids = 某使用者表模型->where("id between $start and $end"
)->field('uid')->select(); $uids||exit('沒有符合條件的資料!'); file_log('send_phone','正常資料的開始時間','phone'); $oss = new OssCore(); $que = 例項化redis; $t1 = $t2 = time();//記錄耗時 $bucket = self::$contact_bucket; // 儲存空間 $result = $save_res =''; $success_count = $false_count = 0;//記錄成功失敗條數 $total_count = count($uids);//記錄總條數 while(list($index,$item)=each($uids)) { $uid = $item['uid']; $exist_oss_path = 某url表->where(array('uid' => $uid)) ->order("id desc")->field('oss_path')->find(); if($exist_oss_path['oss_path']){ file_log('send_phone','已上傳通訊錄的uid: '.$uid,'phone'); continue;//跳過已經上傳過內容的使用者 } $info = 某通話記錄表->where(array('uid' => $uid))->field('name,number')->select(); if(!$info||!is_array($info)){ file_log('send_phone','沒有通訊錄的uid: '.$uid,'phone'); continue;//跳過沒有內容的使用者 } $path = 'phone/' . date("Ymd") . '/' . date('H') . '/' . $uid; $content = 某加密函式(json_encode(make_array_uinque($info,'name','number')),self::$contact_key_prefix.某隨機字串); //組裝資料,並且根據name,number欄位去重 for($i=0;$i<10;$i++){ $result = $oss->uploadToOss($path, $content,$bucket ); //uploadToOss(路徑,內容,$bucket="儲存空間") if($result){ break; }else{ usleep(500);file_log('send_phone','上傳oss失敗uid: '.$uid." 次數".$i,'phone'); } } //echo $result['info']['url']."<hr>";oss返回的url for($i=0;$i<10;$i++){ $save_res = $this->save_oss_path('',$result['info']['url'],$uid);//id是pk(這裡沒pk),傳第二,第三個引數 if($save_res){ break; }else{ usleep(500);file_log('send_phone','修改資料庫失敗uid: '.$uid." 次數".$i,'phone'); } } if(($index>0&&$index%1000)==0){ $t1000 = time()- $t1; file_log('send_phone','千條耗時(秒): '.$t1000,'phone'); $t1 = time(); } if(($index>0&&$index%10000)==0){ $t10000 = time()- $t2; file_log('send_phone','萬條耗時(秒): '.$t10000,'phone'); $t2 = time(); } //異常處理 if (!$result||!$save_res) { file_log('send_phone','上傳失敗的uid: '.$uid,'phone'); $que->lpush('send_phone_ids_error', $uid); $false_count++; }else{ $que->incr('send_phone');//ok的數目 $success_count++;//成功條數 }//異常處理完畢 unset($uids[$index]);//悉放記憶體 } $tt = time()-$t1; file_log('send_phone','結束時間,已上傳總條數:'.$que->get('send_phone'). ",本次總條數:$total_count,成功:$success_count ,失敗:$false_count 消耗時間(秒):$tt,id區間$start-$end",'phone'); }

異常處理函式,每10分鐘執行一次:

    /**
     *  儲存資料和異常處理 written : yangxingyi date: 2017-09-12 11:45 Email: [email protected]
     */
    public function  handle_exception(){
        $oss = new OssCore();
        $que = 例項化redis;
        $bucket = self::$contact_bucket; // 儲存空間
        $list_count = $que->llen('send_phone_ids_error');
        $time = time();
        if ($list_count > 500){
            $error_ids = $que->lrange('send_phone_ids_error', 0, -1);//返回來的一維陣列
            $que->del('send_phone_ids_error');//返回來資料就幹掉它
            file_log('handle_exception','大於500條異常資料的開始時間','phone');

            $result = $save_res = '';
            foreach($error_ids as $item)
            {
                $uid = $item;
                $info = 某源資料表->where(array('uid' => $uid))->field('name,number')->select();
                if(!$info||!is_array($info)){
                    continue;//跳過沒有內容的使用者
                }
                $path = 'phone/' . date("Ymd") . '/' . date('H') . '/' . $uid;
                $content = 加密函式(json_encode(make_array_uinque($info,'name','number')),self::$contact_key_prefix.什麼鬼字串);//組裝資料
                for($i=0;$i<10;$i++){
                    $result = $oss->uploadToOss($path, $content,$bucket );//uploadToOss(路徑,內容,$bucket="儲存空間")
                    if($result){
                        break;
                    }else{
                        sleep(1);
                    }
                }

                for($i=0;$i<10;$i++){
                    $save_res = $this->save_oss_path('',$result['info']['url'],$uid);
                    //id是主鍵索引(這沒有),傳遞兩個引數,需要傳第三個引數uid
                    if($save_res){
                        break;
                    }else{
                        sleep(1);
                    }
                }

                //第二次的異常處理
                if ($result&&$save_res)
                {
                    $que->incr('send_phone');
                }else{
                    file_log('handle_exception','第二次的異常處理uid: '.$uid,'phone');
                    儲存錯誤的uid表->add(array('uid'=>$uid,'time'=>$time));
                }

            }
        }
    }

二維陣列去重的方法:

function make_array_uinque($arr,$type1='username',$type2='uid')
{
    $arr_out = $arr_wish = [];
    foreach($arr as $k => $v)
    {
        $key_out = $v["$type1"]."-".$v["$type2"];//提取內部一維陣列的key(任意值)作為外部陣列的鍵

        if(array_key_exists($key_out,$arr_out)){
            continue;
        }else{
            $arr_out[$key_out] = $v; //以key_out作為外部陣列的鍵
            $arr_wish[$k] = $v;  //實現二維陣列唯一性
        }
    }
    return $arr_wish;
}

如果您讀到了這裡,謝謝各位看官,看完這麼枯燥的文章!!!方法有不足的地方,歡迎各位大神指教!!!如果覺得對您有用的話歡迎轉載,如果您有什麼好文章也歡迎跟我分享!!!小弟不勝感激!!!!

相關推薦

資料遷移實戰方案,真實案例

背景: 公司某業務模組的使用者通訊記錄和通話記錄的表,單表記錄過億了,mysql很堅挺好不好!!!! 其中某表是205773235 約2.0億 另外某表是575213155 約5.7億 使用者數量 約100W 其中通訊錄有的使用者是有很多重複記

資料多條件組合查詢——秒響應解決方案

1 概述 組合查詢為多條件組合查詢,在很多場景下都有使用。購物網站中通過勾選類別、價格、銷售量範圍等屬性來對所有的商品進行篩選,篩選出滿足客戶需要的商品,這是一種典型的組合查詢。在小資料量的情況下,後臺通過簡單的sql語句便能夠快速過濾出需要的資料,但隨著資料量

MySQL作為新的NoSQL解決方案:輕鬆應對資料

MySQL現在是一個更好的NoSQL解決方案。我們這樣說是因為在儲存 鍵/值(key/value) 之類資料時, MySQL 具有效能、易用性和穩定性方面的優勢。MySQL引擎穩定可靠,並且社群和官方支援良好,有非常豐富的線上資料, 涵蓋了各種操作、故障排查,複製以及各種

MySQL 作為新的 NoSQL 解決方案: 輕鬆應對資料

MySQL現在是一個更好的NoSQL解決方案。我們這樣說是因為在儲存 鍵/值(key/value) 之類資料時, MySQL 具有效能、易用性和穩定性方面的優勢。MySQL引擎穩定可靠,並且社群和官方支援良好,有非常豐富的線上資料, 涵蓋了各種操作、故障排查,複製以及各種使用

基於分散式關係型資料庫,實現輕鬆應對百資料分析場景解決方案

MyCat是什麼? 從定義和分類來看,它是一個開源的分散式資料庫系統,是一個實現了MySQL協議的伺服器,前端使用者可以把它看作

基於關係型資料庫和ES搜尋引擎,實現多源,百資料的大資料分析方案

背景: 隨著公司各項業務的快速發展與擴張,伺服器和各種應用系統隨之而增加,同時對應用系統、伺服器的穩定性,可持續性提出了更高的要

百萬資料遷移方案測評小記

### 前言 最近公司在使用 `ABP` 重構之前的老專案,資料庫也由 `SQL SERVER` 切換到了 `MySql`。吐槽一下,之前的產品使用的是 `Windows Server 2008` , `SqlServer 2008R2`, `.Net Framework 4.5`,現在開始擁抱 `.net

基於TableStore的訂單管理解決方案

互聯 全量 hub 增量 termquery last cfb 數據表 創建表 摘要: 一、方案背景 訂單系統存在於各行各業,如電商訂單、銀行流水、運營商話費賬單等,是一個非常廣泛、通用的系統。對於這類系統,在過去十幾年發展中已經形成了經典的做法。但是隨著互聯網的發展,以及

如何用elasticsearch構架資料採集系統(第1集:非生產環境windows安裝篇)

(一)做啥的? 基於Elasticsearch,可以為實現,大資料量(億級)的實時統計查詢的方案設計,提供底層資料框架。 本小節jacky會在非生產環境下,在 window 系統下,給大家分享著部分的相關內容。 (二)Elasticsearch的安裝 2.1 版本選擇:ela

MySQL 資料需求的優化思路(二),100資料,1萬字段屬性的秒檢索

最近在研究億級資料的時候,無意中看到了一個關於寫58同城的文章 https://blog.csdn.net/admin1973/article/details/55251499?from=timeline 其實上面講的version+ext的方式以及壓縮json的思路,對於我來講都可以看得懂

MySQL 資料需求的優化思路(一),交易流水記錄的查詢

對MySQL的效能和億級資料的處理方法思考,以及分庫分表到底該如何做,在什麼場景比較合適? 比如銀行交易流水記錄的查詢  限鹽少許,上實際實驗過程,以下是在實驗的過程中做一些操作,以及踩過的一些坑,我覺得坑對於讀者來講是非常有用的。 首先:建立一個現金流量表,交易歷史是各個金融

MongoDB副本集配置和資料遷移實戰

MongoDB副本集配置和資料遷移實戰 https://gitee.com/et/ops/blob/master/MongoDB副本集配置和資料遷移實戰.md 環境:Ubuntu 16.04, MongoDB 3.6 基本概念 MongoDB 的副本集就是

如何判斷一個元素在資料中是否存在?

前言 最近有朋友問我這麼一個面試題目: 現在有一個非常龐大的資料,假設全是 int 型別。現在我給你一個數,你需要告訴我它是否存在其中(儘量高效)。 需求其實很清晰,只是要判斷一個數據是否存在即可。 但這裡有一個比較重要的前提:非常龐大的資料。 常規實現 先不考慮這個條件,我們腦海中

流量系統架構之如何支撐百資料的儲存與計算

“本文聊一下筆者幾年前所帶的團隊負責的多個專案中的其中一個,用這個專案來聊聊一個億級流量系統架構演進的過程。 一、背景引入 首先簡單介紹一下專案背景,公司對合作商家提供一個付費級產品,這個商業產品背後涉及到數百人的研發團隊協作開發,包括各種業務系統來提供很多強大的業務功能,同時在整個平臺

綜合微軟、AMiner兩大學術圖譜,清華大學唐傑博士如何將Open Academic Graph資料精準匹配

 AI 科技評論按:近日,清華大學副教授、Arnetminer 創始人唐傑博士在微博上公開了開放學術組織(Open Academic Society)釋出的億級學術圖譜——Open Academic Graph。據唐傑博士介紹,該圖譜目前集成了兩個最大的公開學術圖譜:微軟學術圖譜(MAG)

Java架構-流量系統架構之如何支撐百資料的儲存與計算

“本文聊一下筆者幾年前所帶的團隊負責的多個專案中的其中一個,用這個專案來聊聊一個億級流量系統架構演進的過程。 一、背景引入 首先簡單介紹一下專案背景,公司對合作商家提供一個付費級產品,這個商業產品背後涉及到數百人的研發團隊協作開發,包括各種業務系統來提供很多強大的業

Java架構/如何判斷一個元素在資料中是否存在?

前言 最近有朋友問我這麼一個面試題目: 現在有一個非常龐大的資料,假設全是 int 型別。現在我給你一個數,你需要告訴我它是否存在其中(儘量高效)。 需求其實很清晰,只是要判斷一個數據是否存在即可。 但這裡有一個比較重要的前提:非常龐大的資料。 常規實現 先

布隆過濾---判斷一個元素在資料中是否存在

如何判斷一個元素在億級資料中是否存在? https://www.cnblogs.com/crossoverJie/p/10018231.html 前言 最近有朋友問我這麼一個面試題目: 現在有一個非常龐大的資料,假設全是 int 型別。現在我給你一個數,你需要告訴我它是否存在其中(儘量高效)。 需求其

如何判斷一個元素在資料中是否存在?--布隆過濾

本文轉載自微信公眾號:crossoverJie,Hollis 前言 最近有朋友問我這麼一個面試題目: 現在有一個非常龐大的資料,假設全是 int 型別。現在我給你一個數,你需要告訴我它是否存在其中(儘量高效)。 需求其實很清晰,只是要判斷一個數據是否存在即可。

如何判斷一個元素是否在資料是否存在

先闡述一下問題: 現在有一個非常龐大的資料,假設全是 int 型別。給出一個數,判斷這個數是否在其中(儘可能的高效)。 題目要求 文章給出了思路:首先想到的是 Hash 演算法,它的時間複雜度是 O(1),在常量時間判斷出資料是否存在。文章給出的辦法是直接使用了 Java 的集合物件 Hash