網(wǎng)上有很多關(guān)于pos機(jī)密鑰儲(chǔ)存失敗,在 Golang 中編寫基于磁盤的鍵值存儲(chǔ)的知識(shí),也有很多人為大家解答關(guān)于pos機(jī)密鑰儲(chǔ)存失敗的問(wèn)題,今天pos機(jī)之家(www.rcqwhg.com)為大家整理了關(guān)于這方面的知識(shí),讓我們一起來(lái)看下吧!
本文目錄一覽:
pos機(jī)密鑰儲(chǔ)存失敗
我一直在考慮閱讀一篇計(jì)算機(jī)科學(xué)論文,并基于它實(shí)施一個(gè)項(xiàng)目。分布式系統(tǒng)、網(wǎng)絡(luò)和數(shù)據(jù)庫(kù)是讓我著迷的一些東西。但是,我一直在尋求實(shí)施一個(gè)更平易近人的項(xiàng)目,以避免最初被淹沒(méi)。我偶然通過(guò)Avinash的項(xiàng)目:CaskDB看到了Bitcask論文。
在快速閱讀了這篇相當(dāng)短的論文后,我決定編寫一個(gè)相同的 Golang 實(shí)現(xiàn),因?yàn)樗雌饋?lái)像一個(gè)令人興奮的項(xiàng)目。如果您有興趣查看完整的項(xiàng)目,請(qǐng)查看BarrelDB。
Bitcask 是基于磁盤的鍵值存儲(chǔ)引擎,專為快速讀寫操作而設(shè)計(jì)。它主要由Riak(分布式數(shù)據(jù)庫(kù))作為存儲(chǔ)引擎之一在生產(chǎn)中使用。引擎蓋下的Bitc桶具有簡(jiǎn)單而巧妙的設(shè)計(jì)。它以僅追加模式寫入文件。這意味著僅通過(guò)附加到文件末尾來(lái)執(zhí)行寫入,從而避免了執(zhí)行任何隨機(jī)磁盤 I/O 查找的需要。
讓我們看一下Bitcask的各個(gè)組件:
記錄的格式CRC:存儲(chǔ)值的校驗(yàn)和,保證數(shù)據(jù)一致性時(shí)間戳:UNIX 格式的時(shí)間戳,存儲(chǔ)為 int32。到期:如果記錄定義了到期,則 UNIX 格式的時(shí)間戳將存儲(chǔ)為 int32。密鑰大?。好荑€的大?。ㄒ宰止?jié)為單位)值大小:值的大?。ㄒ宰止?jié)為單位)鑰匙價(jià)值與鍵/值一起存儲(chǔ)的附加元數(shù)據(jù)用固定寬度的標(biāo)頭表示。每個(gè)字段表示為 ,因此標(biāo)頭的總大小為 4*5 = 20 字節(jié)。下面是對(duì)此記錄進(jìn)行編碼和解碼的代碼:int32
type Record struct { Header Header Key string Value []byte}// Header represents the fixed width="360px",height="auto" />
Decode takes a record object decodes the binary value the buffer.func (h *Header) decode(record []byte) error { return binary.Read(bytes.NewReader(record), binary.LittleEndian, h)}記錄在存儲(chǔ)在磁盤上之前以二進(jìn)制格式編碼。
數(shù)據(jù)文件“數(shù)據(jù)文件”(用于磁盤上的數(shù)據(jù)庫(kù)文件的術(shù)語(yǔ))是所有寫入操作的僅追加記錄。Bitcask 的一個(gè)實(shí)例可以有多個(gè)數(shù)據(jù)文件。但是,只有一個(gè)“活動(dòng)”數(shù)據(jù)文件。在 BarrelDB 中,goroutine 定期在后臺(tái)運(yùn)行,以檢查活動(dòng)數(shù)據(jù)庫(kù)文件的大小是否已超過(guò)閾值,然后旋轉(zhuǎn)活動(dòng)文件。它將此數(shù)據(jù)庫(kù)文件追加到“過(guò)時(shí)”數(shù)據(jù)文件列表中。所有新的寫入只發(fā)生在“活動(dòng)”數(shù)據(jù)文件上,過(guò)時(shí)的文件作為“壓縮”過(guò)程的一部分進(jìn)行合并(稍后將在帖子中描述)。
以下是 ais 的表示方式:dataFile
type DataFile struct { sync.RWMutex writer *os.File reader *os.File id int offset int}
它包含用于寫入和讀取文件的不同處理程序。我們有 2 個(gè)文件處理程序而不是重用同一個(gè)的原因是,它們僅在“僅追加”模式下打開(kāi)。此外,由于活動(dòng)文件可以旋轉(zhuǎn),因此可以設(shè)置編寫器,確保不會(huì)在該文件上發(fā)生新的寫入。writernil
writer, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { return nil, fmt.Errorf("error opening file for writing db: %w", err) } // Create a reader for reading the db file. reader, err := os.Open(path) if err != nil { return nil, fmt.Errorf("error opening file for reading db: %w", err) }鍵目錄
除了將文件存儲(chǔ)在磁盤上之外,Bitcask 還存儲(chǔ)其他元數(shù)據(jù),這些元數(shù)據(jù)定義了如何檢索記錄。此哈希表是具有此元數(shù)據(jù)的鍵映射,稱為。這里要注意的重要一點(diǎn)是,這張地圖中從未存儲(chǔ)過(guò)。這使得Bitcask可以處理比RAM可以容納的更重要的數(shù)據(jù)集。KeyDirvalue
// KeyDir represents an in-memory hash for faster lookups of the key.// Once the key is found in the map, the additional metadata, like the offset record// and the file ID is used to extract the underlying record from the disk.// Advantage is that this approach only requires a single disk seek of the db file// since the position offset (in bytes) is already stored.type KeyDir map[string]Meta// Meta represents some additional properties for the given key.// The actual value of the key is not stored in the in-memory hashtable.type Meta struct { Timestamp int RecordSize int RecordPos int FileID int}
在這里,告訴記錄在整個(gè)文件中的位置偏移量(以字節(jié)為單位)。由于記錄的位置與密鑰一起存儲(chǔ)在內(nèi)存中,因此檢索密鑰不需要超過(guò)單個(gè)磁盤查找。Bitcask 即使數(shù)據(jù)庫(kù)中有許多密鑰,也能實(shí)現(xiàn)非常低的延遲。文件系統(tǒng)預(yù)讀緩存還有助于提高性能,并且是免費(fèi)的 - 無(wú)需設(shè)計(jì)單獨(dú)的緩存機(jī)制。RecordPos
壓 實(shí)正如我們之前看到的,數(shù)據(jù)文件只是一個(gè)僅追加的寫入序列。對(duì)鍵的任何修改都只是附加到數(shù)據(jù)文件的新記錄。KeyDir 使用包含記錄新位置的新元數(shù)據(jù)覆蓋鍵的條目。因此,所有讀取將自動(dòng)返回更新的值。
通過(guò)為密鑰寫入“邏輯刪除”記錄,以類似的方式處理刪除。當(dāng)用戶在刪除密鑰后請(qǐng)求密鑰時(shí),BarrelDB 可以檢查該值是否等于邏輯刪除值并返回相應(yīng)的錯(cuò)誤。
正如您所猜到的,如果我們不執(zhí)行任何垃圾清理,我們的數(shù)據(jù)庫(kù)將無(wú)限增長(zhǎng)。需要修剪數(shù)據(jù)文件以刪除過(guò)期/刪除的記錄并將所有過(guò)時(shí)的文件合并到單個(gè)活動(dòng)文件中 - 以控制打開(kāi)的文件數(shù)量。所有這些過(guò)程統(tǒng)稱為“壓縮”。
讓我們來(lái)看看這些壓縮例程中的每一個(gè)是如何在后臺(tái)工作的:
合并合并過(guò)程循環(huán)訪問(wèn) KeyDir 中的所有鍵并獲取其值。該值也可能來(lái)自過(guò)時(shí)的文件。更新新的鍵/值后,它會(huì)將它們寫入新的活動(dòng)文件。關(guān)閉所有舊文件處理程序,并從磁盤中刪除過(guò)時(shí)的文件。KeyDir 的更新方式類似,因?yàn)樾掠涗浳挥诓煌奈恢?文件中。
提示文件Bitcask 論文描述了一種創(chuàng)建最初加載到數(shù)據(jù)庫(kù)中的“提示”文件以加快啟動(dòng)時(shí)間的方法。此文件對(duì)于在冷啟動(dòng)后引導(dǎo) KeyDir 至關(guān)重要。這樣可以避免遍歷所有數(shù)據(jù)文件并按順序讀取其值。在 BarrelDB 中,編碼用于將地圖轉(zhuǎn)儲(chǔ)為轉(zhuǎn)儲(chǔ)。gobKeyDirgob
// generateHints encodes the contents of the in-memory hashtable// as `gob` and writes the data to a hints file.func (b *Barrel) generateHints() error { path := filepath.Join(b.opts.dir, HINTS_FILE) if err := b.keydir.Encode(path); err != nil { return err } return nil}
在啟動(dòng)期間,BarrelDB 會(huì)檢查文件是否存在,解碼此 gob 轉(zhuǎn)儲(chǔ),然后將數(shù)據(jù)加載到其中。.hintsKeyDir
刪除過(guò)期的密鑰goroutine以可配置的間隔運(yùn)行,以檢查密鑰的值是否已過(guò)期。如果有,它將從 KeyDir 中刪除該條目。在以下合并過(guò)程中,由于此條目不會(huì)出現(xiàn)在 KeyDir 中,因此在創(chuàng)建新數(shù)據(jù)文件時(shí)會(huì)自動(dòng)刪除該條目。
要檢查密鑰是否已過(guò)期,只需進(jìn)行簡(jiǎn)單的檢查,例如以 UNIX 紀(jì)元格式比較它們的時(shí)間戳,就足夠了:time.Now().Unix() > int64(r.Header.Expiry)
瑞迪斯服務(wù)器除了使用 BarrelDB 作為 Go 庫(kù)之外,我還實(shí)現(xiàn)了一個(gè)與 Redis 兼容的服務(wù)器。我發(fā)現(xiàn)tidwall/redcon是一個(gè)易于使用的庫(kù),可以為 Go 應(yīng)用程序創(chuàng)建一個(gè)與 Redis 兼容的服務(wù)器。我要做的就是包裝 BarrelDB API 方法并定義 / 的處理程序。SETGET
我能夠使用并連接到 BarrelDB 服務(wù)器:redis-cli
127.0.0.1:6379> set hello worldOK127.0.0.1:6379> get hello"world"基準(zhǔn)
可以檢查存儲(chǔ)庫(kù)中的實(shí)際基準(zhǔn)。但是,我想指出一些結(jié)果的推論。redis-benchmark
首先,讓我們使用 50 個(gè)并行客戶端向服務(wù)器發(fā)送 100000 個(gè)請(qǐng)求。此命令為每個(gè)操作創(chuàng)建一個(gè)唯一鍵。SET
redis-benchmark -p 6379 -c 50 -t set -n 100000 -r 100000000Summary: throughput summary: 145985.41 requests per second latency summary (msec): avg min p50 p95 p99 max 0.179 0.016 0.183 0.207 0.399 1.727
因此,對(duì)于基于磁盤的 KV,每秒 140k 個(gè)請(qǐng)求一點(diǎn)也不差。但這里要注意的令人興奮的事情是,即使您通過(guò)增加客戶端來(lái)增加負(fù)載,性能也是可預(yù)測(cè)的:
redis-benchmark -p 6379 -c 200 -t set -n 100000 -r 100000000Summary: throughput summary: 140845.08 requests per second latency summary (msec): avg min p50 p95 p99 max 0.718 0.224 0.711 0.927 1.183 5.775
如果我們也增加請(qǐng)求數(shù)量(5 倍),吞吐量看起來(lái)幾乎相同:
redis-benchmark -p 6379 -c 200 -t set -n 500000 -r 100000000Summary: throughput summary: 138350.86 requests per second latency summary (msec): avg min p50 p95 p99 max 0.748 0.056 0.711 0.879 1.135 63.135
這種魔力完全是因?yàn)锽itcask使用日志結(jié)構(gòu)化哈希表(只是用于寫入數(shù)據(jù)的僅附加記錄)的方式。即使有大量記錄,它所要做的就是寫入文件的末尾,從而避免任何昂貴的 I/O 操作。
總結(jié)總的來(lái)說(shuō),我對(duì)實(shí)施感到滿意,因?yàn)槲液w了論文中描述的所有內(nèi)容。這個(gè)項(xiàng)目對(duì)我來(lái)說(shuō)有很好的學(xué)習(xí)成果。我花了很多時(shí)間想出一個(gè)設(shè)計(jì),用于構(gòu)建不同的組件及其API方法,并在壓縮過(guò)程中處理所有邊緣場(chǎng)景。雖然,完全歸功于Bitcask,因?yàn)樗3至巳绱藘?yōu)雅和簡(jiǎn)約的設(shè)計(jì),但在基準(zhǔn)測(cè)試中取得了一些重要的數(shù)字。這也提醒我們,簡(jiǎn)單不一定意味著不那么強(qiáng)大。BarrelDB
我期待通過(guò)添加對(duì)通過(guò) Raft 連接的多個(gè) BarrelDB 節(jié)點(diǎn)的支持來(lái)實(shí)現(xiàn)分布式 KV 存儲(chǔ)?,F(xiàn)在,去享受一些茶并將這個(gè)項(xiàng)目發(fā)布到 WWW :)
翻譯原文: https://mrkaran.dev/posts/barreldb/?hmsr=toutiao.io&utm_campaign=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io
以上就是關(guān)于pos機(jī)密鑰儲(chǔ)存失敗,在 Golang 中編寫基于磁盤的鍵值存儲(chǔ)的知識(shí),后面我們會(huì)繼續(xù)為大家整理關(guān)于pos機(jī)密鑰儲(chǔ)存失敗的知識(shí),希望能夠幫助到大家!