PHP實現(xiàn)文本快速查找 - 二分查找法

起因

先說說事情的起因哪审,最近在分析數(shù)據(jù)時經(jīng)常遇到一種場景毫痕,代碼需要頻繁的讀某一張數(shù)據(jù)庫的表寒瓦,比如根據(jù)地區(qū)ID獲取地區(qū)名稱、根據(jù)網(wǎng)站分類ID獲取分類名稱柏锄、根據(jù)關(guān)鍵詞ID獲取關(guān)鍵詞等酿箭。雖然以上需求都可以在原始建表時,通過冗余數(shù)據(jù)來解決趾娃。但仍有部分業(yè)務(wù)存的只是關(guān)聯(lián)表的ID缭嫡,數(shù)據(jù)分析時需要頻繁的查表。

所讀的表存在共同的特點

  • 數(shù)據(jù)幾乎不會變更
  • 數(shù)據(jù)量適中茫舶,從一萬到100多萬械巡,如果全加載到內(nèi)存也不太合適。

糾結(jié)的地方

在做數(shù)據(jù)分析時,需要十分頻繁的讀這些表讥耗,每秒有可能需要讀上萬次有勾。其實內(nèi)部的數(shù)據(jù)庫集群完全可以勝任,但會對線上業(yè)務(wù)稍有影響古程。(你懂得蔼卡,小公司不可能為離線分析做一套完整的數(shù)據(jù)存儲服務(wù)。大部分?jǐn)?shù)據(jù)分析還要借助線上的數(shù)據(jù)集群)

優(yōu)化方案的思考

有沒有一種方式可以不增加線上的壓力挣磨,同時提供更高效的查詢方式雇逞?想過redis,但最終選擇用文本存儲茁裙。因為數(shù)據(jù)分析是一個獨立的需求塘砸,不希望與現(xiàn)有的redis集群或者其它存儲服務(wù)有交集。還有一個原因是每次分析的中間結(jié)果晤锥,對下一次分析并沒有很大的實質(zhì)作用掉蔬,并不需要把結(jié)果持久存儲,而且占的內(nèi)存也會較多矾瘾。最終使用文本存儲女轿,然后用二分來查找。特點壕翩,1蛉迹,存儲非常快放妈,雖然redis等nosql服務(wù)雖然已經(jīng)非潮本龋快,但仍無法與文本存儲相提并論大猛;2扭倾,查找的時候使用二分查找淀零,百萬條記錄查詢也可在0.1ms內(nèi)完成(使用線上的普通硬盤挽绩,如果是ssd盤會更快)。

實現(xiàn)步驟

  • 將數(shù)據(jù)庫中需要的字段導(dǎo)出到文本

      方法:使用mysql的phpmyadmin工具驾中,執(zhí)行sql語句查出主建id和相應(yīng)字段
      如以上的關(guān)鍵詞表: select kid, keyword from keyword
      然后使用phpmyadmin的導(dǎo)出工具唉堪,可以快速把結(jié)果導(dǎo)出到文本中
      操作截圖:
    
導(dǎo)出數(shù)據(jù)流程1

導(dǎo)出數(shù)據(jù)流程2
  • 將導(dǎo)出的文本(已經(jīng)按id進行過排序)轉(zhuǎn)換格式重新存儲
  • 程序讀取轉(zhuǎn)換后的格式

文本存儲格式

說明 :需求中,文本每行有兩列肩民,第一列是主建ID(數(shù)字)唠亚,第二列為文本。整個文本已經(jīng)按第一列有序排列持痰,兩列之間用tab鍵分隔灶搜。
之前有看過ip.dat的存儲,本次仿照其存儲格式:將文本中的內(nèi)容每行轉(zhuǎn)換為固定長度后,存儲到新的文件割卖。搜索時前酿,使用文件操作函數(shù)fopen,fseek鹏溯,fgets等函數(shù)按字節(jié)讀取內(nèi)容罢维,并以二分查找法快速定位需要的內(nèi)容。

代碼實現(xiàn)部分

  • 通用類丙挽,類似需求只需要提供符合標(biāo)準(zhǔn)的文本(每行兩列肺孵,第一列為查找的ID,第二列為文本颜阐。同時文本已經(jīng)按第一列有序排序)
  • 生成以上所提到的存儲格式
  • 提供根據(jù)id查詢接口

代碼片斷

  • 重新生成新的存儲格式

      //讀源文件平窘,寫入到新的索引文件
      $readfd = fopen($this->filename, 'rb');
      $writefd = fopen($this->formatFile.'_tmp', 'wb+');
      if ($readfd === false || $writefd === false) {
          return false;
      }
      echo "\n start reformat file $this->filename ..";
      while (!feof($readfd)) {
          $line = fgets($readfd, 8192);
          fwrite($writefd, pack("a".$this->maxLength, $line));
      }
      echo "\n reformat ok\n";
      fclose($readfd);
      fclose($writefd);
      rename($this->formatFile.'_tmp', $this->formatFile);
    
  • 二分查找的代碼片斷

      /**
      * 在索引文件中進行二分查找
      * @param  int $id    進行二分查找的id
      * @return [type]     [description]
      */
      public function search($key)
      {
          $filesize = filesize($this->formatFile);
          $fd = fopen($this->formatFile, "rb");
          $left = 0; //行號
          $right = ($filesize / $this->maxLength) - 1; 
          while ($left <= $right) {
              $middle = intval(($right + $left)/2);
              fseek($fd, ($middle) * $this->maxLength);
              $info = unpack("a*", fread($fd, $this->maxLength))['1'];
              $lineinfo = explode("\t", $info, 2);
              if ($lineinfo['0'] > $key) {
                  $right = $middle - 1;
              } elseif ($lineinfo['0'] < $key) {
                  $left = $middle + 1;
              } else {
                  return $lineinfo['1'];
              }
          }
          return false;
      }
    
  • 整個類庫代碼一共91行,具體可查看github的demo代碼

運行截圖

運行截圖

以上拿100萬的關(guān)鍵詞進行測試凳怨,根據(jù)關(guān)鍵詞id快速查找關(guān)鍵詞初婆,平均速度可以達到0.1毫秒。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末猿棉,一起剝皮案震驚了整個濱河市磅叛,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌萨赁,老刑警劉巖弊琴,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異杖爽,居然都是意外死亡敲董,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進店門慰安,熙熙樓的掌柜王于貴愁眉苦臉地迎上來腋寨,“玉大人,你說我怎么就攤上這事化焕√汛埽” “怎么了?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵撒桨,是天一觀的道長查刻。 經(jīng)常有香客問我,道長凤类,這世上最難降的妖魔是什么唉擂? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任缨伊,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著赶盔,像睡著了一般。 火紅的嫁衣襯著肌膚如雪榆浓。 梳的紋絲不亂的頭發(fā)上于未,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天,我揣著相機與錄音陡鹃,去河邊找鬼烘浦。 笑死狭魂,一個胖子當(dāng)著我的面吹牛寞酿,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播檬嘀,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼脊阴,長吁一口氣:“原來是場噩夢啊……” “哼握侧!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起嘿期,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤品擎,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后备徐,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體萄传,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年蜜猾,在試婚紗的時候發(fā)現(xiàn)自己被綠了秀菱。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡蹭睡,死狀恐怖衍菱,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情肩豁,我是刑警寧澤脊串,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站蓖救,受9級特大地震影響洪规,放射性物質(zhì)發(fā)生泄漏印屁。R本人自食惡果不足惜循捺,卻給世界環(huán)境...
    茶點故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望雄人。 院中可真熱鬧从橘,春花似錦念赶、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至踩萎,卻和暖如春停局,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背香府。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工董栽, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人企孩。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓锭碳,卻偏偏與公主長得像,于是被迫代替她去往敵國和親勿璃。 傳聞我的和親對象是個殘疾皇子擒抛,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,724評論 2 354

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)补疑,斷路器歧沪,智...
    卡卡羅2017閱讀 134,654評論 18 139
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法莲组,內(nèi)部類的語法槽畔,繼承相關(guān)的語法,異常的語法胁编,線程的語...
    子非魚_t_閱讀 31,630評論 18 399
  • 一厢钧、溫故而知新 1. 內(nèi)存不夠怎么辦 內(nèi)存簡單分配策略的問題地址空間不隔離內(nèi)存使用效率低程序運行的地址不確定 關(guān)于...
    SeanCST閱讀 7,808評論 0 27
  • ¥開啟¥ 【iAPP實現(xiàn)進入界面執(zhí)行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開一個線程,因...
    小菜c閱讀 6,409評論 0 17
  • 我抱怨了一整個月我絕不會去看擺渡人這樣的電影,但還是在生日拉了一小幫人去電影院“隨便看看”市框。當(dāng)時的借口是什么竟然...
    濯塵的青泥閱讀 230評論 0 0