2012-05-30 37 views
12

更新2015-10-15

早在2012年,我正在構建一個個人在線應用程序,並且實際上是想重新發明車輪,因爲本質上是好奇的,用於學習目的,並且提高了我的算法和架構技能。我可以使用apache lucene和其他,但正如我所說,我決定建立自己的迷你搜索引擎。如何針對倒排索引和關係數據庫優化「文本搜索」?

問題:那麼除了使用彈性搜索,lucene等可用服務之外,是否真的沒有辦法增強這種架構呢?


原來的問題

我開發一個Web應用程序,在用戶搜索特定的標題(比方說:書的x,書Y,等等。),其數據是一個關係型數據庫(MySQL的)。

我遵循的原則是,從db中獲取的每條記錄都緩存在內存中,以便應用程序對數據庫的調用較少。

我已經開發了我自己的小型搜索引擎,具有以下結構:
Architecture diagram
這是它如何工作的:

  • a)用戶搜索記錄的名字
  • 二)系統檢查查詢開頭的字符,檢查是否有查詢:get record。如果不存在,則使用兩種方法添加它並從數據庫中獲取所有匹配的記錄:
    • 查詢中已存在查詢(這是一種歷史記錄表),從而根據ID獲取記錄(快速性能)
    • 或者,否則使用Mysql LIKE %%語句來獲取記錄/ ids(然後將歷史表查詢中用戶使用的查詢與其映射到的ID一起保存)。
      - >然後它將記錄和它們的ID添加到緩存以及只有ID到倒排索引映射。
  • C)結果被返回到UI

系統工作正常,但是我有兩個主要問題,我無法找到(一直試圖在過去的一個很好的解決方案月):

第一個問題:
如果檢查點(b),情況沒有查詢「歷史」被發現,它使用像%%聲明:此過程變得時間當查詢數據庫(而不是一個或兩個)匹配許多紀錄耗時:

  • 這將需要一段時間才能從MySQL得到記錄(這就是爲什麼我用在特定列索引)
  • 然後花時間去保存查詢歷史
  • 然後及時補充記錄/ IDS緩存和倒排索引映射

第二期:
該應用程序允許用戶自行添加新記錄,即立即可以使用登錄到應用程序的其他用戶。
但是爲了實現這一點,倒排索引圖和表「查詢」必須更新,以便在任何舊查詢匹配新詞時。例如,如果添加新的記錄「woodX」,仍然會將舊查詢「木材」映射到它。因此,爲了重新鉤查詢「木」這一新的記錄,這裏就是我現在所做的:

  • 新紀錄「woodX」被添加到「記錄」表
  • 然後我跑Like %%聲明查看哪個已存在查詢在表「queries」中是否映射到此記錄(例如「wood」),然後將此查詢與新記錄ID一起添加爲新行:[wood,new ID]。
  • 然後在內存中,更新倒排索引地圖的「木」鍵的值(即列表),通過添加新的記錄id到這個列表

- >因此現在如果遠程用戶搜索「木」它會得到內存:木材和woodX

問題這裏也是時間消耗。將所有查詢歷史記錄(在表格查詢中)與新添加的單詞匹配需要很多時間(匹配的查詢越多,時間越多)。然後內存更新也需要很多時間。

我是什麼做來解決這個時間問題,是理想的結果返回給用戶第一,然後讓應用程序發佈一個AJAX調用與所需的數據全部實現這些UPDATE任務。但我不確定這是一種不好的做法還是一種不專業的做事方式?
因此,在過去的一個月(更多一點),我試圖想到這個架構的最佳優化/修改/更新,但我不是文件檢索領域的專家(實際上它是我有史以來第一個小型搜索引擎) 。

我希望能夠實現這種架構的任何反饋或指導我應該做什麼。
在此先感謝。

PS:

  • 它使用servlet J2EE應用程序。
  • 我使用MySQL的InnoDB(因此我不能使用全文搜索選項)
+0

搜索歷史記錄(基本上是您緩存的所有內容)的範圍是由用戶定義的,還是與整個應用程序相同?即用戶2是否能夠找到緩存的對象,因爲用戶1已經查找了相同的密鑰? – theDmi

+0

@theDmi是它是一個共享緩存(像單身人士) – shadesco

回答

2

我強烈建議使用Sphinx Search Server,在全文搜索中進行了最佳優化。訪問http://sphinxsearch.com/

它旨在與MySQL一起使用,因此它是對當前工作空間的補充。

+0

我提到我正在使用MySQL innodb(全文搜索不起作用),那麼sphinx是否與innodb一起工作? – shadesco

+0

它似乎它! http://sphinxsearch.com/forum/view.html?id=479 – shadesco

2

我不假裝有解決方案,但這裏是我的想法。 首先,我雖然喜歡你爲耗時的查詢LIKE %%:我會執行一個查詢限於MySQL中的幾個答案,就像十幾個,返回給用戶,並等待查看用戶是否想要更多匹配記錄,或者在後臺啓動完整查詢,具體取決於您對未來搜索的索引需求。更一般地說,我認爲將內存中的所有東西都存儲在內存中可能會導致內存消耗過多。而且,當搜索引擎將所有內容都保存在內存中時,搜索引擎會變得越來越快,因此當添加或更新數據時,必須保持所有這些緩存都是最新的,並且它肯定會佔用越來越多的時間。

這就是爲什麼我認爲我在一個「開源論壇軟件」(我不記得它的名字)中看到一天的解決方案對於文章中的文本搜索來說不是太糟糕:每次插入數據時,表名爲「單詞」保持現有的每個字的軌道,和另一個表(讓我們說「WordsLinks」)每一個字,它出現在 崗位之間的聯繫,這種解決方案有一些缺點:

  • 每個Insert ,刪除,數據庫中的更新速度慢很多
  • 搜索引擎的數據選擇必須被預見:如果你選擇保留兩個字母的話你永遠不會保留,已經太晚了,生成的數據,除非您啓動完整的數據重新處理。
  • 必須小心刪除的還有更新和插入

但我認爲有一些大的優勢:

  • 計算時間可能比「存儲解決方案」相同(最終),但它被分成每個數據庫的創建/更新/刪除,而不是在查詢時間。
  • 尋找一個完整的單詞,或者「開始於」的單詞是即時的:當索引時,在「單詞」表中搜索是二分法的。和「WordLinks」表查詢是非常快速的索引。
  • 同時尋找多個單詞可能很簡單:爲每個找到的單詞收集一組「WordLinks」,並對它們執行一個交集,以僅保留所有這些組通用的「數據庫ID」。例如,用「樹」和「葉」這兩個詞,第一個可以給出表格記錄{1,4,6},第二個可以給出{1,3,6,9}。因此,對於交叉點,僅保留常見部分很簡單:{1,6}。
  • 單列表中的「Like %%」可能比不同表中的「Like %%」更快。每個數據庫引擎都會處理一些緩存:「Words」表可能足以保存在內存中
  • 我認爲如果數據變得巨大,性能和內存問題的風險很小。
  • 由於每個搜索都很快,您甚至可以查找同義詞。例如,如果用戶沒有發現任何「以太網」,搜索「網絡」。
  • 您可以應用規則,如分割駱駝案例詞以生成例如「woodX」中的3個單詞「wood」,「X」,「woodX」。每個「單詞」的存儲和查找都非常輕便,所以您可以做很多事情。

我認爲你需要的解決方案可以是一個混合的方法:例如,你可以保持輕量級UPDATE,INSERT,DELETE,並啓動「Words」和「WordsLinks」從TRIGGER進食。

只是爲了軼事,我看到一個由我的公司開發的軟件,它決定將「一切」(!)保存在內存中。它使我們建議我們的客戶購買64GB RAM的服務器。有點貴。它解釋了爲什麼當我看到最終導致記憶填充的解決方案時我非常謹慎。

2

我不得不說,我認爲你的設計不太適合這個問題。你現在看到的問題是其後果。除此之外,您當前的解決方案不會擴展。

這裏是一個可能的解決方案:

  1. 重新設計你的數據庫只包含權威數據,但沒有得到的數據。所以所有的緩存條目都必須從MySQL中消失。

  2. 僅在您的應用程序中的內存請求期間保持數據。這使得您的應用程序設計變得更加簡單(考慮競爭條件),並使您能夠擴展到合理數量的客戶端。

  3. 引入一個緩存層。我強烈建議使用已建立的產品,而不是自己構建。這使您可以釋放應用程序中所有定製的緩存邏輯,甚至可以更好地完成這項工作。

您可以查看Redis或Memcached的緩存層。我認爲LRU策略應該適合這裏。根據查詢的複雜程度,像Lucene這樣的專用索引搜索機制也可能有意義。

+0

1 - MySQL中的緩存條目用於重新啓動服務器以重新提取已存在的查詢條件。 2-整個問題的關鍵是實時共享查詢數據在所有用戶應用程序可用的存儲器中。 3-正確,但是這是在2012年完成的,並且對於我實際上想要重新發明輪子以學習(應該提及那個)的個人項目,但是您是100%正確的。 – shadesco

1

我確定這可以在MySQL中實現,但只需使用現有的面向搜索的數據庫(如Elasticsearch)就不那麼費力。它使用Lucene庫來實現倒排索引,具有豐富的文檔,支持水平縮放,相當簡單的查詢語言等等。我認爲實現這一目標已經有很多工作要做,而且要處理緩存,競態條件,錯誤,性能問題等等,使解決方案成爲「生產級」還需要更多的工作。

+0

我可以在2012年使用apache lucene,但是我想爲個人在線項目建立自己的引擎,出於好奇和學習的目的。這怎麼可以在MySQL中實現? – shadesco

+0

重新創造的東西總是非常具有教育意義:)我在遵循這個問題的細節方面遇到了困難,但是使用正確的標記化我想你只需要'WHERE模式LIKE「查詢%」'種查詢(前綴匹配)比任意'WHERE模式LIKE'%quer%''查詢更有效率(假設理智的MySQL索引,應檢查文檔)。 我不認爲內存中的方面在這裏非常重要,主要的一點是MySQL(或哪個數據庫)有一個合適的索引來滿足您的查詢。在db中緩存查詢和結果看起來太複雜了。 – NikoNyrh