接著上一篇的預告,本文將結合本人近來的一些思考,談談對於企業核心業務資料的重構,配合Memcached構思對現有架構上的一些改造。本文觀點純屬一家之言,可能受制於眼界之窄,業務之淺,歡迎各位共同探討,也歡迎拍磚!
Background
公司從事GPRS車載終端產品的研發。作為配合,軟體這塊主要開發車輛管理資訊系統,以提供對裝有終端車輛的全面服務。
說到這裡,我想大家應該明白,這裡最核心的就是——車輛。大部分的功能幾乎都是圍繞著車輛在展開。給購買車機終端的廠家,提供對車輛的有效管理和控制,是系統的主要價值。
現在出現這樣的需求,需要有這樣一個系統,給車廠的服務部門使用,以便於提高他們的在處理車輛維修時的辦公效率(本人目前正參與該系統的開發,系統互動模型如)。
其中最為關鍵的就是服務流程,車主持有手機終端提供報障資訊->web終端為其建立一個任務->任務在web終端上派發->維修人員有另外一套系統可以隨時協同辦公->車主也可以隨時Tracing Service的進展,並對服務予以評價。需要【服務車輛】去為【工程車輛】提供維修服務(這兩種車輛都會裝有我們公司的車載終端,但很明顯對既有的兩種類型的車輛的生命週期的維護管理都由其他系統承擔,而且他們不屬於同一個產品線,甚至可以這麼講,不同的服務車也會在不同的產品線,不同廠商的工程車輛也在不同的庫)。
What’s the matter?
說白了,對於服務流程的管理並不棘手。為難的是,車輛、位置等相關資訊的擷取,目前的訪問形式,請參見我之前一篇博文。有人認為,只要有互動,web service能夠搞定一切。沒錯,某種情況下,當你需要從別的系統那裡獲得服務,你可以採用web service。但我認為這不是一種萬能藥,特別是系統的基礎、核心資料的擷取都依靠它的話,整個系統的效率可想而知。因為核心資料就類似於系統的基石,你從來都不能大量地依賴web 服務。它帶來的不只是效能問題,同時還有資料的同步問題,安全問題等等。如果,你發現有的時候,你不得不這麼去構建一個系統,我認為這其中充斥了設計上的“壞味道”。
各自為政or化零為整
當業務發展到一定的程度,我覺得某種程度上的重構不可避免——公司對各個業務系統的協議解析、資料處理的功能都歸結於統一的平台;原來各個業務系統對於車機的註冊也被一個專門的系統統一管理,某種程度上化零為整是一個趨勢,但這也不是絕對的方向。但不得不說正確的調整有利於讓結構更加清晰。
資料庫設計的變相思維——面向關係與物件導向
資料庫設計的經驗之談,之前也看過不少。關係型資料庫由來已久,優劣自明,不再多談。很多有經驗的開發或者設計人員,經常有這樣的感覺——你只要把需求告訴我,我腦子一轉,就能告訴你大概需要哪些表,表中有哪些欄位?哪個表的主鍵作為另一張表的外鍵等等。這實際上就是某種程度的抽象,業務的抽象映射到關係表。我們現有的車輛表中大概有五六十個欄位,其中大概還預留了幾十個空欄位,以便日後可能新的業務需要(事實上每個產品線的車輛表的設計都大同小異)。不牽涉任何業務,我在邏輯上將這些車輛相關的屬性,分為三大類:
(1) 車輛基本屬性
(2) 車輛擴充屬性
(3) 業務未知屬性
分成這三大類,結構就變得異常清晰。
車輛基本屬性:是較為重要的和系統互動最頻繁的幾乎是各個平台共有屬性。
車輛擴充屬性:說實話這個也幾乎是各個平台的共有屬性,但和系統的互動程度一般,修改的可能性遠小於讀取的可能性也遠小於車輛的基本屬性。
業務未知屬性:這個就應對了上面我們所說的那些預留的空欄位。這些空欄位可能是來自於業務的擴充,也可能來自於實現系統的需要等(這個也是各個業務系統主要的不同所在)。
我覺得對於車輛這種重量級屬性的大對象,所有屬性一鍋端,有失效能。這裡就要談到物件導向設計的一大原則——組合優於繼承。個人認為,車輛表的設計,應該按照上面的分類,採用組合的方式,更為輕巧靈活。也就是說類似的車輛表,可以拆分為三張表。應對上面的屬性分類,我分別稱為表1,表2,表3。表1,表2既然是各個平台的車輛都共有的屬性,那麼這些表的結構理論上在各個平台都是一致的。而表3,通常來自於各個業務平台,根據自有的業務進行擴充,見:
帶來的好處?
(1) 最佳化資料庫操作
(2) 為Memcached帶來清晰的緩衝結構
(3) 有利於與其他平台資料的互動
(4) 有利於Memcached實施不同的緩衝失效策略
以下我逐一分析一下這樣的拆分帶來的好處:
(1) 對於最佳化資料庫操作,以上分類,反映了資料庫不同欄位的讀寫頻度,可以實現讀寫分離,很明顯表1與應用程式互動的頻率最為頻繁,特別是某些操作會鎖表。這樣的隔離,使得對資料庫訪問得到一定程度的最佳化。
(2) 對於為Memcached帶來清晰的緩衝結構,參照下面的結構圖:
物理上的多庫多表,邏輯上的分類單表
由於分布式對於內部的隔離,保證外部存取的透明。在邏輯上,很多資料都是在記憶體中的一個集合。對於,不同的業務平台,資料的訪問方式很可能是隔離與互動的並存,分布式提供了這樣的一個切合點。
隔離:首先資料是物理上的隔離,邏輯上雖然是個大資料集合,但是還是會被訪問時用於標識所屬平台的(basekey)的唯一性完全隔離。
互動:邏輯上是一個大資料集合,它為我們帶來的互動便捷的物理優勢。那麼應該如何互動?這裡串連它們的還是basekey。通常情況下的系統,大家訪問自己的資料(用自己的basekey,訪問自己的資料集合),互不干擾。當遇到需要我的系統為你做業務支撐,或者像我目前做的一個統一性的服務平台而言。如果我通過授權拿到了你的系統的basekey,你的資料和我的資料有何區別?有了basekey,資料就存在著共用性。
(3) 對於利於與其他平台資料的互動,已經在(2)中的互動中進行瞭解釋。
事實上,上面對於車輛表的行為所做的就是縱向切割技術。而對於橫向切割,又是我們業務的一個特殊點——軌跡。對於資料表作橫縱切割的優勢就在於使表變得更輕量級。縱向切割有益於讀寫分離,而橫向切割有益於最佳化查詢(有時查詢需要進行全表掃描,這大大減輕了資料庫的壓力,還有對於每幾十秒上傳一條經緯度資料的軌跡表,壓力可想而知)。
(4) 對於有利於Memcached實施不同的緩衝失效策略,我將在下面談到!
需要注意的問題
對於同一個庫只能怪的這三張拆分過的車輛資訊表都有共同的一個基於產生的GUID作為它們的主鍵,即可建立關聯。不必擔心資料插入的問題。理論上,表1中都是車輛的固有屬性,幾乎是在車機註冊的時候執行insert操作,此時只需要牽扯到表1(這時用於標識表1中該車輛的GUID已經產生),表2的資料可能在註冊完之後,填寫相關補充資訊的insert操作,表3同樣如此。
瓜分資料就牽扯到維護其一致性的問題。很明顯,我們不能允許表1,表2中的車輛資料被刪除,而表3中的資料存在這樣的悲劇發生。事實上,對於車輛表這種核心業務的基表而言,刪除的可能性不是很大(它關聯這太多的商務資訊)。如果真的要刪除一條資料(比如所謂的車機報廢,或者停止服務等,當然這裡還是假設),只能去做邏輯刪除(採用某個識別欄位),不能允許資料不一致的情況出現。
Memcache使用解析
可能你已經發現,系統中最經常被訪問的是兩種類型的表:(1)關係表(如車輛和機構的關係表等);(2)實體表(如同這裡我們所說的車輛表,軌跡表等)。根據訪問頻度的潛在聯絡,我們認為對於Ids的訪問頻度要大於對於單個實體表本身的訪問頻度【事實上你會發現,你對於實體表的訪問就是由關係表牽引過來的】。所以,對於關係表,我們可以採用Vector Cache的緩衝機制,緩衝關係的ID集合。接下來我們再按照上面劃分出的車輛表,做不同側策略的Row Cache(這裡可以將VectorCache以及Row Cache的機制同樣應用於軌跡表)。當然這裡做記憶體式緩衝的典型案例就是開源項目——Memcached。我們的Vector Cache以及Row Cache對於區區十幾萬輛車乃至幾十萬輛車幾乎是100%的命中率!
按照上面的分析,我們可以簡單的分析如下的緩衝類型:
(1) 關係表(例如,車輛與機構關係表,機構與使用者關係表)
(2) 常用核心業務資料(如上分析的表1,表2以及軌跡資料等)
(3) 業務查詢(select vi_sim,vi_terminal,vi_guidfrom vehicleInfo where vi_type=2)
對於它們的key可以參考如下的產生機制(這裡key的分析對應上面的緩衝類別):
首先,這裡假設每個平台都有一個屬於自己平台的標識簡單稱之為basekey【基於一定的演算法產生】
(1)看關聯類型
1. 【一對一】沒有必要單獨搞出關係
2. 【一對多】—key:md5(basekey_一方的Id);value:多方的id集合
3. 【多對多】—key:md5(basekey_表名);value:關係集合
(2) Key:md5(basekey_主鍵);value:主鍵對應的一條資料記錄
(3) Key:md5(basekey_業務sql);value:業務資料集合
註:這裡對於第三種key,為了避免對於相似的邏輯,產生太多的key。比如,某個sql,你只需要尋找一輛車的終端號;另一個sql,你只需要尋找一輛車的sim卡號。你大可參考ORM的方式,對於這些採用一個統一的比如select *操作,封裝到一個實體物件裡。因為資料來自記憶體,所以對於資料的大小與資料的訪問速度上來講,可以和資料庫訪問的傳統方式做一個平衡。
緩衝失效策略分析——分錶帶來的另一個好處
以下談到的表1/2/3指的是上面拆分出的車輛資訊表
對於表1,車輛的基本屬性,可能新增/修改/查詢三種操作都較為頻繁(刪除的這裡暫不做討論,因為本人覺得邏輯刪除更為合適)。那麼這樣的操作,決定了記憶體資料與DB中資料同步的“強一致性”。因此,這種情況下需要採取的緩衝失效策略為——強制失效。比如,在做update操作的時候,強制使得相關的select該表對於的key-value對直接失效。在下次從DB中尋找的時候,重新加入到memcache中。
對於表2,車輛的擴充屬性,該表可能應對有稍多的select,較少的update,這樣該表在記憶體中相應的緩衝失效時間相對於表1就可以較為延長(上面,我已經解釋了,雖然表2儲存的也是車輛的屬性,但它通常都是後期維護到資料庫,並且對於該表的查詢操作並不是那麼頻繁)。
對於表3,它的緩衝策略幾乎達到了設定一個較長的時間自動到期的層級。
我和你有同樣的顧慮——安全
有時系統所處的環境,讓你不得不考慮,我們的資料是否應該圈定在我們設定好的邊界之內。這裡業務性質和行業性質的因素可能佔有很大的因素。你不得不考慮,事情一旦出了你完全控制的範圍,你是否能hold住!我個人認為,我們的軌跡以及和車輛相關的資料,它們集體的邊界只要能圈定在公司之內就可以hold住。有些資訊,失去了情境和關係就毫無任何作用(當然除非你把一切都得到了)。退一萬步而言,這所有的資訊都是來自公司給客戶提供的服務資料。這算是公司資源,公司用十個資產庫或者一個大的資源集區來儲存資料,對於客戶而言都是透明的,一切都為了給客戶提供正確的資料。
你總是會把安全掛在嘴邊嗎? memcache中儲存的資料只是你真實表中的一個備份,或者稱之為快照。再說,我可沒讓你對上面三種形式的緩衝都採用同一種basekey。但話說回來了,還是必須認真考慮授權的設計!
下面我構思了整個系統的構架原型:
註解:
【Node 1-Node N】:是部署的分布式Memcache的單個節點;
【Business DB1-Business DBN】:是各業務平台自身資料庫;
【Memcache Server】:作為所有Node對外的訪問點,以屏蔽物理部署細節;
【監聽/互動Server】:有兩個職責:首先以近似監聽連接埠的方式(可以簡單理解為輪詢),不停地hit Memcache Server中,需要供其他系統互動的key,如果供互動的業務資料不存在,則從特定的業務資料庫抓取,並寫入Memcache Server;其次,因為記憶體作為一種易失性的存放裝置並不值得完全信任,如果發現Memcache Server宕機,則接管互動系統對於資料訪問的職責,直接提供對資料的訪問。這裡需要提示的是,該Server具有對所有資料的某些許可權,比如只開放查詢,不授予刪除許可權等(這取決於你給該server訪問你資料庫所使用的賬戶的角色);
【認證/授權Server】:在系統之間需要互動時其作用,它給需要擷取其他平台業務資料的系統授以access token(就是上面提到的basekey);
【Web/Application Server】:作為Memcache client,有兩種訪問資料情境:(1)訪問本系統資料,首先訪問MemcacheServer,如果能夠命中,則直接擷取資料,如果不能命中(或者是做類似的insert、update等操作),則直接操作資料庫,如果需要刪除Memcache Server中可能不一致的緩衝,則予以刪除,以保持資料同步;(2)訪問其他系統資料,大致走圖中紅色路線,首先向認證/授權 Server請求access token,通過授權之後,就可以直接存取Memcache Server,通過拿到的access token就可以訪問特定的key的資料,如果未命中(很多情況下都是由於Memcache Server宕機引起的,因為監聽/互動Server的首要職責就是保證Memcache Server特定的key-value是正常的)。當然這裡不管是什麼原因引起的,當無法獲得資料時,我們通過accesstoken可以直接向監聽/互動Server請求資料。
【手機終端(補充)】: 由於圖中不太好展示了,這裡簡單談談手機終端對於服務的訪問。對於手機終端而言,還是需要採用http GET或者Web service的方式來訪問。因此我們需要把用到的Web Service都發布到互動伺服器,它會登記所有服務。此時手機終端的訪問方式幾乎和Web/Application Server中(2)的方式一樣,先授權後訪問,只不過它直接請求監聽/互動Server,由其決定訪問memcached還是各個業務平台獨立的庫。
事實上這裡的模型圖只是從邏輯和職責上進行區分,實際情況下,考慮經濟因素,可以對一些裝置加以合并。比如Business DB可以和Node合并,監聽/互動Server與認真/授權Server可以構建在同一個物理機器上等等。
重構的代價——複雜度,工作量
除非確定對於私人資料不會提供互動服務,那各自為政也沒什麼不能接受的。但當你產生這樣的業務時,很明顯重構平台的核心資料能給這樣的業務提供更為簡便的抓取資訊的渠道。對於這樣的重構,將是個不小的工作量的。很明顯,實施的唯一方式,只能是兩種方式並行使用,逐步更替。
總結
本文以公司業務的某些特定資料表為例,展示了如何基於其進行重構,並解釋了重構資料結構帶來的好處,以及結合memcache闡述了重構後的資料結構如何提供互動服務。下篇會談談我認為的改造版的三層架構(考慮增加資料適配層,或者某種可配置的路由形式),它能夠對多種資料存放區形式(記憶體分布式、DB、NoSQL)屏蔽細節,以統一的API對資料進行訪問。