Amazon SimpleDB到底比關(guān)系數(shù)據(jù)庫(kù)好在哪兒?
原創(chuàng)【51CTO獨(dú)家特稿】大家一定都使用過(guò)關(guān)系數(shù)據(jù)庫(kù)管理系統(tǒng)(RDBMS),可以說(shuō)關(guān)系數(shù)據(jù)庫(kù)的身影無(wú)處不在,也有諸如Oracle,微軟,IBM等數(shù)據(jù)庫(kù)廠商為我們提供了大量的RDBMS產(chǎn)品,縱觀這幾十年,關(guān)系數(shù)據(jù)庫(kù)為應(yīng)用程序的快速發(fā)展立下了汗馬功勞,但目前出現(xiàn)了一種由互聯(lián)網(wǎng)和社交網(wǎng)絡(luò)驅(qū)動(dòng)的新型應(yīng)用程序,這種應(yīng)用程序需要充足的擴(kuò)展能力,以滿足高峰時(shí)段大規(guī)模訪問(wèn)和數(shù)據(jù)處理的要求。
這種應(yīng)用場(chǎng)景很難使用傳統(tǒng)的關(guān)系數(shù)據(jù)庫(kù)滿足要求,因?yàn)樗豢赡転楦叻鍟r(shí)段提供足夠的硬件資源,如果非要在傳統(tǒng)關(guān)系數(shù)據(jù)庫(kù)上承載這類應(yīng)用,維護(hù)工作量也是非常驚人的,并且宕機(jī)也是常事,SimpleDB可以解決這些問(wèn)題,但為了解決這些問(wèn)題,SimpleDB提出了一些新的設(shè)計(jì)理念,為了保證你在選擇數(shù)據(jù)庫(kù)時(shí)作出正確的抉擇,你應(yīng)該了解這些新的設(shè)計(jì)理念。
無(wú)范式
范式化是關(guān)系數(shù)據(jù)庫(kù)有效組織數(shù)據(jù)的一個(gè)過(guò)程,其目的是消除冗余數(shù)據(jù),同時(shí)確保數(shù)據(jù)依賴的意義,SimpleDB數(shù)據(jù)模型不遵守任何形式的范式,相反,它是完全反范式的,SimpleDB的無(wú)范式化允許你更靈活地處理你的數(shù)據(jù)模型,允許在你的數(shù)據(jù)中使用多值屬性。
我們先來(lái)看一個(gè)基礎(chǔ)的表格結(jié)構(gòu),然后分別用RDBMS和SimpleDB數(shù)據(jù)模型理念進(jìn)行表結(jié)構(gòu)設(shè)計(jì),在這個(gè)例子中,我們創(chuàng)建一個(gè)簡(jiǎn)單的聯(lián)系人數(shù)據(jù)庫(kù)。
ID | First_Name | Last_Name | Phone_Num |
101 | John | Smith | 555-845-7854 |
101 | John | Smith | 555-854-9885 |
101 | John | Smith | 555-695-7485 |
102 | Bill | Jones | 555-748-7854 |
102 | Bill | Jones | 555-874-8654 |
添加新電話號(hào)碼的難易程度按照這種設(shè)計(jì),要按電話號(hào)碼找一個(gè)人是很容易的。
- SELECT * FROM Contact_Info WHERE Phone_Num = '555-854-9885'
但最明顯的問(wèn)題是名字有重復(fù),這樣的表結(jié)構(gòu)設(shè)計(jì)效率是很低的,下面分析一下這樣設(shè)計(jì)的強(qiáng)項(xiàng)和弱項(xiàng)。
分析項(xiàng) | 強(qiáng)項(xiàng) | 弱項(xiàng) |
存儲(chǔ)效率 | 低 | |
按電話號(hào)碼檢索的效率 | 高 | |
按名字檢索的效率 | 低 | |
添加新電話號(hào)碼的難易程序 | 容易 |
這樣的設(shè)計(jì)很簡(jiǎn)單,但名字重復(fù)了,因此在數(shù)據(jù)同步方面需要小心謹(jǐn)慎,如果名字未同步,按名字檢索電話號(hào)碼時(shí),結(jié)果就不準(zhǔn)確了。
為了改善這個(gè)設(shè)計(jì),更合理地組織數(shù)據(jù),一個(gè)辦法是象下面這樣創(chuàng)建多個(gè)電話號(hào)碼字段,雖然它通過(guò)一個(gè)簡(jiǎn)單的方法解決了當(dāng)前的問(wèn)題,但它限制了最多只能容納三個(gè)電話號(hào)碼,如果還要增加郵件地址和Twitter賬號(hào),表將會(huì)越來(lái)越大。
ID | First_Name | Last_Name | Phone_Num | Phone_Num_2 | Phone_Num_3 |
101 | John | Smith | 555-845-7854 | 555-854-9885 | 555-695-7485 |
102 | Bill | Jones | 555-748-7854 | 555-874-8654 |
要按電話號(hào)碼找一個(gè)人是很恐怖的。
- SELECT * FROM Contact_Info WHERE Phone_Num_1 = '555-854-9885'
- OR Phone_Num_2 = '555-854-9885'
- OR Phone_Num_3 = '555-854-9885'
我們?cè)賮?lái)分析一下這種數(shù)據(jù)庫(kù)結(jié)構(gòu)設(shè)計(jì)的強(qiáng)項(xiàng)和弱項(xiàng)。
分析項(xiàng) | 強(qiáng)項(xiàng) | 弱項(xiàng) |
存儲(chǔ)效率 | 高 | |
按電話號(hào)碼檢索的效率 | 高 | |
按名字檢索的效率 | 高 | |
添加新電話號(hào)碼的難易程序 | 容易 |
這種設(shè)計(jì)也很簡(jiǎn)單,但電話號(hào)碼數(shù)量受到了限制,并且按電話號(hào)碼檢索會(huì)涉及到三個(gè)索引。
另一個(gè)辦法是使用一個(gè)字段存儲(chǔ)所有打電話號(hào)碼,用分隔符進(jìn)行分割。
ID | First_Name | Last_Name | Phone_Num |
101 | John | Smith | 555-845-7854;555-854-9885;555-695-7485 |
102 | Bill | Jones | 555-748-7854;555-874-8654 |
這種設(shè)計(jì)方法的優(yōu)點(diǎn)是無(wú)重復(fù),緊湊,簡(jiǎn)潔,可維護(hù)性好,容易擴(kuò)展,但要按電話號(hào)碼進(jìn)行檢索只能使用子串模糊匹配,效率低下。
- SELECT * FROM Contact_Info WHERE Phone_Nums LIKE %555-854-9885%
這種SQL語(yǔ)句會(huì)強(qiáng)制執(zhí)行全表掃描,如果是小表,不會(huì)有性能影響,但如果有上百萬(wàn)行記錄,數(shù)據(jù)庫(kù)的性能將會(huì)受到嚴(yán)重影響。來(lái)看一下這種設(shè)計(jì)的強(qiáng)項(xiàng)和弱項(xiàng)。
分析項(xiàng) | 強(qiáng)項(xiàng) | 弱項(xiàng) |
存儲(chǔ)效率 | 高 | |
按電話號(hào)碼檢索的效率 | 低 | |
按名字檢索的效率 | 高 | |
添加新電話號(hào)碼的難易程序 | 容易 |
為了遵守關(guān)系數(shù)據(jù)庫(kù)的范式,有時(shí)你必須將數(shù)據(jù)分解到多個(gè)獨(dú)立的表中,然后相互用鍵進(jìn)行關(guān)聯(lián),要從多個(gè)表中檢索數(shù)據(jù),必須使用連接操作。
下面就重新對(duì)數(shù)據(jù)進(jìn)行范式化設(shè)計(jì),首先設(shè)計(jì)一個(gè)Person_Info表。
ID | First_Name | Last_Name |
101 | John | Smith |
102 | Bill | Jones |
再設(shè)計(jì)一個(gè)Phone_Info表。
ID | Phone_Num |
101 | 555-845-7854 |
101 | 555-854-9885 |
101 | 555-695-7485 |
102 | 555-748-7854 |
102 | 555-874-8654 |
現(xiàn)在連接Person_Info和Phone_Info表就可以檢索電話號(hào)碼,也可以檢索郵件地址,除了ID主鍵外,表結(jié)構(gòu)很干凈,無(wú)重復(fù)數(shù)據(jù),給Phone_Num字段加上索引,按電話號(hào)碼檢索聯(lián)系人的效率就很高了。
- SELECT First_Name, Last_Name, Phone_num, Person_Info.ID
- FROM Person_Info JOIN Phone_Info
- ON Person_Info.ID = Phone_Info.ID
- WHERE Phone_Num = '555-854-9885'
再來(lái)分析一下這種設(shè)計(jì)的強(qiáng)項(xiàng)和弱項(xiàng)。
分析項(xiàng) | 強(qiáng)項(xiàng) | 弱項(xiàng) |
存儲(chǔ)效率 | 高 | |
按電話號(hào)碼檢索的效率 | 高 | |
按名字檢索的效率 | 高 | |
添加新電話號(hào)碼的難易程序 | 容易 |
雖然這是一個(gè)高效的關(guān)系模型,但在SimpleDB中沒(méi)有連接命令,使用兩個(gè)表會(huì)強(qiáng)制實(shí)施全表掃描,下面我們就來(lái)看看如何使用SimpleDB的數(shù)據(jù)模型來(lái)實(shí)現(xiàn)。
#p#
無(wú)連接
SimpleDB不支持連接的概念,相反,它為一個(gè)屬性提供了存儲(chǔ)多值的功能,從而避免了檢索所有值需要的連接操作。
ID | |||
101 | First_Name=John | Last_Name=Smith | Phone_Num =555-845-7854 Phone_Num =555-854-9885 Phone_Num =555-695-7485 |
102 | First_Name=Bill | Last_Name=Jones | Phone_Num =555-748-7854 Phone_Num =555-874-8654 |
在SimpleDB表中,每條記錄保存為一個(gè)屬性/值對(duì)形式的條目,這里的區(qū)別是Phone_Num字段有多個(gè)值,和使用分隔符的字段不同,SimpleDB可以索引所有的值,因此檢索任何一個(gè)值的效率都很高。
- SELECT * FROM Contact_Info WHERE Phone_Num = '555-854-9885'
SELECT操作是非常高效的,甚至可以象下面這樣多次使用Phone_Num:
- SELECT * FROM Contact_Info WHERE Phone_Num = '555-854-9885'
- OR Phone_Num = '555-748-7854'
我們?cè)賮?lái)分析一下這種設(shè)計(jì)的強(qiáng)項(xiàng)和弱項(xiàng)。
分析項(xiàng) | 強(qiáng)項(xiàng) | 弱項(xiàng) |
存儲(chǔ)效率 | 高 | |
按電話號(hào)碼檢索的效率 | 高 | |
按名字檢索的效率 | 高 | |
添加新電話號(hào)碼的難易程序 | 容易 |
無(wú)模式
SimpleDB也是無(wú)模式的,你不能創(chuàng)建、修改、升級(jí)或維護(hù)模式,這也是習(xí)慣了傳統(tǒng)關(guān)系數(shù)據(jù)庫(kù)的人難以理解的地方,但這正是SimpleDB可無(wú)限擴(kuò)展的關(guān)鍵之處,你可以按你喜好的模型存儲(chǔ)任意類型的屬性/值數(shù)據(jù),存儲(chǔ)數(shù)據(jù)時(shí)無(wú)需擔(dān)心模式的變化。
我們?cè)谇懊娴幕A(chǔ)上再添加一個(gè)郵件地址字段,在傳統(tǒng)關(guān)系數(shù)據(jù)庫(kù)中,要么在聯(lián)系人信息表中增加一個(gè)字段,要么在電話表中增加一個(gè)字段,要么增加一個(gè)Email_Info表。
ID | Email_Addr |
101 | john@abc.ccc |
102 | bill@def.ccc |
使用傳統(tǒng)的關(guān)系數(shù)據(jù)庫(kù)方法,我們需要連接三個(gè)表才能提取需要的數(shù)據(jù)。
- SELECT First_Name, Last_Name, Phone_num, Person_Info.ID, Email_Addr
- FROM Person_Info JOIN Phone_Info JOIN Email_Info
- ON Person_Info.ID = Phone_Info.ID
- AND Person_Info.ID = Email_Info.ID
- WHERE Phone_Num = '555-854-9885'
分析一下這種設(shè)計(jì)方法的強(qiáng)項(xiàng)和弱項(xiàng)。
分析項(xiàng) | 強(qiáng)項(xiàng) | 弱項(xiàng) |
存儲(chǔ)效率 | 高 | |
按電話號(hào)碼檢索的效率 | 高 | |
按名字檢索的效率 | 高 | |
添加新電話號(hào)碼的難易程序 | 容易 | |
可擴(kuò)充能力 | 強(qiáng) | 定義新表,需要兩個(gè)連接 |
我們忽略join和left outer join的區(qū)別,實(shí)際上這里應(yīng)該使用left outer join,除非所有聯(lián)系人只有一個(gè)電話號(hào)碼和郵件地址,這個(gè)例子只是為了證明必須修改Contact_Info模式。
ID | |||
101 | First_Name=John | Last_Name=Smith | Phone_Num =555-845-7854 Phone_Num =555-854-9885 Phone_Num =555-695-7485 Email_Addr =john@abc.ccc |
102 | First_Name=Bill | Last_Name=Jones |
Phone_Num =555-748-7854 Phone_Num =555-874-8654 Email_Addr =john@def.ccc |
可能你要問(wèn)為什么Email_Addr沒(méi)有屬于它自己的列,在SimpleDB中,表是沒(méi)有列的概念的,SimpleDB數(shù)據(jù)的表格視圖只是為了增強(qiáng)可讀性而設(shè)計(jì)的,并非表現(xiàn)的是它的數(shù)據(jù)結(jié)構(gòu),SimpleDB中唯一的結(jié)構(gòu)就是由項(xiàng)目名和屬性/值對(duì)組成的,下面是更恰當(dāng)?shù)腟impleDB數(shù)據(jù)結(jié)構(gòu)表現(xiàn)形式。
ID | Attribute/Value pairs |
101 |
First_Name=John Last_Name=Smith Phone_Num =555-845-7854 Phone_Num =555-854-9885 Phone_Num =555-695-7485 Email_Addr =john@abc.ccc |
102 |
First_Name=Bill Last_Name=Jones Phone_Num =555-748-7854 Phone_Num =555-874-8654Email_Addr =john@def.ccc |
按郵件地址檢索聯(lián)系人的查詢語(yǔ)句如下:
- SELECT * FROM Contact_Info WHERE Email_Addr = 'john@def.ccc'
我們?cè)賮?lái)分析一下這種設(shè)計(jì)的強(qiáng)行和弱項(xiàng)。
分析項(xiàng) | 強(qiáng)項(xiàng) | 弱項(xiàng) |
存儲(chǔ)效率 | 高 | |
按電話號(hào)碼檢索的效率 | 高 | |
按名字檢索的效率 | 高 | |
添加新電話號(hào)碼的難易程序 | 容易 | |
可擴(kuò)充能力 | 強(qiáng) |
#p#
更簡(jiǎn)單的SQL
SQL在傳統(tǒng)關(guān)系數(shù)據(jù)庫(kù)中廣泛用于訪問(wèn)和操作數(shù)據(jù),經(jīng)過(guò)多年的發(fā)展,SQL已經(jīng)可以在數(shù)據(jù)庫(kù)上做很多事情了,SimpleDB不支持完整的SQL語(yǔ)言,相反,它使用與SQL類似的查詢語(yǔ)言檢索數(shù)據(jù),但語(yǔ)句更加精煉和簡(jiǎn)單,簡(jiǎn)化了查詢數(shù)據(jù)的整個(gè)過(guò)程,它和傳統(tǒng)SQL的***不同就是SimpleDB支持的SQL支持SimpleDB的多值屬性,使得查詢更加簡(jiǎn)單,特別是查詢多值屬性時(shí)更是如此。
SimpleDB SQL語(yǔ)法很簡(jiǎn)單,總結(jié)如下:
- select output_list
- from domain_name
- [where expression]
- [sort_instructions]
- [limit limit]
只有字符串
SimpleDB使用非常簡(jiǎn)單的數(shù)據(jù)模型,所有數(shù)據(jù)都存儲(chǔ)為UTF-8字符串,簡(jiǎn)化了文本數(shù)據(jù)的存儲(chǔ),SimpleDB可以更容易索引你的數(shù)據(jù),使得檢索數(shù)據(jù)的速度更快,如果你需要存儲(chǔ)或檢索其它類型的數(shù)據(jù),如數(shù)字和日期型數(shù)據(jù),必須將這些數(shù)據(jù)編碼成字符串類型,由于SimpleDB沒(méi)有模式的概念,在存儲(chǔ)到SimpleDB之前,確保數(shù)據(jù)編碼的正確性就是開(kāi)發(fā)人員的責(zé)任了。
只有字符串會(huì)在查詢和排序方面帶來(lái)的影響,仔細(xì)看一下下面的Sample_Qty表:
ID | |
101 | Quantity = 1.0 |
102 | Quantity = 1.00 |
103 | Quantity = 10 |
104 | Quantity = 25 |
105 | Quantity = 100 |
嘗試執(zhí)行下面的SQL語(yǔ)句:
- SELECT * FROM Sample_Qty WHERE Quantity= '1'
它不會(huì)返回任何結(jié)果,選擇按Quantity排序的所有記錄,返回的結(jié)果是101,102,103,105,104。日期問(wèn)題就好解決了,可以將日期保存為ISO 8601格式。
最終一致性
SimpleDB可以被看作是一個(gè)寫少讀多的模型,更新只在中央數(shù)據(jù)庫(kù)上執(zhí)行,但讀可以在多個(gè)只讀從數(shù)據(jù)庫(kù)上執(zhí)行。
SimpleDB會(huì)在多個(gè)地方存儲(chǔ)每個(gè)域,無(wú)論是寫入還是更新域內(nèi)的數(shù)據(jù),首先要向你的應(yīng)用程序返回一個(gè)成功狀態(tài)代碼,然后再更新所有數(shù)據(jù)副本,這些變化傳播到所有存儲(chǔ)節(jié)點(diǎn)可能需要一些時(shí)間,但最終所有節(jié)點(diǎn)上的數(shù)據(jù)都會(huì)保持一致性。
SimpleDB提供了最終一致性保證,這意味著從SimpleDB檢索的數(shù)據(jù)可能會(huì)因時(shí)間不同而有所不同,主要原因是SimpleDB是一個(gè)分布式系統(tǒng),所有的信息是跨多個(gè)物理服務(wù)器存儲(chǔ)的,并有可能是跨多個(gè)數(shù)據(jù)中心的,這樣做可以保證有足夠的擴(kuò)展能力,也為數(shù)據(jù)安全提供充分的保障,但代價(jià)就是對(duì)數(shù)據(jù)的操作需要一定時(shí)間才能傳播到整個(gè)分布式SimpleDB系統(tǒng),因此在最終一致前,檢索到的數(shù)據(jù)可能是過(guò)期的。
Amazon已經(jīng)聲明實(shí)現(xiàn)最終一致性現(xiàn)在已經(jīng)只需要數(shù)秒時(shí)間,但這個(gè)時(shí)間是與網(wǎng)絡(luò),SimpleDB負(fù)載等因素緊密相關(guān)的,使用一個(gè)中間層緩存可以有效解決一致性問(wèn)題,最終一致性也是SimpleDB與傳統(tǒng)RDBMS的重要不同點(diǎn)。為了實(shí)現(xiàn)大規(guī)模擴(kuò)展,在應(yīng)用程序設(shè)計(jì)時(shí)就要做出取舍。
雖然最終一致性是SimpleDB的常規(guī)模型,Amazon也推出了多個(gè)一致性讀取擴(kuò)展,使用GetAttributes或SELECT時(shí),可以選擇ConsistentRead = true,強(qiáng)制讀取***的值,這個(gè)參數(shù)告訴SimpleDB直接從主數(shù)據(jù)庫(kù)讀取數(shù)據(jù),而不是從從數(shù)據(jù)庫(kù)讀取數(shù)據(jù)。
此外,Amazon也發(fā)布了帶有條件的PUT和DELETE,只有當(dāng)一個(gè)特定屬性有一個(gè)特定的值或不存在某個(gè)特定的值時(shí),才在數(shù)據(jù)庫(kù)上執(zhí)行PUT或DELETE。
擴(kuò)展性
關(guān)系數(shù)據(jù)庫(kù)是圍繞實(shí)體和實(shí)體之間的關(guān)系設(shè)計(jì)的,要提供高可擴(kuò)展性,在硬件上需要的投入很大,SimpleDB是圍繞數(shù)據(jù)分區(qū)設(shè)計(jì)的,將數(shù)據(jù)分布在多個(gè)節(jié)點(diǎn)上,天生就具有很好的擴(kuò)展能力,SimpleDB提供了數(shù)據(jù)自動(dòng)分區(qū)和復(fù)制功能,同時(shí)保證了數(shù)據(jù)的快速訪問(wèn)和可靠性,你可以按需擴(kuò)展Amazon提供給你的資源,應(yīng)付大規(guī)模訪問(wèn)請(qǐng)求不再是問(wèn)題。
SimpleDB擴(kuò)展性最吸引人的是它是按使用量付費(fèi)的。
低維護(hù)
維護(hù)傳統(tǒng)關(guān)系數(shù)據(jù)庫(kù)正常運(yùn)行是一個(gè)艱巨的任務(wù),應(yīng)用程序是動(dòng)態(tài)的,總是存在各種修改或增加新的功能,這些都可能導(dǎo)致需要修改數(shù)據(jù)庫(kù)模式,無(wú)疑增加了維護(hù)和調(diào)整成本,SimpleDB是由Amazon托管和維護(hù)的,你的任務(wù)就是存儲(chǔ)和檢索數(shù)據(jù),簡(jiǎn)化的數(shù)據(jù)結(jié)構(gòu)和無(wú)模式都有助于讓你的應(yīng)用程序更加靈活,適應(yīng)變化的能力更強(qiáng),SimpleDB自動(dòng)索引所有數(shù)據(jù),確保你的查詢更快。
SimpleDB模型的優(yōu)點(diǎn)
與傳統(tǒng)關(guān)系數(shù)據(jù)庫(kù)相比,SimpleDB有以下優(yōu)點(diǎn):
◆與關(guān)系數(shù)據(jù)庫(kù)相比,減少了維護(hù)工作量;
◆自動(dòng)索引所有數(shù)據(jù),提高查詢性能;
◆靈活修改存儲(chǔ)的數(shù)據(jù),無(wú)需擔(dān)心模式的變化;
◆由Amazon提供自動(dòng)的故障轉(zhuǎn)移能力;
◆跨多個(gè)節(jié)點(diǎn)復(fù)制你的數(shù)據(jù),安全性有保障;
◆可無(wú)限擴(kuò)展,無(wú)需擔(dān)心硬件資源不夠用;
◆使用簡(jiǎn)單的API簡(jiǎn)化了數(shù)據(jù)存儲(chǔ)和查詢操作;
◆無(wú)傳統(tǒng)RDBMS中的對(duì)象-關(guān)系映射,允許你的結(jié)構(gòu)化數(shù)據(jù)直接映射到你的底層應(yīng)用程序代碼,減少應(yīng)用程序開(kāi)發(fā)周期。
SimpleDB模型的缺點(diǎn)
當(dāng)然SimpleDB與傳統(tǒng)關(guān)系數(shù)據(jù)庫(kù)相比,它也是有缺點(diǎn)的:
◆那些需要數(shù)據(jù)立即一致性的應(yīng)用程序不能采用SimpleDB;
◆使用SimpleDB需要開(kāi)發(fā)團(tuán)隊(duì)成員熟悉有別于RDBMS的存儲(chǔ)模型;
◆因?yàn)殛P(guān)系不象關(guān)系數(shù)據(jù)庫(kù)中定義的那么明確,需要在應(yīng)用程序代碼中實(shí)現(xiàn)對(duì)數(shù)據(jù)的約束;
◆如果你的應(yīng)用程序需要存儲(chǔ)非字符串?dāng)?shù)據(jù)類型的數(shù)據(jù),存儲(chǔ)之前需要先編碼;
◆SimpleDB存儲(chǔ)多個(gè)屬性的方法需要習(xí)慣了RDBMS的開(kāi)發(fā)人員適應(yīng)它。
原文名:Amazon SimpleDB versus RDBMS
【編輯推薦】