用Python實現一個大資料搜尋及原始碼

來源:互聯網
上載者:User

標籤:Python程式設計語言   Python案例講解   Python基礎精講   

在日常生活中,大家瞭解搜尋引擎如百度、360、搜狗、Google等,搜尋是大資料領域裡常見的需求。Splunk和ELK分別是該領域在非開源和開源領域裡的領導者。本文利用很少的Python代碼實現了一個基本的資料搜尋功能,試圖讓大家理解大資料搜尋的基本原理。

  布隆過濾器(BloomFilter)

  第一步我們先要實現一個布隆過濾器。

  布隆過濾器是大資料領域的一個常見演算法,它的目的是過濾掉那些不是目標的元素。也就是說如果一個要搜尋的詞並不存在與我的資料中,那麼它可以以很快的速度返回目標不存在。

  讓我們看看以下布隆過濾器的代碼:

  classBloomfilter(object):

  """

  ABloomfilterisaprobabilisticdata-structurethattradesspaceforaccuracy

  whendeterminingifavalueisinaset.Itcantellyouifavaluewaspossibly

  added,orifitwasdefinitelynotadded,butitcan‘ttellyouforcertainthat

  itwasadded.

  """

  definit(self,size):

  """SetuptheBFwiththeappropriatesize"""

  self.values=[False]*size

  self.size=size

  defhash_value(self,value):

  """HashthevalueprovidedandscaleittofittheBFsize"""

  returnhash(value)%self.size

  defadd_value(self,value):

  """AddavaluetotheBF"""

  h=self.hash_value(value)

  self.values[h]=True

  defmight_contain(self,value):

  """CheckifthevaluemightbeintheBF"""

  h=self.hash_value(value)

  returnself.values[h]

  defprint_contents(self):

  """DumpthecontentsoftheBFfordebuggingpurposes"""

  printself.values

  基本的資料結構是個數組(實際上是個位元影像,用1/0來記錄資料是否存在),初始化是沒有任何內容,所以全部置False。實際的使用當中,該數組的長度是非常大的,以保證效率。

  利用雜湊演算法來決定資料應該存在哪一位,也就是數組的索引

  當一個資料被加入到布隆過濾器的時候,計算它的雜湊值然後把相應的位置為True

  當檢查一個資料是否已經存在或者說被索引過的時候,只要檢查對應的雜湊值所在的位的True/Fasle

  看到這裡,大家應該可以看出,如果布隆過濾器返回False,那麼資料一定是沒有索引過的,然而如果返回True,那也不能說資料一定就已經被索引過。在搜尋過程中使用布隆過濾器可以使得很多沒有命中的搜尋提前返回來提高效率。

  我們看看這段code是如何啟動並執行:

  bf=Bloomfilter(10)

  bf.add_value(‘dog‘)

  bf.add_value(‘fish‘)

  bf.add_value(‘cat‘)

  bf.print_contents

  bf.add_value(‘bird‘)

  bf.print_contents

  #Note:contentsareunchangedafteraddingbird-itcollides

  fortermin[‘dog‘,‘fish‘,‘cat‘,‘bird‘,‘duck‘,‘emu‘]:

  print‘{}:{}{}‘.format(term,bf.hash_value(term),bf.might_contain(term))

  結果:

  [False,False,False,False,True,True,False,False,False,True]

  [False,False,False,False,True,True,False,False,False,True]

  dog:5True

  fish:4True

  cat:9True

  bird:9True

  duck:5True

  emu:8False

  首先建立了一個容量為10的的布隆過濾器

  然後分別加入‘dog’,‘fish’,‘cat’三個對象,這時的布隆過濾器的內容如下:

  然後加入‘bird’對象,布隆過濾器的內容並沒有改變,因為‘bird’和‘fish’恰好擁有相同的雜湊。

  最後我們檢查一堆對象(’dog’,‘fish’,‘cat’,‘bird’,‘duck’,’emu’)是不是已經被索引了。結果發現‘duck’返回True,2而‘emu’返回False。因為‘duck’的雜湊恰好和‘dog’是一樣的。

  分詞

  下面一步我們要實現分詞。分詞的目的是要把我們的文本資料分割成可搜尋的最小單元,也就是詞。這裡我們主要針對英語,因為中文的分詞涉及到自然語言處理,比較複雜,而英文基本只要用標點符號就好了。

  下面我們看看分詞的代碼:

  defmajor_segments(s):

  """

  Performmajorsegmentingonastring.Splitthestringbyallofthemajor

  breaks,andreturnthesetofeverythingfound.Thebreaksinthisimplementation

  aresinglecharacters,butinSplunkpropertheycanbemultiplecharacters.

  Asetisusedbecauseorderingdoesn‘tmatter,andduplicatesarebad.

  """

  major_breaks=‘‘

  last=-1

  results=set

  #enumeratewillgiveus(0,s[0]),(1,s[1]),...

  foridx,chinenumerate(s):

  ifchinmajor_breaks:

  segment=s[last+1:idx]

  results.add(segment)

  last=idx

  #Thelastcharactermaynotbeabreaksoalwayscapture

  #thelastsegment(whichmayendupbeing"",butyolo)

  segment=s[last+1:]

  results.add(segment)

  returnresults

  主要分割

  主要分割使用空格來分詞,實際的分詞邏輯中,還會有其它的分隔字元。例如Splunk的預設分割符包括以下這些,使用者也可以定義自己的分割符。

  ]<>{}|!;,‘”*\n\r\s\t&?+%21%26%2526%3B%7C%20%2B%3D—%2520%5D%5B%3A%0A%2C%28%29

  defminor_segments(s):

  """

  Performminorsegmentingonastring.Thisislikemajor

  segmenting,exceptitalsocapturesfromthestartofthe

  inputtoeachbreak.

  """

  minorbreaks=‘.‘

  last=-1

  results=set

  foridx,chinenumerate(s):

  ifchinminor_breaks:

  segment=s[last+1:idx]

  results.add(segment)

  segment=s[:idx]

  results.add(segment)

  last=idx

  segment=s[last+1:]

  results.add(segment)

  results.add(s)

  returnresults

  次要分割

  次要分割和主要分割的邏輯類似,只是還會把從開始部分到當前分割的結果加入。例如“1.2.3.4”的次要分割會有1,2,3,4,1.2,1.2.3

  defsegments(event):

  """Simplewrapperaroundmajor_segments/minor_segments"""

  results=set

  formajorinmajor_segments(event):

  forminorinminor_segments(major):

  results.add(minor)

  returnresults

  分詞的邏輯就是對文本先進行主要分割,對每一個主要分割在進行次要分割。然後把所有分出來的詞返回。

  我們看看這段code是如何啟動並執行:

  forterminsegments(‘src_ip=1.2.3.4‘):

  printterm

  src

  1.2

  1.2.3.4

  src_ip

  3

  1

  1.2.3

  ip

  2

  =

  4

  搜尋

  好了,有個分詞和布隆過濾器這兩個利器的支撐後,我們就可以來實現搜尋的功能了。上代碼:

  classSplunk(object):

  definit(self):

  self.bf=Bloomfilter(64)

  self.terms={}#Dictionaryoftermtosetofevents

  self.events=

  defadd_event(self,event):

  """Addsaneventtothisobject"""

  #GenerateauniqueIDfortheevent,andsaveit

  event_id=len(self.events)

  self.events.append(event)

  #Addeachtermtothebloomfilter,andtracktheeventbyeachterm

  forterminsegments(event):

  self.bf.add_value(term)

  iftermnotinself.terms:

  self.terms[term]=set

  self.terms[term].add(event_id)

  defsearch(self,term):

  """Searchforasingleterm,andyieldalltheeventsthatcontainit"""

  #InSplunkthisrunsinO(1),andislikelytobeinfilesystemcache(memory)

  ifnotself.bf.might_contain(term):

  return

  #InSplunkthisprobablyrunsinO(logN)whereNisthenumberoftermsinthetsidx

  iftermnotinself.terms:

  return

  forevent_idinsorted(self.terms[term]):

  yieldself.events[event_id]

  Splunk代表一個擁有搜尋功能的索引集合

  每一個集合中包含一個布隆過濾器,一個倒排詞表(字典),和一個儲存所有事件的數組

  當一個事件被加入到索引的時候,會做以下的邏輯

  為每一個事件產生一個unqieid,這裡就是序號

  對事件進行分詞,把每一個詞加入到倒排詞表,也就是每一個詞對應的事件的id的映射結構,注意,一個詞可能對應多個事件,所以倒排表的的值是一個Set。倒排表是絕大部分搜尋引擎的核心功能。

  當一個詞被搜尋的時候,會做以下的邏輯

  檢查布隆過濾器,如果為假,直接返回

  檢查詞表,如果被搜尋單詞不在詞表中,直接返回

  在倒排表中找到所有對應的事件id,然後返回事件的內容

  我們運行下看看把:

  s=Splunk

  s.add_event(‘src_ip=1.2.3.4‘)

  s.add_event(‘src_ip=5.6.7.8‘)

  s.add_event(‘dst_ip=1.2.3.4‘)

  foreventins.search(‘1.2.3.4‘):

  printevent

  print‘-‘

  foreventins.search(‘src_ip‘):

  printevent

  print‘-‘

  foreventins.search(‘ip‘):

  printevent

  src_ip=1.2.3.4

  dst_ip=1.2.3.4

  -

  src_ip=1.2.3.4

  src_ip=5.6.7.8

  -

  src_ip=1.2.3.4

  src_ip=5.6.7.8

  dst_ip=1.2.3.4

  是不是很贊!

  更複雜的搜尋

  更進一步,在搜尋過程中,我們想用And和Or來實現更複雜的搜尋邏輯。

  上代碼:

  classSplunkM(object):

  definit(self):

  self.bf=Bloomfilter(64)

  self.terms={}#Dictionaryoftermtosetofevents

  self.events=

  defadd_event(self,event):

  """Addsaneventtothisobject"""

  #GenerateauniqueIDfortheevent,andsaveit

  event_id=len(self.events)

  self.events.append(event)

  #Addeachtermtothebloomfilter,andtracktheeventbyeachterm

  forterminsegments(event):

  self.bf.add_value(term)

  iftermnotinself.terms:

  self.terms[term]=set

  self.terms[term].add(event_id)

  defsearch_all(self,terms):

  """SearchforanANDofallterms"""

  #Startwiththeuniverseofallevents...

  results=set(range(len(self.events)))

  forterminterms:

  #Ifatermisn‘tpresentatallthenwecanstoplooking

  ifnotself.bf.might_contain(term):

  return

  iftermnotinself.terms:

  return

  #Dropeventsthatdon‘tmatchfromourresults

  results=results.intersection(self.terms[term])

  forevent_idinsorted(results):

  yieldself.events[event_id]

  defsearch_any(self,terms):

  """SearchforanORofallterms"""

  results=set

  forterminterms:

  #Ifatermisn‘tpresent,weskipit,butdon‘tstop

  ifnotself.bf.might_contain(term):

  continue

  iftermnotinself.terms:

  continue

  #Addtheseeventstoourresults

  results=results.union(self.terms[term])

  forevent_idinsorted(results):

  yieldself.events[event_id]

  利用Python集合的intersection和union操作,可以很方便的支援And(求交集)和Or(求合集)的操作。

  運行結果如下:

  s=SplunkM

  s.add_event(‘src_ip=1.2.3.4‘)

  s.add_event(‘src_ip=5.6.7.8‘)

  s.add_event(‘dst_ip=1.2.3.4‘)

  foreventins.search_all([‘src_ip‘,‘5.6‘]):

  printevent

  print‘-‘

  foreventins.search_any([‘src_ip‘,‘dst_ip‘]):

  printevent

  src_ip=5.6.7.8

  -

  src_ip=1.2.3.4

  src_ip=5.6.7.8

  dst_ip=1.2.3.4

用Python實現一個大資料搜尋及原始碼

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.