MapReduce'un basit açıklaması?


166

Benim CouchDB sorumla ilgili.

MapReduce'u uyuşuklukların anlayabileceği bir şekilde açıklayan var mı?




@MichaelHausenblas - Örneğinizi seviyorum: tüm aile için anlaşılması kolay ve eğlenceli.
Lee

Yanıtlar:


187

Harita ve Küçült ile ilgili temel bilgilere kadar inmek.


Harita , bir tür listedeki öğeleri başka bir öğeye "dönüştüren" ve aynı tür listeye geri koyan bir işlevdir.

bir sayı listemiz olduğunu varsayalım: [1,2,3] ve her sayıyı ikiye katlamak istiyorum, bu durumda, "her sayıyı ikiye katlama" işlevi x = x * 2 işlevidir. Ve eşlemeler olmadan, yazabilirim basit bir döngü diyelim

A = [1, 2, 3]
foreach (item in A) A[item] = A[item] * 2

ve A = [2, 4, 6] olurdu ama döngüler yazmak yerine, bir harita fonksiyonum varsa yazabilirim

A = [1, 2, 3].Map(x => x * 2)

x => x * 2, [1,2,3] 'deki elemanlara karşı yürütülecek bir fonksiyondur. Olan şey, programın her bir öğeyi alması, x'i her öğeye eşit hale getirerek (x => x * 2) çalıştırması ve sonuçların bir listesini üretmesidir.

1 : 1 => 1 * 2 : 2  
2 : 2 => 2 * 2 : 4  
3 : 3 => 3 * 2 : 6  

böylece harita işlevini (x => x * 2) ile yürüttükten sonra [2, 4, 6] elde edersiniz.


Küçült , listelerdeki öğeleri "toplayan" ve hepsinde bir miktar hesaplama yapan , böylece bunları tek bir değere düşüren bir işlevdir .

Bir toplamı bulmak veya ortalamaları bulmak bir azaltma işlevinin tüm örnekleridir. Örneğin, bir sayı listeniz varsa, [7, 8, 9] deyin ve bunların toplanmasını istiyorsanız, böyle bir döngü yazarsınız

A = [7, 8, 9]
sum = 0
foreach (item in A) sum = sum + A[item]

Ancak, bir azaltma işlevine erişiminiz varsa, bunu böyle yazabilirsiniz

A = [7, 8, 9]
sum = A.reduce( 0, (x, y) => x + y )

Şimdi neden 2 argüman (0 ve x ve y ile fonksiyon) geçti biraz kafa karıştırıcı. Bir azaltma işlevinin yararlı olması için, 2 öğe alabilmesi, bir şeyi hesaplayabilmesi ve bu 2 öğeyi tek bir değere "indirgeyebilmesi" gerekir, böylece program tek bir değere sahip olana kadar her çifti azaltabilir.

yürütme şöyle olur:

result = 0
7 : result = result + 7 = 0 + 7 = 7
8 : result = result + 8 = 7 + 8 = 15
9 : result = result + 9 = 15 + 9 = 24

Ancak her zaman sıfırlarla başlamak istemezsiniz, bu nedenle ilk argüman, bir tohum değerini özellikle ilk result =satırdaki değeri belirtmenize izin vermek içindir .

2 listeyi toplamak istediğinizi varsayalım, şöyle görünebilir:

A = [7, 8, 9]
B = [1, 2, 3]
sum = 0
sum = A.reduce( sum, (x, y) => x + y )
sum = B.reduce( sum, (x, y) => x + y )

veya gerçek dünyada bulma olasılığınız daha yüksek bir sürüm:

A = [7, 8, 9]
B = [1, 2, 3]

sum_func = (x, y) => x + y
sum = A.reduce( B.reduce( 0, sum_func ), sum_func )

Bu bir DB yazılımında iyi bir şey, çünkü Map \ Reduce desteği ile bir DB motorunun ne olduğunu, veri kullanmak için bir veritabanında nasıl saklandığını bilmek zorunda kalmadan veritabanı ile çalışabilirsiniz.

Sadece bir Harita veya Küçültme işlevi sağlayarak motora ne istediğinizi "söyleyebilmeniz" gerekir ve daha sonra DB motoru verilerin etrafında yol bulabilir, işlevinizi uygulayabilir ve sonuçları bulabilir tüm kayıtlar üzerinde nasıl döngüler bilmeden hepsini istiyorum.

Dizinler, anahtarlar, birleşimler ve görünümler ve tek bir veritabanının tutabileceği birçok şey vardır, bu nedenle verilerin gerçekte nasıl saklandığına karşı sizi koruyarak, kodunuzun yazılması ve bakımı daha kolay hale getirilir.

Aynı şey paralel programlama için de geçerlidir, eğer döngü döngüsünü gerçekte uygulamak yerine sadece verilerle ne yapmak istediğinizi belirtirseniz, temel altyapı sizin için eşzamanlı bir paralel döngüde "paralelleşebilir" ve işlevinizi yürütebilir.


Tamam, haritayı anlıyorum ve bireysel olarak azaltıyorum. Ancak indirgeme konusunda hangi uygulamalara sahip olabilirim? Bir Google senaryosunda, örneğin belirli bir anahtar kelime için bir sayfanın sıralamasını veren bir dizi parametreyi toplamak için kullanırlar mıydı?
Lorenzo

@lbolognini var total = orderes.Sum (o => o.UnitPrice * o.Quantity)
chakrit

@lbolognini Döngü kavramını soyutlamanın birçok faydası vardır. Google'ın senaryosunda sayfa sayfalarını, bağlantıları ve neyi hesaplamak için muhtemelen 1000'lerce makine var. Birkaç sunucu daha eklemeleri gerektiğinde ne yaparlar? Her bir döngü kodunu değiştirmek muhtemelen bir seçenek değildir. Böylece yaptıkları şey, hesaplama kodlarını "Küçült" işlevine karşı yazmalarıdır ... Ve sunucu listesi değiştiğinde, yalnızca "Küçült" işlevinin değiştirilmesi gerekir. Anladım?
chakrit

ortalamaları nasıl düşürür? gördüklerimden tahmin edemiyorum? belki pay ve payda haritasını çıkarabilir ve her ikisini de toplamanın sonunda bölünebilir?
andyczerwonka

@arcticpenguin Orada biraz fazla genel oluyorum. Aslında Average()tepesinde buzlanma sözde Sum(). Ama fonksiyonun neden "Azalt" olarak adlandırıldığını göstermek için bundan bahsettim ... Ortalama bir fonksiyon, bir sayı listesi alan ve onu tek bir sayıya (ortalama olan) düşüren bir şeydir.
chakrit

60

MapReduce, geliştiricinin eşleyici dışında başka bir kod yazmasına ve işlevleri azaltmasına gerek kalmadan çok sayıda veriyi paralel olarak işlemek için bir yöntemdir.

Harita işlevi bir engel tutulur bir sonucu, çıkış veri ve yayıklar sürer. Bu işlev, çok sayıda aynı harita görevine paralel olarak çalışabilir . Veri kümesi daha sonra skaler bir değere düşürülebilir .

Eğer bunu bir SQL ifadesi gibi düşünüyorsanız

SELECT SUM(salary)
FROM employees
WHERE salary > 1000
GROUP by deptname

Biz kullanabilirsiniz harita maaş> grup boyutu kova içine bariyere yaydığı map 1000 ile çalışanların bizim alt kümesini almak için.

Azalt , bu grupların her birini toplar. Sonuç kümenizi veriyor.

bunu google gazetesinin üniversite çalışma notlarından aldım


33
  1. Bir sürü veri alın
  2. Her datumu başka bir datuma dönüştüren bir tür dönüşüm gerçekleştirin
  3. Bu yeni verileri daha basit verilerle birleştirin

Adım 2 Harita'dır. Adım 3 Azaltmaktır.

Örneğin,

  1. Yoldaki bir çift basınç metresindeki iki impuls arasında zaman kazanın
  2. Bu süreleri sayaçların mesafesine göre hızlara eşleyin
  3. Bu hızları ortalama bir hıza düşürün

MapReduce öğesinin Harita ve Küçült arasında bölünmesinin nedeni, farklı parçaların paralel olarak kolayca yapılabilmesidir. (Özellikle Reduce belirli matematiksel özelliklere sahipse.)

MapReduce uygulamasının karmaşık ancak iyi bir açıklaması için bkz. Google'ın MapReduce Programlama Modeli - Revisited (PDF) .


1
Adım 3 için "transform" yerine "birleştir" diyebilirim
TraumaPony

İlk kez, üç cevap EN İYİ cevaptır. Önce Nasser'in makale bağlantısını okuyun (teorik yüksek seviye) Sonra chakrit'in cevabı (harita azaltmanın bireysel açıklaması) Şimdi Frank'in cevabı (Ünlü MapReduce deyimi nedir.) Üçünüze teşekkür ederiz. :)
Ajeet Ganga

20

HARİTA ve REDÜCE, insanın son dinozorları öldürdüğü bir dönemdeki eski Lisp işlevleridir.

Şehir adı, orada yaşayan insan sayısı ve şehrin büyüklüğü hakkında bilgi veren bir şehir listeniz olduğunu düşünün:

(defparameter *cities*
  '((a :people 100000 :size 200)
    (b :people 200000 :size 300)
    (c :people 150000 :size 210)))

Şimdi en yüksek nüfus yoğunluğuna sahip şehri bulmak isteyebilirsiniz.

Önce MAP kullanarak şehir isimleri ve nüfus yoğunluğu listesi oluşturuyoruz:

(map 'list
     (lambda (city)
         (list (first city)
               (/ (getf (rest city) :people)
                  (getf (rest city) :size))))
     *cities*)

=>   ((A 500) (B 2000/3) (C 5000/7))

REDUCE kullanarak artık en büyük nüfus yoğunluğuna sahip şehri bulabiliriz.

(reduce (lambda (a b)
          (if (> (second a) (second b))
             a
             b))
        '((A 500) (B 2000/3) (C 5000/7)))

 =>   (C 5000/7)

Her iki parçayı birleştirerek aşağıdaki kodu alırız:

(reduce (lambda (a b)
          (if (> (second a) (second b))
             a
             b))
        (map 'list
             (lambda (city)
                (list (first city)
                   (/ (getf (rest city) :people)
                      (getf (rest city) :size))))
             *cities*))

Fonksiyonları tanıyalım:

(defun density (city)
   (list (first city)
         (/ (getf (rest city) :people)
            (getf (rest city) :size))))

(defun max-density (a b)
   (if (> (second a) (second b))
          a
          b))

Sonra MAP REDUCE kodumuzu şöyle yazabiliriz:

(reduce 'max-density
        (map 'list 'density *cities*))

 =>   (C 5000/7)

Çağırır MAPve REDUCE(değerlendirme içten dışa doğrudur), bu yüzden harita azaltma denir .


@MoMolog: MAX işlevi zaten var ve biraz farklı bir şey yapıyor. Ayrıca: kişi MAX'ı yeniden tanımlamamalıdır.
Rainer Joswig

max-densityaktarılan argümanların ikinci öğesini karşılaştırır , değil mi? Aptalca düzenleme için üzgünüm.
Alexander Presber

@ Moolog: evet, bu ikinci element ve bu sadece bu küçük örnek bağlamında faydalı. Kod ayrıca veri yapıları olarak listeleri ile biraz eski stil Lisp yazılmış ...
Rainer Joswig

17

Örneği Google gazetesinden alalım . MapReduce'un amacı, bir çeşit algoritma için paralel olarak çalışan bir işlem birimi yükünü verimli bir şekilde kullanabilmektir. Örnek şudur: bir dizi belgede tüm kelimeleri ve sayılarını ayıklamak istiyorsunuz.

Tipik uygulama:

for each document
    for each word in the document
        get the counter associated to the word for the document
        increment that counter 
    end for
end for

MapReduce uygulaması:

Map phase (input: document key, document)
for each word in the document
    emit an event with the word as the key and the value "1"
end for

Reduce phase (input: key (a word), an iterator going through the emitted values)
for each value in the iterator
    sum up the value in a counter
end for

Bunun çevresinde, belge kümesini Harita aşaması için paralel olarak işlenecek olan "bölünmeler" e ayıracak bir ana programınız olacaktır. Yayılan değerler, çalışan tarafından işçiye özgü bir arabellekte yazılır. Master programı daha sonra tamponun işlenmeye hazır olduğu bildirilir bildirilmez diğer çalışanları Azaltma aşamasını gerçekleştirmeleri için yetkilendirir.

Her çalışan çıktısı (Harita veya Küçültme işçisi olmak) aslında dağıtılmış dosya sisteminde (Google için GFS) veya CouchDB için dağıtılmış veritabanında depolanan bir dosyadır.


10

MapReduce'a gerçekten kolay , hızlı ve "aptallar için" tanıtımını şu adreste bulabilirsiniz: http://www.marcolotz.com/?p=67

İçeriğinin bir kısmını gönderme:

Her şeyden önce, MapReduce orijinal olarak neden oluşturuldu?

Temel olarak Google, büyük hesaplama işlerini kolayca paralel hale getirmek için bir çözüme ihtiyaç duyuyordu ve bu da verilerin bir ağ üzerinden bağlı birkaç makineye dağıtılmasına izin verdi. Bunun yanı sıra, makine arızasını şeffaf bir şekilde ele almak ve yük dengeleme sorunlarını yönetmek zorunda kaldı.

MapReduce'un gerçek güçlü yanları nelerdir?

MapReduce sihrinin Harita ve Azaltma fonksiyonları uygulamasına dayandığı söylenebilir. İtiraf etmeliyim ki, kesinlikle katılmıyorum. MapReduce'u bu kadar popüler yapan ana özellik, basit arayüzle birlikte otomatik paralelleştirme ve dağıtım yeteneğidir. Bu faktör, hataların çoğu için şeffaf hata işleme ile özetlendi ve bu çerçeveyi bu kadar popüler hale getirdi.

Kağıt üzerinde biraz daha derinlik:

MapReduce başlangıçta bir Google makalesinde (Dean & Ghemawat, 2004 - buraya bağlantı), Büyük Veri'de paralel bir yaklaşım ve emtia-bilgisayar kümeleri kullanarak hesaplamalar yapmak için bir çözüm olarak belirtilmiştir. Java ile yazılmış olan Hadoop'un aksine, Google'ın çerçevesi C ++ ile yazılmıştır. Belgede, büyük veri kümeleri üzerinde işlevsel programlamanın Harita ve Küçült işlevlerini kullanarak paralel bir çerçevenin nasıl davranacağı açıklanmaktadır.

Bu çözümde, Kombine olarak adlandırılan birinci ve ikinci arasında isteğe bağlı bir adımla Harita ve Küçült adı verilen iki ana adım olacaktır. Harita adımı önce çalışır, giriş anahtar / değer çiftinde hesaplamalar yapar ve yeni bir çıkış anahtar / değer çifti oluşturur. Giriş anahtar / değer çiftlerinin formatının mutlaka çıkış formatı çiftiyle eşleşmesi gerekmediğini akılda tutmak gerekir. Küçült adımı, aynı anahtarın tüm değerlerini birleştirerek üzerinde başka hesaplamalar gerçekleştirir. Sonuç olarak, bu son adım anahtar / değer çiftlerini çıktılar. MapReduce uygulamasının en önemsiz uygulamalarından biri kelime sayıları uygulamaktır.

Bu uygulama için sözde kod aşağıda verilmiştir:

map(String key, String value):

// key: document name
// value: document contents
for each word w in value:
EmitIntermediate(w, “1”);

reduce(String key, Iterator values):

// key: a word
// values: a list of counts
int result = 0;
for each v in values:
    result += ParseInt(v);
Emit(AsString(result));

Fark edilebileceği gibi, harita bir kayıttaki tüm kelimeleri okur (bu durumda kayıt bir satır olabilir) ve kelimeyi bir anahtar olarak ve 1 sayısını bir değer olarak yayar. Daha sonra, azaltma aynı anahtarın tüm değerlerini gruplayacaktır. Bir örnek verelim: 'ev' kelimesinin kayıtta üç kez göründüğünü hayal edin. Redüktörün girdisi [ev, [1,1,1]] olacaktır. Redüktörde, anahtar ev için tüm değerleri toplayacak ve çıktı olarak şu anahtar değeri verecektir: [house, [3]].

İşte bunun bir MapReduce çerçevesinde nasıl görüneceğine dair bir resim:

Orijinal Haritadan Resim Google kağıdını azalt

MapReduce uygulamalarına birkaç klasik örnek olarak şunu söyleyebiliriz:

• URL erişim sıklığı sayısı

• Ters Web Bağlantı Grafiği

• Dağıtılmış Grep

• Ev sahibi başına Terim Vektörü

Çok fazla ağ trafiğinden kaçınmak için makalede çerçevenin veri yerini nasıl korumaya çalışması gerektiği açıklanmaktadır. Bu, her zaman Harita işlerini çalıştıran bir makinenin bellekte / yerel depoda veri içerdiğinden ve ağdan getirmekten kaçındığından emin olması gerektiği anlamına gelir. Bir eşleyici koymak suretiyle ağı azaltmayı amaçlayan, daha önce açıklanan isteğe bağlı birleştirici adım kullanılır. Birleştirici, belirli bir makinedeki eşleyicilerin çıktıları üzerinde, başka bir makinede olabilecek Redüktörlere göndermeden önce hesaplamalar yapar.

Belge aynı zamanda çerçeve elemanlarının hata durumunda nasıl davranması gerektiğini de açıklamaktadır. Kağıttaki bu unsurlara işçi ve usta denir. Açık kaynak uygulamalarında daha spesifik unsurlara bölüneceklerdir. Google, yaklaşımı yalnızca makalede açıkladığından ve özel yazılımını yayınlamadığından, modeli uygulamak için birçok açık kaynaklı çerçeve oluşturulmuştur. Örnek olarak Hadoop veya MongoDB'deki sınırlı MapReduce özelliği söylenebilir.

Çalışma zamanı, giriş verilerinin bölümlere ayrılması, programın büyük makinalar arasında yürütülmesini zamanlama, makine arızalarını (tabii ki şeffaf bir şekilde) yönetme ve makineler arası iletişimi yönetme gibi uzman olmayan programcıların ayrıntılarına dikkat etmelidir. . Deneyimli bir kullanıcı, girdi verilerinin çalışanlar arasında nasıl bölüneceği gibi bu parametreleri ayarlayabilir.

Anahtar kavramlar:

Hata Toleransı:Makine arızasını nazikçe tolere etmelidir. Bunu yapmak için, usta işçilere periyodik olarak ping atıyor. Kaptan, belirli bir işçiden belirli bir zaman aşımı içinde yanıt almazsa, kaptan, işi o işçide başarısız olarak tanımlar. Bu durumda, arızalı çalışan tarafından tamamlanan tüm harita görevleri atılır ve mevcut başka bir çalışana verilir. Benzer şekilde, işçi hala bir haritayı işliyor veya görevi azaltıyorsa da olur. İşçi azaltma kısmını zaten tamamladıysa, tüm hesaplama başarısız olduğu zamana kadar tamamlanmış ve sıfırlanması gerekmediğine dikkat edin. Birincil başarısızlık noktası olarak, master başarısız olursa, tüm iş başarısız olur. Bu nedenle, master'ın veri yapısını kaydetmek için periyodik kontrol noktaları tanımlanabilir.

Konum: Ağ trafiğini önlemek için, çerçeve tüm girdi verilerinin, üzerinde hesaplamalar yapacak makineler için yerel olarak kullanılabilir olduğundan emin olmaya çalışır. Orijinal açıklamada, çoğaltma faktörü 3 olarak ayarlanmış ve blok boyutları 64 MB olan Google Dosya Sistemi (GFS) kullanılır. Bu, 64 MB'lık (dosya sisteminde bir dosya oluşturan) aynı bloğun üç farklı makinede aynı kopyaya sahip olacağı anlamına gelir. Master blokların nerede olduğunu bilir ve o makinedeki harita işlerini planlamaya çalışır. Bu başarısız olursa, master, görev giriş verilerinin bir kopyasının (yani veri makinesinin aynı rafındaki bir çalışan makine) yakınında bir makine tahsis etmeye çalışır.

Görev Ayrıntı Düzeyi: Her bir harita aşamasının M parçalarına bölündüğü ve her bir Azaltma aşamasının R parçalarına bölündüğü varsayıldığında, ideal olan, M ve R'nin çalışan makine sayısından çok daha büyük olmasıdır. Bunun nedeni, birçok farklı görevi gerçekleştiren bir işçinin dinamik yük dengelemeyi geliştirmesidir. Bunun yanı sıra, işçi arızası durumunda kurtarma hızını artırır (tamamladığı birçok harita görevi diğer tüm makinelere dağıtılabildiğinden).

Yedekleme Görevleri: Bazen, bir Harita veya Redüktör çalışanı kümedeki diğerlerinden çok daha yavaş davranabilir. Bu, toplam işlem süresini tutabilir ve bu tek yavaş makinenin işlem süresine eşit olabilir. Orijinal belgede, bir MapReduce işlemi tamamlanmaya yakın olduğunda master tarafından zamanlanan Yedekleme Görevleri adlı bir alternatif açıklanmaktadır. Bunlar, devam eden görevlerin Ana Verileri tarafından zamanlanan görevlerdir. Böylece, birincil veya yedekleme tamamlandığında MapReduce işlemi tamamlanır.

Sayaçlar: Bazen olay olaylarını saymak istenebilir. Bu nedenle, yaratılan yeri sayar. Her işçideki sayaç değerleri periyodik olarak kaptana yayılır. Daha sonra master toplanır (Evet, Pregel toplayıcıları bu yerden geliyor gibi) başarılı bir haritanın sayaç değerlerini hesaplar ve görevi azaltır ve MapReduce işlemi tamamlandığında bunları kullanıcı koduna döndürür. Ana durumda geçerli bir sayaç değeri de vardır, bu nedenle süreci izleyen bir insan nasıl davrandığını takip edebilir.

Yukarıdaki tüm kavramlarla sanırım, Hadoop sizin için çok kolay olacak. Orijinal MapReduce makalesi veya ilgili bir şey hakkında herhangi bir sorunuz varsa lütfen bize bildirin.


4

Sesi duymak istemiyorum, ama bu bana çok yardımcı oldu ve oldukça basit:

cat input | map | reduce > output

4

Python'u biliyorsanız, MapReduce uygulamasının olası en basit açıklaması aşağıdadır:

In [2]: data = [1, 2, 3, 4, 5, 6]
In [3]: mapped_result = map(lambda x: x*2, data)

In [4]: mapped_result
Out[4]: [2, 4, 6, 8, 10, 12]

In [10]: final_result = reduce(lambda x, y: x+y, mapped_result)

In [11]: final_result
Out[11]: 42

Ham verilerin her bir bölümünün ayrı ayrı nasıl işlendiğini, bu durumda 2 ile çarpın (MapReduce'un harita kısmı). Dayanarak mapped_result, biz sonuç olacağı sonucuna varmıştır 42( azaltmak MapReduce parçası).

Bu örnekten önemli bir sonuç, her bir işlem yığınının başka bir yığına bağlı olmamasıdır. Örneğin, thread_1haritalar [1, 2, 3]ve thread_2haritalar varsa [4, 5, 6], her iki iş parçacığının nihai sonucu yine de olur, [2, 4, 6, 8, 10, 12]ancak bunun için işlem süresini yarıya indirdik . Aynı şey azaltma işlemi için de söylenebilir ve MapReduce'un paralel hesaplamada nasıl çalıştığının özüdür.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.