網(wǎng)上有很多關(guān)于pos機密鑰儲存失敗,在 Golang 中編寫(xiě)基于磁盤(pán)的鍵值存儲的知識,也有很多人為大家解答關(guān)于pos機密鑰儲存失敗的問(wèn)題,今天pos機之家(m.xjcwpx.cn)為大家整理了關(guān)于這方面的知識,讓我們一起來(lái)看下吧!
本文目錄一覽:
pos機密鑰儲存失敗
我一直在考慮閱讀一篇計算機科學(xué)論文,并基于它實(shí)施一個(gè)項目。分布式系統、網(wǎng)絡(luò )和數據庫是讓我著(zhù)迷的一些東西。但是,我一直在尋求實(shí)施一個(gè)更平易近人的項目,以避免最初被淹沒(méi)。我偶然通過(guò)Avinash的項目:CaskDB看到了Bitcask論文。
在快速閱讀了這篇相當短的論文后,我決定編寫(xiě)一個(gè)相同的 Golang 實(shí)現,因為它看起來(lái)像一個(gè)令人興奮的項目。如果您有興趣查看完整的項目,請查看BarrelDB。
Bitcask 是基于磁盤(pán)的鍵值存儲引擎,專(zhuān)為快速讀寫(xiě)操作而設計。它主要由Riak(分布式數據庫)作為存儲引擎之一在生產(chǎn)中使用。引擎蓋下的Bitc桶具有簡(jiǎn)單而巧妙的設計。它以?xún)H追加模式寫(xiě)入文件。這意味著(zhù)僅通過(guò)附加到文件末尾來(lái)執行寫(xiě)入,從而避免了執行任何隨機磁盤(pán) I/O 查找的需要。
讓我們看一下Bitcask的各個(gè)組件:
記錄的格式CRC:存儲值的校驗和,保證數據一致性時(shí)間戳:UNIX 格式的時(shí)間戳,存儲為 int32。到期:如果記錄定義了到期,則 UNIX 格式的時(shí)間戳將存儲為 int32。密鑰大?。好荑€的大?。ㄒ宰止潪閱挝唬┲荡笮。褐档拇笮。ㄒ宰止潪閱挝唬╄€匙價(jià)值與鍵/值一起存儲的附加元數據用固定寬度的標頭表示。每個(gè)字段表示為 ,因此標頭的總大小為 4*5 = 20 字節。下面是對此記錄進(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)}記錄在存儲在磁盤(pán)上之前以二進(jìn)制格式編碼。
數據文件“數據文件”(用于磁盤(pán)上的數據庫文件的術(shù)語(yǔ))是所有寫(xiě)入操作的僅追加記錄。Bitcask 的一個(gè)實(shí)例可以有多個(gè)數據文件。但是,只有一個(gè)“活動(dòng)”數據文件。在 BarrelDB 中,goroutine 定期在后臺運行,以檢查活動(dòng)數據庫文件的大小是否已超過(guò)閾值,然后旋轉活動(dòng)文件。它將此數據庫文件追加到“過(guò)時(shí)”數據文件列表中。所有新的寫(xiě)入只發(fā)生在“活動(dòng)”數據文件上,過(guò)時(shí)的文件作為“壓縮”過(guò)程的一部分進(jìn)行合并(稍后將在帖子中描述)。
以下是 ais 的表示方式:dataFile
type DataFile struct { sync.RWMutex writer *os.File reader *os.File id int offset int}
它包含用于寫(xiě)入和讀取文件的不同處理程序。我們有 2 個(gè)文件處理程序而不是重用同一個(gè)的原因是,它們僅在“僅追加”模式下打開(kāi)。此外,由于活動(dòng)文件可以旋轉,因此可以設置編寫(xiě)器,確保不會(huì )在該文件上發(fā)生新的寫(xiě)入。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) }鍵目錄
除了將文件存儲在磁盤(pán)上之外,Bitcask 還存儲其他元數據,這些元數據定義了如何檢索記錄。此哈希表是具有此元數據的鍵映射,稱(chēng)為。這里要注意的重要一點(diǎn)是,這張地圖中從未存儲過(guò)。這使得Bitcask可以處理比RAM可以容納的更重要的數據集。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è)文件中的位置偏移量(以字節為單位)。由于記錄的位置與密鑰一起存儲在內存中,因此檢索密鑰不需要超過(guò)單個(gè)磁盤(pán)查找。Bitcask 即使數據庫中有許多密鑰,也能實(shí)現非常低的延遲。文件系統預讀緩存還有助于提高性能,并且是免費的 - 無(wú)需設計單獨的緩存機制。RecordPos
壓 實(shí)正如我們之前看到的,數據文件只是一個(gè)僅追加的寫(xiě)入序列。對鍵的任何修改都只是附加到數據文件的新記錄。KeyDir 使用包含記錄新位置的新元數據覆蓋鍵的條目。因此,所有讀取將自動(dòng)返回更新的值。
通過(guò)為密鑰寫(xiě)入“邏輯刪除”記錄,以類(lèi)似的方式處理刪除。當用戶(hù)在刪除密鑰后請求密鑰時(shí),BarrelDB 可以檢查該值是否等于邏輯刪除值并返回相應的錯誤。
正如您所猜到的,如果我們不執行任何垃圾清理,我們的數據庫將無(wú)限增長(cháng)。需要修剪數據文件以刪除過(guò)期/刪除的記錄并將所有過(guò)時(shí)的文件合并到單個(gè)活動(dòng)文件中 - 以控制打開(kāi)的文件數量。所有這些過(guò)程統稱(chēng)為“壓縮”。
讓我們來(lái)看看這些壓縮例程中的每一個(gè)是如何在后臺工作的:
合并合并過(guò)程循環(huán)訪(fǎng)問(wèn) KeyDir 中的所有鍵并獲取其值。該值也可能來(lái)自過(guò)時(shí)的文件。更新新的鍵/值后,它會(huì )將它們寫(xiě)入新的活動(dòng)文件。關(guān)閉所有舊文件處理程序,并從磁盤(pán)中刪除過(guò)時(shí)的文件。KeyDir 的更新方式類(lèi)似,因為新記錄位于不同的位置/文件中。
提示文件Bitcask 論文描述了一種創(chuàng )建最初加載到數據庫中的“提示”文件以加快啟動(dòng)時(shí)間的方法。此文件對于在冷啟動(dòng)后引導 KeyDir 至關(guān)重要。這樣可以避免遍歷所有數據文件并按順序讀取其值。在 BarrelDB 中,編碼用于將地圖轉儲為轉儲。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 轉儲,然后將數據加載到其中。.hintsKeyDir
刪除過(guò)期的密鑰goroutine以可配置的間隔運行,以檢查密鑰的值是否已過(guò)期。如果有,它將從 KeyDir 中刪除該條目。在以下合并過(guò)程中,由于此條目不會(huì )出現在 KeyDir 中,因此在創(chuàng )建新數據文件時(shí)會(huì )自動(dòng)刪除該條目。
要檢查密鑰是否已過(guò)期,只需進(jìn)行簡(jiǎn)單的檢查,例如以 UNIX 紀元格式比較它們的時(shí)間戳,就足夠了:time.Now().Unix() > int64(r.Header.Expiry)
瑞迪斯服務(wù)器除了使用 BarrelDB 作為 Go 庫之外,我還實(shí)現了一個(gè)與 Redis 兼容的服務(wù)器。我發(fā)現tidwall/redcon是一個(gè)易于使用的庫,可以為 Go 應用程序創(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"基準
可以檢查存儲庫中的實(shí)際基準。但是,我想指出一些結果的推論。redis-benchmark
首先,讓我們使用 50 個(gè)并行客戶(hù)端向服務(wù)器發(fā)送 100000 個(gè)請求。此命令為每個(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
因此,對于基于磁盤(pán)的 KV,每秒 140k 個(gè)請求一點(diǎn)也不差。但這里要注意的令人興奮的事情是,即使您通過(guò)增加客戶(hù)端來(lái)增加負載,性能也是可預測的:
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
如果我們也增加請求數量(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
這種魔力完全是因為Bitcask使用日志結構化哈希表(只是用于寫(xiě)入數據的僅附加記錄)的方式。即使有大量記錄,它所要做的就是寫(xiě)入文件的末尾,從而避免任何昂貴的 I/O 操作。
總結總的來(lái)說(shuō),我對實(shí)施感到滿(mǎn)意,因為我涵蓋了論文中描述的所有內容。這個(gè)項目對我來(lái)說(shuō)有很好的學(xué)習成果。我花了很多時(shí)間想出一個(gè)設計,用于構建不同的組件及其API方法,并在壓縮過(guò)程中處理所有邊緣場(chǎng)景。雖然,完全歸功于Bitcask,因為它保持了如此優(yōu)雅和簡(jiǎn)約的設計,但在基準測試中取得了一些重要的數字。這也提醒我們,簡(jiǎn)單不一定意味著(zhù)不那么強大。BarrelDB
我期待通過(guò)添加對通過(guò) Raft 連接的多個(gè) BarrelDB 節點(diǎn)的支持來(lái)實(shí)現分布式 KV 存儲?,F在,去享受一些茶并將這個(gè)項目發(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機密鑰儲存失敗,在 Golang 中編寫(xiě)基于磁盤(pán)的鍵值存儲的知識,后面我們會(huì )繼續為大家整理關(guān)于pos機密鑰儲存失敗的知識,希望能夠幫助到大家!
