The two most important days in your life are the day you were born and the day you find out why. -Mark Twain

#96 分散式系統介紹

分散式系統是我念書過程裡最喜歡的課程之一,也是以前我的研究領域.這一門課通常開設在碩博士班的課程裡,因為這門課需要不少基礎的課程,如果開在大學部的話,也一定是屬於大四選修課的內容.它的先修課程包含了作業系統, 演算法, 資料庫理論, 網路理論,網路程式設計等.它有一個兄弟課程叫分散式演算法.這兩門課蠻接近的,對我而言,分散式系統是比較以工程實作導向來討論分散式系統,而分散式演算法是以數學和演算法更嚴謹的電腦理論來談許多分散式系統裡所需要的運作細節. 我剛好都修過這兩門課,所以都清楚這兩個課程的內容.由於這個部落格的目的是將較不好懂的電腦理論用較白話易懂的方式來介紹給大家,所以在分散式系統的文章裡,不會著重在分散式演算法的內容.這是分散式系統系列文章的第一篇,所以會用一個較宏觀的角度來說明什麼是分散式系統.


在 1980 年代以前,電腦科學的理論已經發展的很快了,但實際上的 "商業化產品" 並還沒有大量地出現.然後在 80 年代開始,個人電腦漸漸地普及,並且 Internet 也開始大量地商業化.因此,從這個年代開始漸漸地有商業的分散式系統出現. 我們先簡單定義一下什麼是分散式系統. 用一個不是很精確的定義,分散式系統是由一群電腦一起互相合作然後達成一個共同目標或任務.舉個例子, email.發電子郵件對現在的你來說是幾乎每天都會做的事情.當你在電腦上打開郵件程式,編寫一封 email,寄給在其他位置的使用者.這封信件會從你的電腦傳送到你對應的 email server,然後再從你對應的 email server 一路展轉到目地端的 email server, 最後收件人打開他的郵件程式再把這封 email 下載到他的電腦上.所以, 整個電子郵件就是一種分散式系統,它需要一堆電腦,這些電腦可以連線用來傳送訊息,進而達成讓人們溝通的目的.分散式系統就是在討論這麼多電腦在彼此互相合作達成某個任務或目的時會遇到的問題以及相關的基礎知識和解決方式.


從這樣的定義你可以了解到現在的時代 (2022) 裡有許許多多的分散式系統.例如,WWW 可說是目前全世界最大的分散式系統. 還有許多 online game 也是分散式系統的一種. 像台灣人常用的 LINE 也是分散式系統的一種. 現在因為處理器能力大增加上軟體工具的進步,所以 AI 的應用可以漸漸地在生活上看見.如果那一天你發現某一個計程車行推出了自動駕駛計程車,不需要司機操作,完全由系統自行決定如何運行,從被客人呼叫,載客,到個計程車之間的行程調派等.這也是分散式系統的一種. 再舉另外一個例子,如果某一個國家或公司發展出先進的飛彈系統,這些飛彈能在發射之後,在空中彼此溝通,彼此協調目標,然後把所有的目標都命中. 不在是傳統的一個飛彈飛到一個固定的點或目標.別懷疑,這也是分散式系統的一種.


從這些例子,我相信你可以強烈地感受到什麼是分散式系統.這門學問基本上就是一種討論如何 "打群架" (多台電腦協同運作),用一台電腦打不過 (達不成目標),那只好找一堆電腦來一起打.要叫一台電腦做一件事,這是簡單的,但要叫一堆電腦一起做事來達成一個共同目標,這就顯的複雜許多.這也呼應了一開始所說的,分散式系統的先修科目有作業系統, 演算法, 資料庫,網路等等. 


因此,你可以知道,分散式系統是一堆電腦透過網路連線能彼此溝通進而達成一個任務.電腦不局限是電腦,也可以看成是某一個電腦程式.網路不局限於有線或無線,也不局限於區域網路或廣域網路.概念上,分散式系統的定義是可以很廣泛的.不同的任務所需要底層知識會有差別.例如,email 是一種分散式系統,不論你是用那一種電腦系統,不論你的 email 程式是用什麼語言寫的,也不論你的 email server 在世界上的什麼地方,這些都不應該是障礙.所以,為了讓某一個分散式系統能夠商業化地運行下去,必須定義許多 "標準" 好讓不同的軟體廠商可以依這些標準來產生可行的工具,這樣這個分散式系統才能成功.所以,不同分散式系統的應用就能看到不同的標準.例如, email 系統要定義出 email address 的合法格式,要定義出 email 內容的合法規格,要定義出 server 和 server 之間的溝通方式和通訊協定等,也要定義 server 和 mail client 之間的溝通方式等.從 email 系統的例子,你可以看到光是網路通信就有很多標準要完成.有許多的分散式系統不見得需要走向標準化.因為一些商業考量,有些公司喜歡有自己的應用,例如早期的 ICQ, MSN messenger, 到現在的 LINE.這類似的分散式系統是相對較封閉的分散式系統,因為是由某一家公司自己主導與開發,所以每家公司所使用的 "標準" 就不見得是一致的,各家都可能不一樣.


希望這一篇短文能幫助你了解什麼是分散式系統,下一篇文章,我們將介紹分散式系統裡會面臨到的挑戰,而整個系列文章將會圍繞在這些挑戰來介紹相關的解決方案.


Share:

#95 Priority Queue 是不是 Queue ?

 在日常生活的情況中,排隊是一件很常見的事.原因是提供資源的人少,而使用資源的人多.例如,一堆人到便利商站買東西,買完後要結帳,而櫃檯人員只有一個,所以結帳得一個一個來,因此買商品的人就得排隊.在此時,如果出現一個文化水準低落的人來插隊,想必你一定會很生氣去跟對方說.在便利商店的排隊結帳來說,插隊通常不是件好事,但日常生活裡,某些特殊情況下,插隊是需要的,比如在醫院的急診室,或是馬路上遇到救護車救火車之類的,這種特殊情況,不緊急的必須先讓給緊急的.

當我們撰寫程式碼時,一般情況下我們會使用 Queue 資料結構來達成 "排隊" 的目的,然後,因應需求,如同急診室或救護車的例子,我們必須提供一個方法讓 "插隊" 可以成真.試著想一下,如果我們要用 Queue 結構的概念來達成插隊這件事,該怎麼做呢 ? 

假設我們用 Linked list 來實現 Queue 結構,

 
source: https://en.wikipedia.org/wiki/Linked_list

上面的 List 一共有三個元素,當實做 Enqueue() 時,我們可以把最新的元素加到最後面,這個動作的時間複雜度是 O(n),n 是元素數量,當實做 Dequeue() 時,我們把前最面的元素讀取出來,然後第二個元素將變成第一個元素,時間複雜度是 O(1).當我們要實做 "插隊" 時,我們該怎麼插隊呢 ? 首先,我們需要了解如何定義優先順序.以 List 而言,它的 index 位置就是優先順序 (Priority),因為排在越前面會越先被 dequeue.假設我希望有一個元素能插隊在第二個位置,這表示我們得走到第二個元素,然後做插入的動作.這樣子有一個小缺點,我們必須知道每個元素的優先順序才能正確給出位置.如果我們必須先知道優先順序,這表示我們需要了解每一個元素的內容,這樣才能幫助你決定正確插入的位置是什麼.這顯然有點缺點,因為我們還得寫更多的程式碼來記錄每一個元素的意義,然後來決定該元素是重要還是不重要.

另外一個方法,我們可以將元素的內容改成兩個東西,一個是元素值和另一個是優先順序值.

上面的數字代表優先順序值,下面的數字是原來的元素值.當我們需要 Enqueue() 時,優先順序值就必須先指定.執行時間一久之後,你就會發現這方法也是有問題,因為若我們要將新元素指定到最後面,優先順序值就必須不斷新增,總有一天,這個數字將會超過 integer 的範圍.因此,用這個方法並不好,原因就在於我們必須指定優先順序值.如果我們不要指定優先順序值,同時有這樣的效果,那豈不是更好嗎 ? 此時,你就該了解到用  Queue 結構來實做並不是個好方法.

在資料結構的世界裡,有那一個結構能做到 Queue 的行為並且能定義優先順序的能力呢 ? 有的,它的名字叫 Heap,其範例如下圖所示:


source: https://en.wikipedia.org/wiki/Heap_(data_structure)

Heap 是一種特別的 Tree結構,它有一個很重要的特性,任何一個節點的值都必須大於等於或小於等於該節點以下所有的節點值.以上圖為例,這是一個大於等於的例子,意思就是每一個節點值都會比在它之下所有的節點值還要大或一樣.我們稱它為 max heap,若是小於等於,稱為 min heap.

節點值就是優先順序,只要我們能將需求面的優先順序定義清楚,就能把每一個工作 (節點) 建立 (Enqueue)  max heap,而建立的時間複雜度是 O(lg n),其中 n 是所有節點的數量,這樣效能就比前面說的 Linked list 要來的好.取資料時 (Dequeue),就直接將 root 取出,因為當下 root 是最大值的節點,然後再從 root 的子節點中挑出一個較大的節點做為新的 root 即可,以上圖例子而言將是 36,取出的時間複雜度是 O(1).

使用 max/min heap 來做為具有優先順序功能的 Queue,就稱它為 Priority queue.在主要的程式語言裡 C++/Java 等的 library 都有實作 priority queue,所以讓大家可以直接用,方便許多.所以,當你需要一個有插隊功能的 queue 時,別忘了 priority queue.

Share: