天天看點

《大規模Java平台虛拟化與調優》——2.2 SQLFire特性

本節書摘來自華章計算機《大規模java平台虛拟化與調優》一書中的第2章,第2.2節,作者:(美)emad benjaminliang) 更多章節内容可以通路雲栖社群“華章計算機”公衆号檢視。

本節将會介紹sqlfire的關鍵特性,這些特性使其成為一個面向記憶體同時支援磁盤持久化的資料管理系統。sqlfire的特性如下:

伺服器分組(server group):這能夠讓你對sqlfire成員(jvm)進行邏輯分組,使其具有更好的可擴充性權重(也就是在sqlfire資料fabric的特定分區上部署更多的計算資源)。伺服器分組指明了為某個表儲存資料的sqlfire成員。你可以使用伺服器分組來對sqlfire的資料存儲進行邏輯分組,以管理表中的資料。存放資料的任意數量的sqlfire成員可以分到一個或更多的伺服器分組。當啟動sqlfire資料存儲時,你需要指明已命名的伺服器分組。

在本書中,術語data fabric和data cluster是可以替換使用的。

分區(partitioning):這項功能所描述的是在分布式系統中,将特定表中的資料分割為更小的可管理的資料塊(實際上就是跨多jvm分割資料的一種機制)。傳統的關系型資料庫管理者(dba)可能會熟悉這種機制,如果表超過了一個特定的可管理和可執行的規模,就可以使用這種機制,它會将表分割為多張表。按照類似的方式,sqlfire使用分區機制來完成類似的資料分割,不過sqlfire并不像rdbms那樣将其分割到嚴格的磁盤空間之中,而是将其分割到多個sqlfire成員jvm上(不過,它當然也能夠輸出到磁盤上,以實作額外的持久化保證)。你需要在create table語句中使用partition by子句來指明某個表的分區政策。可用的政策包括基于每行的主鍵值進行hash分區、基于非主鍵列進行hash分區、區間分區(range partitioning)以及清單分區(list partitioning)。sqlfire将分區表中的每一行映射到一個邏輯桶(bucket)中。将行映射到桶的過程基于你所指定的分區政策。例如,如果基于主鍵進行hash分區,sqlfire就會通過對表的主鍵進行hash操作來确定邏輯桶。每個桶被配置設定到一個或多個成員上,這取決于你為該表所配置的副本數量。配置分區表有一個或更多的資料備援副本能夠確定即便有一個成員出現故障,分區的資料依然是可用的。當成員出現故障或被移除時,邏輯桶會根據負載被重新分派到新的成員中。你可以使用create table語句的buckets子句來指定要使用的桶的總數。桶的預設數量是113。

備援(redundancy):該功能指定了你希望sqlfire為你管理多少份資料副本。在生産環境的系統中,通常會希望至少有一份備援。備援是分區資料在記憶體中一個額外的副本。

位置協同(colocation):例如,如果兩個表通常要聯合起來以完成一個業務查詢,那麼就可以使用位置協同機制使主資料和外鍵部分的資料放到同一個分區中,這樣的話查詢就會在一個sqlfire成員jvm中執行,該jvm中會包含所有需要的資料。

磁盤持久化(disk persistence):你可以将資料複制到磁盤上,以實作額外的彈性。

事務(transactions):在記憶體送出之時,通過樂觀鎖機制鎖定資料進而控制資料一緻性的能力。這是使sqlfire成為資料庫的關鍵特性之一。

緩存插件(cache plug-in):在緩存缺失的場景下,執行自定義業務邏輯的能力。

監聽器(listener):在vfabric sqlfire中,你可以實作任意數量的監聽器,這些監聽器的觸發可以基于應用所執行的特定sql dml操作。

writer:vfabric sqlfire writer是一個事件處理器,它會在表真正發生變化之前同步處理這些變化。緩存writer的主要用途就是執行輸入校驗。

異步監聽器(asynchronous listener):asynceventlistener執行個體會有專用的線程為其提供服務,線上程中會調用一個回調方法。與dml操作相對應的事件會放到一個内部隊列之中,專用的線程會将一批事件一次性地分發給使用者實作的回調類。

dbsynchronizer:dbsynchronizer是一個内置的asynceventlistener實作,你可以使用它異步地将資料持久化到相容jdbc 4.0的第三方資料庫中。

ddlutils:vfabric sqlfire提供了一個指令行界面和ddlutils,它能夠幫助你基于衆多支援的rdbms源模式,生成目标模式和資料加載檔案。

2.2.1 伺服器分組

伺服器分組指明了為某個表存儲資料的vfabric sqlfire成員。為了管理表中的資料,你可以使用伺服器分組實作vfabric sqlfire資料存儲的邏輯化分組。存儲資料的任意數量的vfabric sqlfire成員可以參與到一個或多個伺服器分組中。當你啟動vfabric sqlfire資料存儲時,需要指定已命名的伺服器分組。預設情況下,所有儲存資料的伺服器會被添加到default伺服器分組中。不同的邏輯資料庫模式通常由不同的伺服器分組來管理。例如,訂單管理系統可能會将所有的客戶和訂單放到一個orders模式中進行管理并部署到一個伺服器分組中。

同一個系統中可能會将派送和物流資料放到一個不同的伺服器分組中進行管理。一個端點(peer)或伺服器可以參與到多個伺服器分組中,一般的做法會将相關的資料放到一起或者在複制表中控制備援副本的數量。因為支援分組成員的動态性,是以伺服器分組中存儲資料的程序數量可以動态變化。但是,伺服器分組成員的動态性對應用開發人員進行了抽象,他們可以将伺服器分組視為一個邏輯伺服器。

伺服器分組隻是決定了表的資料由那些端和伺服器進行管理。表可以由分布式系統中的任何一個端來進行通路,也可以由連接配接到某個伺服器上的瘦用戶端通路。當觸發服務端程式時,你可以将該程式在伺服器分組中的所有成員中并行執行。這些資料感覺(data-aware)的程式也會在屬于伺服器分組端點的用戶端上執行。因為不必将表關聯到特定成員的ip位址上,是以伺服器分組的容量可以動态添加或減少,而不會影響到已有的伺服器和用戶端應用。vfabric sqlfire可以自動重平衡伺服器分組中的表到新添加的成員上。圖2-7展現了vfabric sqlfire的伺服器分組。

《大規模Java平台虛拟化與調優》——2.2 SQLFire特性

最佳實踐5:使用伺服器分組

你可以将你的伺服器指定到邏輯分組上,用戶端可以在其連接配接配置中指明邏輯分組。例如,如果伺服器的一個子集為某個特定的表存儲資料,你可能會使用分組以確定那些隻對這些表感興趣地用戶端會連接配接到這些伺服器上。或者你可以使用一個分組将所有以資料庫為中心的流量都轉移到伺服器的子集上,這一部分伺服器直接連接配接到後端的資料庫上。伺服器可以屬于多個組。用戶端隻需指明要使用哪個組,而無須知道哪個伺服器屬于哪個組。

圖2-7中的方案1展現了vfabric sqlfire資料管理系統被分為group 1和group 2。

圖2-7中的方案2展示了第三個組group 3,這個組包含了group 1和group 2。像group 3這樣的分組通常會考慮到引用資料(reference data)被資料fabric中的所有成員(group 1與group 2中的成員)所共享,而group 1與group 2有不同的資料分區。一個這樣的樣例就是股票交易資料放在group 1中,定價資料放在group 2中,而引用資料放在group 3中。

伺服器分組要與各種業務功能結合使用(如風險管理儲存在一個分組中,而庫存管理放在另外一個分組中),這樣你就能夠為不同的業務線提供不同的服務品質,這是通過為每個獨立的分組添加或移除處理功能實作的。

例如,在伺服器分組中,通常會遵循如下的最佳實踐:

引用資料可以放到group 1中,這是因為它主要是不經常發生變化的複制表資料,是以需要較少的計算資源。但是,資料快速發生變化且資料量足夠大需要進行分區的表(如交易、位置以及定價資料)可以由更強大的計算資源來進行管理,就像group 2中那樣。

借助伺服器分組,你能夠為特定的分組添加/移除處理能力。如果添加了處理能力,你必須在所有伺服器都啟動後執行一條重平衡的指令。

對于具體的存儲過程,你可以選擇并行地在伺服器分組的所有成員上執行伺服器端的存儲過程,也可以隻在特定伺服器分組的成員上執行。

在表的定義中,始終都要設定伺服器分組以指明表存在于哪個分組中。如果你不指名伺服器分組的名字,就會使用預設的分組,這個分組就會包含系統中所有的vfabric sqlfire成員。這樣表就會在整個預設伺服器分組的成員中進行分區和複制。如果某個複制表中的資料經常發生變化,在預設分組的每個伺服器上來維護副本的成本會是相當高的。在這種情況下,你可以考慮用一個更小的分組,将這種類型的資料放到特定的一組成員/主機上,将其封裝到專用的分組中。複制表隻應該用于滿足資料元素之間的多對多關系。通過設定所需的備援等級,分區表也可以按照類似raid的方式進行複制。

伺服器分組的便利性有助于為某個特定的分組設定回收政策(eviction policy)的堆百分比,是以能夠控制資源消耗。

你可以在某個特定的分組中使用asyncheventlistener。

2.2.2 分區

在後端,由rdbms模式支撐的企業級應用中,有些表在正常業務中被通路的頻率很高,分區政策能夠提升性能進而更容易滿足sla的需求。這種被經常通路的表通常被稱之為熱表(hot table),這是因為它進行資料插入、更新、删除以及讀取的頻率很高。除了高頻率的資料變化,這些熱表通常還會因為資料規模導緻在單個節點上無法對其進行管理。在這種情況下,你可以使用vfabric sqlfire的水準分區政策将資料分割到多個更小的可管理的資料分區中。

借助vfabric sqlfire的水準分區,一整行的資料會被存儲到同一個hash索引的桶(hash indexed bucket)中。桶是資料的容器,它會确定資料的存儲點、備援點以及用來實作重平衡的遷移單元。你可以基于表的主鍵來對表進行hash分區,如果表沒有主鍵的話,也可以使用内部生成的唯一行id。其他的分區政策可以在create table語句中的partition by子句中指定。vfabric sqlfire支援的政策包括基于非主鍵的列進行hash分區、區間分區以及清單分區。

圖2-8展現了一個樣例表flights,該表基于flight_id(表flights的主鍵)分區為3個桶,第一個桶中包含了行1~3,位于vfabric sqlfire伺服器1中,第二個桶中包含了行4~6,位于vfabric sqlfire伺服器2中,第三個桶中包含了行7~9,位于vfabric sqlfire伺服器3中。所有根據flight_id主鍵對行1~3的航班資料通路都會被vfabric sqlfire轉移到vfabric sqlfire伺服器1上執行,其他的行情況類似。vfabric sqlfire會通過這種桶的系統自動化地管理所有分區,設計師隻需在表的定義中提供正确的partition by column(flight_id)子句即可。

《大規模Java平台虛拟化與調優》——2.2 SQLFire特性

在圖2-9中,基于前面的讨論再看一下航班樣例的模式快照。這裡展現的模式是典型的master-detail設計模式,在大多數rdbms模式中都可以看到。圖2-9展現的航班模式包括了表flights、flightavailability和airlines。flights和flightavailability有一對多關系,而airlines與flights和flightavailability之間存在多對多關系。

表flights按照其主鍵flight_id進行分區并且redundancy值為1。這意味着,每一行flight資料都會有一份備份的副本,該副本存在于叢集中某一個備援vfabric sqlfire伺服器上。而flightavilability根據flight_id進行分區,并且colocate被設定為flights,這意味着vfabric sqlfire會管理資料分區,當出現flights和flightavilability表之間的關聯查詢時,查詢會在同一個vfabric sqlfire成員/記憶體空間中執行,進而優化了性能。airlines表中是不會經常發生變化的引用資料,與flights和flightavailability存在多對多關系。為了在vfabric sqlfire中配置這種關系,你可以使用replicate關鍵字來指定在分布式系統的每個vfabric sqlfire成員上都存有一份完整的airlines表副本。

《大規模Java平台虛拟化與調優》——2.2 SQLFire特性

最佳實踐6:水準分區

水準分區能夠極大地提升企業級應用的可擴充性和性能。

最為常見的形式是将partition by應用到表的主鍵上。

vfabric sqlfire水準分區将一整行放到相同的hash索引桶中,是以對桶中某一行的所有資料操作會在相同的記憶體空間中執行。

要經常測試你的分區模式以確定所有的vfabric sqlfire成員能夠保持平衡。

2.2.3 備援

在vfabric sqlfire中,你可以指定特定的資料要保持多少備援副本。vfabric sqlfire會管理主副本以及所有備份副本之間資料變化的同步。當一台伺服器出現故障時,試圖在故障成員中讀取和寫入資料的操作會被vfabric sqlfire自動重新路由到可用的成員中。在圖2-10 中,備援子句被聲明為redundancy 1,這表明vfabric sqlfire會将一份備援副本複制到叢集中的另一個成員之中。

最佳實踐7:備援

當你在一個vfabric sqlfire表定義中使用redundancy 1聲明時,vfabric sqlfire會在記憶體中管理資料的一份備援副本。當vfabric sqlfire成員發生故障時,vfabric sqlfire會将備份伺服器更新為主伺服器,并将所有的通路請求路由到這個新的主伺服器上,是以為企業級應用實作了持續的容錯性。

因為vfabric sqlfire是面向記憶體的資料管理平台,是以具備一定等級的備援是推薦的做法。但是,備援的等級必須要在增加備援數量所帶來的優勢和不足間保持平衡。增加備援的數量能夠形成更為健壯的系統,但也會帶來性能的損耗。額外的備援副本會導緻網絡傳輸和記憶體使用的增長,因為vfabric sqlfire會一直保持備援副本處于同步狀态。

在生産環境的系統中,redundancy 1就足夠了,尤其是聯合使用磁盤asynchronous persistence時。對于嚴重依賴vfabric sqlfire 作為資料管理系統的大多數生産環境系統來說,redundancy 1并聯合使用磁盤asynchronous persistence能夠提供很好的性能、可擴充性以及可靠性。

規劃好系統的規模大小,一旦發生故障時,剩餘的節點有足夠的處理能力承擔新資料和新的用戶端請求。

《大規模Java平台虛拟化與調優》——2.2 SQLFire特性

https://yqfile.alicdn.com/4ab8723821add0cbcd4b4b8dda1baac555f76244.png

" >

2.2.4 位置協同

如前面圖2-9所示,flights和flightavailability表之間存在父子關系。這表明flights和flightavailability這兩個表的sql聯合查詢必須要在相同的記憶體空間之中。vfabric sqlfire的colocate子句表明這兩個表是要放在一起的,也就是說,這兩個表中用于外鍵關聯的列如果具有相同的值,會被強制分區到同一個vfabric sqlfire成員之中。在圖2-11中,colocate with (flights)子句被用到了flightavai-lability上。

《大規模Java平台虛拟化與調優》——2.2 SQLFire特性

最佳實踐8:位置協同

如果你使用圖2-9中的模式進行如下的select語句查詢時,這個查詢聯合了表flight和flightavailability,在表定義時需要使用colocate with子句:

《大規模Java平台虛拟化與調優》——2.2 SQLFire特性

聯合的條件必須要在flight_id列上,因為它是flights和flightavailability表的分區列。在這種情況下,你應該使用colocate with(flights),如圖2-11所示。

在你建立分區表時,colocate with子句中所引用的表必須已經存在。當這兩個表進行分區和聯合确定位置時,兩個表中用于外鍵關聯關系的列如果具有相同的值,會被強制分區到同一個vfabric sqlfire成員之中。

兩個分區表要進行位置協同時,兩個表的create table語句中,server group子句必須是一緻的。

2.2.5 磁盤持久化

為了提供額外的資料可靠性,vfabric sqlfire可以将表資料持久化到磁盤上作為記憶體資料的備份,同時還可以将溢出的表資料存儲到磁盤上。這兩種磁盤存儲方案,也就是持久化和溢出,可以單獨使用也可以聯合使用。溢出使用磁盤存儲作為記憶體表管理的一種擴充,可以用于分區表和複制表。持久化則是存儲每個端中所管理表資料的完整備份。圖2-12展現了vfabric sqlfire的主成員和備援成員如何持久化到外部磁盤存儲之中的。

《大規模Java平台虛拟化與調優》——2.2 SQLFire特性

最佳實踐9:磁盤持久化

網關發送者、asynceventlistener以及dbsynchronizer隊列需要進行溢出處理并且可以進行持久化。

為了達到最佳的性能,将每個磁盤存儲放到一塊單獨的實體磁盤上。

對網關發送者隊列使用磁盤持久化,因為它們通常已經啟用了溢出功能,持久化這些隊列的能夠提供額外的高可用性。

表可以進行溢出處理,可以進行持久化,也可以兩者兼具。為了提升性能,要将用于存儲溢出表資料的磁盤存儲放到一塊專用的實體磁盤上。将持久化的表資料或者兼有持久化和溢出的表資料,放到不同的實體磁盤上。

當計算磁盤需求時,要考慮表的修改模式(modification pattern)以及壓縮政策(compaction strategy)。vfabric sqlfire建立的每個oplog檔案都有指定的maxlogsize。過期的dml操作隻有在壓縮的時候才會在oplog檔案中移除,是以你需要有足夠的空間來存儲兩次壓縮之間的所有操作。對于兼具更新和删除的表,如果你使用自動壓縮的話,所需磁盤空間的一個合适上限如下:(1 / (1 – (compaction_threshold / 100) ) ) * data size,在這裡,data size指的是在磁盤存儲中所有的表資料的總量。預設的compactionthreshold為50,是以磁盤空間大緻是資料量的兩倍。壓縮線程可能延後于其他操作執行,是以會導緻磁盤的使用暫時超過門檻值。如果你禁用自動壓縮的話,所需的磁盤量依賴于兩次手動壓縮之間會累計多少的過期操作。

當你啟動系統時,并行啟動所有具備持久化表的成員。為了保證一緻性和完整性,可以建立和使用啟動腳本。

使用sqlf shut-down-all指令來關閉系統。這是一個順序化的關閉過程,會完整的重新整理資料并将其安全地存儲到磁盤上。

确定檔案壓縮政策,如果需要的話,開發程式來監控你的檔案并定時執行壓縮。

為磁盤存儲确定備份政策并遵循之。你可以在系統離線的時候,通過複制檔案的方式進行備份,也可以使用sqlf指令備份線上的系統。如果你在磁盤存儲離線的時候删除或修改任何的持久化表,要考慮同步磁盤存儲中的表。

如果你要将整個資料fabric停掉,在系統關閉的時候壓縮磁盤存儲,這樣能夠使啟動的性能達到最優。

2.2.6 事務

在vfabric sqlfire中,一條或多條sql語句組成一個邏輯工作單元的場景具有事務的語義。也就是,整個工作單元可以完整地送出或復原。vfabric sqlfire的分布式事務協調機制能夠實作線性可擴充性且遵循原子性、一緻性、隔離性和持久性,也就是所謂的acid屬性。因為參與事務的vfabric sqlfire成員會保持其本身的事務狀态,對資料庫的查詢看到的總是已送出的資料,并且不需要擷取鎖。是以會在read_committed隔離級别,讀和寫可以并行運作。

在事務寫的情況下,vfabric sqlfire會在每個相關節點上鎖定要更新行的每一份副本。這減弱了對分布式鎖管理器的需求,允許有更大的可擴充性。同時,vfabric sqlfire對repeatable_read使用了特殊的讀取鎖和外鍵檢查,以確定在事務執行期間這些行不會發生變化。如果有其他活躍事務的并發寫操作正在運作導緻無法擷取鎖時,vfabric sqlfire鎖會立即出錯(fail-fast)并提示沖突異常。這種立即出錯行為的一種例外情況就是初始化事務的vfabric sqlfire節點如果同時也是存儲資料的節點的時候。在這種情況下,基于性能考慮,vfabric sqlfire會将事務分批放在本地成員中,并且隻有在送出前vfabric sqlfire重新整理批處理資料時,沖突才可能被探測到。

當資料由分區表所管理時,對于非事務性的操作,每行資料都由一個成員所持有。但是,在分布式事務中,行的所有副本都會被同等對待 ,更新操作會并行地路由到所有副本中。這樣分區表的事務性行為類似于複制表的行為。事務管理器會與vfabric sqlfire成員管理系統緊密協作來確定不管是發生失敗還是添加/移除成員,在送出時所有行的變更要麼會應用到所有副本中,要麼這些變更沒有應用到任何副本上。

例如,如圖2-13所示,初始化了一個事務來執行updateflights。在這個方法中的所有語句被視為一個邏輯單元,并且會并行地應用到系統的所有副本中。在這個例子中,vfabric sqlfire伺服器1會作為“擁有行的成員(row-owning member)”,協調事務的執行,将事務視為一個邏輯工作單元。

《大規模Java平台虛拟化與調優》——2.2 SQLFire特性

https://yqfile.alicdn.com/6e87d2e0f18cbeb130f533d5c8a1f7824b3b0a52.png

圖2-13~圖2-16描述了同一個事務的多個步驟。

圖2-14展現了圖2-13中updateflights事務的後續過程。具有updateflights事

《大規模Java平台虛拟化與調優》——2.2 SQLFire特性

https://yqfile.alicdn.com/01ff829f0ac61e52f9a3087be93e47168fbc6d60.png

務的vfabric sqlfire成員也就是擁有行的成員,會并行地分發更新指令到所有副本上。同時,該成員在執行送出之前,會等待所有的确認消息(acknowledgment,ack)到達。ack表明收到了更新操作,事務可以進入送出狀态了。

圖2-15中展現了有可能會初始化第二個事務,也就是圖中的事務2。事務2可能會與最初的事務updateflights競争相同的資料,為了避免沖突,vfabric sqlfire将會復原事務2的整個邏輯單元,進而讓最初的updateflights事務得以執行。

《大規模Java平台虛拟化與調優》——2.2 SQLFire特性

在圖2-16中,updateflights事務執行到了送出狀态,在将要結束送出狀态之前,它會在所有對應的資料副本上添加鎖,并且以一緻性的方式傳播資料的變更。vfabric sqlfire伺服器1是“持有行的成員”,因為它在事務送出過程中會作為事務的協調者,也就是擷取本地的寫入鎖并将所有的變更分發到副本上。

vfabric sqlfire沒有中心化的事務協調者。事務發起的成員在事務的持久化過程中會作為事務協調者。如果應用要更新一行或多行資料,事務的協調者會确定哪些成員會被涉及,并且會在所有行的副本上擷取本地的“寫入”鎖。在送出時,所有的變更都會應用到本地資料存儲以及所有備援的副本中。如果有另外一個并發的線程試圖修改其中的一行時,對那一行擷取本地寫入鎖時會失敗,是以那個事務會自動復原。

如果沒有涉及持久化表的操作,那就沒有必要對備援成員使用二階段送出。在這種情況下,送出會是高效的、單階段操作。不像傳統的分布式資料庫,vfabric sqlfire沒有使用寫前日志(write-ahead logging)的功能,複制或備援更新到一個或多個成員時如果發生失敗的話,這些日志會用于事務恢複。最可能出現的失敗場景就是成員處于不正常的狀态并在分布式系統中被移除掉,vfabric sqlfire能夠保證資料的一緻性。當失敗的成員重新線上時,它

《大規模Java平台虛拟化與調優》——2.2 SQLFire特性

會自動恢複複制/備援的資料集并與其他的成員建立一緻。如果有些資料的所有副本在事務送出前都出現了故障,那麼這種情況會被組成員系統探測到,事務會在所有成員上復原。

最佳實踐10:事務

vfabric sqlfire實作了樂觀的事務機制。該事務模型對位置協同(colocated)的資料進行了高度優化,這種情況下事務更新的所有行都由單個成員持有。

讓事務的持續時間以及事務所涉及的行數盡可能越少越好。vfabric sqlfire會立即(eagerly)擷取鎖,是以長時間的事務會增加沖突和事務失敗的可能性。

了解vfabric sqlfire 所提供的所有隔離級别。vfabric sqlfire 的隔離級别如下:

none:預設情況下,vfabric sqlfire的連接配接并不會參與事務,這與其他的資料庫有所不同。但是,這種預設行為并不意味着不存在任何的隔離級别,也不意味着連接配接能夠通路到其他正在處理事務的未送出狀态。如果沒有事務的話(事務的隔離被設定為none),vfabric sqlfire會確定先入先出(first in first out,fifo)的表更新一緻性。某個線程的寫入操作在送出後,所有其他的程序都能看到寫入的結果,但是不同程序的寫入操作對于其他的程序來說,看到的順序可能是不一樣的。當一個表被分區到分布式系統的多個成員之中的時候,vfabric sqlfire會在成員間均勻分布資料集,是以這些存儲表的成員都不會成為可擴充性的瓶頸。vfabric sqlfire在指定的時間内,會為特定的行(通過主鍵辨別)配置設定一個擁有者。當擁有行的成員發生故障時,行的擁有權會以一緻的方式轉移到另一個可用成員上,這樣所有的端伺服器會看到相同的行擁有者。

read_uncommitted:vfabric sqlfire内部将這種隔離級别更新為read_committed。

read_committed:vfabric sqlfire不允許正在進行中的事務以及非事務(隔離級别為none)操作讀取未送出的(髒)資料。在實作這一點時,vfabric sqlfire會将事務的變更維持在一個單獨的事務狀态中,它隻有在送出的時候才會應用到資料存儲的實際表中。

repeatable_read:vfabric sqlfire支援這種隔離級别,遵循了ansi sql标準。在事務内,對某一行兩次以上的讀取都能看到相同的列值。repeatable_read也能保證在事務持續期間,在第一次讀取後,表中送出的行就不會發生變更。vfabric sqlfire會将這種讀寫鎖應用到標明資料的副本中,這樣在事務持續期間讀取就是可重複的。vfabric sqlfire沒有使用範圍鎖(range lock),是以在這種隔離級别依然是有可能出現幻讀(phantom read)的。

vfabric sqlfire會克隆已有的行、應用更新(修改一個或多個域)然後自動使用更新過的行替換原有的行。這種機制避免了并發線程讀取和寫入行時通路到的是部分更新的資料。如果你的系統中存在事務的話,要小心規劃重平衡操作。重平衡可能會在不同的成員間轉移資料,這可能會導緻事務出現transactiondatarebalancedexception異常而失敗。在應用中要有足夠的異常處理和事務重試邏輯。

vfabric sqlfire vsd提供了事務送出、復原以及失敗的統計資訊,這樣你就能夠監控vfabric sqlfire事務了。

2.2.7 緩存插件

當分布式系統對某個具有特定辨別符的資料行的查詢請求無法滿足時,可以調用一個加載器(loader)在外部資料源中檢索資料。vfabric sqlfire會鎖定相關的行并阻止并發擷取同一行的資料,進而避免後端資料庫的負荷過載。

在圖2-17中,在分布式系統的每個vfabric sqlfire都注冊了一個rowloader。這個rowloader實作了getrow()方法,當緩存缺失時就會調用該方法。rowloader.getrow會包含必要的業務邏輯來從rdbms或外部系統檢索所需的資料。

最佳實踐11:rowloader

當為一個表配置了加載器時,對該表執行select語句時,在内部會擷取到資料并将其插入到vfabric sqlfire表中。基于這種行為模式,配置了加載器的表可以進行限制校驗(比如,加載器試圖将後端資料庫得到的資料插入到vfabric sqlfire的表中,但這些資料違反了表的限制條件)。

當實作rowloader時,要遵循rowloader接口。

《大規模Java平台虛拟化與調優》——2.2 SQLFire特性

rowloader接口由如下的方法所組成:

init(string initstr)方法:當将你的實作類注冊到表上的時候,這個方法可以用來擷取參數。當使用sys.attach_loader程式将rowloader注冊到表上時,vfabric sqlfire會調用實作類的init()方法。所有的參數會在一個string對象中傳遞進來。

getrow(string schemaname, string tablename, object[ ] primarykey)方法:每次加載器被觸發從外部資料源擷取資料時,vfabric sqlfire會調用這個方法的實作來提供模式名、表名以及主鍵值所組成的一個對象數組(按照表定義中的順序)。

在你的實作中,getrow的傳回值必須是以下幾種情況之一:

一個對象數組,其中的元素是每列的值,包括主鍵,要按照vfabric sqlfire 中表定義的順序。

null,适用于找不到元素的情況。

java.sql.resultset執行個體,可能是對另一個資料庫的查詢結果,隻會使用第一行資料。結果中的列要與vfabric sqlfire表相比對。

空的java.sql.resultset,适用于找不到元素的情況。

在編譯完rowloader實作之後,将其添加到類路徑中,通過執行内置的sys.attach_loader程式将rowloader注冊到表中。

2.2.8 監聽器

在vfabric sqlfire中,你可以實作任意數量的監聽器,這些監聽器可以在應用程式執行特定的sql dml操作時觸發。監聽器在插入/更新/删除操作之後觸發,也就是所謂的事後事件(after-event)。在圖2-18中,注冊了3個監聽器來監聽sql dml的變更。

《大規模Java平台虛拟化與調優》——2.2 SQLFire特性

各監聽器詳述如下:

updatelistener在圖2-19中以綠色文本框來顯示。當對表flights進行更新時,這個監聽器就會被觸發。flights表來源于前文圖2-9中所介紹的模式。在vfabric sqlfire中,任何使用sql dml對flights表所做的更新都會觸發updatelistener以事後事件的方式來執行。這也就是說,vfabric sqlfire中flights表的資料變更在先,然後updatelistener回調會以同步的方式執行,進而将資料變更同步到後端的傳統關系型資料庫之中。在圖2-18中,更新dml事件是通過“更新flight f22”觸發的,也就是圖中針對綠色箭頭的那個綠色訓示框所示。圖中的綠色實線箭頭表示vfabric sqlfire成員會首先發生變更,然後綠色的點線箭頭觸發updatelistener,它會作為同步的事後事件。當updatelistener觸發時,會執行監聽器的回調處理器,它會将變更通過圖中的綠色點線發送到資料庫中,那個小的文檔片段圖檔表示變更已經發送到了資料庫之中。

《大規模Java平台虛拟化與調優》——2.2 SQLFire特性

https://yqfile.alicdn.com/50e864a51df3405b26724f07c77bb9692d5e1fad.png

insertlistener和deletelistener也注冊到了flights表上,如圖2-18所示。這些監聽器的行為與前面所描述的udpatelistener類似。

在圖2-19中,如果updatelistener因為某些原因發生了阻塞,而此時變更已經應用到持有特定flights分區的vfabric sqlfire成員上了,監聽器會抛出異常并且不會完成。

最佳實踐12:監聽器

監聽器能夠讓其他的系統接收到表變更(插入、更新和删除)的事後事件通知。如果要內建企業範圍内依賴于資料變化的系統,這會是很有用的一項技術,這些資料變化都來源于資料fabric。

在有些場景下,你可能想要隻執行dml,而不觸發監聽器。你可以在每個連接配接上使用skip-listeners屬性來忽略所配置的監聽器。設定這個屬性不會影響到wan分布式系統。dml事件的發送會始終跨wan設定/網關發送者。

你可以使用内置的sys.add_listener程式将一個或多個監聽器關聯到表上。

同一個表上可以定義任意數量的監聽器。這樣有助于提供多個自定義的插入/更新/删除監聽器來滿足不同的業務需求。如果是隻允許定義一個監聽器的話,那麼事件處理代碼将會需要複雜的if/else上下文切換或很長的case語句,這會使得代碼難以維護。

如果監聽器中抛出異常的話,更新會成功,但異常會傳播回最初的節點中。

2.2.9 writer

vfabric sqlfire的writer是一個事件處理器,它會在變更發生之前同步地處理對表所作出的修改。緩存writer的主要用處是進行輸入校驗,進而保護資料庫不會存儲錯誤的資料。在資料對vfabric sqlfire的表中可見之前,可以使用這種機制來更新外部的資料源。writer為外部的資料源提供了write-through式緩存。與監聽器不同,對于某個表隻能關聯一個writer。當表要因為插入、更新和删除操作而進行修改時,vfabric sqlfire會使用回調機制通知writer,使原有的操作暫時挂起。writer可以通過抛出sqlexception進而拒絕正在進行中的變更操作。writer回調是同步執行的,如果回調阻塞的話,操作也會阻塞。

在圖2-20中,表flights上注冊了一個insertwriter。“插入flight 747”會觸發insertwriter的回調事件處理器,首先同步外部資料庫,然後再往vfabric sqlfire flights資料分區中執行插入操作。這是它與vfabric sqlfire監聽器的關鍵不同之處,監聽器是在dml已經應用到vfabric sqlfire資料分區之後再将變更傳播到外部的系統中。另外一個重要的不同點在于你可以注冊多個監聽器,但是隻能注冊一個writer。

在圖2-21中,insertwriter事件處理器在試圖往外部資料庫寫入時抛出了一個異常。在這種情況下,資料庫的整個資料變更會復原,資料變更也不會應用到vfabric sqlfire的flights資料分區之中。

《大規模Java平台虛拟化與調優》——2.2 SQLFire特性
《大規模Java平台虛拟化與調優》——2.2 SQLFire特性

最佳實踐13:writer

writer能夠讓其他的系統在表發生變更(插入、更新和删除)時接收到事前事件通知。為了保持外部db與vfabric sqlfire同步,應用于vfabric sqlfire的變更已經通過了外部系統的校驗,這是writer可以做到的一點。

你可以使用内置的sys.attach_writer程式關聯writer。

需要注意的是,一個表隻能注冊一個writer。

vfabric sqlfire安裝了一個樣例writer實作,位址是/vfabric_sqlfire_10x/examples/eventcallbackwriterimpl.javam。這個writer實作可以執行對任意jdbc資料源的寫入,隻要對應的驅動器位于類路徑下即可。

事件的類型可以分為type.before_insert、type.before_update以及type.before_delete。

2.2.10 異步監聽器

前面的讨論主要集中在監聽器和writer上,它們本質上都是同步的回調處理器。如果企業的應用程式不适合使用同步處理器的話,這可能是因為同步處理器會産生阻塞或者使用它的用戶端可靠性不佳,那麼異步處理器可以作為可行的備選方案。asynceventlistener執行個體會使用專用的線程調用其回調方法。對應于dml操作的事件會放到内部隊列中,有一個專用的線程會将一批事件分發給使用者實作的回調類。事件分發的頻率通過vfabric sqlfire中asynceventlistener的配置來管理。

在圖2-22中,在flights表上注冊了testasyncheventlistener,當dml事件發生時,就會觸發回調。dml事件會放到一個事件隊列中,然後根據所配置的周期,再從隊列中取出。當事件從隊列中取出時,會調用已注冊的testasynceventlistener.processevents()方法。

《大規模Java平台虛拟化與調優》——2.2 SQLFire特性

最佳實踐14:異步監聽器

asynceventlistener執行個體通過專用的線程來提供服務,線上程中會調用回調方法。

對應于dml操作的事件會放到内部隊列中,有一個專用的線程會将一批事件分發給使用者實作的回調類。在內建企業内的系統時,這種方式會很有用,因為這些被內建的系統可能會具有不同的可靠性以及響應時間sla。

如果多個線程同時更新的資料導緻asynceventlistener實作類排隊的話,那麼有可能會出現一定的資料一緻性問題。這說明事件并不能保證順序。

隻有對于單線程執行的情況,vfabric sqlfire才能夠保證dml語句按照順序應用于分布式系統(以及asynceventlistener隊列和遠端wan站點)。多個線程的更新會保證fifo順序。vfabric sqlfire并沒有“總體順序(total ordering)”的保證。

如果多個線程并發更新複制表的相同行,或者多線程并發地更新複制表中存在父子關系的相關行,有可能會出現資料不一緻性的問題。在複制表中并發更新相同的行可能會導緻一些副本的值與其他副本的值不一緻。并發删除父表中的行并在子表中插入一行,有可能會出現孤立無關聯的(orphaned)行。

當dml操作要在asynceventlistener或遠端wan站點上排隊時,并發通路表會遇到類似的不一緻性問題。例如,如果兩個獨立的線程并發更新父表和子表,vfabric sqlfire将這些更新在asynceventlistener或wan網關上排隊時的順序并不一定與主分布式系統的更新順序相比對。這可能會導緻在後端資料庫上違反外鍵限制(例如,使用dbsynchronizer)或者當初始的表更新時,在遠端wan并沒有發生更新。如果多個線程并發更新分區表中相同的key,并不會發生這種無序更新。但是,對于更新多行的任意操作,應用中都應該使用事務。

所有的asynceventlistener實作都應該檢查已有的資料庫連接配接是否由于之前出現的異常已經關閉。例如,在catch代碼塊中檢查connection.isclosed(),并在執行進一步操作之前,如果需要的話,重建立立連接配接。vfabric sqlfire中的dbsynchronizer實作在重用連接配接前會自動執行這種檢查。

asynceventlistener實作必須安裝到一個或多個vfabric sqlfire系統的成員中。你隻能将asynceventlistener安裝到資料存儲成員上(也就是将host-data屬性設定為true的端)。

可以将監聽器安裝到多個成員上以提供高可用性并保證事件的正常遞送,以防備帶有活躍asynceventlistener的vfabric sqlfire成員被關閉。在任意的時間點上,隻會有一個成員會包含活躍的線程來派發事件。其他成員的線程會處于備援的備用狀态。

為了實作高可用性以及事件投遞的可靠性,将事件隊列配置為支援持久化和備援。

2.2.11 dbsynchronizer

dbsynchronizer是内置的asynceventlistener實作,可以使用它同步持久化資料到第三方相容jdbc 4.0的rdbms中。你可以将dbsynchronizer作為asyncevent-listener安裝到多個資料存儲之中。在vfabric sqlfire上執行的dml語句會傳遞到dbsynchronizer以及所配置的jdbc rdbms上。dbsynchronizer隻應該安裝到資料存儲成員上(也就是将host-data屬性設定為true的成員)。每個dbsynchronizer執行個體維護了一個内部隊列來分批處理dml語句,并且會有一個專用的線程來做這件事。該線程從隊列中取出dml語句,并使用prepared statement将它們應用到外部資料庫中,如圖2-23所示。

《大規模Java平台虛拟化與調優》——2.2 SQLFire特性

最佳實踐15:dbsynchronizer

配置dbsynchronizer隊列使其支援持久化和備援,以實作高可用性和事件投遞的可靠性。要在不止一個資料存儲上安裝dbsynchronizer以保證高可用性。在任意的時間點,隻有一個成員中會有活躍的dbsynchronizer線程,它會在外部資料庫上執行dml。其他成員上的線程會作為備用(備援)來保證當含有活躍dbsynchronizer線程的成員出現故障時,dml依然能夠執行。

預設情況下,如果正在活躍的成員被關閉,在dbsynchronizer内部隊列中所有未執行的dml操作都會丢失。如果你想避免丢失操作的話,需要将dbsynchronizer的内部隊列配置為支援持久化。

由于性能和記憶體的原因,所安裝的備用dbsynchronizer不要超過一個(最多一個備援)。

如果活躍的dbsynchronizer線程出現故障時,dml操作可能會重新應用到rdbms上。如果具有活躍dbsynchronizer的成員出現故障時,恰好正在發送一批操作,此時這一批事件中有一些dml語句可能已經應用到了rdbms上面。在故障恢複時,新的dbsynchronizer線程會重新發送失敗的批處理操作并重新應用最初的dml操作。當發生這種情況時,rdbms可能會處于不同步的狀态,這取決于dml操作的特性、如何修改表的列以及是否存在列限制。

如果表定義了限制(主鍵、唯一鍵)的話,在故障恢複時,重新執行如下類型的dml操作并不會導緻不同步現象。

在具有主鍵的表上重新執行建立操作。這種情況下,會違反主鍵限制并抛出sqlexception,但是dbsynchronizer會忽略該異常。

導緻違反唯一鍵的建立或更新操作。重新執行建立和更新操作導緻重複的值,這會違反唯一性限制。dbsynchronizer會忽略這種情況下所抛出的sqlexception。

導緻違反檢查限制的建立或更新操作。重新執行建立或更新操作(如,遞增或遞減某一個列的值)可能會導緻違反檢查限制。dbsynchronizer會忽略這種情況下所抛出的sqlexception。

如果dbsynchronizer在更新或送出至資料庫時,遇到了異常,這批操作會被保留,dbsynchronizer線程會繼續嘗試執行這批操作直至成功。

如果在vfabric sqlfire系統中并發執行的dml操作是針對父子表的外鍵關聯關系,那麼将dml應用到外部資料庫時可能會出現違反外鍵限制的情況。這種現象甚至可能發生在vfabric sqlfire系統已成功執行dml的情況之中。盡管在vfabric sqlfire系統中對父子表的插入是按照順序執行的,但是插入資料的操作到達dbsynchronizer時,可能是相反的順序。為了避免這種行為,要在一個應用線程中執行父表和子表的更新。

2.2.12 sqlf指令與ddlutils

vfabric sqlfire提供了指令行界面以及ddlutils,它們可以基于衆多支援的rdbms源模式生成目标模式以及資料加載檔案。圖2-24列出了ddlutils能夠遷移的rdbms。

《大規模Java平台虛拟化與調優》——2.2 SQLFire特性