『壹』 如何從持久化存儲中讀取數據
對於數據的持久化存儲,ios中一般提供了4種不同的機制。
1.屬性列表
2.對象歸檔
3.資料庫存儲(SQLite3)
4.蘋果公司提供的持久性工具Core Data。
其實儲存的形式無非就這么幾種,而我們還必須要關心的是,這些文件會被放置在那個文件下,然後如何讀取。
也就是說:IOS上數據存儲,我們要了解的兩點,數據存儲格式(也就是存儲機制),數據存儲位置。
1》文件如何存儲(如上面4點)
2》文件存儲在哪裡。
對於數據的操作,其實我們關心的是操作的速率。
就好比在Adnroid中偏好存儲,資料庫存儲,io存儲一樣。
『貳』 Linux裡面什麼是數據持久化
數據持久化顧名思義就是把程序中的數據以某種形式保存到某存貯介質中,以達到持久化的目的。當程序運行時,一些數據是臨時保存在內存中,一旦退出系統,這些數據就丟失了。那麼,使用某種手段將數據保存在硬碟上或者資料庫中,這樣即使退出系統後又重新啟動系統,那麼這些數據仍然可以重新找回來。
例如管理員向一個用戶管理系統中添加了一個用戶的資料,那麼這個系統需要將新添加的資料保存到資料庫中,否則系統退出或者電腦重啟後該用戶資料就會丟失。將數據從內存保存到資料庫中,這便是數據的持久化。當然,資料庫只是持久化方式中的一種,也可以保存在其他的永久存貯介質中。
圖為數據持久化的過程示意圖。
持久化(Persistence),即把數據(如內存中的對象)保存到可永久保存的存儲設備中(如磁碟)。持久化的主要應用是將內存中的對象存儲在資料庫中,或者存儲在磁碟文件中、XML數據文件中等等。
持久化是將程序數據在持久狀態和瞬時狀態間轉換的機制。
DBC就是一種持久化機制。文件IO也是一種持久化機制。
日常持久化的方法
在一定周期內保持不變就是持久化,持久化是針對時間來說的。資料庫中的數據就是持久化了的數據,只要你不去刪除或修改。比如在瀏覽器中一次Session會話中Session對象變數也是不變的,是Session容器中持久化。對象持久化的方式有很多種,根據周期不同有,page,Session,Application。對象序列化機制對於需要將對象的狀態保存到文件中,而後能夠通過讀入對象狀態來重新構造對象,恢復程序狀態. 對象序列化的過程是對象持久化的方法之一,把對象保存到文件中。
簡單的理解持久化可以在二個層面:應用層和系統層、
應用層
如果關閉(shutdown)你的應用然後重新啟動則先前的數據依然存在。
系統層
如果關閉(shutdown)你的系統(電腦)然後重新啟動則先前的數據依然存在。
持久化是一種對象服務實現至少3個介面
,就是把內存中的對象保存到外存中,讓以後能夠取回。需要實現至少3個介面:
void Save(object o) 把一個對象保存到外存中
Object Load(object oid) 通過對象標識從外存中取回對象
boolExists(object oid) 檢查外存中是否存在某個對象.
類似概念序列化
我們先跳開一下,看看另一個類似的有用概念:序列化Serialize也是一種對象服務,就是把內存中的對象序列化成流、或者把流反序列化成對象。需要實現2個介面:
void Serialize(Stream stream,object o) 把對象序列化到流中
object Deserialize(Stream stream) 把流反序列化成對象
序列化和持久化很相似,有些人甚至混為一談,其實還是有區別的,序列化是為了解決對象的傳輸問題,傳輸可以在線程之間、進程之間、內存外存之間、主機之間進行。我之所以在這里提到序列化,是因為我們可以利用序列化來輔助持久化,可以說凡是可以持久化的對象都可以序列化,因為序列化相對容易一些(也不是很容易),所以主流的軟體基礎設施,比如.net和java,已經把序列化的框架完成了。
持久化方案可以分為關系資料庫方案、文件方案、對象資料庫方案、xml資料庫方案
現今主流的持久化方案是關系資料庫方案,
關系資料庫方案不僅解決了並發的問題,更重要的是,關系資料庫還提供了持久化服務之外的價值:統計分析功能。剛才我說到,凡是可以序列化的對象都可以持久化,極端的說,我們可以只建立一個表Object(OID,Bytes),但基本上沒有人這么做,因為一旦這樣,我們就失去了關系資料庫額外的統計分析功能。關系資料庫和面向對象之間有一條鴻溝,因為二者模式不匹配,所以就存在一個OR映射問題。
Redis支持兩種數據持久化方式:rdb方式和aof方式。前者會根據配置的規則定時將內存中的數據持久化到硬碟上,後者則是在每次執行寫命令之後將命令記錄下來。兩種持久化方式可以單獨使用,但是通常會將兩者結合使用。
1、RDB方式
RDB方式的持久化是通過快照的方式完成的。當符合某種規則時,會將內存中的數據全量生成一份副本存儲到硬碟上,這個過程稱作」快照」,redis默認開啟該持久化功能,具體配置如下:
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename mp.rdb
#文件名稱
dir ./
#rdb文件存放路徑
配置後系統會自動進行快照,save 60 10000表示60秒內有10000次寫入,那麼就會調用bgsave
除了系統自動進行快照外,我們也可以手動執行SAVE或BGSAVE命令主動進行快照操作:
執行SAVE或BGSAVE命令
執行FLUSHALL命令
2、AOF方式
在使用Redis存儲非臨時數據時,一般都需要打開AOF持久化來降低進程終止導致的數據丟失,AOF可以將Redis執行的每一條寫命令追加到硬碟文件中,這一過程會降低Redis的性能。
默認情況下,Redis沒有開啟AOF(append only file)持久化功能,可以通過在配置文件中作如下配置啟用:
appendonly no #是否開啟aof,開啟時將no改為yes
appendfilename "appendonly.aof" 持久化文件名稱
auto-aof-rewrite-percentage 100
#當前AOF文件大小是上次日誌重寫得到AOF文件大小的二倍時,自動啟動新的日誌重寫過程。
auto-aof-rewrite-min-size 64mb
#當前AOF文件啟動新的日誌重寫過程的最小值,避免剛剛啟動Reids時由於文件尺寸較小導致頻繁的重寫。
appendfsync :everysec (推薦配置)
#持久化策略
always (同步持久化,每次發生數據變更會被立即記錄到磁碟,性能差但數據完整性比較好)
everysec (非同步操作,每秒記錄,如果一秒鍾內宕機,有數據丟失)
no (將緩存回寫的策略交給系統,linux 默認是30秒將緩沖區的數據回寫硬碟的)
一般來說可以考慮同時使用兩種持久化方案.
『叄』 Redis持久化
Redis支持RDB和AOF兩種持久化機制,持久化功能有效地避免因進程退出造成的數據丟失問題,當下次重啟時利用之前持久化的文件即可實現數據恢復。理解掌握持久化機制對於Redis運維非常重要。本章內容如下:
·首先介紹RDB、AOF的配置和運行流程,以及控制持久化的相關命令,如bgsave和bgrewriteaof。
·其次對常見持久化問題進行分析定位和優化。
·最後結合Redis常見 的單機多實例部署場景進行優化。
5.1RDB
RDB持久化是把當前進程數據生成快照保存到硬碟的過程,觸發RDB持久化過程分為手動觸發和自動觸發。
5.1.1觸發機制
手動觸發分別對應save和bgsave命令:
·save命令:阻塞當前Redis伺服器,直到RDB過程完成為止,對於內存比較大的實例會造成長時間阻塞,線上環境不建議使用。運行save命令對應
的Redis日誌如下:
* DB saved on disk
·bgsave命令:Redis進程執行fork操作創建子進程,RDB持久化過程由子進程負責,完成後自動結束。阻塞只發生在fork階段,一般時間很短。運行bgsave命令對應的Redis日誌如下:
* Background saving started by pid 3151
* DB saved on disk
* RDB: 0 MB of memory used by -on-write
* Background saving terminated with success
顯然bgsave命令是針對save阻塞問題做的優化。因此Redis內部所有的涉及RDB的操作都採用bgsave的方式,而save命令已經廢棄。
除了執行命令手動觸發之外,Redis內部還存在自動觸發RDB的持久化機制,例如以下場景:
1)使用save相關配置,如「save m n」。表示m秒內數據集存在n次修改時,自動觸發bgsave。
2)如果從節點執行全量復制操作,主節點自動執行bgsave生成RDB文件並發送給從節點,更多細節見6.3節介紹的復制原理。
3)執行debug reload命令重新載入Redis時,也會自動觸發save操作。
4)默認情況下執行shutdown命令時,如果沒有開啟AOF持久化功能則自動執行bgsave。
5.1.2流程說明
bgsave是主流的觸發RDB持久化方式,下面根據圖5-1了解它的運作流程。
1)執行bgsave命令,Redis父進程判斷當前是否存在正在執行的子進程,如RDB/AOF子進程,如果存在bgsave命令直接返回。
2)父進程執行fork操作創建子進程,fork操作過程中父進程會阻塞,通過info stats命令查看latest_fork_usec選項,可以獲取最近一個fork操作的耗時,單位為微秒。
3)父進程fork完成後,bgsave命令返回「Background saving started」信息並不再阻塞父進程,可以繼續響應其他命令。
4)子進程創建RDB文件,根據父進程內存生成臨時快照文件,完成後對原有文件進行原子替換。執行lastsave命令可以獲取最後一次生成RDB的時間,對應info統計的rdb_last_save_time選項。
5)進程發送信號給父進程表示完成,父進程更新統計信息,具體見info Persistence下的rdb_*相關選項。
5.1.3RDB文件的處理
保存:RDB文件保存在dir配置指定的目錄下,文件名通過dbfilename配置指定。可以通過執行config set dir{newDir}和config setdbfilename{newFileName}運行期動態執行,當下次運行時RDB文件會保存到新目錄。
運維提示
當遇到壞盤或磁碟寫滿等情況時,可以通過config set dir{newDir}在線修改文件路徑到可用的磁碟路徑,之後執行bgsave進行磁碟切換,同樣適用於AOF持久化文件。
壓縮:Redis默認採用LZF演算法對生成的RDB文件做壓縮處理,壓縮後的文件遠遠小於內存大小,默認開啟,可以通過參數config set rdbcompression{yes|no}動態修改。
運維提示
雖然壓縮RDB會消耗CPU,但可大幅降低文件的體積,方便保存到硬碟或通過網路發送給從節點,因此線上建議開啟。
校驗:如果Redis載入損壞的RDB文件時拒絕啟動,並列印如下日誌:
# Short read or OOM loading DB. Unrecoverable error, aborting now.
這時可以使用Redis提供的redis-check-mp工具檢測RDB文件並獲取對應的錯誤報告。
5.1.4RDB的優缺點
RDB的優點:
·RDB是一個緊湊壓縮的二進制文件,代表Redis在某個時間點上的數據快照。非常適用於備份,全量復制等場景。比如每6小時執行bgsave備份,並把RDB文件拷貝到遠程機器或者文件系統中(如hdfs),用於災難恢復。
·Redis載入RDB恢復數據遠遠快於AOF的方式。
RDB的缺點:
·RDB方式數據沒辦法做到實時持久化/秒級持久化。因為bgsave每次運行都要執行fork操作創建子進程,屬於重量級操作,頻繁執行成本過高。
·RDB文件使用特定二進制格式保存,Redis版本演進過程中有多個格式的RDB版本,存在老版本Redis服務無法兼容新版RDB格式的問題。針對RDB不適合實時持久化的問題,Redis提供了AOF持久化方式來解決。
5.2AOF
AOF(append only file)持久化:以獨立日誌的方式記錄每次寫命令,重啟時再重新執行AOF文件中的命令達到恢復數據的目的。AOF的主要作用是解決了數據持久化的實時性,目前已經是Redis持久化的主流方式。理解掌握好AOF持久化機制對我們兼顧數據安全性和性能非常有幫助。
5.2.1使用AOF
開啟AOF功能需要設置配置:appendonly yes,默認不開啟。AOF文件名通過appendfilename配置設置,默認文件名是appendonly.aof。保存路徑同RDB持久化方式一致,通過dir配置指定。AOF的工作流程操作:命令寫入(append)、文件同步(sync)、文件重寫(rewrite)、重啟載入(load),如圖5-2所示。
1)所有的寫入命令會追加到aof_buf(緩沖區)中。
2)AOF緩沖區根據對應的策略向硬碟做同步操作。
3)隨著AOF文件越來越大,需要定期對AOF文件進行重寫,達到壓縮的目的。
4)當Redis伺服器重啟時,可以載入AOF文件進行數據恢復。了解AOF工作流程之後,下面針對每個步驟做詳細介紹。
5.2.2命令寫入
AOF命令寫入的內容直接是文本協議格式。例如set hello world這條命令,在AOF緩沖區會追加如下文本:*3\r\n$3\r\nset\r\n$5\r\nhello\r\n$5\r\nworld\r\n
Redis協議格式具體說明見4.1客戶端協議小節,這里不再贅述,下面介
紹關於AOF的兩個疑惑:
1)AOF為什麼直接採用文本協議格式?可能的理由如下:
·文本協議具有很好的兼容性。
·開啟AOF後,所有寫入命令都包含追加操作,直接採用協議格式,避免了二次處理開銷。
·文本協議具有可讀性,方便直接修改和處理。
2)AOF為什麼把命令追加到aof_buf中?Redis使用單線程響應命令,如果每次寫AOF文件命令都直接追加到硬碟,那麼性能完全取決於當前硬碟負載。先寫入緩沖區aof_buf中,還有另一個好處Redis可以提供多種緩沖區同步硬碟的策略,在性能和安全性方面做出平衡。
5.2.3文件同步
Redis提供了多種AOF緩沖區同步文件策略,由參數appendfsync控制,不同值的含義如表5-1所示。
表5-1AOF緩沖區同步文件策略
系統調用write和fsync說明:
·write操作會觸發延遲寫(delayed write)機制。Linux在內核提供頁緩沖區用來提高硬碟IO性能。write操作在寫入系統緩沖區後直接返回。同步硬碟操作依賴於系統調度機制,例如:緩沖區頁空間寫滿或達到特定時間周期。同步文件之前,如果此時系統故障宕機,緩沖區內數據將丟失。
·fsync針對單個文件操作(比如AOF文件),做強制硬碟同步,fsync將阻塞直到寫入硬碟完成後返回,保證了數據持久化。除了write、fsync,Linux還提供了sync、fdatasync操作,具體API說明參
見:http://linux.die.net/man/2/write,http://linux.die.net/man/2/fsync,http://linux.die.net/man/2/sync
·配置為always時,每次寫入都要同步AOF文件,在一般的SATA硬碟上,Redis只能支持大約幾百TPS寫入,顯然跟Redis高性能特性背道而馳,不建議配置。
·配置為no,由於操作系統每次同步AOF文件的周期不可控,而且會加大每次同步硬碟的數據量,雖然提升了性能,但數據安全性無法保證。
·配置為everysec,是建議的同步策略,也是默認配置,做到兼顧性能和數據安全性。理論上只有在系統突然宕機的情況下丟失1秒的數據。(嚴格來說最多丟失1秒數據是不準確的,5.3節會做具體介紹到。)
5.2.4重寫機制
隨著命令不斷寫入AOF,文件會越來越大,為了解決這個問題,Redis引入AOF重寫機制壓縮文件體積。AOF文件重寫是把Redis進程內的數據轉化為寫命令同步到新AOF文件的過程。
重寫後的AOF文件為什麼可以變小?有如下原因:
1)進程內已經超時的數據不再寫入文件。
2)舊的AOF文件含有無效命令,如del key1、hdel key2、srem keys、set
a111、set a222等。重寫使用進程內數據直接生成,這樣新的AOF文件只保留最終數據的寫入命令。
3)多條寫命令可以合並為一個,如:lpush list a、lpush list b、lpush list c可以轉化為:lpush list a b c。為了防止單條命令過大造成客戶端緩沖區溢出,對於list、set、hash、zset等類型操作,以64個元素為界拆分為多條。
AOF重寫降低了文件佔用空間,除此之外,另一個目的是:更小的AOF文件可以更快地被Redis載入。AOF重寫過程可以手動觸發和自動觸發:
·手動觸發:直接調用bgrewriteaof命令。
·自動觸發:根據auto-aof-rewrite-min-size和auto-aof-rewrite-percentage參數確定自動觸發時機。
·auto-aof-rewrite-min-size:表示運行AOF重寫時文件最小體積,默認為64MB。
·auto-aof-rewrite-percentage:代表當前AOF文件空間(aof_current_size)和上一次重寫後AOF文件空間(aof_base_size)的比值。自動觸發時機=aof_current_size>auto-aof-rewrite-min-size&&(aof_current_size-aof_base_size)/aof_base_size>=auto-aof-rewrite-percentage其中aof_current_size和aof_base_size可以在info Persistence統計信息中查看。當觸發AOF重寫時,內部做了哪些事呢?下面結合圖5-3介紹它的運行流程。
圖5-3AOF重寫運作流程
流程說明:
1)執行AOF重寫請求。
如果當前進程正在執行AOF重寫,請求不執行並返回如下響應:
ERR Background append only file rewriting already in progress
如果當前進程正在執行bgsave操作,重寫命令延遲到bgsave完成之後再執行,返回如下響應:
Background append only file rewriting scheled
2)父進程執行fork創建子進程,開銷等同於bgsave過程。
3.1)主進程fork操作完成後,繼續響應其他命令。所有修改命令依然寫入AOF緩沖區並根據appendfsync策略同步到硬碟,保證原有AOF機制正確性。
3.2)由於fork操作運用寫時復制技術,子進程只能共享fork操作時的內存數據。由於父進程依然響應命令,Redis使用「AOF重寫緩沖區」保存這部分新數據,防止新AOF文件生成期間丟失這部分數據。
4)子進程根據內存快照,按照命令合並規則寫入到新的AOF文件。每次批量寫入硬碟數據量由配置aof-rewrite-incremental-fsync控制,默認為32MB,防止單次刷盤數據過多造成硬碟阻塞。
5.1)新AOF文件寫入完成後,子進程發送信號給父進程,父進程更新統計信息,具體見info persistence下的aof_*相關統計。
5.2)父進程把AOF重寫緩沖區的數據寫入到新的AOF文件。
5.3)使用新AOF文件替換老文件,完成AOF重寫。
5.2.5重啟載入
AOF和RDB文件都可以用於伺服器重啟時的數據恢復。如圖5-4所示,表示Redis持久化文件載入流程。
流程說明:
1)AOF持久化開啟且存在AOF文件時,優先載入AOF文件,列印如下日誌:
* DB loaded from append only file: 5.841 seconds
2)AOF關閉或者AOF文件不存在時,載入RDB文件,列印如下日誌:
* DB loaded from disk: 5.586 seconds
3)載入AOF/RDB文件成功後,Redis啟動成功。
4)AOF/RDB文件存在錯誤時,Redis啟動失敗並列印錯誤信息。
5.2.6文件校驗
載入損壞的AOF文件時會拒絕啟動,並列印如下日誌:
# Bad file format reading the append only file: make a backup of your AOF file,
then use ./redis-check-aof --fix <filename>
運維提示
對於錯誤格式的AOF文件,先進行備份,然後採用redis-check-aof--fix命令進行修復,修復後使用diff-u對比數據的差異,找出丟失的數據,有些可以人工修改補全。
AOF文件可能存在結尾不完整的情況,比如機器突然掉電導致AOF尾部文件命令寫入不全。Redis為我們提供了aof-load-truncated配置來兼容這種情況,默認開啟。載入AOF時,當遇到此問題時會忽略並繼續啟動,同時列印
如下警告日誌:
# !!! Warning: short read while loading the AOF file !!!
# !!! Truncating the AOF at offset 397856725 !!!
# AOF loaded anyway because aof-load-truncated is enabled
5.3問題定位與優化
Redis持久化功能一直是影響Redis性能的高發地,本節我們結合常見的持久化問題進行分析定位和優化。
5.3.1fork操作
當Redis做RDB或AOF重寫時,一個必不可少的操作就是執行fork操作創建子進程,對於大多數操作系統來說fork是個重量級錯誤。雖然fork創建的子進程不需要拷貝父進程的物理內存空間,但是會復制父進程的空間內存頁表。例如對於10GB的Redis進程,需要復制大約20MB的內存頁表,因此fork操作耗時跟進程總內存量息息相關,如果使用虛擬化技術,特別是Xen虛擬機,fork操作會更耗時。
fork耗時問題定位:對於高流量的Redis實例OPS可達5萬以上,如果fork操作耗時在秒級別將拖Redis幾萬條命令執行,對線上應用延遲影響非常明顯。正常情況下fork耗時應該是每GB消耗20毫秒左右。可以在info stats統計中查latest_fork_usec指標獲取最近一次fork操作耗時,單位微秒。
如何改善fork操作的耗時:
1)優先使用物理機或者高效支持fork操作的虛擬化技術,避免使用Xen。
2)控制Redis實例最大可用內存,fork耗時跟內存量成正比,線上建議每個Redis實例內存控制在10GB以內。
3)合理配置Linux內存分配策略,避免物理內存不足導致fork失敗,具體細節見12.1節「Linux配置優化」。
4)降低fork操作的頻率,如適度放寬AOF自動觸發時機,避免不必要的全量復制等。
5.3.2子進程開銷監控和優化
子進程負責AOF或者RDB文件的重寫,它的運行過程主要涉及CPU、內存、硬碟三部分的消耗。
1.CPU
·CPU開銷分析。子進程負責把進程內的數據分批寫入文件,這個過程屬於CPU密集操作,通常子進程對單核CPU利用率接近90%.
·CPU消耗優化。Redis是CPU密集型服務,不要做綁定單核CPU操作。由於子進程非常消耗CPU,會和父進程產生單核資源競爭。不要和其他CPU密集型服務部署在一起,造成CPU過度競爭。如果部署多個Redis實例,盡量保證同一時刻只有一個子進程執行重寫工作,具體細節見5.4節多實例部署」。
2.內存
·內存消耗分析。子進程通過fork操作產生,佔用內存大小等同於父進程,理論上需要兩倍的內存來完成持久化操作,但Linux有寫時復制機制(-on-write)。父子進程會共享相同的物理內存頁,當父進程處理寫請求時會把要修改的頁創建副本,而子進程在fork操作過程中共享整個父進程內存快照。
·內存消耗監控。RDB重寫時,Redis日誌輸出容如下:
* Background saving started by pid 7692
* DB saved on disk
* RDB: 5 MB of memory used by -on-write
* Background saving terminated with success
如果重寫過程中存在內存修改操作,父進程負責創建所修改內存頁的副本,從日誌中可以看出這部分內存消耗了5MB,可以等價認為RDB重寫消耗了5MB的內存。
AOF重寫時,Redis日誌輸出容如下:
* Background append only file rewriting started by pid 8937
* AOF rewrite child asks to stop sending diffs.
* Parent agreed to stop sending diffs. Finalizing AOF...
* Concatenating 0.00 MB of AOF diff received from parent.
* SYNC append only file rewrite performed
* AOF rewrite: 53 MB of memory used by -on-write
* Background AOF rewrite terminated with success
* Resial parent diff successfully flushed to the rewritten AOF (1.49 MB)
* Background AOF rewrite finished successfully
父進程維護頁副本消耗同RDB重寫過程類似,不同之處在於AOF重寫需要AOF重寫緩沖區,因此根據以上日誌可以預估內存消耗為:53MB+1.49MB,也就是AOF重寫時子進程消耗的內存量。
運維提示
編寫shell腳本根據Redis日誌可快速定位子進程重寫期間內存過度消耗情況。
內存消耗優化:
1)同CPU優化一樣,如果部署多個Redis實例,盡量保證同一時刻只有一個子進程在工作。
2)避免在大量寫入時做子進程重寫操作,這樣將導致父進程維護大量頁副本,造成內存消耗。Linux kernel在2.6.38內核增加了Transparent Huge Pages(THP),支持huge page(2MB)的頁分配,默認開啟。當開啟時可以降低fork創建子進程的速度,但執行fork之後,如果開啟THP,復制頁單位從原來4KB變為2MB,會大幅增加重寫期間父進程內存消耗。建議設置「sudo echo never>/sys/kernel/mm/transparent_hugepage/enabled」關閉THP。更多THP細節和配置見12.1Linux配置優化」。
3.硬碟
·硬碟開銷分析。子進程主要職責是把AOF或者RDB文件寫入硬碟持久化。勢必造成硬碟寫入壓力。根據Redis重寫AOF/RDB的數據量,結合系統工具如sar、iostat、iotop等,可分析出重寫期間硬碟負載情況。·硬碟開銷優化。優化方法如下:
a)不要和其他高硬碟負載的服務部署在一起。如:存儲服務、消息隊列服務等。
b)AOF重寫時會消耗大量硬碟IO,可以開啟配置no-appendfsync-on-rewrite,默認關閉。表示在AOF重寫期間不做fsync操作。
c)當開啟AOF功能的Redis用於高流量寫入場景時,如果使用普通機械磁碟,寫入吞吐一般在100MB/s左右,這時Redis實例的瓶頸主要在AOF同步硬碟上。
d)對於單機配置多個Redis實例的情況,可以配置不同實例分盤存儲AOF文件,分攤硬碟寫入壓力。運維提示
配置no-appendfsync-on-rewrite=yes時,在極端情況下可能丟失整個AOF重寫期間的數據,需要根據數據安全性決定是否配置。
5.3.3AOF追加阻塞
當開啟AOF持久化時,常用的同步硬碟的策略是everysec,用於平衡性能和數據安全性。對於這種方式,Redis使用另一條線程每秒執行fsync同步硬碟。當系統硬碟資源繁忙時,會造成Redis主線程阻塞,如圖5-5所示。
阻塞流程分析:
1)主線程負責寫入AOF緩沖區。
2)AOF線程負責每秒執行一次同步磁碟操作,並記錄最近一次同步時間。
3)主線程負責對比上次AOF同步時間:
·如果距上次同步成功時間在2秒內,主線程直接返回。
·如果距上次同步成功時間超過2秒,主線程將會阻塞,直到同步操作完成。
通過對AOF阻塞流程可以發現兩個問題:
1)everysec配置最多可能丟失2秒數據,不是1秒。
2)如果系統fsync緩慢,將會導致Redis主線程阻塞影響效率。
AOF阻塞問題定位:
1)發生AOF阻塞時,Redis輸出如下日誌,用於記錄AOF fsync阻塞導致拖慢Redis服務的行為:
Asynchronous AOF fsync is taking too long (disk is busy). Writing the AOF buffer
without waiting for fsync to complete, this may slow down Redis
2)每當發生AOF追加阻塞事件發生時,在info Persistence統計中,aof_delayed_fsync指標會累加,查看這個指標方便定位AOF阻塞問題。
3)AOF同步最多允許2秒的延遲,當延遲發生時說明硬碟存在高負載問題,可以通過監控工具如iotop,定位消耗硬碟IO資源的進程。優化AOF追加阻塞問題主要是優化系統硬碟負載,優化方式見上一節。
5.4多實例部署
Redis單線程架構導致無法充分利用CPU多核特性,通常的做法是在一台機器上部署多個Redis實例。當多個實例開啟AOF重寫後,彼此之間會產生對CPU和IO的競爭。本節主要介紹針對這種場景的分析和優化。上一節介紹了持久化相關的子進程開銷。對於單機多Redis部署,如果同一時刻運行多個子進程,對當前系統影響將非常明顯,因此需要採用一種措施,把子進程工作進行隔離。Redis在info Persistence中為我們提供了監控子進程運行狀況的度量指標,如表5-2所示。
我們基於以上指標,可以通過外部程序輪詢控制AOF重寫操作的執行,整個過程如圖5-6所示。
流程說明:
1)外部程序定時輪詢監控機器(machine)上所有Redis實例。
2)對於開啟AOF的實例,查看(aof_current_size-aof_base_size)/aof_base_size確認增長率。
3)當增長率超過特定閾值(如100%),執行bgrewriteaof命令手動觸發當前實例的AOF重寫。
4)運行期間循環檢查aof_rewrite_in_progress和aof_current_rewrite_time_sec指標,直到AOF重寫結束。
5)確認實例AOF重寫完成後,再檢查其他實例並重復2)~4)步操作。從而保證機器內每個Redis實例AOF重寫串列化執行。
5.5本章重點回顧
1)Redis提供了兩種持久化方式:RDB和AOF。
2)RDB使用一次性生成內存快照的方式,產生的文件緊湊壓縮比更高,因此讀取RDB恢復速度更快。由於每次生成RDB開銷較大,無法做到實時持久化,一般用於數據冷備和復制傳輸。
3)save命令會阻塞主線程不建議使用,bgsave命令通過fork操作創建子進程生成RDB避免阻塞。
4)AOF通過追加寫命令到文件實現持久化,通過appendfsync參數可以控制實時/秒級持久化。因為需要不斷追加寫命令,所以AOF文件體積逐漸變大,需要定期執行重寫操作來降低文件體積。
5)AOF重寫可以通過auto-aof-rewrite-min-size和auto-aof-rewrite-percentage參數控制自動觸發,也可以使用bgrewriteaof命令手動觸發。
6)子進程執行期間使用-on-write機制與父進程共享內存,避免內存消耗翻倍。AOF重寫期間還需要維護重寫緩沖區,保存新的寫入命令避免數據丟失。
7)持久化阻塞主線程場景有:fork阻塞和AOF追加阻塞。fork阻塞時間跟內存量和系統有關,AOF追加阻塞說明硬碟資源緊張。
8)單機下部署多個實例時,為了防止出現多個子進程執行重寫操作,建議做隔離控制,避免CPU和IO資源競爭。
『肆』 數據持久化方案解析(八) —— UIDocument的數據存儲(一)
首先看下框架基本信息
使用 UIDocument 及其底層架構的應用程序可為其文檔帶來許多好處:
在 Model-View-Controller 設計模式中, UIDocument 對象是模型對象或模型控制器對象 - 它管理文檔的數據或共同構成文檔數據的聚合模型對象。您通常將其與視圖控制器配對,該視圖控制器管理顯示文檔內容的視圖。 UIDocument 不支持管理文檔視圖。
基於文檔的應用程序包括可以生成多個文檔的應用程序,每個文檔都有自己的文件系統位置。基於文檔的應用程序必須為其文檔創建 UIDocument 的子類。有關詳細信息,請參閱下面的 Subclassing Notes 。
UIDocument 體系結構中文檔的主要屬性是其文件URL。 通過調用 initWithFileURL: 初始化文檔子類的實例時,必須傳遞在應用程序沙箱中查找文檔文件的文件 URL 。 UIDocument 從文件URL確定文件類型(與文件擴展名關聯的統一類型標識符)和文檔名稱(文件名組件)。 您可以覆蓋 fileType 和 localizedName 屬性的訪問器方法以提供不同的值。
以下概述了典型 document 的生命周期(有關實現細節,請參閱 Subclassing Notes ):
典型的基於文檔的應用程序在主線程上調用 openWithCompletionHandler: , closeWithCompletionHandler: 和 saveToURL:forSaveOperation:completionHandler: 。當這些方法啟動的讀取或保存操作結束時,完成處理程序塊在調用該方法的同一調度隊列上執行,允許您根據讀取或保存操作完成任何任務。如果操作不成功,則將 NO 傳遞到完成 - 處理 (completion-hander) 程序塊。
UIDocument 類採用 NSFilePresenter 協議。當另一個客戶端嘗試讀取基於 UIDocument 的應用程序的文檔時,該讀取將暫停,直到 UIDocument 對象有機會保存對該文檔所做的任何更改。
雖然有些實現什麼都不做,但 UIDocument 實現了所有 NSFilePresenter 方法。具體來說, UIDocument :
在您的 UIDocument 子類中,如果重寫 NSFilePresenter 方法,則始終可以調用超類實現 (super) 。
每個基於文檔的應用程序必須創建 UIDocument 的子類,其實例表示其文檔。大多數應用程序的子類化要求很簡單:
contentsForType:error: 和 loadFromContents:ofType:error: 通常在主隊列上調用方法。進一步來說:
如果您對讀取和寫入 contentsForType:error: 和 loadFromContents:ofType:error: 方法的文檔數據有特殊要求,則可以重寫 UIDocument 類的其他方法。有關這些要求和方法的討論,請參閱 Advanced Overrides 。
要啟用 UIDocument 的自動保存功能,您必須在用戶更改文檔時通知它。 UIDocument 定期檢查 hasUnsavedChanges 方法是否返回 YES ; 如果是,則啟動文檔的保存操作。
在 UIDocument 子類中實現更改跟蹤有兩種主要方法:
UIDocument 對象在其生命周期中的任何時刻都具有特定狀態。您可以通過查詢 documentState 屬性來檢查當前狀態,並通過觀察 通知獲得有關更改的通知。
如果為 iCloud 啟用了文檔,則檢查是否存在沖突版本並嘗試解決沖突非常重要。通過偵聽 通知然後檢查文檔狀態是否為 UIDocumentStateInConflict 來執行此操作。此狀態表示文檔存在沖突版本,您可以通過調用 NSFileVersion 類方法 : 來訪問該文檔,並傳入文檔的文件URL。如果您無需用戶交互即可正確解決沖突,請執行此操作。否則,離散地通知用戶存在沖突並讓他們選擇如何解決沖突。可能的方法包括:
除了指示文件間沖突之外,文檔狀態可以指示錯誤。例如, UIDocumentStateClosed 表示讀取時出錯, UIDocumentStateSavingError 表示保存或還原文檔時出錯。通過傳遞給 openWithCompletionHandler: , closeWithCompletionHandler: , revertToContentsOfURL:completionHandler: 和 saveToURL:forSaveOperation:completionHandler: 方法的完成處理程序的 success 參數,通知您的應用程序讀取和寫入錯誤。
您可以通過調用或實現 handleError:userInteractionPermitted: 方法來處理錯誤;此方法由 openWithCompletionHandler 的默認實現調用和 saveToURL:forSaveOperation:completionHandler: 分別在 UIDocument 對象遇到讀取或寫入錯誤時的方法。您可以通過通知用戶來處理讀取,保存和還原錯誤,如果情況允許,則嘗試從錯誤中恢復。
請務必閱讀 contentsForType:error: 方法的說明,以獲取有關處理文檔保存期間遇到的錯誤的指導。
如果應用程序對讀取或寫入文檔數據有特殊要求,它可以覆蓋除 loadFromContents:ofType:error: 和 contentsForType:error: 之外的 UIDocument 方法。這些要求通常包括以下內容:
如果覆蓋大多數這些方法,請注意所有文檔數據的讀取和寫入必須在後台隊列上完成,並且必須與其他嘗試讀取和寫入同一文檔文件相協調。因此,您通常應該將超類實現 (super) 作為覆蓋的一部分來調用,如果調用其他 UIDocument 方法,則通常應該在傳入 : 方法調用的塊中調用它們。閱讀方法描述以獲取詳細信息。
如果通過覆蓋相關的訪問器方法來覆蓋任何文檔屬性屬性(在 Accessing Document Attributes 下列出),請注意 UIKit 框架可以在後台線程上調用這些訪問器方法。 因此,您的重寫實現必須是線程安全的。
返回使用其文件系統位置初始化的文檔對象。
『伍』 k8s etcd 與持久化存儲
1、是什麼
2、etcd架構及工作原理
(1) 數據流程
一個用戶的請求發送過來,會經過HTTP Server轉發給store進行具體事務處理,如果涉及到節點的修改,則需要交給raft模塊進行狀態的變更,日誌的記錄,然後再同步給別的etcd節點確認數據提交,最後進行數據提交,再次同步
(2)工作原理
Etcd使用 Raft協議 來維護集群內各個節點狀態的 一致性 。簡單說,ETCD集群是一個分布式系統,由多個節點相互通信構成整體對外服務, 每個節點都存儲了完整的數據 ,並且通過Raft協議保證每個節點維護的數據是一致的
(3) 主要組成部分
(4)etcd集群中的術語
3、k8s中的etcd
(1)etcd在k8s中的作用: etcd在kubernetes集群是用來存放數據並通知變動的
(2)為什麼k8s選擇etcd:
PV 目前支持的類型包括:gcePersistentDisk 、AWSElasticBlockStore 、AzureFile 、AzureDisk 、FC ( Fibre Channel ) 、Flocker、NFS 、iSCSI 、RBD (Rados Block Device )、CephFS 、Cinder、GlusterFS 、V sphere Volume 、Quobyte Volumes 、VMware Photon 、Portwonc
Volumes 、ScaleIO Volumes 和HostPath (僅供單機測試)。
如果某個Pod 想申請某種類型的PY ,則首先需要定義一個PersistentVolurneClaim ( PVC )對象,然後,在Pod 的Volume 定義中引用上述PVC 即可:
『陸』 如何在java里長期存儲數據 不要資料庫的那種
長期存儲數據,即把數據(如內存中的)保存到可永久保存的存儲設備中(如硬碟、U盤),也就是人們常說的持久化。
常用持久化的方案有資料庫、XML文件和文件存儲。
資料庫是按照數據結構來存儲和管理數據的倉庫,後文不再做詳細介紹。
XML是可擴展標記語言,最早是為了簡化Internet的文檔數據傳輸,它提供統一的語法格式來描述數據的結構,通常XML文件用於一些少量且無特殊類型要求的文本存儲。示例代碼使用W3C標準的介面生成XML:
importjava.io.FileOutputStream;
importjava.io.PrintWriter;
importjavax.xml.parsers.DocumentBuilderFactory;
importjavax.xml.transform.OutputKeys;
importjavax.xml.transform.Transformer;
importjavax.xml.transform.TransformerFactory;
importjavax.xml.transform.dom.DOMSource;
importjavax.xml.transform.stream.StreamResult;
importorg.w3c.dom.Document;
importorg.w3c.dom.Element;
publicclass${
publicstaticvoidmain(String[]args)throwsException{
Documentdocument=DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
//創建根節點為students的XML文件
Elementstudents=document.createElement("students");
document.appendChild(students);
//在根節點下創建一個子節點學生
Elementstudent=document.createElement("student");
students.appendChild(student);
//創建節點學生姓名,值為張三
Elementname=document.createElement("name");
name.appendChild(document.createTextNode("張三"));
student.appendChild(name);
//創建節點學生年齡,值為18
Elementage=document.createElement("age");
age.appendChild(document.createTextNode("18"));
student.appendChild(age);
//創建節點學生編號,值為150101
Elementnumber=document.createElement("number");
number.appendChild(document.createTextNode("150101"));
student.appendChild(number);
//在根節點下創建第二個子節點學生
student=document.createElement("student");
students.appendChild(student);
//創建節點學生姓名,值為李四
name=document.createElement("name");
name.appendChild(document.createTextNode("李四"));
student.appendChild(name);
//創建節點學生年齡,值為20
age=document.createElement("age");
age.appendChild(document.createTextNode("20"));
student.appendChild(age);
//創建節點學生編號,值為150102
number=document.createElement("number");
number.appendChild(document.createTextNode("150102"));
student.appendChild(number);
//將XML文件保存到硬碟
Transformertransformer=TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.ENCODING,"utf-8");
transformer.setOutputProperty(OutputKeys.INDENT,"yes");
PrintWriterwriter=newPrintWriter(newFileOutputStream("/home/test.xml"));
transformer.transform(newDOMSource(document),newStreamResult(writer));
}
}
無論是資料庫還是XML文件,它們都使用了能讓數據快速方便進出的標准規范。其它文件如propeties、json,都可以使用類似XML的方式來打包數據,然後通過Java豐富的io流介面保存到磁碟中。
『柒』 什麼是數據持久化為什麼要持久化
數據持久化就是將內存中的數據模型轉換為存儲模型,以及將存儲模型轉換為內存中的數據模型的統稱. 數據模型可以是任何數據結構或對象模型,存儲模型可以是關系模型、XML、二進制流等。cmp和Hibernate只是對象模型到關系模型之間轉換的不同實現。
數據持久化對象的基本操作有:保存、更新、刪除、查詢等。
Hibernate框架中數據持久化機制:
在業務程序與資料庫之間,Hibernate框架使用Session會話,來完成數據的提交、更新、刪除、查詢等等。
1、向資料庫提交數據
在程序中保存對象時,會把數據保存到Session會話中,然後根據框架的配置文件,自動或手動決定什麼時候把這種保存提交到資料庫。
2、從資料庫中查詢數據
在查詢數據之前,需要清理緩存(手動清理,或者通過配置文件框架自動清理)清理緩存的目的是為了使Session會話中的數據與資料庫中的數據保持一致。然後程序只需要查詢Session會話中的數據即可。
(7)具有返回值的持久化存儲模塊擴展閱讀:
使用數據持久化有以下好處:
1、程序代碼重用性強,即使更換資料庫,只需要更改配置文件,不必重寫程序代碼。
2、業務邏輯代碼可讀性強,在代碼中不會有大量的SQL語言,提高程序的可讀性。
3、持久化技術可以自動優化,以減少對資料庫的訪問量,提高程序運行效率。
『捌』 JPA2.0對於javaEE的進步在哪兒
JavaEE 5平台引入了Java持久化API(Java Persistence API,JPA),它為Java EE和Java SE應用程序提供了一個基於POJO的持久化模塊。JPA處理關系數據與Java對象之間的映射,它使對象/關系(O/R)映射標准化,JPA已經被廣泛採用,已經成為事實上的O/R持久化企業標准。
Java EE 6帶來了JPA的最新版本 — JSR 317:Java持久化2.0,JPA 2.0帶來了許多新特性和增強,包括:
1、對象/關系映射增強;
2、Java持久化查詢語言增強;
3、一種新的基於標準的查詢API;
4、支持悲觀鎖定。
對象/關系映射增強
JPA 1.0支持集合的映射,但這些集合只能包含實體,JPA 2.0增加了集合映射的基礎數據類型,如String和Integer,以及嵌入式對象的集合。JPA中的嵌入式對象是一個不能存在於它自身的對象,而是作為父對象的一部分存在,即它的數據不是存在於它自己的表中,而是嵌入在父對象的表中。
JPA 2.0增加了兩個支持新的集合映射的註解:@ElementCollection 和 @CollectionTable。使用@ElementCollection註解指定集合的嵌入式對象,這些集合是獨立存儲 href=」http://storage.it168.com/」 target=_blank>存儲在集合表中的,使用@CollectionTable註解指定集合表的詳細信息,如它包含的列。
下面是一個嵌入式類,表示了車輛的訪問服務,它存儲了訪問的日期,描述和費用,此外,車輛可以配備一或多個可選功能,每個功能是FeatureType類型的一個枚舉值。
publicenumFeatureType { AC, CRUISE, PWR, BLUETOOTH, TV, … }
@Embeddable
publicclassServiceVisit {
@Temporal(DATE)
@Column(name=」SVC_DATE」)
Date serviceDate;
String workDesc;
intcost;
}
枚舉值和嵌入式對象可以在一個表示車輛服務歷史的實體中使用,如:
@Entity
public
class
Vehicle {
@Id
int
vin;
@ElementCollection
@CollectionTable
(name=」VEH_OPTNS」)
. @Column(name=」FEAT」) Set<FeatureType> optionalFeatures;
@ElementCollection
@CollectionTable(name=」VEH_SVC」)
@OrderBy(「serviceDate」)
List<ServiceVisit>
serviceHistory;
… }
Vehicle實體中的第一對註解@ElementCollection 和 @CollectionTable指定FeatureType值存儲在VEH_OPTNS集合表中,第二對註解@ElementCollection 和 @CollectionTable指定ServiceVisit嵌入式對象存儲在VEH_SVC集合表中。
雖然在例子中沒有顯示,@ElementCollection註解有兩個屬性:targetClass 和 fetch。targetClass屬性指定基礎類或嵌入式類的類名,如果欄位或屬性是使用泛型定義的,那這兩個屬性是可選的,上面這個例子就是這樣。Fetch屬性是可選的,它指定集合是延後檢索還是立即檢索,使用javax.persistence.FetchType常量,值分別用LAZY和EAGER,默認情況下,集合是延後匹配的。
JPA 2.0中還有其它許多關於對象/關系映射的增強,例如,JPA 2.0支持嵌套式嵌入,關系嵌入和有序列表,也增加了新的註解增強映射功能,通過@Access註解更靈活地支持特定的訪問類型,更多用於實體關系的映射選項,如對單向一對多關系的外鍵映射支持,通過@MapsId註解支持派生身份,支持孤體刪除。
Java持久化查詢語言增強
JPA 1.0定義了一個廣泛的Java持久化查詢語言,使用它你可以查詢實體和它們的持久化狀態。JPA 2.0對JPQL做了大量改進,如現在可以在查詢中使用case表達式。在下面的查詢中,如果雇員的評分為1,則通過乘以1.1對雇員的薪水進行了增長,如果評分為2,則乘以1.05,其它評分則乘以1.01。
UPDATE
Employeee
SETe.salary
=
CASEWHENe.rating =1THENe.salary *
1.1
WHENe.rating =2THENe.salary *
1.05
ELSEe.salary *
1.01
END
JPA 2.0也為JPQL增加了大量新的運算符,如NULLIF和COALESCE,當資料庫使用其它非空數據解碼時,NULLIF運算符是非常有用的,使用NULLIF,你可以在查詢中將這些值轉換為空值,如果參數等於NULLIF,NULLIF會返回空值,否則返回第一個參數的值。
假設薪水數據保存在employee表中,數據類型為整數,卻掉的薪水解碼為-9999,下面的查詢返回薪水的平均值,為了正確地忽略卻掉的薪水,查詢使用NULLIF將-9999轉換為空值。
SELECTAVG(NULLIF(e.salary, -
99999
))
FROM
Employeee
COALESCE運算符接收一串參數,從列表中返回第一個非空值,相當於下面的case表達式:
CASEWHENvalue1 ISNOTNULL
THEN
value1
WHENvalue2 ISNOTNULL
THEN
value2
WHENvalue3 ISNOTNULL
THEN
value3
…
ELSENULL
END
COALESCE運算符接收一串參數,從列表中返回第一個非空值,相當於下面的case表達式:
SELECTName,
COALESCE
(e.work_phone, e.home_phone) phone
FROM
Employeee
假設employee表包括一個辦公電話號碼和家庭電話號碼列,無電話號碼的列使用空值表示。下面的查詢返回每個雇員的姓名和電話號碼,COALESCE運算符指定查詢返回辦公電話號碼,但如果為空,則返回家庭電話號碼,如果兩者都為空,則返回一個空值。
JPA 2.0向JPQL增加的其它運算符是INDEX,TYPE,KEY,VALUE和ENTRY。INDEX運算符指定查詢時的排序順序,TYPE運算符選擇一個實體的類型,將查詢限制到一或多個實體類型,KEY,VALUE和ENTRY運算符是JPA 2.0中的泛化映射功能的一部分。使用KEY運算符提取映射鍵,VALUE運算符提取映射值,ENTRY運算符選擇一個映射實體。
此外,JPA 2.0增加了選擇列表、以及集合值參數和非多態查詢中運算符的支持。
標準的API
JPA 2.0中引入的另一個重要特性是標準的API,利用這個API可以動態地構建基於對象的查詢,本質上,這個標准API等價於面向對象的JPQL,使用它,你可以使用基於對象的方法創建查詢,不再需要JPQL使用的字元串操作。
標准API是基於元模型的,元模型是一個提供了架構級關於持久化單元託管類的元數據的抽象模型, 元模型讓你構建強類型的查詢,它也允許你瀏覽持久化單元的邏輯結構。
通常,一個註解處理器使用元模型生成靜態元模型類,這些類提供持久化狀態和持久化單元託管類的關系,但你可以對靜態元模型類編碼。下面是一個實體實例:
@Entity
public
class Employee {
@Id
Long
Id;
String firstName;
String lastName;
Department dept; }
下面是對應的靜態元模型類:
importjavax.persistence.meta,model.SingularAttribute;
import
javax.persistence.meta,model.StaticMetamodel;
@Generated(「EclipseLink JPA 2.0 Canonical Model Generation」
@StaticMetamodel(Employee.
class
)
public
class
Employee_ {
publicstatic
volatile
SingularAttribute<Employee, Long> id;
publicstatic
volatile
SingularAttribute<Employee, String> firstName;
publicstatic
volatile
SingularAttribute<Employee, String> lastName;
publicstatic
volatile
SingularAttribute<Employee, Department> dept;
}
此外,JPA 2.0元模型API允許你動態訪問元模型,因此當你使用標准API時,既可以靜態訪問元模型類,也可以動態訪問元模型類。標准API提供了更好的靈活性,既可以使用基於對象的方法,又可以使用基於字元串的方法導航元模型,這意味著你有四種使用標准API的方法:
1、靜態使用元模型類
2、靜態使用字元串
3、動態使用元模型
4、動態使用字元串
無論你使用哪種方法,通過構造一個CriteriaQuery對象定義一個基於標准API的查詢時,使用一個工廠對象CriteriaBuilder構造CriteriaQuery,可以從EntityManager 或 EntityManagerFactory類中獲得CriteriaBuilder。下面的代碼構造一個CriteriaQuery對象:
EntityManager em = … ;
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Customer> cq = cb.createQuery(Customer.
class
);
注意CriteriaQuery對象是泛型類型,使用CriteriaBuilder 的createQuery方法創建一個CriteriaQuery,並為查詢結果指定類型。在這個例子中,createQuery 方法的Employee.class參數指定查詢結果類型是Employee。CriteriaQuery對象和創建它們的方法是強類型的。
接下來,為CriteriaQuery對象指定一個或多個查詢源,查詢源表示查詢基於的實體。你創建一個查詢源,然後使用AbstractQuery介面的from()方法將其添加到查詢。AbstractQuery介面是眾多介面中的一員,如CriteriaQuery,From和root,它們都定義在標准API中。CriteriaQuery介面繼承AbstractQuery介面的屬性。
from()方法的參數是實體類或EntityType實體的實例,from()方法的結果是一個Root對象,Root介面擴展From介面,它表示某個查詢的from子句中可能出現的對象。
下面的代碼增加一個查詢源到CriteriaQuery對象:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Employee> cq = cb.createQuery(Employee.
class
);
Root<Employee> emp = cq.from(Employee.
class
);
當你向CriteriaQuery對象添加一個或多個查詢源後,你訪問元模型,然後構造一個查詢表達式,你如何做取決於你是以靜態方式提交查詢還是以動態方式提交查詢,以及是使用元模型還是字元串導航元模型。下面是一個使用元模型類靜態查詢的例子:
cq.select(emp);
cq.where(cb.equal(emp.get(Employee_.lastName), 「Smith」));
TypedQuery<Employee> query = em.createQuery(cq);
List<Employee> rows = query.getResultList();
CriteriaQuery介面的select() 和 where()方法指定查詢結果返回的選擇項目。
注意,你使用EntityManager創建查詢時,可以在輸入中指定一個CriteriaQuery對象,它返回一個TypedQuery,它是JPA 2.0引入javax.persistence.Query介面的一個擴展,TypedQuery介面知道它返回的類型。
在元模型術語中,Employee_是對應於Employee實體類的規范化元模型類,一個規范化元模型類遵循JPA 2.0規范中描述的某些規則。例如,元模型類的名字來源於託管類,一般都是在託管類名字後面追加一個下劃線「_」。一個規范化元模型是一個包含靜態元模型類的元模型,這個靜態元模型對應於實體,映射的超類,以及持久化單元中的嵌入式類。實際上,這個查詢使用了規范化元模型。下面是一個完整的查詢:
EntityManager em = … ;
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Employee> cq = cb.createQuery(Employee.
class
);
Root<Employee> emp = cq.from(Employee.
class
);
cq.select(emp);
cq.where(cb.equal(emp.get(Employee_.lastName), 「Smith」));
TypedQuery<Employee> query = em.createQuery(cq);
List<Employee> rows = query.getResultList();
下面是使用元模型API查詢的動態版本:
EntityManager em = … ;
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Employee> cq = cb.createQuery(Employee.
class
);
Root<Employee> emp = cq.from(Employee.
class
);
EntityType<Employee> emp_ = emp.getModel();
cq.select(emp);
cq.where(cb.equal(emp.get(emp_.getSingularAttribute(「lastName」, String.
class
)),」Smith」));
TypedQuery<Employee> query=em.createQuery(cq);
List<Employee> rows=query.getResultList();
使用元模型API的標准查詢提供了與使用規范化元模型相同的類型,但它比基於規范化元模型的查詢更冗長。
Root的getModel()方法返回根對應的元模型實體,它也允許運行時訪問在Employee實體中聲明的持久化屬性。
getSingularAttribute()方法是一個元模型API方法,它返回一個持久化的單值屬性或欄位,在這個例子中,它返回值為Smith 的lastName屬性。下面是使用字元串的元數據導航查詢的靜態版本:
EntityManager em = … ;
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Employee> cq = cb.createQuery(Employee.
class
);
Root<Employee> emp = cq.from(Employee.
class
);
cq.select(emp);
cq.where(cb.equal(emp.get(「lastName」), 「Smith」));
TypedQuery query = em.createQuery(cq);
List <Employee>rows = query.getResultList();
這個基於字元串的方法要相對容易使用些,但卻失去了元模型具有的類型安全href=」http://safe.it168.com/」 target=_blank>安全。
支持悲觀鎖
鎖是處理資料庫事務並發的一種技術,當兩個或更多資料庫事務並發地訪問相同數據時,鎖可以保證同一時間只有一個事務可以修改數據。
鎖的方法通常有兩種:樂觀鎖和悲觀鎖。樂觀鎖認為多個並發事務之間很少出現沖突,也就是說不會經常出現同一時間讀取或修改相同數據,在樂觀鎖中,其目標是讓並發事務自由地同時得到處理,而不是發現或預防沖突。兩個事務在同一時刻可以訪問相同的數據,但為了預防沖突,需要對數據執行一次檢查,檢查自上次讀取數據以來發生的任何變化。
悲觀鎖認為事務會經常發生沖突,在悲觀鎖中,讀取數據的事務會鎖定數據,在前面的事務提交之前,其它事務都不能修改數據。
JPA 1.0隻支持樂觀鎖,你可以使用EntityManager類的lock()方法指定鎖模式的值,可以是READ或WRITE,如:
EntityManager em = … ;em.lock (p1, READ);
對於READ鎖模式,JPA實體管理器在事務提交前都會鎖定實體,檢查實體的版本屬性確定實體自上次被讀取以來是否有更新,如果版本屬性被更新了,實體管理器會拋出一個OptimisticLockException異常,並回滾事務。
對於WRITE鎖模式,實體管理器執行和READ鎖模式相同的樂觀鎖操作,但它也會更新實體的版本列。
JPA 2.0增加了6種新的鎖模式,其中兩個是樂觀鎖。JPA 2.0也允許悲觀鎖,並增加了3種悲觀鎖,第6種鎖模式是無鎖。
下面是新增的兩個樂觀鎖模式:
1、OPTIMISTIC:它和READ鎖模式相同,JPA 2.0仍然支持READ鎖模式,但明確指出在新應用程序中推薦使用OPTIMISTIC。
2、OPTIMISTIC_FORCE_INCREMENT:它和WRITE鎖模式相同,JPA 2.0仍然支持WRITE鎖模式,但明確指出在新應用程序中推薦使用OPTIMISTIC_FORCE_INCREMENT。
下面是新增的三個悲觀鎖模式:
1、PESSIMISTIC_READ:只要事務讀實體,實體管理器就鎖定實體,直到事務完成鎖才會解開,當你想使用重復讀語義查詢數據時使用這種鎖模式,換句話說就是,當你想確保數據在連續讀期間不被修改,這種鎖模式不會阻礙其它事務讀取數據。
2、PESSIMISTIC_WRITE:只要事務更新實體,實體管理器就會鎖定實體,這種鎖模式強制嘗試修改實體數據的事務串列化,當多個並發更新事務出現更新失敗幾率較高時使用這種鎖模式。
3、PESSIMISTIC_FORCE_INCREMENT:當事務讀實體時,實體管理器就鎖定實體,當事務結束時會增加實體的版本屬性,即使實體沒有修改。
你也可以指定新的鎖模式NONE,在這種情況下表示沒有鎖發生。
JPA 2.0也提供了多種方法為實體指定鎖模式,你可以使用EntityManager的lock() 和 find()方法指定鎖模式。此外,EntityManager.refresh()方法可以恢復實體實例的狀態。
下面的代碼顯示了使用PESSIMISTIC_WRITE鎖模式的悲觀鎖:
// read
Part p = em.find(Part.
class
, pId);
// lock and refresh before update
em.refresh(p, PESSIMISTIC_WRITE);
int
pAmount = p.getAmount();
p.setAmount(pAmount - uCount);
在這個例子中,它首先讀取一些數據,然後應用PESSIMISTIC_WRITE鎖,在更新數據之前調用EntityManager.refresh()方法,當事務更新實體時,PESSIMISTIC_WRITE鎖鎖定實體,其它事務就不能更新相同的實體,直到前面的事務提交。
『玖』 android中數據持久化方式有哪些
android中數據持久化方式有以下五種:
1、Shared Preferences
Store private primitive data in key-value pairs。
2、Internal Storage
Store private data on the device memory,把數據持久化存儲到手機內部存儲空間,它主要用於私有數據存儲。
3、External Storage
Store public data on the shared external storage,把數據持久化存儲到手機外部SD卡中,它主要用於非隱秘數據存儲。
4、SQLite Databases
Store structured data in a private database。
5、Network Connection
Store data on the web with your own network server,
Android provides a way for you to expose even your private data to other applications — with a content provider. A content provider is an optional component that exposes read/write access to your application data, subject to whatever restrictions you want to impose. For more information about using content providers, see the Content Providers documentation。
『拾』 Redis的持久化機制 (RDB&amp;AOF&amp;混合模式)
RDB(Redis DataBase,快照方式) 是將某一個時刻的內存數據,以二進制的方式寫入磁碟。 AOF(Append Only File,文件追加方式) 是指將所有的操作命令,以文本的形式追加到文件中。
RDB
RDB 默認的保存文件為 mp.rdb,優點是以二進制存儲的,因此 佔用的空間更小 、數據存儲更緊湊,並且與 AOF 相比,RDB 具備 更快的重啟恢復能力 。
AOF
AOF 默認的保存文件為 appendonly.aof,它的優點是存儲頻率更高,因此 丟失數據的風險就越低 ,並且 AOF 並不是以二進制存儲的,所以它的存儲信息更易懂。缺點是 佔用空間大 , 重啟之後的數據恢復速度比較慢 。
混合
在 Redis 4.0 就推出了混合持久化的功能。Redis 混合持久化的存儲模式是, 開始的數據以 RDB 的格式進行存儲 ,因此只會佔用少量的空間, 並且之後的命令會以 AOF 的方式進行數據追加 ,這樣就可以減低數據丟失的風險,同時可以提高數據恢復的速度。
Fork
Redis會單獨創建(fork)一個子進程來進行持久化,會先將數據寫入到一個臨時文件中,待持久化過程都結束了,再用這個臨時文件替換上次持久化好的文件。Fork的作用是復制一個與當前進程一樣的進程。新進程的所有數據(變數、環境變數、程序計數器等)數值都和原進程一致,但是是一個全新的進程,並作為原進程的子進程。
AOF採用文件追加方式,文件會越來越大為避免出現此種情況,新增了重寫機制,當AOF文件的大小超過所設定的閾值時( 默認值 64M ),Redis就會啟動AOF文件的內容壓縮,只保留可以恢復數據的最小指令集。Redis 會fork出一條新進程來將 文件重寫Rewrite (也是先寫臨時文件最後再rename),遍歷新進程的內存中數據,每條記錄有一條的set語句。重寫aof文件的操作,並沒有讀取舊的aof文件, 而是將整個內存中的資料庫內容用命令的方式重寫了一個新的aof文件,這點和快照有點類似。